[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:
committed by
Space Team
parent
a768ece70d
commit
4aaf290c99
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+50
-11
@@ -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
|
||||
}
|
||||
|
||||
+2
-2
@@ -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() {}<!>
|
||||
|
||||
+2
-2
@@ -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() {}
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
Vendored
+34
@@ -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>) {}
|
||||
|
||||
Vendored
+34
@@ -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>)<!> {}
|
||||
|
||||
Vendored
+33
@@ -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>) {}
|
||||
Generated
+6
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user