diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java index a9cc3cfa34b..de5ec49228b 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java @@ -16920,6 +16920,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia runTest("compiler/testData/diagnostics/tests/inference/receiverTypeMismatch_withoutProper.kt"); } + @Test + @TestMetadata("recursiveGenericExtensionReceiver.kt") + public void testRecursiveGenericExtensionReceiver() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/recursiveGenericExtensionReceiver.kt"); + } + @Test @TestMetadata("reportAboutUnresolvedReferenceAsUnresolved.kt") public void testReportAboutUnresolvedReferenceAsUnresolved() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java index 3cfb5a9fccc..ec6af487215 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java @@ -16920,6 +16920,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated runTest("compiler/testData/diagnostics/tests/inference/receiverTypeMismatch_withoutProper.kt"); } + @Test + @TestMetadata("recursiveGenericExtensionReceiver.kt") + public void testRecursiveGenericExtensionReceiver() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/recursiveGenericExtensionReceiver.kt"); + } + @Test @TestMetadata("reportAboutUnresolvedReferenceAsUnresolved.kt") public void testReportAboutUnresolvedReferenceAsUnresolved() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java index 985441fcfe1..4ab6adbb7b0 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java @@ -16914,6 +16914,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/tests/inference/receiverTypeMismatch_withoutProper.kt"); } + @Test + @TestMetadata("recursiveGenericExtensionReceiver.kt") + public void testRecursiveGenericExtensionReceiver() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/recursiveGenericExtensionReceiver.kt"); + } + @Test @TestMetadata("reportAboutUnresolvedReferenceAsUnresolved.kt") public void testReportAboutUnresolvedReferenceAsUnresolved() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java index e3ab8da8bb9..944a73fd1c6 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java @@ -16920,6 +16920,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia runTest("compiler/testData/diagnostics/tests/inference/receiverTypeMismatch_withoutProper.kt"); } + @Test + @TestMetadata("recursiveGenericExtensionReceiver.kt") + public void testRecursiveGenericExtensionReceiver() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/recursiveGenericExtensionReceiver.kt"); + } + @Test @TestMetadata("reportAboutUnresolvedReferenceAsUnresolved.kt") public void testReportAboutUnresolvedReferenceAsUnresolved() throws Exception { diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt index 50beb0578c3..48e4d05c20b 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt @@ -27,7 +27,7 @@ class ConstraintIncorporator( // if such type variable is fixed then it is error fun getTypeVariable(typeConstructor: TypeConstructorMarker): TypeVariableMarker? - fun getConstraintsForVariable(typeVariable: TypeVariableMarker): Collection + fun getConstraintsForVariable(typeVariable: TypeVariableMarker): List fun addNewIncorporatedConstraint( lowerType: KotlinTypeMarker, @@ -67,7 +67,7 @@ class ConstraintIncorporator( // \alpha <: constraint.type if (constraint.kind != ConstraintKind.LOWER) { - getConstraintsForVariable(typeVariable).forEach { + forEachConstraint(typeVariable) { if (it.kind != ConstraintKind.UPPER) { addNewIncorporatedConstraint(it.type, constraint.type, shouldBeTypeVariableFlexible, it.isNullabilityConstraint) } @@ -76,7 +76,7 @@ class ConstraintIncorporator( // constraint.type <: \alpha if (constraint.kind != ConstraintKind.UPPER) { - getConstraintsForVariable(typeVariable).forEach { + forEachConstraint(typeVariable) { if (it.kind != ConstraintKind.LOWER) { val isFromDeclaredUpperBound = it.position.from is DeclaredUpperBoundConstraintPosition<*> && !it.type.typeConstructor().isTypeVariable() @@ -92,6 +92,16 @@ class ConstraintIncorporator( } } + private inline fun Context.forEachConstraint(typeVariable: TypeVariableMarker, action: (Constraint) -> Unit) { + // We use an indexed loop because the collection might be modified during the iteration. + // However, the only modification is appending, so we should be fine. + val constraints = getConstraintsForVariable(typeVariable) + var i = 0 + while (i < constraints.size) { + action(constraints[i++]) + } + } + // \alpha <: Number, \beta <: Inv<\alpha> => \beta <: Inv private fun Context.insideOtherConstraint( typeVariable: TypeVariableMarker, diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt index 5b85be1293c..700ad4e1b21 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt @@ -46,6 +46,14 @@ class MutableVariableWithConstraints private constructor( private val mutableConstraints = if (constraints == null) SmartList() else SmartList(constraints) + /** + * The contract for mutating this list is that the only allowed mutation is appending items. + * In any other case, it must be set to `null`, so that it will be recomputed when [constraints] is called. + * + * The reason is that the list might be mutated while it's being iterated. + * For this reason, we use an index loop in + * [org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintIncorporator.forEachConstraint]. + */ private var simplifiedConstraints: SmartList? = mutableConstraints val rawConstraintsCount get() = mutableConstraints.size diff --git a/compiler/testData/diagnostics/tests/inference/recursiveGenericExtensionReceiver.kt b/compiler/testData/diagnostics/tests/inference/recursiveGenericExtensionReceiver.kt new file mode 100644 index 00000000000..37240518110 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/recursiveGenericExtensionReceiver.kt @@ -0,0 +1,10 @@ +// FIR_IDENTICAL +// ISSUE: KT-60225 + +class Klass> + +fun > Klass.foo() {} + +fun main() { + Klass().foo() +} \ No newline at end of file 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 b6ca432d9cb..e228afe9fa5 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 @@ -16920,6 +16920,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/inference/receiverTypeMismatch_withoutProper.kt"); } + @Test + @TestMetadata("recursiveGenericExtensionReceiver.kt") + public void testRecursiveGenericExtensionReceiver() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/recursiveGenericExtensionReceiver.kt"); + } + @Test @TestMetadata("reportAboutUnresolvedReferenceAsUnresolved.kt") public void testReportAboutUnresolvedReferenceAsUnresolved() throws Exception {