Add temporary hack for wildcards in Collections
By default we would render 'MutableCollection<String>.addAll(Collection<String>)' as '(LCollection<String>;)' (without wildcard) because String is final and effectively it's the same as '(LCollection<? extends String>;)'. But that's wrong signature in a sense that java.util.Collection has different signature: '(LCollection<? extends E>)'. Actually the problem is much wider than collections, it concerns any Java code that uses Kotlin classes with covariant parameters without '? extends E' wildcards. Temporary solution is just to hardcode/enumerate builtin methods with special signature.
This commit is contained in:
@@ -19,16 +19,20 @@ package org.jetbrains.kotlin.codegen
|
||||
import org.jetbrains.kotlin.backend.common.bridges.DescriptorBasedFunctionHandle
|
||||
import org.jetbrains.kotlin.backend.common.bridges.findAllReachableDeclarations
|
||||
import org.jetbrains.kotlin.backend.common.bridges.findConcreteSuperDeclaration
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.load.java.*
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature
|
||||
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.getSpecialSignatureInfo
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
||||
import org.jetbrains.kotlin.load.java.getOverriddenBuiltinWithDifferentJvmDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtPsiUtil
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getParentCall
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.firstOverridden
|
||||
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
||||
import java.util.*
|
||||
|
||||
|
||||
@@ -1200,7 +1200,18 @@ public class JetTypeMapper {
|
||||
@NotNull CallableDescriptor callableDescriptor
|
||||
) {
|
||||
sw.writeParameterType(kind);
|
||||
mapType(type, sw, TypeMappingMode.getOptimalModeForValueParameter(type));
|
||||
|
||||
TypeMappingMode typeMappingMode;
|
||||
|
||||
if (TypeMappingUtil.isMethodWithDeclarationSiteWildcards(callableDescriptor) && !type.getArguments().isEmpty()) {
|
||||
typeMappingMode = TypeMappingMode.GENERIC_TYPE; // Render all wildcards
|
||||
}
|
||||
else {
|
||||
typeMappingMode = TypeMappingMode.getOptimalModeForValueParameter(type);
|
||||
}
|
||||
|
||||
mapType(type, sw, typeMappingMode);
|
||||
|
||||
sw.writeParameterTypeEnd();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,11 @@
|
||||
package org.jetbrains.kotlin.codegen.state
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.firstOverridden
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.propertyIfAccessor
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
|
||||
@@ -67,3 +70,16 @@ public fun getEffectiveVariance(parameterVariance: Variance, projectionKind: Var
|
||||
return Variance.OUT_VARIANCE
|
||||
}
|
||||
|
||||
val CallableDescriptor.isMethodWithDeclarationSiteWildcards: Boolean
|
||||
get() {
|
||||
if (this !is CallableMemberDescriptor) return false
|
||||
return firstOverridden {
|
||||
METHODS_WITH_DECLARATION_SITE_WILDCARDS.containsRaw(it.propertyIfAccessor.fqNameOrNull())
|
||||
} != null
|
||||
}
|
||||
|
||||
private val METHODS_WITH_DECLARATION_SITE_WILDCARDS = setOf(
|
||||
FqName("kotlin.MutableCollection.addAll"),
|
||||
FqName("kotlin.MutableList.addAll"),
|
||||
FqName("kotlin.MutableMap.putAll")
|
||||
)
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import java.util.*;
|
||||
|
||||
public class J {
|
||||
|
||||
private static class MyList<E> extends KList<E> {}
|
||||
|
||||
public static String foo() {
|
||||
Collection<String> collection = new MyList<String>();
|
||||
if (!collection.contains("ABCDE")) return "fail 1";
|
||||
if (!collection.containsAll(Arrays.asList(1, 2, 3))) return "fail 2";
|
||||
return "OK";
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
open class KList<E> : List<E> {
|
||||
override val size: Int
|
||||
get() = throw UnsupportedOperationException()
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
override fun contains(o: E) = true
|
||||
override fun containsAll(c: Collection<E>) = true
|
||||
|
||||
override fun iterator(): Iterator<E> {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun get(index: Int): E {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun indexOf(element: E): Int {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun lastIndexOf(element: E): Int {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun listIterator(): ListIterator<E> {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun listIterator(index: Int): ListIterator<E> {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun subList(fromIndex: Int, toIndex: Int): List<E> {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
|
||||
fun box() = J.foo()
|
||||
@@ -1,4 +1,4 @@
|
||||
open class KList : MutableList<String> {
|
||||
abstract class KList : MutableList<String> {
|
||||
override val size: Int
|
||||
get() = throw UnsupportedOperationException()
|
||||
|
||||
|
||||
+6
@@ -287,6 +287,12 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege
|
||||
doTestWithJava(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("readOnlyList")
|
||||
public void testReadOnlyList() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/collections/readOnlyList/");
|
||||
doTestWithJava(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("removeAtInt")
|
||||
public void testRemoveAtInt() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/collections/removeAtInt/");
|
||||
|
||||
+1
-33
@@ -27,13 +27,8 @@ import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
||||
import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||
|
||||
object BuiltinSpecialProperties {
|
||||
private val PROPERTY_FQ_NAME_TO_JVM_GETTER_NAME_MAP = mapOf(
|
||||
@@ -280,33 +275,6 @@ private fun CallableMemberDescriptor.isFromBuiltins(): Boolean {
|
||||
this.module == this.builtIns.builtInsModule
|
||||
}
|
||||
|
||||
private val CallableMemberDescriptor.propertyIfAccessor: CallableMemberDescriptor
|
||||
get() = if (this is PropertyAccessorDescriptor) correspondingProperty else this
|
||||
|
||||
private fun CallableDescriptor.fqNameOrNull(): FqName? = fqNameUnsafe.check { it.isSafe }?.toSafe()
|
||||
|
||||
public fun CallableMemberDescriptor.firstOverridden(
|
||||
predicate: (CallableMemberDescriptor) -> Boolean
|
||||
): CallableMemberDescriptor? {
|
||||
var result: CallableMemberDescriptor? = null
|
||||
return DFS.dfs(listOf(this),
|
||||
object : DFS.Neighbors<CallableMemberDescriptor> {
|
||||
override fun getNeighbors(current: CallableMemberDescriptor?): Iterable<CallableMemberDescriptor> {
|
||||
return current?.overriddenDescriptors ?: emptyList()
|
||||
}
|
||||
},
|
||||
object : DFS.AbstractNodeHandler<CallableMemberDescriptor, CallableMemberDescriptor?>() {
|
||||
override fun beforeChildren(current: CallableMemberDescriptor) = result == null
|
||||
override fun afterChildren(current: CallableMemberDescriptor) {
|
||||
if (result == null && predicate(current)) {
|
||||
result = current
|
||||
}
|
||||
}
|
||||
override fun result(): CallableMemberDescriptor? = result
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
public fun CallableMemberDescriptor.isFromJavaOrBuiltins() = isFromJava || isFromBuiltins()
|
||||
|
||||
private fun Map<FqName, Name>.getInversedShortNamesMap(): Map<Name, List<Name>> =
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.constants.EnumValue
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||
|
||||
public fun ClassDescriptor.getClassObjectReferenceTarget(): ClassDescriptor = getCompanionObjectDescriptor() ?: this
|
||||
|
||||
@@ -197,3 +198,29 @@ public val DeclarationDescriptor.parentsWithSelf: Sequence<DeclarationDescriptor
|
||||
public val DeclarationDescriptor.parents: Sequence<DeclarationDescriptor>
|
||||
get() = parentsWithSelf.drop(1)
|
||||
|
||||
val CallableMemberDescriptor.propertyIfAccessor: CallableMemberDescriptor
|
||||
get() = if (this is PropertyAccessorDescriptor) correspondingProperty else this
|
||||
|
||||
fun CallableDescriptor.fqNameOrNull(): FqName? = fqNameUnsafe.check { it.isSafe }?.toSafe()
|
||||
|
||||
public fun CallableMemberDescriptor.firstOverridden(
|
||||
predicate: (CallableMemberDescriptor) -> Boolean
|
||||
): CallableMemberDescriptor? {
|
||||
var result: CallableMemberDescriptor? = null
|
||||
return DFS.dfs(listOf(this),
|
||||
object : DFS.Neighbors<CallableMemberDescriptor> {
|
||||
override fun getNeighbors(current: CallableMemberDescriptor?): Iterable<CallableMemberDescriptor> {
|
||||
return current?.overriddenDescriptors ?: emptyList()
|
||||
}
|
||||
},
|
||||
object : DFS.AbstractNodeHandler<CallableMemberDescriptor, CallableMemberDescriptor?>() {
|
||||
override fun beforeChildren(current: CallableMemberDescriptor) = result == null
|
||||
override fun afterChildren(current: CallableMemberDescriptor) {
|
||||
if (result == null && predicate(current)) {
|
||||
result = current
|
||||
}
|
||||
}
|
||||
override fun result(): CallableMemberDescriptor? = result
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user