[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
This commit is contained in:
Roman Efremov
2023-11-20 11:42:20 +01:00
committed by Space Team
parent a768ece70d
commit 4aaf290c99
14 changed files with 190 additions and 20 deletions
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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
}
@@ -14,7 +14,7 @@ annotation class AnnImpl {
annotation class Nested
}
actual typealias <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>Ann<!> = AnnImpl
actual typealias Ann = AnnImpl
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>@AnnImpl.Nested
actual fun <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>foo<!>() {}<!>
actual fun foo() {}<!>
@@ -14,7 +14,7 @@ annotation class AnnImpl {
annotation class Nested
}
actual typealias <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>Ann<!> = AnnImpl
actual typealias Ann = AnnImpl
@AnnImpl.Nested
actual fun <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>foo<!>() {}
actual fun foo() {}
@@ -11,4 +11,4 @@ class DefaultArgsInNestedClassImpl {
}
// Incompatible because of bug KT-31636
actual typealias <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>DefaultArgsInNestedClass<!> = DefaultArgsInNestedClassImpl
actual typealias DefaultArgsInNestedClass = DefaultArgsInNestedClassImpl
@@ -18,7 +18,7 @@ class FooImpl {
class Inner
}
actual typealias <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>Foo<!> = FooImpl
actual typealias Foo = FooImpl
class SeveralInnerImpl {
class Inner1 {
@@ -28,4 +28,4 @@ class SeveralInnerImpl {
}
}
actual typealias <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>SeveralInner<!> = SeveralInnerImpl
actual typealias SeveralInner = SeveralInnerImpl
@@ -18,7 +18,7 @@ class FooImpl {
class Inner
}
actual typealias <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>Foo<!> = FooImpl
actual typealias Foo = FooImpl
class SeveralInnerImpl {
class Inner1 {
@@ -28,4 +28,4 @@ class SeveralInnerImpl {
}
}
actual typealias <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>SeveralInner<!> = SeveralInnerImpl
actual typealias SeveralInner = SeveralInnerImpl
@@ -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<T>
}
expect fun substituted(p: Foo.Inner<Any>)
<!EXPECT_ACTUAL_MISMATCH{JVM}!>expect fun substitutedIncorrect(p: Foo.Inner<Any>)<!>
expect fun <T> withTypeParam(p: Foo.Inner<T>)
<!EXPECT_ACTUAL_MISMATCH{JVM}!>expect fun <T, R> withTypeParamIncorrect(p: Foo.Inner<R>)<!>
expect fun star(p: Foo.Inner<*>)
<!EXPECT_ACTUAL_MISMATCH{JVM}!>expect fun starVsNonStar(p: Foo.Inner<*>)<!>
// MODULE: m2-jvm()()(m1-common)
// FILE: jvm.kt
class FooImpl {
class Inner<T>
}
actual typealias Foo = FooImpl
actual fun substituted(p: Foo.Inner<Any>) {}
actual fun <!ACTUAL_WITHOUT_EXPECT!>substitutedIncorrect<!>(p: Foo.Inner<String>) {}
actual fun <T> withTypeParam(p: Foo.Inner<T>) {}
actual fun <T, R> <!ACTUAL_WITHOUT_EXPECT!>withTypeParamIncorrect<!>(p: Foo.Inner<T>) {}
actual fun star(p: Foo.Inner<*>) {}
actual fun <!ACTUAL_WITHOUT_EXPECT!>starVsNonStar<!>(p: Foo.Inner<Any>) {}
@@ -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<T>
}
expect fun substituted<!NO_ACTUAL_FOR_EXPECT{JVM}!>(p: Foo.<!UNRESOLVED_REFERENCE{JVM}!>Inner<!><Any>)<!>
expect fun substitutedIncorrect<!NO_ACTUAL_FOR_EXPECT{JVM}!>(p: Foo.<!UNRESOLVED_REFERENCE{JVM}!>Inner<!><Any>)<!>
expect fun <T> withTypeParam<!NO_ACTUAL_FOR_EXPECT{JVM}!>(p: Foo.<!UNRESOLVED_REFERENCE{JVM}!>Inner<!><T>)<!>
expect fun <T, R> withTypeParamIncorrect<!NO_ACTUAL_FOR_EXPECT{JVM}!>(p: Foo.<!UNRESOLVED_REFERENCE{JVM}!>Inner<!><R>)<!>
expect fun star<!NO_ACTUAL_FOR_EXPECT{JVM}!>(p: Foo.<!UNRESOLVED_REFERENCE{JVM}!>Inner<!><*>)<!>
expect fun starVsNonStar<!NO_ACTUAL_FOR_EXPECT{JVM}!>(p: Foo.<!UNRESOLVED_REFERENCE{JVM}!>Inner<!><*>)<!>
// MODULE: m2-jvm()()(m1-common)
// FILE: jvm.kt
class FooImpl {
class Inner<T>
}
actual typealias Foo = FooImpl
actual fun substituted<!ACTUAL_WITHOUT_EXPECT!>(p: Foo.<!UNRESOLVED_REFERENCE!>Inner<!><Any>)<!> {}
actual fun substitutedIncorrect<!ACTUAL_WITHOUT_EXPECT!>(p: Foo.<!UNRESOLVED_REFERENCE!>Inner<!><String>)<!> {}
actual fun <T> withTypeParam<!ACTUAL_WITHOUT_EXPECT!>(p: Foo.<!UNRESOLVED_REFERENCE!>Inner<!><T>)<!> {}
actual fun <T, R> withTypeParamIncorrect<!ACTUAL_WITHOUT_EXPECT!>(p: Foo.<!UNRESOLVED_REFERENCE!>Inner<!><T>)<!> {}
actual fun star<!ACTUAL_WITHOUT_EXPECT!>(p: Foo.<!UNRESOLVED_REFERENCE!>Inner<!><*>)<!> {}
actual fun starVsNonStar<!ACTUAL_WITHOUT_EXPECT!>(p: Foo.<!UNRESOLVED_REFERENCE!>Inner<!><Any>)<!> {}
@@ -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<T>
}
expect fun substituted(p: Foo.Inner<Any>)
expect fun substitutedIncorrect(p: Foo.Inner<Any>)
expect fun <T> withTypeParam(p: Foo.Inner<T>)
expect fun <T, R> withTypeParamIncorrect(p: Foo.Inner<R>)
expect fun star(p: Foo.Inner<*>)
expect fun starVsNonStar(p: Foo.Inner<*>)
// MODULE: m2-jvm()()(m1-common)
// FILE: jvm.kt
class FooImpl {
class Inner<T>
}
actual typealias Foo = FooImpl
actual fun substituted(p: Foo.Inner<Any>) {}
actual fun <!ACTUAL_WITHOUT_EXPECT!>substitutedIncorrect<!>(p: Foo.Inner<String>) {}
actual fun <T> withTypeParam(p: Foo.Inner<T>) {}
actual fun <T, R> <!ACTUAL_WITHOUT_EXPECT!>withTypeParamIncorrect<!>(p: Foo.Inner<T>) {}
actual fun star(p: Foo.Inner<*>) {}
actual fun <!ACTUAL_WITHOUT_EXPECT!>starVsNonStar<!>(p: Foo.Inner<Any>) {}
@@ -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 {