Fix type parameter bound check in expect-actual checker
Also make TypeParameterUpperBounds a "strong" incompatibility, meaning that non-actual members from platform module are _not_ going to be matched to the expected members if this incompatibility exists between them, and therefore NO_ACTUAL_FOR_EXPECT will be reported on the expected declaration, instead of ACTUAL_MISSING on the platform member. This is needed because the difference in type parameter upper bounds can have effect on the function signature on the platform (e.g. on JVM, Array<T> -> T[], but Array<T> -> Comparable[] if T : Comparable<T>), and it would be incorrect to report ACTUAL_MISSING on the member that has nothing to do with the expected declaration that happens to coincide with it in everything except type parameter bounds #KT-21864 Fixed
This commit is contained in:
+9
-3
@@ -382,7 +382,7 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
|
||||
object Modality : Incompatible("modality is different")
|
||||
object Visibility : Incompatible("visibility is different")
|
||||
|
||||
object TypeParameterUpperBounds : Incompatible("upper bounds of type parameters are different")
|
||||
object TypeParameterUpperBounds : Incompatible("upper bounds of type parameters are different", IncompatibilityKind.STRONG)
|
||||
object TypeParameterVariance : Incompatible("declaration-site variances of type parameters are different")
|
||||
object TypeParameterReified : Incompatible("some type parameter is reified in one declaration and non-reified in the other")
|
||||
|
||||
@@ -523,8 +523,14 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
|
||||
platformModule: ModuleDescriptor,
|
||||
substitutor: Substitutor
|
||||
): Compatibility {
|
||||
if (!areCompatibleTypeLists(a.map { substitutor(it.defaultType) }, b.map { it.defaultType }, platformModule))
|
||||
return Incompatible.TypeParameterUpperBounds
|
||||
for (i in a.indices) {
|
||||
val aBounds = a[i].upperBounds
|
||||
val bBounds = b[i].upperBounds
|
||||
if (aBounds.size != bBounds.size || !areCompatibleTypeLists(aBounds.map(substitutor), bBounds, platformModule)) {
|
||||
return Incompatible.TypeParameterUpperBounds
|
||||
}
|
||||
}
|
||||
|
||||
if (!equalsBy(a, b, TypeParameterDescriptor::getVariance)) return Incompatible.TypeParameterVariance
|
||||
|
||||
// Removing "reified" from an expected function's type parameter is fine
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// !LANGUAGE: +MultiPlatformProjects
|
||||
// MODULE: m1-common
|
||||
// FILE: common.kt
|
||||
|
||||
expect fun <T : Comparable<T>> Array<out T>.sort(): Unit
|
||||
|
||||
// MODULE: m2-jvm(m1-common)
|
||||
// FILE: jvm.kt
|
||||
|
||||
actual fun <T : Comparable<T>> Array<out T>.sort(): Unit {}
|
||||
|
||||
fun <T> Array<out T>.sort(): Unit {}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// -- Module: <m1-common> --
|
||||
package
|
||||
|
||||
public expect fun </*0*/ T : kotlin.Comparable<T>> kotlin.Array<out T>.sort(): kotlin.Unit
|
||||
|
||||
|
||||
// -- Module: <m2-jvm> --
|
||||
package
|
||||
|
||||
public fun </*0*/ T> kotlin.Array<out T>.sort(): kotlin.Unit
|
||||
public actual fun </*0*/ T : kotlin.Comparable<T>> kotlin.Array<out T>.sort(): kotlin.Unit
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// !LANGUAGE: +MultiPlatformProjects
|
||||
// MODULE: m1-common
|
||||
// FILE: common.kt
|
||||
|
||||
expect class A {
|
||||
fun <T : Any> foo(): Unit
|
||||
|
||||
fun <S : Comparable<S>> bar(): List<S>
|
||||
}
|
||||
|
||||
// MODULE: m2-jvm(m1-common)
|
||||
// FILE: jvm.kt
|
||||
|
||||
actual typealias A = JavaA
|
||||
|
||||
// FILE: JavaA.java
|
||||
import java.util.List;
|
||||
|
||||
public class JavaA {
|
||||
public <T> void foo() {}
|
||||
|
||||
public <S extends Comparable<S>> List<S> bar() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// -- Module: <m1-common> --
|
||||
package
|
||||
|
||||
public final expect class A {
|
||||
public final expect fun </*0*/ S : kotlin.Comparable<S>> bar(): kotlin.collections.List<S>
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public final expect fun </*0*/ T : kotlin.Any> foo(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
|
||||
// -- Module: <m2-jvm> --
|
||||
package
|
||||
|
||||
public open class JavaA {
|
||||
public constructor JavaA()
|
||||
public open fun </*0*/ S : kotlin.Comparable<S!>!> bar(): kotlin.collections.(Mutable)List<S!>!
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open fun </*0*/ T : kotlin.Any!> foo(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
public actual typealias A = JavaA
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// !LANGUAGE: +MultiPlatformProjects
|
||||
// MODULE: m1-common
|
||||
// FILE: common.kt
|
||||
|
||||
interface A
|
||||
interface B
|
||||
|
||||
expect fun <!JVM:NO_ACTUAL_FOR_EXPECT!><T><!> List<T>.foo() where T : A, T : B
|
||||
|
||||
// MODULE: m2-jvm(m1-common)
|
||||
// FILE: jvm.kt
|
||||
|
||||
fun <T> List<T>.foo() where T : B, T : A {}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
// -- Module: <m1-common> --
|
||||
package
|
||||
|
||||
public expect fun </*0*/ T : A> kotlin.collections.List<T>.foo(): kotlin.Unit where T : B
|
||||
|
||||
public interface A {
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public interface B {
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
|
||||
// -- Module: <m2-jvm> --
|
||||
package
|
||||
|
||||
public fun </*0*/ T : B> kotlin.collections.List<T>.foo(): kotlin.Unit where T : A
|
||||
|
||||
public interface A {
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public interface B {
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -50,6 +50,24 @@ The following declaration is incompatible because number of type parameters is d
|
||||
|
||||
expect fun <T> f7()
|
||||
^
|
||||
compiler/testData/multiplatform/incompatibleCallables/common.kt:19:12: error: expected function 'f11' has no actual declaration in module
|
||||
The following declaration is incompatible because upper bounds of type parameters are different:
|
||||
public actual fun <T : Annotation> f11(): Unit
|
||||
|
||||
expect fun <T : Number> f11()
|
||||
^
|
||||
compiler/testData/multiplatform/incompatibleCallables/common.kt:20:12: error: expected function 'f12' has no actual declaration in module
|
||||
The following declaration is incompatible because upper bounds of type parameters are different:
|
||||
public actual fun <U : MutableList<out String>> f12(): Unit
|
||||
|
||||
expect fun <U : MutableList<String>> f12()
|
||||
^
|
||||
compiler/testData/multiplatform/incompatibleCallables/common.kt:21:12: error: expected function 'f13' has no actual declaration in module
|
||||
The following declaration is incompatible because upper bounds of type parameters are different:
|
||||
public actual fun <A, B : Comparable<B>> f13(): Unit
|
||||
|
||||
expect fun <A, B : Comparable<A>> f13()
|
||||
^
|
||||
compiler/testData/multiplatform/incompatibleCallables/common.kt:32:15: error: expected function 'f21' has no actual declaration in module
|
||||
The following declaration is incompatible because parameter types are different:
|
||||
public actual fun f21(c: Unit.() -> Unit): Unit
|
||||
@@ -122,6 +140,24 @@ The following declaration is incompatible because visibility is different:
|
||||
|
||||
internal actual fun f10() {}
|
||||
^
|
||||
compiler/testData/multiplatform/incompatibleCallables/jvm.kt:19:12: error: actual function 'f11' has no corresponding expected declaration
|
||||
The following declaration is incompatible because upper bounds of type parameters are different:
|
||||
public expect fun <T : Number> f11(): Unit
|
||||
|
||||
actual fun <T : Annotation> f11() {}
|
||||
^
|
||||
compiler/testData/multiplatform/incompatibleCallables/jvm.kt:20:12: error: actual function 'f12' has no corresponding expected declaration
|
||||
The following declaration is incompatible because upper bounds of type parameters are different:
|
||||
public expect fun <U : MutableList<String>> f12(): Unit
|
||||
|
||||
actual fun <U : MutableList<out String>> f12() {}
|
||||
^
|
||||
compiler/testData/multiplatform/incompatibleCallables/jvm.kt:21:12: error: actual function 'f13' has no corresponding expected declaration
|
||||
The following declaration is incompatible because upper bounds of type parameters are different:
|
||||
public expect fun <A, B : Comparable<A>> f13(): Unit
|
||||
|
||||
actual fun <A, B : Comparable<B>> f13() {}
|
||||
^
|
||||
compiler/testData/multiplatform/incompatibleCallables/jvm.kt:23:19: error: actual function 'f14' has no corresponding expected declaration
|
||||
The following declaration is incompatible because some type parameter is reified in one declaration and non-reified in the other:
|
||||
public expect inline fun <X> f14(): Unit
|
||||
|
||||
@@ -11,6 +11,18 @@ The following declaration is incompatible because number of type parameters is d
|
||||
|
||||
expect class C1<A>
|
||||
^
|
||||
compiler/testData/multiplatform/incompatibleClasses/common.kt:16:16: error: expected class 'C3' has no actual declaration in module
|
||||
The following declaration is incompatible because upper bounds of type parameters are different:
|
||||
public final actual class C3<D, E : D?>
|
||||
|
||||
expect class C3<D, E : D>
|
||||
^
|
||||
compiler/testData/multiplatform/incompatibleClasses/common.kt:18:16: error: expected class 'C4' has no actual declaration in module
|
||||
The following declaration is incompatible because upper bounds of type parameters are different:
|
||||
public typealias C4<F> = C4Impl<F>
|
||||
|
||||
expect class C4<F>
|
||||
^
|
||||
compiler/testData/multiplatform/incompatibleClasses/jvm.kt:1:8: error: actual interface 'PClass' has no corresponding expected declaration
|
||||
The following declaration is incompatible because class kinds are different (class, interface, object, enum, annotation):
|
||||
public final expect class PClass
|
||||
@@ -83,6 +95,18 @@ The following declaration is incompatible because declaration-site variances of
|
||||
|
||||
actual class C2<out B>
|
||||
^
|
||||
compiler/testData/multiplatform/incompatibleClasses/jvm.kt:16:16: error: actual class 'C3' has no corresponding expected declaration
|
||||
The following declaration is incompatible because upper bounds of type parameters are different:
|
||||
public final expect class C3<D, E : D>
|
||||
|
||||
actual class C3<D, E : D?>
|
||||
^
|
||||
compiler/testData/multiplatform/incompatibleClasses/jvm.kt:18:20: error: actual typealias 'C4' has no corresponding expected declaration
|
||||
The following declaration is incompatible because upper bounds of type parameters are different:
|
||||
public final expect class C4<F>
|
||||
|
||||
actual typealias C4<F> = C4Impl<F>
|
||||
^
|
||||
compiler/testData/multiplatform/incompatibleClasses/jvm.kt:21:39: error: actual class 'ExtendsNumber' has no corresponding expected declaration
|
||||
The following declaration is incompatible because some supertypes are missing in the actual declaration:
|
||||
public abstract expect class ExtendsNumber : Number
|
||||
|
||||
@@ -14290,11 +14290,29 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform/generic"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("functionTypeParameterBounds.kt")
|
||||
public void testFunctionTypeParameterBounds() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/generic/functionTypeParameterBounds.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("genericMemberBounds.kt")
|
||||
public void testGenericMemberBounds() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/generic/genericMemberBounds.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("membersInGenericClass.kt")
|
||||
public void testMembersInGenericClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/generic/membersInGenericClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("typeParameterBoundsDifferentOrderActualMissing.kt")
|
||||
public void testTypeParameterBoundsDifferentOrderActualMissing() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/generic/typeParameterBoundsDifferentOrderActualMissing.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass")
|
||||
|
||||
Generated
+18
@@ -14290,11 +14290,29 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform/generic"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("functionTypeParameterBounds.kt")
|
||||
public void testFunctionTypeParameterBounds() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/generic/functionTypeParameterBounds.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("genericMemberBounds.kt")
|
||||
public void testGenericMemberBounds() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/generic/genericMemberBounds.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("membersInGenericClass.kt")
|
||||
public void testMembersInGenericClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/generic/membersInGenericClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("typeParameterBoundsDifferentOrderActualMissing.kt")
|
||||
public void testTypeParameterBoundsDifferentOrderActualMissing() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/generic/typeParameterBoundsDifferentOrderActualMissing.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass")
|
||||
|
||||
Reference in New Issue
Block a user