[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:
Nikita Bobko
2023-10-13 13:57:28 +02:00
committed by teamcity
parent e778ddfd21
commit bc5180656d
7 changed files with 70 additions and 15 deletions
@@ -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 {
@@ -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 {
@@ -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
}
@@ -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()
@@ -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()
@@ -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 {
@@ -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")