From 4aaf290c99f8b84f4db2a2f8edef151a9e98d1f2 Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Mon, 20 Nov 2023 11:42:20 +0100 Subject: [PATCH] [FIR] Fix incorrect matching of nested classes when actualized via typealias There are still some places in `.fir.kt` where false-positive reports remained. This is because of IR checker, which will be fixed in next commit. ^KT-31636 --- ...CompilerTestFE10TestdataTestGenerated.java | 6 ++ ...sticCompilerFE10TestDataTestGenerated.java | 6 ++ ...DiagnosticsWithLightTreeTestGenerated.java | 6 ++ ...endMPPDiagnosticsWithPsiTestGenerated.java | 6 ++ .../mpp/FirExpectActualMatchingContextImpl.kt | 61 +++++++++++++++---- ...edAnnotationClassViaActualTypeAlias.fir.kt | 4 +- ...tedAnnotationClassViaActualTypeAlias.ll.kt | 4 +- ...edAnnotationClassViaActualTypealias.fir.kt | 2 +- .../nestedClassViaActualTypealias.fir.kt | 4 +- .../nestedClassViaActualTypealias.ll.kt | 4 +- ...lassViaActualTypealiasWithTypeParam.fir.kt | 34 +++++++++++ ...tedClassViaActualTypealiasWithTypeParam.kt | 34 +++++++++++ ...ClassViaActualTypealiasWithTypeParam.ll.kt | 33 ++++++++++ .../test/runners/DiagnosticTestGenerated.java | 6 ++ 14 files changed, 190 insertions(+), 20 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealiasWithTypeParam.fir.kt create mode 100644 compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealiasWithTypeParam.kt create mode 100644 compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealiasWithTypeParam.ll.kt 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 fde941751a2..5550e20e4cf 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 @@ -23976,6 +23976,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia runTest("compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealias.kt"); } + @Test + @TestMetadata("nestedClassViaActualTypealiasWithTypeParam.kt") + public void testNestedClassViaActualTypealiasWithTypeParam() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealiasWithTypeParam.kt"); + } + @Test @TestMetadata("optionalExpectationDiagnostics.kt") public void testOptionalExpectationDiagnostics() 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 c6f14603f1c..c7403d7e6db 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 @@ -23976,6 +23976,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated runTest("compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealias.kt"); } + @Test + @TestMetadata("nestedClassViaActualTypealiasWithTypeParam.kt") + public void testNestedClassViaActualTypealiasWithTypeParam() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealiasWithTypeParam.kt"); + } + @Test @TestMetadata("optionalExpectationDiagnostics.kt") public void testOptionalExpectationDiagnostics() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java index fd0548a7a2b..4265962ca97 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java @@ -337,6 +337,12 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst runTest("compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealias.kt"); } + @Test + @TestMetadata("nestedClassViaActualTypealiasWithTypeParam.kt") + public void testNestedClassViaActualTypealiasWithTypeParam() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealiasWithTypeParam.kt"); + } + @Test @TestMetadata("optionalExpectationDiagnostics.kt") public void testOptionalExpectationDiagnostics() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java index eb2fe39a265..5958eb2402d 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java @@ -337,6 +337,12 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi runTest("compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealias.kt"); } + @Test + @TestMetadata("nestedClassViaActualTypealiasWithTypeParam.kt") + public void testNestedClassViaActualTypealiasWithTypeParam() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealiasWithTypeParam.kt"); + } + @Test @TestMetadata("optionalExpectationDiagnostics.kt") public void testOptionalExpectationDiagnostics() throws Exception { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt index 74089901fb0..b2ca950a57e 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt @@ -11,8 +11,12 @@ import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.isActual +import org.jetbrains.kotlin.fir.declarations.utils.isExpect import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.resolve.* +import org.jetbrains.kotlin.fir.resolve.providers.getRegularClassSymbolByClassId +import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider +import org.jetbrains.kotlin.fir.resolve.providers.toSymbol import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.scopes.* import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol @@ -30,12 +34,10 @@ import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.Annota import org.jetbrains.kotlin.resolve.checkers.OptInNames import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility import org.jetbrains.kotlin.types.AbstractTypeChecker +import org.jetbrains.kotlin.types.AbstractTypeRefiner import org.jetbrains.kotlin.types.TypeCheckerState import org.jetbrains.kotlin.types.Variance -import org.jetbrains.kotlin.types.model.KotlinTypeMarker -import org.jetbrains.kotlin.types.model.SimpleTypeMarker -import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker -import org.jetbrains.kotlin.types.model.TypeSystemContext +import org.jetbrains.kotlin.types.model.* import org.jetbrains.kotlin.utils.zipIfSizesAreEqual class FirExpectActualMatchingContextImpl private constructor( @@ -321,21 +323,23 @@ class FirExpectActualMatchingContextImpl private constructor( return false } } + val actualizedExpectType = (expectType as ConeKotlinType).actualize() + val actualizedActualType = (actualType as ConeKotlinType).actualize() - if (parameterOfAnnotationComparisonMode && expectType is ConeClassLikeType && expectType.isArrayType && - actualType is ConeClassLikeType && actualType.isArrayType + if (parameterOfAnnotationComparisonMode && actualizedExpectType is ConeClassLikeType && actualizedExpectType.isArrayType && + actualizedActualType is ConeClassLikeType && actualizedActualType.isArrayType ) { return AbstractTypeChecker.equalTypes( createTypeCheckerState(), - expectType.convertToArrayWithOutProjections(), - actualType.convertToArrayWithOutProjections() + actualizedExpectType.convertToArrayWithOutProjections(), + actualizedActualType.convertToArrayWithOutProjections() ) } return AbstractTypeChecker.equalTypes( actualSession.typeContext, - expectType, - actualType + actualizedExpectType, + actualizedActualType ) } @@ -356,6 +360,41 @@ class FirExpectActualMatchingContextImpl private constructor( ) } + private fun ConeKotlinType.actualize(): ConeKotlinType { + val classId = classId + if (this is ConeClassLikeType && classId?.isNestedClass == true) { + val classSymbol = classId.toSymbol(actualSession) + if (classSymbol is FirRegularClassSymbol && classSymbol.isExpect) { + tryExpandExpectNestedClassActualizedViaTypealias(this, classSymbol)?.let { return it } + } + } + return fullyExpandedType(actualSession) + } + + /** + * In case of `expect` nested classes actualized via typealias we can't simply find actual symbol by `expect` `ClassId` + * (like we do for top-level classes), because `ClassId` is different. + * For example, `expect` class `com/example/ExpectClass.Nested` may have actual with id `real/package/ActualTypeliasTarget.Nested`. + * So, we first expand outermost class, and then construct `ClassId` for nested class. + */ + private fun tryExpandExpectNestedClassActualizedViaTypealias( + expectNestedClassType: ConeClassLikeType, + expectNestedClassSymbol: FirRegularClassSymbol, + ): ConeClassLikeType? { + val expectNestedClassId = expectNestedClassSymbol.classId + val expectOutermostClassId = expectNestedClassId.outermostClassId + val actualTypealiasSymbol = expectOutermostClassId.toSymbol(actualSession) as? FirTypeAliasSymbol ?: return null + val actualOutermostClassId = actualTypealiasSymbol.fullyExpandedClass(actualSession)?.classId ?: return null + val actualNestedClassId = ClassId.fromString( + expectNestedClassId.asString().replaceFirst( + expectOutermostClassId.asString(), actualOutermostClassId.asString() + ) + ) + return actualNestedClassId.constructClassLikeType( + expectNestedClassType.typeArguments, expectNestedClassType.isNullable, expectNestedClassType.attributes + ) + } + private fun createTypeCheckerState(): TypeCheckerState { return actualSession.typeContext.newTypeCheckerState(errorTypesEqualToAnything = true, stubTypesEqualToAnything = false) } @@ -451,7 +490,7 @@ class FirExpectActualMatchingContextImpl private constructor( getAnnotationConeType()?.toRegularClassSymbol(actualSession) private fun getAnnotationConeType(): ConeClassLikeType? { - val coneType = annotation.toAnnotationClassLikeType(actualSession) + val coneType = annotation.toAnnotationClassLikeType(actualSession)?.actualize() as? ConeClassLikeType if (coneType is ConeErrorType) { return null } diff --git a/compiler/testData/diagnostics/tests/multiplatform/actualAnnotationsNotMatchExpect/nestedAnnotationClassViaActualTypeAlias.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/actualAnnotationsNotMatchExpect/nestedAnnotationClassViaActualTypeAlias.fir.kt index db95c3efba1..ff8141f8f02 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/actualAnnotationsNotMatchExpect/nestedAnnotationClassViaActualTypeAlias.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/actualAnnotationsNotMatchExpect/nestedAnnotationClassViaActualTypeAlias.fir.kt @@ -14,7 +14,7 @@ annotation class AnnImpl { annotation class Nested } -actual typealias Ann = AnnImpl +actual typealias Ann = AnnImpl @AnnImpl.Nested -actual fun foo() {} +actual fun foo() {} diff --git a/compiler/testData/diagnostics/tests/multiplatform/actualAnnotationsNotMatchExpect/nestedAnnotationClassViaActualTypeAlias.ll.kt b/compiler/testData/diagnostics/tests/multiplatform/actualAnnotationsNotMatchExpect/nestedAnnotationClassViaActualTypeAlias.ll.kt index 240cd9300df..b51ac1693cc 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/actualAnnotationsNotMatchExpect/nestedAnnotationClassViaActualTypeAlias.ll.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/actualAnnotationsNotMatchExpect/nestedAnnotationClassViaActualTypeAlias.ll.kt @@ -14,7 +14,7 @@ annotation class AnnImpl { annotation class Nested } -actual typealias Ann = AnnImpl +actual typealias Ann = AnnImpl @AnnImpl.Nested -actual fun foo() {} +actual fun foo() {} diff --git a/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/nestedAnnotationClassViaActualTypealias.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/nestedAnnotationClassViaActualTypealias.fir.kt index 8534be0c5bf..7643467e5de 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/nestedAnnotationClassViaActualTypealias.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/defaultArguments/nestedAnnotationClassViaActualTypealias.fir.kt @@ -11,4 +11,4 @@ class DefaultArgsInNestedClassImpl { } // Incompatible because of bug KT-31636 -actual typealias DefaultArgsInNestedClass = DefaultArgsInNestedClassImpl +actual typealias DefaultArgsInNestedClass = DefaultArgsInNestedClassImpl diff --git a/compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealias.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealias.fir.kt index f22c5df5de3..9734e9a47e0 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealias.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealias.fir.kt @@ -18,7 +18,7 @@ class FooImpl { class Inner } -actual typealias Foo = FooImpl +actual typealias Foo = FooImpl class SeveralInnerImpl { class Inner1 { @@ -28,4 +28,4 @@ class SeveralInnerImpl { } } -actual typealias SeveralInner = SeveralInnerImpl +actual typealias SeveralInner = SeveralInnerImpl diff --git a/compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealias.ll.kt b/compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealias.ll.kt index 9196eb449c3..663e2eb8e39 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealias.ll.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealias.ll.kt @@ -18,7 +18,7 @@ class FooImpl { class Inner } -actual typealias Foo = FooImpl +actual typealias Foo = FooImpl class SeveralInnerImpl { class Inner1 { @@ -28,4 +28,4 @@ class SeveralInnerImpl { } } -actual typealias SeveralInner = SeveralInnerImpl +actual typealias SeveralInner = SeveralInnerImpl diff --git a/compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealiasWithTypeParam.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealiasWithTypeParam.fir.kt new file mode 100644 index 00000000000..4b025690e5f --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealiasWithTypeParam.fir.kt @@ -0,0 +1,34 @@ +// False-positive reports in K1, because fixed only in K2 +// MODULE: m1-common +// FILE: common.kt +expect class Foo { + class Inner +} + +expect fun substituted(p: Foo.Inner) +expect fun substitutedIncorrect(p: Foo.Inner) + +expect fun withTypeParam(p: Foo.Inner) +expect fun withTypeParamIncorrect(p: Foo.Inner) + +expect fun star(p: Foo.Inner<*>) +expect fun starVsNonStar(p: Foo.Inner<*>) + + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt +class FooImpl { + class Inner +} + +actual typealias Foo = FooImpl + +actual fun substituted(p: Foo.Inner) {} +actual fun substitutedIncorrect(p: Foo.Inner) {} + +actual fun withTypeParam(p: Foo.Inner) {} +actual fun withTypeParamIncorrect(p: Foo.Inner) {} + +actual fun star(p: Foo.Inner<*>) {} +actual fun starVsNonStar(p: Foo.Inner) {} + diff --git a/compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealiasWithTypeParam.kt b/compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealiasWithTypeParam.kt new file mode 100644 index 00000000000..bf17fafe40f --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealiasWithTypeParam.kt @@ -0,0 +1,34 @@ +// False-positive reports in K1, because fixed only in K2 +// MODULE: m1-common +// FILE: common.kt +expect class Foo { + class Inner +} + +expect fun substituted(p: Foo.Inner) +expect fun substitutedIncorrect(p: Foo.Inner) + +expect fun withTypeParam(p: Foo.Inner) +expect fun withTypeParamIncorrect(p: Foo.Inner) + +expect fun star(p: Foo.Inner<*>) +expect fun starVsNonStar(p: Foo.Inner<*>) + + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt +class FooImpl { + class Inner +} + +actual typealias Foo = FooImpl + +actual fun substituted(p: Foo.Inner) {} +actual fun substitutedIncorrect(p: Foo.Inner) {} + +actual fun withTypeParam(p: Foo.Inner) {} +actual fun withTypeParamIncorrect(p: Foo.Inner) {} + +actual fun star(p: Foo.Inner<*>) {} +actual fun starVsNonStar(p: Foo.Inner) {} + diff --git a/compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealiasWithTypeParam.ll.kt b/compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealiasWithTypeParam.ll.kt new file mode 100644 index 00000000000..93860c459a1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealiasWithTypeParam.ll.kt @@ -0,0 +1,33 @@ +// False-positive reports in K1, because fixed only in K2 +// MODULE: m1-common +// FILE: common.kt +expect class Foo { + class Inner +} + +expect fun substituted(p: Foo.Inner) +expect fun substitutedIncorrect(p: Foo.Inner) + +expect fun withTypeParam(p: Foo.Inner) +expect fun withTypeParamIncorrect(p: Foo.Inner) + +expect fun star(p: Foo.Inner<*>) +expect fun starVsNonStar(p: Foo.Inner<*>) + + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt +class FooImpl { + class Inner +} + +actual typealias Foo = FooImpl + +actual fun substituted(p: Foo.Inner) {} +actual fun substitutedIncorrect(p: Foo.Inner) {} + +actual fun withTypeParam(p: Foo.Inner) {} +actual fun withTypeParamIncorrect(p: Foo.Inner) {} + +actual fun star(p: Foo.Inner<*>) {} +actual fun starVsNonStar(p: Foo.Inner) {} 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 b913b4bfbd4..c3f1736eaa0 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 @@ -23976,6 +23976,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealias.kt"); } + @Test + @TestMetadata("nestedClassViaActualTypealiasWithTypeParam.kt") + public void testNestedClassViaActualTypealiasWithTypeParam() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/nestedClassViaActualTypealiasWithTypeParam.kt"); + } + @Test @TestMetadata("optionalExpectationDiagnostics.kt") public void testOptionalExpectationDiagnostics() throws Exception {