diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index 8729c48ea60..f3f7b6e2de6 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -883,7 +883,9 @@ public class FunctionCodegen { @NotNull Method bridge, @NotNull final Method delegateTo ) { - if (BuiltinMethodsWithSpecialGenericSignature.getOverriddenBuiltinFunctionWithErasedValueParametersInJava(descriptor) == null) return; + BuiltinMethodsWithSpecialGenericSignature.DefaultValue defaultValue = + BuiltinMethodsWithSpecialGenericSignature.getDefaultValueForOverriddenBuiltinFunction(descriptor); + if (defaultValue == null) return; assert descriptor.getValueParameters().size() == 1 : "Should be descriptor with one value parameter, but found: " + descriptor; @@ -904,12 +906,10 @@ public class FunctionCodegen { iv.ifne(afterBarrier); - String shortName = getFqName(descriptor).shortName().asString(); - StackValue returnValue = - ("indexOf".equals(shortName) || "lastIndexOf".equals(shortName)) ? StackValue.constant(-1, Type.INT_TYPE) : StackValue.none(); + Type returnType = bridge.getReturnType(); - returnValue.put(bridge.getReturnType(), iv); - iv.areturn(bridge.getReturnType()); + StackValue.constant(defaultValue.getValue(), returnType).put(returnType, iv); + iv.areturn(returnType); iv.visitLabel(afterBarrier); } diff --git a/compiler/testData/codegen/box/specialBuiltins/emptyList.kt b/compiler/testData/codegen/box/specialBuiltins/emptyList.kt new file mode 100644 index 00000000000..857d218abcd --- /dev/null +++ b/compiler/testData/codegen/box/specialBuiltins/emptyList.kt @@ -0,0 +1,25 @@ +private object EmptyList : List { + override fun contains(element: Nothing): Boolean = false + override fun containsAll(elements: Collection): Boolean = elements.isEmpty() + override fun indexOf(element: Nothing): Int = -2 + override fun lastIndexOf(element: Nothing): Int = -2 + + override val size: Int get() = 0 + override fun isEmpty(): Boolean = true + + override fun iterator(): Iterator = throw UnsupportedOperationException() + override fun get(index: Int): Nothing = throw UnsupportedOperationException() + override fun listIterator(): ListIterator = throw UnsupportedOperationException() + override fun listIterator(index: Int): ListIterator = throw UnsupportedOperationException() + override fun subList(fromIndex: Int, toIndex: Int): List = throw UnsupportedOperationException() +} + +fun box(): String { + val n = EmptyList as List + + if (n.contains(null)) return "fail 1" + if (n.indexOf(null) != -1) return "fail 2" + if (n.lastIndexOf(null) != -1) return "fail 3" + + return "OK" +} diff --git a/compiler/testData/codegen/box/specialBuiltins/emptyMap.kt b/compiler/testData/codegen/box/specialBuiltins/emptyMap.kt new file mode 100644 index 00000000000..c0770bd6b2a --- /dev/null +++ b/compiler/testData/codegen/box/specialBuiltins/emptyMap.kt @@ -0,0 +1,22 @@ +private object EmptyMap : Map { + override val size: Int get() = 0 + override fun isEmpty(): Boolean = true + + override fun containsKey(key: Any): Boolean = false + override fun containsValue(value: Nothing): Boolean = false + override fun get(key: Any): Nothing? = null + override val entries: Set> get() = null!! + override val keys: Set get() = null!! + override val values: Collection get() = null!! +} + + +fun box(): String { + val n = EmptyMap as Map + + if (n.get(null) != null) return "fail 1" + if (n.containsKey(null)) return "fail 2" + if (n.containsValue(null)) return "fail 3" + + return "OK" +} diff --git a/compiler/testData/codegen/box/specialBuiltins/emptyStringMap.kt b/compiler/testData/codegen/box/specialBuiltins/emptyStringMap.kt new file mode 100644 index 00000000000..fd4953af8ec --- /dev/null +++ b/compiler/testData/codegen/box/specialBuiltins/emptyStringMap.kt @@ -0,0 +1,21 @@ +private object EmptyStringMap : Map { + override val size: Int get() = 0 + override fun isEmpty(): Boolean = true + + override fun containsKey(key: String): Boolean = false + override fun containsValue(value: Nothing): Boolean = false + override fun get(key: String): Nothing? = null + override val entries: Set> get() = null!! + override val keys: Set get() = null!! + override val values: Collection get() = null!! +} + +fun box(): String { + val n = EmptyStringMap as Map + + if (n.get(null) != null) return "fail 1" + if (n.containsKey(null)) return "fail 2" + if (n.containsValue(null)) return "fail 3" + + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java index 9b8015c4a1e..bf69af456dc 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -7327,6 +7327,24 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("emptyList.kt") + public void testEmptyList() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/emptyList.kt"); + doTest(fileName); + } + + @TestMetadata("emptyMap.kt") + public void testEmptyMap() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/emptyMap.kt"); + doTest(fileName); + } + + @TestMetadata("emptyStringMap.kt") + public void testEmptyStringMap() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/emptyStringMap.kt"); + doTest(fileName); + } + @TestMetadata("enumAsOrdinaled.kt") public void testEnumAsOrdinaled() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/enumAsOrdinaled.kt"); diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt index 94051062961..6a9675003e6 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt @@ -59,8 +59,8 @@ object BuiltinSpecialProperties { return callableMemberDescriptor.hasBuiltinSpecialPropertyFqNameImpl() } - fun CallableMemberDescriptor.hasBuiltinSpecialPropertyFqNameImpl(): Boolean { - if (fqNameOrNull() in FQ_NAMES) return true + private fun CallableMemberDescriptor.hasBuiltinSpecialPropertyFqNameImpl(): Boolean { + if (FQ_NAMES.containsRaw(fqNameOrNull())) return true if (!isFromBuiltins()) return false return overriddenDescriptors.any { hasBuiltinSpecialPropertyFqName(it) } @@ -83,19 +83,26 @@ object BuiltinMethodsWithSpecialGenericSignature { FqName("kotlin.MutableCollection.removeAll"), FqName("kotlin.MutableCollection.retainAll") ) - private val GENERIC_PARAMETERS_FQ_NAMES = setOf( - FqName("kotlin.Collection.contains"), - FqName("kotlin.MutableCollection.remove"), - FqName("kotlin.Map.containsKey"), - FqName("kotlin.Map.containsValue"), - FqName("kotlin.Map.get"), - FqName("kotlin.MutableMap.remove"), - FqName("kotlin.List.indexOf"), - FqName("kotlin.List.lastIndexOf") + + public enum class DefaultValue(val value: Any?) { + NULL(null), INDEX(-1), FALSE(false) + } + + private val GENERIC_PARAMETERS_METHODS_TO_DEFAULT_VALUES_MAP = mapOf( + FqName("kotlin.Collection.contains") to DefaultValue.FALSE, + FqName("kotlin.MutableCollection.remove") to DefaultValue.FALSE, + FqName("kotlin.Map.containsKey") to DefaultValue.FALSE, + FqName("kotlin.Map.containsValue") to DefaultValue.FALSE, + + FqName("kotlin.Map.get") to DefaultValue.NULL, + FqName("kotlin.MutableMap.remove") to DefaultValue.NULL, + + FqName("kotlin.List.indexOf") to DefaultValue.INDEX, + FqName("kotlin.List.lastIndexOf") to DefaultValue.INDEX ) private val ERASED_VALUE_PARAMETERS_FQ_NAMES = - GENERIC_PARAMETERS_FQ_NAMES + ERASED_COLLECTION_PARAMETER_FQ_NAMES + GENERIC_PARAMETERS_METHODS_TO_DEFAULT_VALUES_MAP.keys + ERASED_COLLECTION_PARAMETER_FQ_NAMES private val ERASED_VALUE_PARAMETERS_SHORT_NAMES = ERASED_VALUE_PARAMETERS_FQ_NAMES.map { it.shortName() }.toSet() @@ -111,6 +118,14 @@ object BuiltinMethodsWithSpecialGenericSignature { return functionDescriptor.firstOverridden { it.hasErasedValueParametersInJava } as FunctionDescriptor? } + @JvmStatic + fun getDefaultValueForOverriddenBuiltinFunction(functionDescriptor: FunctionDescriptor): DefaultValue? { + if (functionDescriptor.name !in ERASED_VALUE_PARAMETERS_SHORT_NAMES) return null + return functionDescriptor.firstOverridden { + GENERIC_PARAMETERS_METHODS_TO_DEFAULT_VALUES_MAP.keys.containsRaw(it.fqNameOrNull()) + }?.let { GENERIC_PARAMETERS_METHODS_TO_DEFAULT_VALUES_MAP[it.fqNameSafe] } + } + val Name.sameAsBuiltinMethodWithErasedValueParameters: Boolean get () = this in ERASED_VALUE_PARAMETERS_SHORT_NAMES @@ -131,7 +146,7 @@ object BuiltinMethodsWithSpecialGenericSignature { return when (builtinFqName) { in ERASED_COLLECTION_PARAMETER_FQ_NAMES -> SpecialSignatureInfo.ONE_COLLECTION_PARAMETER - in GENERIC_PARAMETERS_FQ_NAMES -> SpecialSignatureInfo.GENERIC_PARAMETER + in GENERIC_PARAMETERS_METHODS_TO_DEFAULT_VALUES_MAP -> SpecialSignatureInfo.GENERIC_PARAMETER else -> error("Unexpected kind of special builtin: $builtinFqName") } }