[FIR] ReturnType expect-actual incompatibility: convert from strong to weak
It should have been WeakIncompatible from the beginning because it's not possible to overload by return type in Kotlin This commit is a step forward to fix KT-62591 Unfortunately, the test cannot demonstrate the problem because of another bug in K2 KT-59887 ^KT-62752 Fixed Review: https://jetbrains.team/p/kt/reviews/12750/timeline Motivation: It makes expect-actual matching-checking model more consistent. expect-actual "matching" is run before FirResolvePhase.BODY_RESOLVE. You can't know return types, until you run BODY_RESOLVE. That's why the return type can't be checked during expect-actual matching. But it's cursed: you have something that have to match by, but, at the same time, you can't do it. expect-actual "checking" is run after FirResolvePhase.BODY_RESOLVE. That's why if we convert ReturnType incompatibility to WeakIncompatible (which should have been called CheckingIncompatible), then expect-actual matching model becomes consistent. We will also be able to get rid of unnecessary FirActualCallableDeclarationChecker. Because it won't be necessary. Return types will be checked by common logic of expect-actual "checker"
This commit is contained in:
+6
@@ -301,6 +301,12 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/privateTopLevelDeclarations.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("returnTypeVsGenericsUpperBoundIncompatibility.kt")
|
||||
public void testReturnTypeVsGenericsUpperBoundIncompatibility() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/returnTypeVsGenericsUpperBoundIncompatibility.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("sealedClassWithPrivateConstructor.kt")
|
||||
public void testSealedClassWithPrivateConstructor() throws Exception {
|
||||
|
||||
+6
@@ -301,6 +301,12 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/privateTopLevelDeclarations.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("returnTypeVsGenericsUpperBoundIncompatibility.kt")
|
||||
public void testReturnTypeVsGenericsUpperBoundIncompatibility() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/returnTypeVsGenericsUpperBoundIncompatibility.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("sealedClassWithPrivateConstructor.kt")
|
||||
public void testSealedClassWithPrivateConstructor() throws Exception {
|
||||
|
||||
+29
-14
@@ -132,7 +132,7 @@ object AbstractExpectActualCompatibilityChecker {
|
||||
|
||||
val substitutor = createExpectActualTypeParameterSubstitutor(
|
||||
(expectTypeParameterSymbols zipIfSizesAreEqual actualTypeParameterSymbols)
|
||||
?: error("expect/actual type parameters sizes are checked above"),
|
||||
?: error("expect/actual type parameters sizes are checked earlier"),
|
||||
parentSubstitutor
|
||||
)
|
||||
|
||||
@@ -323,7 +323,14 @@ object AbstractExpectActualCompatibilityChecker {
|
||||
// We must prioritize to return STRONG incompatible over WEAK incompatible (because STRONG incompatibility allows to search for overloads)
|
||||
val annotationMode = expectContainingClass?.classKind == ClassKind.ANNOTATION_CLASS
|
||||
return getCallablesStrongIncompatibility(expectDeclaration, actualDeclaration, annotationMode, parentSubstitutor)
|
||||
?: getCallablesWeakIncompatibility(expectDeclaration, actualDeclaration, expectContainingClass, actualContainingClass)
|
||||
?: getCallablesWeakIncompatibility(
|
||||
expectDeclaration,
|
||||
actualDeclaration,
|
||||
annotationMode,
|
||||
parentSubstitutor,
|
||||
expectContainingClass,
|
||||
actualContainingClass
|
||||
)
|
||||
?: ExpectActualCompatibility.Compatible
|
||||
}
|
||||
|
||||
@@ -358,7 +365,7 @@ object AbstractExpectActualCompatibilityChecker {
|
||||
|
||||
val substitutor = createExpectActualTypeParameterSubstitutor(
|
||||
(expectedTypeParameters zipIfSizesAreEqual actualTypeParameters)
|
||||
?: error("expect/actual type parameters sizes are checked above"),
|
||||
?: error("expect/actual type parameters sizes are checked earlier"),
|
||||
parentSubstitutor
|
||||
)
|
||||
|
||||
@@ -376,17 +383,6 @@ object AbstractExpectActualCompatibilityChecker {
|
||||
return Incompatible.ParameterTypes
|
||||
}
|
||||
|
||||
if (shouldCheckReturnTypesOfCallables) {
|
||||
if (!areCompatibleExpectActualTypes(
|
||||
substitutor.safeSubstitute(expectDeclaration.returnType),
|
||||
actualDeclaration.returnType,
|
||||
parameterOfAnnotationComparisonMode = insideAnnotationClass
|
||||
)
|
||||
) {
|
||||
return Incompatible.ReturnType
|
||||
}
|
||||
}
|
||||
|
||||
if (!areCompatibleTypeParameterUpperBounds(expectedTypeParameters, actualTypeParameters, substitutor)) {
|
||||
return Incompatible.FunctionTypeParameterUpperBounds
|
||||
}
|
||||
@@ -398,6 +394,8 @@ object AbstractExpectActualCompatibilityChecker {
|
||||
private fun getCallablesWeakIncompatibility(
|
||||
expectDeclaration: CallableSymbolMarker,
|
||||
actualDeclaration: CallableSymbolMarker,
|
||||
insideAnnotationClass: Boolean,
|
||||
parentSubstitutor: TypeSubstitutorMarker?,
|
||||
expectContainingClass: RegularClassSymbolMarker?,
|
||||
actualContainingClass: RegularClassSymbolMarker?,
|
||||
): Incompatible.WeakIncompatible<*>? {
|
||||
@@ -406,6 +404,23 @@ object AbstractExpectActualCompatibilityChecker {
|
||||
val expectedValueParameters = expectDeclaration.valueParameters
|
||||
val actualValueParameters = actualDeclaration.valueParameters
|
||||
|
||||
if (shouldCheckReturnTypesOfCallables) {
|
||||
val substitutor = createExpectActualTypeParameterSubstitutor(
|
||||
(expectedTypeParameters zipIfSizesAreEqual actualTypeParameters)
|
||||
?: error("expect/actual type parameters sizes are checked earlier"),
|
||||
parentSubstitutor
|
||||
)
|
||||
|
||||
if (!areCompatibleExpectActualTypes(
|
||||
substitutor.safeSubstitute(expectDeclaration.returnType),
|
||||
actualDeclaration.returnType,
|
||||
parameterOfAnnotationComparisonMode = insideAnnotationClass
|
||||
)
|
||||
) {
|
||||
return Incompatible.ReturnType
|
||||
}
|
||||
}
|
||||
|
||||
if (actualDeclaration.hasStableParameterNames && !equalsBy(expectedValueParameters, actualValueParameters) { it.name }) {
|
||||
return Incompatible.ParameterNames
|
||||
}
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// MODULE: m1-common
|
||||
// FILE: common.kt
|
||||
|
||||
interface A
|
||||
<!INCOMPATIBLE_MATCHING{JVM}, INCOMPATIBLE_MATCHING{JVM}!>expect fun <T : A> foo(t: T): String<!>
|
||||
|
||||
// MODULE: m2-jvm()()(m1-common)
|
||||
// FILE: jvm.kt
|
||||
|
||||
fun <T : A> foo(t: T): T = TODO()
|
||||
fun <T> foo(t: T): String = TODO()
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
// MODULE: m1-common
|
||||
// FILE: common.kt
|
||||
|
||||
interface A
|
||||
expect fun <T : A> foo(t: T): <!NO_ACTUAL_FOR_EXPECT{JVM}!>String<!>
|
||||
|
||||
// MODULE: m2-jvm()()(m1-common)
|
||||
// FILE: jvm.kt
|
||||
|
||||
fun <T : A> foo(t: T): T = TODO()
|
||||
fun <T> foo(t: T): String = TODO()
|
||||
Generated
+6
@@ -23668,6 +23668,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/privateTopLevelDeclarations.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("returnTypeVsGenericsUpperBoundIncompatibility.kt")
|
||||
public void testReturnTypeVsGenericsUpperBoundIncompatibility() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/returnTypeVsGenericsUpperBoundIncompatibility.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("sealedClassWithPrivateConstructor.kt")
|
||||
public void testSealedClassWithPrivateConstructor() throws Exception {
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ sealed class ExpectActualCompatibility<out D> {
|
||||
object ClassTypeParameterCount : WeakIncompatible<Nothing>(FunctionTypeParameterCount.reason)
|
||||
|
||||
object ParameterTypes : StrongIncompatible<Nothing>("parameter types are different")
|
||||
object ReturnType : StrongIncompatible<Nothing>("return type is different")
|
||||
object ReturnType : WeakIncompatible<Nothing>("return type is different")
|
||||
|
||||
object ParameterNames : WeakIncompatible<Nothing>("parameter names are different")
|
||||
object TypeParameterNames : WeakIncompatible<Nothing>("names of type parameters are different")
|
||||
|
||||
Reference in New Issue
Block a user