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 09de7e75117..90e4cb2f811 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 @@ -620,6 +620,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/RecursiveTypeInference.kt"); } + @Test + @TestMetadata("RecursiveTypeParameterEqualityCheck.kt") + public void testRecursiveTypeParameterEqualityCheck() throws Exception { + runTest("compiler/testData/diagnostics/tests/RecursiveTypeParameterEqualityCheck.kt"); + } + @Test @TestMetadata("ReserveYield.kt") public void testReserveYield() throws Exception { 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 438f08b5971..69506977f3d 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 @@ -620,6 +620,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/RecursiveTypeInference.kt"); } + @Test + @TestMetadata("RecursiveTypeParameterEqualityCheck.kt") + public void testRecursiveTypeParameterEqualityCheck() throws Exception { + runTest("compiler/testData/diagnostics/tests/RecursiveTypeParameterEqualityCheck.kt"); + } + @Test @TestMetadata("ReserveYield.kt") public void testReserveYield() throws Exception { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/ConeTypeCompatibilityChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/ConeTypeCompatibilityChecker.kt index 16a259edff0..84fe85636c2 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/ConeTypeCompatibilityChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/ConeTypeCompatibilityChecker.kt @@ -98,7 +98,8 @@ internal object ConeTypeCompatibilityChecker { private fun ConeInferenceContext.areCompatible( upperBounds: Set, lowerBounds: Set, - compatibilityUpperBound: Compatibility + compatibilityUpperBound: Compatibility, + checkedTypeParameters: MutableSet = mutableSetOf(), ): Compatibility { val upperBoundClasses: Set = upperBounds.mapNotNull { it.toFirClassWithSuperClasses(this) }.toSet() @@ -134,8 +135,17 @@ internal object ConeTypeCompatibilityChecker { } } var result = Compatibility.COMPATIBLE - val typeArgsCompatibility = typeArgumentMapping.values.asSequence() - .map { (upper, lower, compatibilityUpperBound) -> areCompatible(upper, lower, compatibilityUpperBound) } + val typeArgsCompatibility = typeArgumentMapping.asSequence() + .map { (paramRef, boundTypeArguments) -> + val (upper, lower, compatibility) = boundTypeArguments + if (paramRef in checkedTypeParameters) { + // if we are already checking this type parameter, simply bail out to prevent infinite recursion. + Compatibility.COMPATIBLE + } else { + checkedTypeParameters.add(paramRef) + areCompatible(upper, lower, compatibility, checkedTypeParameters) + } + } for (compatibility in typeArgsCompatibility) { if (compatibility == compatibilityUpperBound) return compatibility if (compatibility > result) { diff --git a/compiler/testData/diagnostics/tests/RecursiveTypeParameterEqualityCheck.fir.kt b/compiler/testData/diagnostics/tests/RecursiveTypeParameterEqualityCheck.fir.kt new file mode 100644 index 00000000000..92ff5dc9e4c --- /dev/null +++ b/compiler/testData/diagnostics/tests/RecursiveTypeParameterEqualityCheck.fir.kt @@ -0,0 +1,9 @@ +interface Foo +interface Bar1 : Foo +interface Bar2 : Foo +class Bar3 : Foo + +fun test(b1: Bar1, b2: Bar2, b3: Bar3) { + b1 == b2 + b1 == b3 +} diff --git a/compiler/testData/diagnostics/tests/RecursiveTypeParameterEqualityCheck.kt b/compiler/testData/diagnostics/tests/RecursiveTypeParameterEqualityCheck.kt new file mode 100644 index 00000000000..6f90d1ea1d1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/RecursiveTypeParameterEqualityCheck.kt @@ -0,0 +1,9 @@ +interface Foo +interface Bar1 : Foo +interface Bar2 : Foo +class Bar3 : Foo + +fun test(b1: Bar1, b2: Bar2, b3: Bar3) { + b1 == b2 + b1 == b3 +} diff --git a/compiler/testData/diagnostics/tests/RecursiveTypeParameterEqualityCheck.txt b/compiler/testData/diagnostics/tests/RecursiveTypeParameterEqualityCheck.txt new file mode 100644 index 00000000000..d8f02876789 --- /dev/null +++ b/compiler/testData/diagnostics/tests/RecursiveTypeParameterEqualityCheck.txt @@ -0,0 +1,28 @@ +package + +public fun test(/*0*/ b1: Bar1, /*1*/ b2: Bar2, /*2*/ b3: Bar3): kotlin.Unit + +public interface Bar1 : Foo { + 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 +} + +public interface Bar2 : Foo { + 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 +} + +public final class Bar3 : Foo { + public constructor Bar3() + 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 +} + +public interface Foo { + 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 a88e743ac83..2e55dc0e168 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 @@ -620,6 +620,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/RecursiveTypeInference.kt"); } + @Test + @TestMetadata("RecursiveTypeParameterEqualityCheck.kt") + public void testRecursiveTypeParameterEqualityCheck() throws Exception { + runTest("compiler/testData/diagnostics/tests/RecursiveTypeParameterEqualityCheck.kt"); + } + @Test @TestMetadata("ReserveYield.kt") public void testReserveYield() throws Exception {