diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index ef38b88c9b3..243c7970454 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -14175,6 +14175,34 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti public void testTwoTypeConstructors() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/recursiveTypes/twoTypeConstructors.kt"); } + + @Nested + @TestMetadata("compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes") + @TestDataPath("$PROJECT_ROOT") + public class SelfTypes { + @Test + public void testAllFilesPresentInSelfTypes() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @Test + @TestMetadata("basicInferenceForImplicitSelfType.kt") + public void testBasicInferenceForImplicitSelfType() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/basicInferenceForImplicitSelfType.kt"); + } + + @Test + @TestMetadata("recursiveTypeWithTwoTypeParams.kt") + public void testRecursiveTypeWithTwoTypeParams() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/recursiveTypeWithTwoTypeParams.kt"); + } + + @Test + @TestMetadata("writerAppenderExampleRecursive.kt") + public void testWriterAppenderExampleRecursive() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.kt"); + } + } } @Nested diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index 8ec97696058..70bcbb00f95 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -14175,6 +14175,34 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac public void testTwoTypeConstructors() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/recursiveTypes/twoTypeConstructors.kt"); } + + @Nested + @TestMetadata("compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes") + @TestDataPath("$PROJECT_ROOT") + public class SelfTypes { + @Test + public void testAllFilesPresentInSelfTypes() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @Test + @TestMetadata("basicInferenceForImplicitSelfType.kt") + public void testBasicInferenceForImplicitSelfType() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/basicInferenceForImplicitSelfType.kt"); + } + + @Test + @TestMetadata("recursiveTypeWithTwoTypeParams.kt") + public void testRecursiveTypeWithTwoTypeParams() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/recursiveTypeWithTwoTypeParams.kt"); + } + + @Test + @TestMetadata("writerAppenderExampleRecursive.kt") + public void testWriterAppenderExampleRecursive() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.kt"); + } + } } @Nested diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt index cbd0cb02edb..e642300118b 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt @@ -293,17 +293,51 @@ class KotlinConstraintSystemCompleter( val variableWithConstraints = notFixedTypeVariables.getValue(variableForFixation.variable) - if (variableForFixation.hasProperConstraint) { - fixVariable(this, variableWithConstraints, topLevelAtoms) - return true - } else { - processVariableWhenNotEnoughInformation(this, variableWithConstraints, topLevelAtoms, diagnosticsHolder) + when { + variableForFixation.hasProperConstraint -> { + fixVariable(this, variableWithConstraints, topLevelAtoms) + return true + } + + hasDeclaredUpperBoundSelfTypes(variableWithConstraints) -> { + fixVariable(this, variableWithConstraints, topLevelAtoms) + return true + } + + else -> processVariableWhenNotEnoughInformation(this, variableWithConstraints, topLevelAtoms, diagnosticsHolder) } } return false } + private fun ConstraintSystemCompletionContext.hasDeclaredUpperBoundSelfTypes(variable: VariableWithConstraints): Boolean { + val constraints = variable.constraints + return constraints.isNotEmpty() && constraints.all { + it.position.from is DeclaredUpperBoundConstraintPosition<*> && isSelfType(it.type.typeConstructor()) + } + } + + private fun ConstraintSystemCompletionContext.isSelfType(typeConstructor: TypeConstructorMarker): Boolean { + if (typeConstructor.isTypeParameterTypeConstructor()) { + val supertype = typeConstructor.supertypes().firstOrNull() ?: return false + return isSelfType(supertype.typeConstructor()) + } + + val parametersCount = typeConstructor.parametersCount() + if (parametersCount == 0) return false + + for (parameterId in 0 until parametersCount) { + val parameter = typeConstructor.getParameter(parameterId) + if (parameter.upperBoundCount() != 1) return false + + val upperBound = parameter.getUpperBound(0) + if (upperBound.typeConstructor() == typeConstructor) return true + } + + return false + } + private fun processVariableWhenNotEnoughInformation( c: ConstraintSystemCompletionContext, variableWithConstraints: VariableWithConstraints, diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/typeParameterBounds.kt b/compiler/testData/diagnostics/tests/generics/projectionsScope/typeParameterBounds.kt index cbb6a8249de..dd6ce679c4e 100644 --- a/compiler/testData/diagnostics/tests/generics/projectionsScope/typeParameterBounds.kt +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/typeParameterBounds.kt @@ -12,15 +12,15 @@ class A { } fun foo2(a: A, b: A) { - a.foo1(Out()) - a.foo1<Out>(Out()) + a.foo1(Out()) + a.foo1>(Out()) a.foo1(Out()) a.foo1(Out()) - a.foo2(Inv()) - a.foo2(Inv()) - a.foo2<Inv>(Inv()) + a.foo2(Inv()) + a.foo2(Inv()) + a.foo2>(Inv()) a.foo3(In()) a.foo3(In()) @@ -30,13 +30,13 @@ fun foo2(a: A, b: A) { b.foo1(Out()) b.foo1>(Out()) - b.foo2(Inv()) - b.foo2(Inv()) - b.foo2<Inv>(Inv()) + b.foo2(Inv()) + b.foo2(Inv()) + b.foo2>(Inv()) - b.foo3(In()) - b.foo3<In>(In()) + b.foo3(In()) + b.foo3>(In()) b.foo3(In()) b.foo3(In()) diff --git a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/basicInferenceForImplicitSelfType.fir.kt b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/basicInferenceForImplicitSelfType.fir.kt new file mode 100644 index 00000000000..905f12fc3ab --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/basicInferenceForImplicitSelfType.fir.kt @@ -0,0 +1,29 @@ +class Builder> { + fun test(): T = TODO() + + fun foo() {} +} + +fun testStar(builder: Builder<*>) { + builder.test() + + builder + .test() + .foo() +} + +fun > testTypeParam(builder: Builder) { + builder.test() + + builder + .test() + .foo() +} + +interface BodySpec> { + fun isEqualTo(expected: B): T +} + +fun test(b: BodySpec) { + b.isEqualTo("") +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/basicInferenceForImplicitSelfType.kt b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/basicInferenceForImplicitSelfType.kt new file mode 100644 index 00000000000..2bf74c06de7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/basicInferenceForImplicitSelfType.kt @@ -0,0 +1,21 @@ +class Builder> { + fun test(): T = TODO() + + fun foo() {} +} + +fun testStar(builder: Builder<*>) { + ")!>builder.test() + + builder + .test() + .foo() +} + +fun > testTypeParam(builder: Builder) { + builder.test() + + builder + .test() + .foo() +} diff --git a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/basicInferenceForImplicitSelfType.txt b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/basicInferenceForImplicitSelfType.txt new file mode 100644 index 00000000000..37b74c7fc6f --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/basicInferenceForImplicitSelfType.txt @@ -0,0 +1,14 @@ +package + +public fun testStar(/*0*/ builder: Builder<*>): kotlin.Unit +public fun > testTypeParam(/*0*/ builder: Builder): kotlin.Unit + +public final class Builder> { + public constructor Builder>() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final fun test(): T + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + diff --git a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/recursiveTypeWithTwoTypeParams.fir.kt b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/recursiveTypeWithTwoTypeParams.fir.kt new file mode 100644 index 00000000000..6d30685c076 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/recursiveTypeWithTwoTypeParams.fir.kt @@ -0,0 +1,8 @@ +interface BodySpec> { + fun isEqualTo(expected: B): T +} + +fun test(b: BodySpec) { + val x = b.isEqualTo("") + x +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/recursiveTypeWithTwoTypeParams.kt b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/recursiveTypeWithTwoTypeParams.kt new file mode 100644 index 00000000000..fa0b9dfc29e --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/recursiveTypeWithTwoTypeParams.kt @@ -0,0 +1,8 @@ +interface BodySpec> { + fun isEqualTo(expected: B): T +} + +fun test(b: BodySpec) { + val x = b.isEqualTo("") + ")!>x +} diff --git a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/recursiveTypeWithTwoTypeParams.txt b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/recursiveTypeWithTwoTypeParams.txt new file mode 100644 index 00000000000..220f4c9f9b9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/recursiveTypeWithTwoTypeParams.txt @@ -0,0 +1,10 @@ +package + +public fun test(/*0*/ b: BodySpec): kotlin.Unit + +public interface BodySpec> { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract fun isEqualTo(/*0*/ expected: B): T + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.fir.kt b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.fir.kt new file mode 100644 index 00000000000..a12bdc16f6a --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.fir.kt @@ -0,0 +1,15 @@ +fun test() { + WriterAppender.newBuilder() +} + +object WriterAppender { + class Builder> { + fun asBuilder(): B { + return this as B + } + } + + fun > newBuilder(): B { + return Builder().asBuilder() + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.kt b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.kt new file mode 100644 index 00000000000..84256060a91 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.kt @@ -0,0 +1,15 @@ +fun test() { + WriterAppender.newBuilder() +} + +object WriterAppender { + class Builder> { + fun asBuilder(): B { + return this as B + } + } + + fun > newBuilder(): B { + return Builder().asBuilder() + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.txt b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.txt new file mode 100644 index 00000000000..2272f255742 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.txt @@ -0,0 +1,19 @@ +package + +public fun test(): kotlin.Unit + +public object WriterAppender { + private constructor WriterAppender() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final fun > newBuilder(): B + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public final class Builder> { + public constructor Builder>() + public final fun asBuilder(): B + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 2d25d7ef6fd..f932a367bd2 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -14181,6 +14181,34 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { public void testTwoTypeConstructors() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/recursiveTypes/twoTypeConstructors.kt"); } + + @Nested + @TestMetadata("compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes") + @TestDataPath("$PROJECT_ROOT") + public class SelfTypes { + @Test + public void testAllFilesPresentInSelfTypes() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @Test + @TestMetadata("basicInferenceForImplicitSelfType.kt") + public void testBasicInferenceForImplicitSelfType() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/basicInferenceForImplicitSelfType.kt"); + } + + @Test + @TestMetadata("recursiveTypeWithTwoTypeParams.kt") + public void testRecursiveTypeWithTwoTypeParams() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/recursiveTypeWithTwoTypeParams.kt"); + } + + @Test + @TestMetadata("writerAppenderExampleRecursive.kt") + public void testWriterAppenderExampleRecursive() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.kt"); + } + } } @Nested diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/DescriptorSubstitutor.java b/core/descriptors/src/org/jetbrains/kotlin/types/DescriptorSubstitutor.java index f618390fffc..147c34c6d22 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/DescriptorSubstitutor.java +++ b/core/descriptors/src/org/jetbrains/kotlin/types/DescriptorSubstitutor.java @@ -84,7 +84,7 @@ public class DescriptorSubstitutor { for (TypeParameterDescriptor descriptor : typeParameters) { TypeParameterDescriptorImpl substituted = substitutedMap.get(descriptor); for (KotlinType upperBound : descriptor.getUpperBounds()) { - KotlinType substitutedBound = substitutor.substitute(upperBound, Variance.IN_VARIANCE); + KotlinType substitutedBound = substitutor.substitute(upperBound, Variance.OUT_VARIANCE); if (substitutedBound == null) return null; if (substitutedBound != upperBound && wereChanges != null) {