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 57b7956a477..e171449382f 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 @@ -9753,6 +9753,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/declarationChecks/nonExpansiveInheritanceRestriction"), Pattern.compile("^(.+)\\.(kt|kts)$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true); } + @Test + @TestMetadata("Comparable.kt") + public void testComparable() throws Exception { + runTest("compiler/testData/diagnostics/tests/declarationChecks/nonExpansiveInheritanceRestriction/Comparable.kt"); + } + @Test @TestMetadata("JavaWithKotlin.kt") public void testJavaWithKotlin() 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 58c46a07a69..4b798bfe427 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 @@ -9753,6 +9753,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/declarationChecks/nonExpansiveInheritanceRestriction"), Pattern.compile("^(.+)\\.(kt|kts)$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true); } + @Test + @TestMetadata("Comparable.kt") + public void testComparable() throws Exception { + runTest("compiler/testData/diagnostics/tests/declarationChecks/nonExpansiveInheritanceRestriction/Comparable.kt"); + } + @Test @TestMetadata("JavaWithKotlin.kt") public void testJavaWithKotlin() 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 866e13eec46..eef4c47ff31 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 @@ -9747,6 +9747,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/declarationChecks/nonExpansiveInheritanceRestriction"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true, "multiplatform"); } + @Test + @TestMetadata("Comparable.kt") + public void testComparable() throws Exception { + runTest("compiler/testData/diagnostics/tests/declarationChecks/nonExpansiveInheritanceRestriction/Comparable.kt"); + } + @Test @TestMetadata("JavaWithKotlin.kt") public void testJavaWithKotlin() 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 594d894949b..49b16ce2912 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 @@ -9753,6 +9753,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/declarationChecks/nonExpansiveInheritanceRestriction"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true, "multiplatform"); } + @Test + @TestMetadata("Comparable.kt") + public void testComparable() throws Exception { + runTest("compiler/testData/diagnostics/tests/declarationChecks/nonExpansiveInheritanceRestriction/Comparable.kt"); + } + @Test @TestMetadata("JavaWithKotlin.kt") public void testJavaWithKotlin() throws Exception { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirNonExpansiveInheritanceRestrictionChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirNonExpansiveInheritanceRestrictionChecker.kt index 712eb27cfe6..0616c8c12ae 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirNonExpansiveInheritanceRestrictionChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirNonExpansiveInheritanceRestrictionChecker.kt @@ -12,6 +12,8 @@ import org.jetbrains.kotlin.fir.analysis.checkers.collectUpperBounds import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor +import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap import org.jetbrains.kotlin.fir.scopes.impl.toConeType import org.jetbrains.kotlin.fir.symbols.impl.FirClassifierSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol @@ -19,6 +21,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.utils.DFS import org.jetbrains.kotlin.utils.SmartSet +import org.jetbrains.kotlin.utils.addToStdlib.runIf /** * @see org.jetbrains.kotlin.resolve.NonExpansiveInheritanceRestrictionChecker @@ -81,6 +84,7 @@ object FirNonExpansiveInheritanceRestrictionChecker : FirRegularClassChecker() { val visitedSymbols = SmartSet.create>() fun visit(symbol: FirRegularClassSymbol) { val typeParameters = symbol.typeParameterSymbols + if (typeParameters.isEmpty()) return // For each type parameter T, let ST be the set of all constituent types of all immediate supertypes of the owner of T. // If T appears as a constituent type of a simple type argument A in a generic type in ST, add an edge from T @@ -89,10 +93,16 @@ object FirNonExpansiveInheritanceRestrictionChecker : FirRegularClassChecker() { for (constituentType in symbol.resolvedSuperTypes.flatMap { it.constituentTypes() }) { val constituentTypeSymbol = constituentType.toRegularClassSymbol(session) ?: continue if (visitedSymbols.add(constituentTypeSymbol)) visit(constituentTypeSymbol) - if (constituentTypeSymbol.typeParameterSymbols.size != constituentType.typeArguments.size) continue - for ((i, typeProjection) in constituentType.typeArguments.withIndex()) { - val constituentTypeParameterSymbol = constituentTypeSymbol.typeParameterSymbols[i] + val parameters = constituentTypeSymbol.typeParameterSymbols + val arguments = constituentType.typeArguments.asList() + if (parameters.size != arguments.size) continue + val substitutor = runIf(arguments.any { it.kind != ProjectionKind.INVARIANT }) { + substitutorByType(parameters, arguments, session) + } + + for ((i, typeProjection) in arguments.withIndex()) { + val constituentTypeParameterSymbol = parameters[i] if (typeProjection.kind == ProjectionKind.INVARIANT) { val constituents = typeProjection.type!!.constituentTypes() @@ -109,7 +119,7 @@ object FirNonExpansiveInheritanceRestrictionChecker : FirRegularClassChecker() { // upper bounds of a skolem type variable Q in a skolemization of a projected generic type in ST, add an // expanding edge from T to V, where V is the type parameter corresponding to Q. val bounds = SmartSet.create() - constituentTypeParameterSymbol.resolvedBounds.mapTo(bounds) { it.type } + constituentTypeParameterSymbol.resolvedBounds.mapNotNullTo(bounds) { substitutor!!.substituteOrNull(it.type) } typeProjection.type?.let(bounds::add) val boundClosure = bounds.flatMapTo(SmartSet.create()) { it.collectUpperBounds() } @@ -173,3 +183,22 @@ private fun ConeKotlinType.constituentTypes(): Set { forEachType { constituentTypes.add(it) } return constituentTypes } + +private fun substitutorByType( + parameters: List, + arguments: List, + session: FirSession, +): ConeSubstitutor { + require(parameters.size == arguments.size) + + val substitution = buildMap { + for (index in parameters.indices) { + val argumentType = arguments[index].type + if (argumentType != null) { + put(parameters[index], argumentType) + } + } + } + + return substitutorByMap(substitution, session) +} diff --git a/compiler/testData/diagnostics/tests/declarationChecks/nonExpansiveInheritanceRestriction/Comparable.kt b/compiler/testData/diagnostics/tests/declarationChecks/nonExpansiveInheritanceRestriction/Comparable.kt new file mode 100644 index 00000000000..0b8f6551f88 --- /dev/null +++ b/compiler/testData/diagnostics/tests/declarationChecks/nonExpansiveInheritanceRestriction/Comparable.kt @@ -0,0 +1,22 @@ +// FIR_IDENTICAL +// SKIP_TXT +// ISSUE: KT-63649 + +interface Kind +interface System> + +interface Units< + K : Kind, + S : System, + U : Units, + M : Measure, + > + : Comparable> + +interface Measure< + K : Kind, + S : System, + U : Units, + M : Measure, + > + : Comparable> 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 c718fbcbaa6..ab1cf325bea 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 @@ -9753,6 +9753,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/declarationChecks/nonExpansiveInheritanceRestriction"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true); } + @Test + @TestMetadata("Comparable.kt") + public void testComparable() throws Exception { + runTest("compiler/testData/diagnostics/tests/declarationChecks/nonExpansiveInheritanceRestriction/Comparable.kt"); + } + @Test @TestMetadata("JavaWithKotlin.kt") public void testJavaWithKotlin() throws Exception {