diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java index 28fa6166b49..9ba6a9ccaca 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java @@ -969,8 +969,12 @@ public class KotlinTypeMapper { } for (ValueParameterDescriptor parameter : valueParameters) { - if (writeCustomParameter(f, parameter, sw)) continue; - writeParameter(sw, parameter.getType(), f); + boolean forceBoxing = MethodSignatureMappingKt.forceSingleValueParameterBoxing(f); + writeParameter( + sw, + forceBoxing ? TypeUtils.makeNullable(parameter.getType()) : parameter.getType(), + f + ); } sw.writeReturnType(); @@ -1020,24 +1024,6 @@ public class KotlinTypeMapper { } } - private boolean writeCustomParameter( - @NotNull FunctionDescriptor f, - @NotNull ValueParameterDescriptor parameter, - @NotNull JvmSignatureWriter sw - ) { - FunctionDescriptor overridden = - BuiltinMethodsWithSpecialGenericSignature.getOverriddenBuiltinFunctionWithErasedValueParametersInJava(f); - if (overridden == null) return false; - if (SpecialBuiltinMembers.isFromJavaOrBuiltins(f)) return false; - - if (overridden.getName().asString().equals("remove") && mapType(parameter.getType()).getSort() == Type.INT) { - writeParameter(sw, TypeUtils.makeNullable(parameter.getType()), f); - return true; - } - - return false; - } - @NotNull private static String getDefaultDescriptor(@NotNull Method method, @Nullable String dispatchReceiverDescriptor, boolean isExtension) { String descriptor = method.getDescriptor(); diff --git a/compiler/testData/codegen/boxAgainstJava/platformTypes/specializedMapFull.kt b/compiler/testData/codegen/boxAgainstJava/platformTypes/specializedMapFull.kt new file mode 100644 index 00000000000..6829b8262f8 --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/platformTypes/specializedMapFull.kt @@ -0,0 +1,119 @@ +// FILE: AbstractSpecializedMap.java +public abstract class AbstractSpecializedMap implements java.util.Map { + public abstract double put(int x, double y); + public abstract double remove(int k); + public abstract double get(int k); + + public abstract boolean containsKey(int k); + public boolean containsKey(Object x) { + return false; + } + + public abstract boolean containsValue(double v); + public boolean containsValue(Object x) { + return false; + } +} + +// FILE: SpecializedMap.java +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +public class SpecializedMap extends AbstractSpecializedMap { + public double put(int x, double y) { + return 123.0; + } + + @Override + public Double get(Object key) { + return null; + } + + @Override + public Double put(Integer key, Double value) { + return null; + } + + public double remove(int k) { + return 456.0; + } + + + public Double remove(Object ok) { + return null; + } + + + public double get(int k) { + return 789.0; + } + + public boolean containsKey(int k) { + return true; + } + + public boolean containsValue(double v) { + return true; + } + + @Override + public void putAll(Map m) { + } + + @Override + public void clear() { + + } + + @NotNull + @Override + public Set keySet() { + return null; + } + + @NotNull + @Override + public Collection values() { + return null; + } + + @NotNull + @Override + public Set> entrySet() { + return null; + } + + @Override + public int size() { + return 0; + } + + @Override + public boolean isEmpty() { + return false; + } +} + +// FILE: main.kt +fun box(): String { + val x = SpecializedMap() + if (!x.containsKey(1)) return "fail 1" + if (x.containsKey(null)) return "fail 2" + + if (!x.containsValue(2.0)) return "fail 3" + if (x.containsValue(null)) return "fail 4" + + if (x.put(1, 5.0) != 123.0) return "fail 5" + if (x.put(1, null) != null) return "fail 6" + + if (x.remove(1) != 456.0) return "fail 7" + if (x.remove(null) != null) return "fail 8" + + if (x.get(1) != 789.0) return "fail 9" + if (x.get(null) != null) return "fail 10" + + return "OK" +} diff --git a/compiler/testData/codegen/boxAgainstJava/platformTypes/specializedMapPut.kt b/compiler/testData/codegen/boxAgainstJava/platformTypes/specializedMapPut.kt new file mode 100644 index 00000000000..abeca9a65b3 --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/platformTypes/specializedMapPut.kt @@ -0,0 +1,21 @@ +// FILE: A.java +import java.util.HashMap; + +public class A extends HashMap { + public double put(int x, double y) { + return 1.0; + } + + @Override + public Double put(Integer key, Double value) { + return super.put(key, value); + } +} + +// FILE: main.kt +fun box(): String { + val o = A() + o.put(1, 2.0) + + return "OK" +} diff --git a/compiler/testData/diagnostics/tests/j+k/primitiveOverrides/kt11140.kt b/compiler/testData/diagnostics/tests/j+k/primitiveOverrides/kt11140.kt new file mode 100644 index 00000000000..9fc2f5446c7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/primitiveOverrides/kt11140.kt @@ -0,0 +1,23 @@ +// FILE: B.java +public interface B { + double put(int x, double y); + T2 put(T1 x, T2 y); +} +// FILE: A.java +import java.util.HashMap; + +public class A extends HashMap implements B { + public double put(int x, double y) { + return 1.0; + } + + @Override + public Double put(Integer key, Double value) { + return super.put(key, value); + } +} +// FILE: main.kt +fun test(){ + val o = A() + o.put(1, 2.0) +} diff --git a/compiler/testData/diagnostics/tests/j+k/primitiveOverrides/kt11140.txt b/compiler/testData/diagnostics/tests/j+k/primitiveOverrides/kt11140.txt new file mode 100644 index 00000000000..300490dcd06 --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/primitiveOverrides/kt11140.txt @@ -0,0 +1,73 @@ +package + +public fun test(): kotlin.Unit + +public open class A : java.util.HashMap, B { + public constructor A() + public open override /*1*/ /*fake_override*/ val entries: kotlin.collections.MutableSet> + invisible_fake final override /*1*/ /*fake_override*/ var entrySet: kotlin.collections.(Mutable)Set!>! + invisible_fake final override /*1*/ /*fake_override*/ val hashSeed: kotlin.Int + invisible_fake final override /*1*/ /*fake_override*/ var keySet: kotlin.collections.(Mutable)Set! + public open override /*1*/ /*fake_override*/ val keys: kotlin.collections.MutableSet + invisible_fake final override /*1*/ /*fake_override*/ val loadFactor: kotlin.Float + invisible_fake final override /*1*/ /*fake_override*/ var modCount: kotlin.Int + invisible_fake final override /*1*/ /*fake_override*/ var size: kotlin.Int + public open override /*1*/ /*fake_override*/ val size: kotlin.Int + invisible_fake final override /*1*/ /*fake_override*/ var table: kotlin.Array<(out) java.util.HashMap.Entry!>! + invisible_fake final override /*1*/ /*fake_override*/ var threshold: kotlin.Int + invisible_fake final override /*1*/ /*fake_override*/ var useAltHashing: kotlin.Boolean + invisible_fake final override /*1*/ /*fake_override*/ var values: kotlin.collections.(Mutable)Collection! + public open override /*1*/ /*fake_override*/ val values: kotlin.collections.MutableCollection + invisible_fake open override /*1*/ /*fake_override*/ fun addEntry(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Int!, /*2*/ p2: kotlin.Double!, /*3*/ p3: kotlin.Int): kotlin.Unit + invisible_fake open override /*1*/ /*fake_override*/ fun capacity(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun clear(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public open override /*1*/ /*fake_override*/ fun containsKey(/*0*/ key: kotlin.Int!): kotlin.Boolean + invisible_fake open override /*1*/ /*fake_override*/ fun containsNullValue(): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun containsValue(/*0*/ value: kotlin.Double!): kotlin.Boolean + invisible_fake open override /*1*/ /*fake_override*/ fun createEntry(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Int!, /*2*/ p2: kotlin.Double!, /*3*/ p3: kotlin.Int): kotlin.Unit + invisible_fake open override /*1*/ /*fake_override*/ fun entrySet0(): kotlin.collections.(Mutable)Set!>! + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun get(/*0*/ key: kotlin.Int!): kotlin.Double? + invisible_fake final override /*1*/ /*fake_override*/ fun getEntry(/*0*/ p0: kotlin.Any!): java.util.HashMap.Entry! + invisible_fake open override /*1*/ /*fake_override*/ fun getForNullKey(): kotlin.Double! + invisible_fake final override /*1*/ /*fake_override*/ fun hash(/*0*/ p0: kotlin.Any!): kotlin.Int + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + invisible_fake open override /*1*/ /*fake_override*/ fun init(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun isEmpty(): kotlin.Boolean + invisible_fake open override /*1*/ /*fake_override*/ fun loadFactor(): kotlin.Float + invisible_fake open override /*1*/ /*fake_override*/ fun newEntryIterator(): kotlin.collections.(Mutable)Iterator!>! + invisible_fake open override /*1*/ /*fake_override*/ fun newKeyIterator(): kotlin.collections.(Mutable)Iterator! + invisible_fake open override /*1*/ /*fake_override*/ fun newValueIterator(): kotlin.collections.(Mutable)Iterator! + public open override /*1*/ fun put(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Double): kotlin.Double + @java.lang.Override() public open override /*2*/ fun put(/*0*/ key: kotlin.Int!, /*1*/ value: kotlin.Double!): kotlin.Double? + public open override /*1*/ /*fake_override*/ fun putAll(/*0*/ from: kotlin.collections.Map): kotlin.Unit + invisible_fake open override /*1*/ /*fake_override*/ fun putAllForCreate(/*0*/ p0: (kotlin.collections.MutableMap..kotlin.collections.Map?)): kotlin.Unit + invisible_fake open override /*1*/ /*fake_override*/ fun putForCreate(/*0*/ p0: kotlin.Int!, /*1*/ p1: kotlin.Double!): kotlin.Unit + invisible_fake open override /*1*/ /*fake_override*/ fun putForNullKey(/*0*/ p0: kotlin.Double!): kotlin.Double! + invisible_fake open override /*1*/ /*fake_override*/ fun readObject(/*0*/ p0: java.io.ObjectInputStream!): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun remove(/*0*/ key: kotlin.Int!): kotlin.Double? + invisible_fake final override /*1*/ /*fake_override*/ fun removeEntryForKey(/*0*/ p0: kotlin.Any!): java.util.HashMap.Entry! + invisible_fake final override /*1*/ /*fake_override*/ fun removeMapping(/*0*/ p0: kotlin.Any!): java.util.HashMap.Entry! + invisible_fake open override /*1*/ /*fake_override*/ fun resize(/*0*/ p0: kotlin.Int): kotlin.Unit + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + invisible_fake open override /*1*/ /*fake_override*/ fun transfer(/*0*/ p0: kotlin.Array<(out) java.util.HashMap.Entry<(raw) kotlin.Any!, (raw) kotlin.Any!>!>!, /*1*/ p1: kotlin.Boolean): kotlin.Unit + invisible_fake open override /*1*/ /*fake_override*/ fun writeObject(/*0*/ p0: java.io.ObjectOutputStream!): kotlin.Unit + + // Static members + invisible_fake const final override /*1*/ /*fake_override*/ val ALTERNATIVE_HASHING_THRESHOLD_DEFAULT: kotlin.Int + invisible_fake const final override /*1*/ /*fake_override*/ val DEFAULT_INITIAL_CAPACITY: kotlin.Int + invisible_fake const final override /*1*/ /*fake_override*/ val DEFAULT_LOAD_FACTOR: kotlin.Float + invisible_fake const final override /*1*/ /*fake_override*/ val MAXIMUM_CAPACITY: kotlin.Int + invisible_fake const final override /*1*/ /*fake_override*/ val serialVersionUID: kotlin.Long + invisible_fake open override /*1*/ /*fake_override*/ fun eq(/*0*/ p0: kotlin.Any!, /*1*/ p1: kotlin.Any!): kotlin.Boolean + invisible_fake open override /*1*/ /*fake_override*/ fun indexFor(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Int): kotlin.Int +} + +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 abstract fun put(/*0*/ x: T1!, /*1*/ y: T2!): T2! + public abstract fun put(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Double): kotlin.Double + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/j+k/primitiveOverrides/notNullAnnotated.kt b/compiler/testData/diagnostics/tests/j+k/primitiveOverrides/notNullAnnotated.kt new file mode 100644 index 00000000000..36ce0a1aa39 --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/primitiveOverrides/notNullAnnotated.kt @@ -0,0 +1,22 @@ +// FILE: A.java +import org.jetbrains.annotations.*; + +public class A { + public void foo(int x) {} + public void bar(@NotNull Double x) {} +} + +// FILE: B.java +import org.jetbrains.annotations.*; +public class B extends A { + public void foo(@NotNull Integer x) {} + public void bar(double x) {} +} + +// FILE: main.kt + +fun foo(b: B) { + // See KT-9182 + b.foo(1) + b.bar(2.0) +} diff --git a/compiler/testData/diagnostics/tests/j+k/primitiveOverrides/notNullAnnotated.txt b/compiler/testData/diagnostics/tests/j+k/primitiveOverrides/notNullAnnotated.txt new file mode 100644 index 00000000000..cc1dbdf050b --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/primitiveOverrides/notNullAnnotated.txt @@ -0,0 +1,23 @@ +package + +public fun foo(/*0*/ b: B): kotlin.Unit + +public open class A { + public constructor A() + public open fun bar(/*0*/ @org.jetbrains.annotations.NotNull() x: kotlin.Double): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open fun foo(/*0*/ x: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public open class B : A { + public constructor B() + public open fun bar(/*0*/ x: kotlin.Double): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun bar(/*0*/ @org.jetbrains.annotations.NotNull() x: kotlin.Double): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open fun foo(/*0*/ @org.jetbrains.annotations.NotNull() x: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun foo(/*0*/ x: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/j+k/primitiveOverrides/specializedMap.kt b/compiler/testData/diagnostics/tests/j+k/primitiveOverrides/specializedMap.kt new file mode 100644 index 00000000000..1aaaa82660d --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/primitiveOverrides/specializedMap.kt @@ -0,0 +1,113 @@ +// FILE: AbstractSpecializedMap.java +public abstract class AbstractSpecializedMap implements java.util.Map { + public abstract double put(int x, double y); + public abstract double remove(int k); + public abstract double get(int k); + + public abstract boolean containsKey(int k); + public boolean containsKey(Object x) { + return false; + } + + public abstract boolean containsValue(double v); + public boolean containsValue(Object x) { + return false; + } +} + +// FILE: SpecializedMap.java +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +public class SpecializedMap extends AbstractSpecializedMap { + public double put(int x, double y) { + return 123.0; + } + + @Override + public Double get(Object key) { + return null; + } + + @Override + public Double put(Integer key, Double value) { + return null; + } + + public double remove(int k) { + return 456.0; + } + + + public Double remove(Object ok) { + return null; + } + + + public double get(int k) { + return 789.0; + } + + public boolean containsKey(int k) { + return true; + } + + public boolean containsValue(double v) { + return true; + } + + @Override + public void putAll(Map m) { + } + + @Override + public void clear() { + + } + + @NotNull + @Override + public Set keySet() { + return null; + } + + @NotNull + @Override + public Collection values() { + return null; + } + + @NotNull + @Override + public Set> entrySet() { + return null; + } + + @Override + public int size() { + return 0; + } + + @Override + public boolean isEmpty() { + return false; + } +} + +// FILE: main.kt +fun foo(x: SpecializedMap) { + x.containsKey(1) + x.containsKey(null) + + x.get(2) + x.get(null) + + x.remove(3) + x.remove(null) + + x.put(4, 5.0) + x.put(4, null) +} diff --git a/compiler/testData/diagnostics/tests/j+k/primitiveOverrides/specializedMap.txt b/compiler/testData/diagnostics/tests/j+k/primitiveOverrides/specializedMap.txt new file mode 100644 index 00000000000..2dd6f367098 --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/primitiveOverrides/specializedMap.txt @@ -0,0 +1,51 @@ +package + +public fun foo(/*0*/ x: SpecializedMap): kotlin.Unit + +public abstract class AbstractSpecializedMap : kotlin.collections.MutableMap { + public constructor AbstractSpecializedMap() + public abstract override /*1*/ /*fake_override*/ val entries: kotlin.collections.MutableSet> + public abstract override /*1*/ /*fake_override*/ val keys: kotlin.collections.MutableSet + public abstract override /*1*/ /*fake_override*/ val size: kotlin.Int + public abstract override /*1*/ /*fake_override*/ val values: kotlin.collections.MutableCollection + public abstract override /*1*/ /*fake_override*/ fun clear(): kotlin.Unit + public abstract fun containsKey(/*0*/ k: kotlin.Int): kotlin.Boolean + public open override /*1*/ fun containsKey(/*0*/ x: kotlin.Int!): kotlin.Boolean + public abstract fun containsValue(/*0*/ v: kotlin.Double): kotlin.Boolean + public open override /*1*/ fun containsValue(/*0*/ x: kotlin.Double!): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract operator fun get(/*0*/ k: kotlin.Int): kotlin.Double + public abstract override /*1*/ /*fake_override*/ fun get(/*0*/ key: kotlin.Int!): kotlin.Double? + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun isEmpty(): kotlin.Boolean + public abstract fun put(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Double): kotlin.Double + public abstract override /*1*/ /*fake_override*/ fun put(/*0*/ key: kotlin.Int!, /*1*/ value: kotlin.Double!): kotlin.Double? + public abstract override /*1*/ /*fake_override*/ fun putAll(/*0*/ from: kotlin.collections.Map): kotlin.Unit + public abstract fun remove(/*0*/ k: kotlin.Int): kotlin.Double + public abstract override /*1*/ /*fake_override*/ fun remove(/*0*/ key: kotlin.Int!): kotlin.Double? + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public open class SpecializedMap : AbstractSpecializedMap { + public constructor SpecializedMap() + public open override /*1*/ val entries: kotlin.collections.MutableSet> + public open override /*1*/ val keys: kotlin.collections.MutableSet + public open override /*1*/ val size: kotlin.Int + public open override /*1*/ val values: kotlin.collections.MutableCollection + @java.lang.Override() public open override /*1*/ fun clear(): kotlin.Unit + public open override /*1*/ fun containsKey(/*0*/ k: kotlin.Int): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun containsKey(/*0*/ x: kotlin.Int!): kotlin.Boolean + public open override /*1*/ fun containsValue(/*0*/ v: kotlin.Double): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun containsValue(/*0*/ x: kotlin.Double!): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ fun get(/*0*/ k: kotlin.Int): kotlin.Double + @java.lang.Override() public open override /*1*/ fun get(/*0*/ key: kotlin.Int!): kotlin.Double? + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + @java.lang.Override() public open override /*1*/ fun isEmpty(): kotlin.Boolean + public open override /*1*/ fun put(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Double): kotlin.Double + @java.lang.Override() public open override /*1*/ fun put(/*0*/ key: kotlin.Int!, /*1*/ value: kotlin.Double!): kotlin.Double? + @java.lang.Override() public open override /*1*/ fun putAll(/*0*/ from: kotlin.collections.Map): kotlin.Unit + public open override /*1*/ fun remove(/*0*/ k: kotlin.Int): kotlin.Double + public open override /*1*/ fun remove(/*0*/ ok: kotlin.Int!): kotlin.Double? + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index ccb8906f76d..939a2a32f42 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -11118,6 +11118,33 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { } } + @TestMetadata("compiler/testData/diagnostics/tests/j+k/primitiveOverrides") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class PrimitiveOverrides extends AbstractDiagnosticsTest { + public void testAllFilesPresentInPrimitiveOverrides() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/j+k/primitiveOverrides"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("kt11140.kt") + public void testKt11140() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/j+k/primitiveOverrides/kt11140.kt"); + doTest(fileName); + } + + @TestMetadata("notNullAnnotated.kt") + public void testNotNullAnnotated() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/j+k/primitiveOverrides/notNullAnnotated.kt"); + doTest(fileName); + } + + @TestMetadata("specializedMap.kt") + public void testSpecializedMap() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/j+k/primitiveOverrides/specializedMap.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/diagnostics/tests/j+k/properties") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxAgainstJavaCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxAgainstJavaCodegenTestGenerated.java index 99315396b8c..13b39e298e3 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxAgainstJavaCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxAgainstJavaCodegenTestGenerated.java @@ -366,6 +366,18 @@ public class BlackBoxAgainstJavaCodegenTestGenerated extends AbstractBlackBoxAga String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxAgainstJava/platformTypes/genericUnit.kt"); doTest(fileName); } + + @TestMetadata("specializedMapFull.kt") + public void testSpecializedMapFull() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxAgainstJava/platformTypes/specializedMapFull.kt"); + doTest(fileName); + } + + @TestMetadata("specializedMapPut.kt") + public void testSpecializedMapPut() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxAgainstJava/platformTypes/specializedMapPut.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/codegen/boxAgainstJava/property") diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JavaIncompatibilityRulesOverridabilityCondition.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JavaIncompatibilityRulesOverridabilityCondition.kt index b1fe5c591f4..5cdab84e488 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JavaIncompatibilityRulesOverridabilityCondition.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JavaIncompatibilityRulesOverridabilityCondition.kt @@ -16,15 +16,19 @@ package org.jetbrains.kotlin.load.java +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.load.java.BuiltinMethodsWithDifferentJvmName.sameAsRenamedInJvmBuiltin import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.sameAsBuiltinMethodWithErasedValueParameters import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor +import org.jetbrains.kotlin.load.kotlin.JvmType +import org.jetbrains.kotlin.load.kotlin.forceSingleValueParameterBoxing +import org.jetbrains.kotlin.load.kotlin.mapToJvmType import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition.Result -import org.jetbrains.kotlin.resolve.OverridingUtil -import org.jetbrains.kotlin.resolve.descriptorUtil.isDocumentedAnnotation +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.types.typeUtil.makeNullable /** * This class contains Java-related overridability conditions that may force incompatibility @@ -39,6 +43,10 @@ class JavaIncompatibilityRulesOverridabilityCondition : ExternalOverridabilityCo return Result.INCOMPATIBLE } + if (doesJavaOverrideHaveIncompatibleValueParameterKinds(superDescriptor, subDescriptor)) { + return Result.INCOMPATIBLE + } + return Result.UNKNOWN } @@ -84,5 +92,53 @@ class JavaIncompatibilityRulesOverridabilityCondition : ExternalOverridabilityCo return true } + override fun getContract() = ExternalOverridabilityCondition.Contract.CONFLICTS_ONLY + + companion object { + /** + * Checks if any pair of corresponding value parameters has different type kinds, e.g. one is primitive and another is not + * + * As it comes from it's name it only checks overrides in Java classes + */ + fun doesJavaOverrideHaveIncompatibleValueParameterKinds( + superDescriptor: CallableDescriptor, + subDescriptor: CallableDescriptor + ): Boolean { + if (subDescriptor !is JavaMethodDescriptor || superDescriptor !is FunctionDescriptor) return false + assert(subDescriptor.valueParameters.size == superDescriptor.valueParameters.size) { + "External overridability condition with CONFLICTS_ONLY should not be run with different value parameters size" + } + + for ((subParameter, superParameter) in subDescriptor.original.valueParameters.zip(superDescriptor.original.valueParameters)) { + val isSubPrimitive = mapValueParameterType(subDescriptor, subParameter) is JvmType.Primitive + val isSuperPrimitive = mapValueParameterType(superDescriptor, superParameter) is JvmType.Primitive + + if (isSubPrimitive != isSuperPrimitive) { + return true + } + } + + return false + } + + private fun mapValueParameterType(f: FunctionDescriptor, valueParameterDescriptor: ValueParameterDescriptor) = + if (forceSingleValueParameterBoxing(f) || isPrimitiveCompareTo(f)) + valueParameterDescriptor.type.makeNullable().mapToJvmType() + else + valueParameterDescriptor.type.mapToJvmType() + + // It's useful here to suppose that 'Int.compareTo(Int)' requires boxing of it's value parameter + // As it happens in java.lang.Integer analogue + // It only affects additional built-ins loading (see 'testLoadBuiltIns' tests) + private fun isPrimitiveCompareTo(f: FunctionDescriptor): Boolean { + if (f.valueParameters.size != 1) return false + val classDescriptor = + f.containingDeclaration as? ClassDescriptor ?: return false + val parameterClass = + f.valueParameters.single().type.constructor.declarationDescriptor as? ClassDescriptor + ?: return false + return KotlinBuiltIns.isPrimitiveClass(classDescriptor) && classDescriptor.fqNameSafe == parameterClass.fqNameSafe + } + } } diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt index 6bf5848c755..10b680b0a71 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt @@ -49,6 +49,7 @@ import org.jetbrains.kotlin.load.java.typeEnhancement.enhanceSignatures import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorFactory import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition import org.jetbrains.kotlin.resolve.OverridingUtil import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter @@ -164,9 +165,12 @@ class LazyJavaClassMemberScope( } private fun CallableDescriptor.doesOverride(superDescriptor: CallableDescriptor): Boolean { - return OverridingUtil.DEFAULT.isOverridableByWithoutExternalConditions( - superDescriptor, this, /* checkReturnType = */ true - ).result == OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE + val commonOverridabilityResult = + OverridingUtil.DEFAULT.isOverridableByWithoutExternalConditions(superDescriptor, this, true).result + + return commonOverridabilityResult == OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE && + !JavaIncompatibilityRulesOverridabilityCondition.doesJavaOverrideHaveIncompatibleValueParameterKinds( + superDescriptor, this) } private fun PropertyDescriptor.findGetterOverride( diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/methodSignatureMapping.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/methodSignatureMapping.kt index 0d8584d2130..79375f7dcf4 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/methodSignatureMapping.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/methodSignatureMapping.kt @@ -17,6 +17,8 @@ package org.jetbrains.kotlin.load.kotlin import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature +import org.jetbrains.kotlin.load.java.isFromJavaOrBuiltins import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.platform.JavaToKotlinClassMap import org.jetbrains.kotlin.resolve.DescriptorUtils @@ -44,6 +46,17 @@ fun FunctionDescriptor.computeJvmDescriptor() } }.toString() +// Boxing is only necessary for 'remove(E): Boolean' of a MutableList implementation +// Otherwise this method will clash with 'remove(I): E' also defined in the JDK interface (mapped to kotlin 'removeAt') +fun forceSingleValueParameterBoxing(f: FunctionDescriptor): Boolean { + if (f.isFromJavaOrBuiltins()) return false + + if (f.name.asString() != "remove" || + (f.original.valueParameters.single().type.mapToJvmType() as? JvmType.Primitive)?.jvmPrimitiveType != JvmPrimitiveType.INT) return false + + return BuiltinMethodsWithSpecialGenericSignature.getOverriddenBuiltinFunctionWithErasedValueParametersInJava(f) != null +} + // This method only returns not-null for class methods internal fun CallableDescriptor.computeJvmSignature(): String? = signatures { if (DescriptorUtils.isLocal(this@computeJvmSignature)) return null @@ -72,16 +85,19 @@ internal val ClassId.internalName: String } private fun StringBuilder.appendErasedType(type: KotlinType) { - append( - JvmTypeFactoryImpl.toString( - mapType(type, JvmTypeFactoryImpl, TypeMappingMode.DEFAULT, TypeMappingConfigurationImpl, descriptorTypeWriter = null))) + append(type.mapToJvmType()) } -private sealed class JvmType { +internal fun KotlinType.mapToJvmType() = + mapType(this, JvmTypeFactoryImpl, TypeMappingMode.DEFAULT, TypeMappingConfigurationImpl, descriptorTypeWriter = null) + +sealed class JvmType { // null means 'void' class Primitive(val jvmPrimitiveType: JvmPrimitiveType?) : JvmType() class Object(val internalName: String) : JvmType() class Array(val elementType: JvmType) : JvmType() + + override fun toString() = JvmTypeFactoryImpl.toString(this) } private object JvmTypeFactoryImpl : JvmTypeFactory {