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:
Denis Zharkov
2016-03-21 12:13:17 +03:00
parent c3e44ec199
commit 56477f0af8
6 changed files with 103 additions and 4 deletions
@@ -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 {