From 5b1ee13db8dddf008218587ba08cf46ecb80d1c7 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Tue, 9 Aug 2016 15:42:42 +0300 Subject: [PATCH] Make KClasses for primitives equal to KClasses for wrapper types Both primitive int and wrapper type java.lang.Integer are represented by the single type kotlin.Int in Kotlin, so inequality between the corresponding KClasses was confusing here. To keep the old behavior, one may call 'k1.java == k2.java' instead of `k1 == k2` #KT-13462 Fixed --- .../box/classLiteral/primitiveKClassEquality.kt | 15 +++++++++++++++ .../reflection/classes/primitiveKClassEquality.kt | 15 +++++++++++++++ .../box/reflection/supertypes/primitives.kt | 7 +++---- .../codegen/BlackBoxCodegenTestGenerated.java | 12 ++++++++++++ core/builtins/src/kotlin/reflect/KClass.kt | 12 ++++-------- .../src/kotlin/reflect/jvm/internal/KClassImpl.kt | 4 ++-- .../src/kotlin/jvm/internal/ClassReference.kt | 4 ++-- 7 files changed, 53 insertions(+), 16 deletions(-) create mode 100644 compiler/testData/codegen/box/classLiteral/primitiveKClassEquality.kt create mode 100644 compiler/testData/codegen/box/reflection/classes/primitiveKClassEquality.kt diff --git a/compiler/testData/codegen/box/classLiteral/primitiveKClassEquality.kt b/compiler/testData/codegen/box/classLiteral/primitiveKClassEquality.kt new file mode 100644 index 00000000000..21ca908c6ed --- /dev/null +++ b/compiler/testData/codegen/box/classLiteral/primitiveKClassEquality.kt @@ -0,0 +1,15 @@ +// WITH_RUNTIME + +import kotlin.test.assertEquals +import kotlin.test.assertFalse + +fun box(): String { + val x = Int::class.javaPrimitiveType!!.kotlin + val y = Int::class.javaObjectType.kotlin + + assertEquals(x, y) + assertEquals(x.hashCode(), y.hashCode()) + assertFalse(x === y) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/classes/primitiveKClassEquality.kt b/compiler/testData/codegen/box/reflection/classes/primitiveKClassEquality.kt new file mode 100644 index 00000000000..9a5ab6b07fd --- /dev/null +++ b/compiler/testData/codegen/box/reflection/classes/primitiveKClassEquality.kt @@ -0,0 +1,15 @@ +// WITH_REFLECT + +import kotlin.test.assertEquals +import kotlin.test.assertFalse + +fun box(): String { + val x = Int::class.javaPrimitiveType!!.kotlin + val y = Int::class.javaObjectType.kotlin + + assertEquals(x, y) + assertEquals(x.hashCode(), y.hashCode()) + assertFalse(x === y) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/supertypes/primitives.kt b/compiler/testData/codegen/box/reflection/supertypes/primitives.kt index 2d54b7ae8aa..8e3b4764ba5 100644 --- a/compiler/testData/codegen/box/reflection/supertypes/primitives.kt +++ b/compiler/testData/codegen/box/reflection/supertypes/primitives.kt @@ -11,10 +11,9 @@ fun box(): String { assertTrue(primitiveInt.isSubclassOf(primitiveInt)) assertTrue(wrapperInt.isSubclassOf(wrapperInt)) - // Currently KClass for int != KClass for java.lang.Integer, thus they are not a subclass of each other either - // TODO: reconsider this decision - assertFalse(primitiveInt.isSubclassOf(wrapperInt)) - assertFalse(wrapperInt.isSubclassOf(primitiveInt)) + // KClass for int equals KClass for java.lang.Integer, so they are also a subclass of each other + assertTrue(primitiveInt.isSubclassOf(wrapperInt)) + assertTrue(wrapperInt.isSubclassOf(primitiveInt)) return "OK" } diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 0410c7dff26..ebb671bfbbc 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -2356,6 +2356,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/classLiteral"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("primitiveKClassEquality.kt") + public void testPrimitiveKClassEquality() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classLiteral/primitiveKClassEquality.kt"); + doTest(fileName); + } + @TestMetadata("compiler/testData/codegen/box/classLiteral/bound") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -12003,6 +12009,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("primitiveKClassEquality.kt") + public void testPrimitiveKClassEquality() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/classes/primitiveKClassEquality.kt"); + doTest(fileName); + } + @TestMetadata("qualifiedName.kt") public void testQualifiedName() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/classes/qualifiedName.kt"); diff --git a/core/builtins/src/kotlin/reflect/KClass.kt b/core/builtins/src/kotlin/reflect/KClass.kt index 46590f103f5..e4bb27d11c1 100644 --- a/core/builtins/src/kotlin/reflect/KClass.kt +++ b/core/builtins/src/kotlin/reflect/KClass.kt @@ -123,15 +123,11 @@ public interface KClass : KDeclarationContainer, KAnnotatedElement, KCl /** * Returns `true` if [other] is a [KClass] instance representing the same class on a given platform. + * On JVM this means that all of the following conditions are satisfied: * - * On JVM this means that the given instance is backed by the same [Class] object as this one. In particular, it requires - * that the two classes are loaded with the same class loader and have the same name. Note that there are cases where the behavior - * of this method may seem unintuitive: - * * For each JVM primitive type, there are two classes at runtime: one for the primitive itself, and another for the wrapper class. - * [KClass] instances for those classes are different: [KClass] for `int` is **not equal** to [KClass] for `java.lang.Integer`, - * although both have the same qualified name [kotlin.Int]. - * * For JVM arrays of different types, [KClass] instances are different, - * although all of them have the same qualified name [kotlin.Array]. + * 1. [other] has the same fully qualified name as this instance. + * 2. [other]'s backing [Class] object is loaded with the same class loader as the [Class] object of this instance. + * 3. If the classes represent [Array], then [Class] objects of their element types are equal. */ override fun equals(other: Any?): Boolean diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt index 790cd71606c..de30d5054ad 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt @@ -196,10 +196,10 @@ internal class KClassImpl(override val jClass: Class) : get() = descriptor.isCompanionObject override fun equals(other: Any?): Boolean = - other is KClassImpl<*> && jClass == other.jClass + other is KClassImpl<*> && javaObjectType == other.javaObjectType override fun hashCode(): Int = - jClass.hashCode() + javaObjectType.hashCode() override fun toString(): String { return "class " + classId.let { classId -> diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/ClassReference.kt b/core/runtime.jvm/src/kotlin/jvm/internal/ClassReference.kt index fa9a750e150..0820f000f4f 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/ClassReference.kt +++ b/core/runtime.jvm/src/kotlin/jvm/internal/ClassReference.kt @@ -75,10 +75,10 @@ class ClassReference(override val jClass: Class<*>) : KClass, ClassBasedDec private fun error(): Nothing = throw KotlinReflectionNotSupportedError() override fun equals(other: Any?) = - other is ClassReference && jClass == other.jClass + other is ClassReference && javaObjectType == other.javaObjectType override fun hashCode() = - jClass.hashCode() + javaObjectType.hashCode() override fun toString() = jClass.toString() + Reflection.REFLECTION_NOT_AVAILABLE