Fix loading container type from Java
Load immutable flexible upper bound for 'Iterable<? super T>' We load 'Collection<? super CharSequence>' as 'MutableCollection<in CharSequence>' instead of 'MutableCollection<in CharSequence>..Collection<*>' because it's obviously not typesafe to use any 'Collection<*>' as argument for such type. But there'se nothing bad with loading 'Iterable<? super CharSequence>' as 'MutableIterable<*>..Collection<*>'. Same for other declarations that have covariant mutable representation (currently Iterator, ListIterator). Also there are useful use-cases when it's neccessary to use 'Iterable<*>' as an argument for parameter with type 'Iterable<? super T>' (see matchers.kt test). NB: Star-projections appear in examples because types like 'Collection<in CharSequence>' with conflicting use-site projections are invalid in Kotlin, but they are valid in Java.
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
// FILE: A.java
|
||||
import java.util.*;
|
||||
public class A {
|
||||
public static void foo(Iterable<? super CharSequence> x) {}
|
||||
public static void bar(Iterator<? super CharSequence> x) {}
|
||||
}
|
||||
|
||||
// FILE: main.kt
|
||||
|
||||
fun test(x: List<String>, y: List<*>, z: MutableList<*>, w: MutableList<in CharSequence>) {
|
||||
A.foo(x)
|
||||
A.foo(y)
|
||||
A.foo(z)
|
||||
A.foo(w)
|
||||
|
||||
A.bar(x.iterator())
|
||||
A.bar(y.iterator())
|
||||
A.bar(z.iterator())
|
||||
A.bar(w.iterator())
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package
|
||||
|
||||
public fun test(/*0*/ x: kotlin.collections.List<kotlin.String>, /*1*/ y: kotlin.collections.List<*>, /*2*/ z: kotlin.collections.MutableList<*>, /*3*/ w: kotlin.collections.MutableList<in kotlin.CharSequence>): kotlin.Unit
|
||||
|
||||
public open class A {
|
||||
public constructor 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
|
||||
|
||||
// Static members
|
||||
public open fun bar(/*0*/ x: kotlin.collections.(Mutable)Iterator<*>!): kotlin.Unit
|
||||
public open fun foo(/*0*/ x: kotlin.collections.(Mutable)Iterable<*>!): kotlin.Unit
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// FILE: Assert.java
|
||||
|
||||
public class Assert {
|
||||
public static <T> void assertThat(T actual, Matcher<? super T> matcher) {
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: Matcher.java
|
||||
public class Matcher<T> {
|
||||
public static <T> Matcher<java.lang.Iterable<? super T>> hasItem(T item) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: main.kt
|
||||
fun test(x: List<String>) {
|
||||
Assert.assertThat(x, Matcher.hasItem("abc"))
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package
|
||||
|
||||
public fun test(/*0*/ x: kotlin.collections.List<kotlin.String>): kotlin.Unit
|
||||
|
||||
public open class Assert {
|
||||
public constructor Assert()
|
||||
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
|
||||
|
||||
// Static members
|
||||
public open fun </*0*/ T : kotlin.Any!> assertThat(/*0*/ actual: T!, /*1*/ matcher: Matcher<in T!>!): kotlin.Unit
|
||||
}
|
||||
|
||||
public open class Matcher</*0*/ T : kotlin.Any!> {
|
||||
public constructor Matcher</*0*/ T : kotlin.Any!>()
|
||||
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
|
||||
|
||||
// Static members
|
||||
public open fun </*0*/ T : kotlin.Any!> hasItem(/*0*/ item: T!): Matcher<kotlin.collections.(Mutable)Iterable<*>!>!
|
||||
}
|
||||
@@ -10005,6 +10005,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("contravariantIterable.kt")
|
||||
public void testContravariantIterable() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/j+k/contravariantIterable.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("differentFilename.kt")
|
||||
public void testDifferentFilename() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/j+k/differentFilename.kt");
|
||||
@@ -10179,6 +10185,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("matchers.kt")
|
||||
public void testMatchers() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/j+k/matchers.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("mutableIterator.kt")
|
||||
public void testMutableIterator() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/j+k/mutableIterator.kt");
|
||||
|
||||
+16
-4
@@ -148,10 +148,8 @@ class LazyJavaTypeResolver(
|
||||
|
||||
if (javaToKotlin.isReadOnly(kotlinDescriptor)) {
|
||||
if (howThisTypeIsUsedEffectively == MEMBER_SIGNATURE_COVARIANT
|
||||
|| howThisTypeIsUsedEffectively == SUPERTYPE
|
||||
// Convert (Mutable)List<in A> to MutableList<in A>
|
||||
// Same for (Mutable)Map<K, in V>
|
||||
|| javaType.typeArguments.lastOrNull().isSuperWildcard()) {
|
||||
|| howThisTypeIsUsedEffectively == SUPERTYPE
|
||||
|| javaType.argumentsMakeSenseOnlyForMutableContainer(readOnlyContainer = kotlinDescriptor)) {
|
||||
return javaToKotlin.convertReadOnlyToMutable(kotlinDescriptor)
|
||||
}
|
||||
}
|
||||
@@ -159,6 +157,20 @@ class LazyJavaTypeResolver(
|
||||
return kotlinDescriptor
|
||||
}
|
||||
|
||||
// Returns true for covariant read-only container that has mutable pair with invariant parameter
|
||||
// List<in A> does not make sense, but MutableList<in A> does
|
||||
// Same for Map<K, in V>
|
||||
// But both Iterable<in A>, MutableIterable<in A> don't make sense as they are covariant, so return false
|
||||
private fun JavaClassifierType.argumentsMakeSenseOnlyForMutableContainer(
|
||||
readOnlyContainer: ClassDescriptor
|
||||
): Boolean {
|
||||
if (!typeArguments.lastOrNull().isSuperWildcard()) return false
|
||||
val mutableLastParameterVariance = JavaToKotlinClassMap.INSTANCE.convertReadOnlyToMutable(readOnlyContainer)
|
||||
.typeConstructor.parameters.lastOrNull()?.variance ?: return false
|
||||
|
||||
return mutableLastParameterVariance != OUT_VARIANCE
|
||||
}
|
||||
|
||||
private fun JavaType?.isSuperWildcard(): Boolean = (this as? JavaWildcardType)?.let { it.bound != null && !it.isExtends } ?: false
|
||||
|
||||
private fun isRaw(): Boolean {
|
||||
|
||||
Reference in New Issue
Block a user