From a7e48c3af13c29ba54f8569ee1fd53d349253511 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 8 Jul 2021 22:34:43 +0200 Subject: [PATCH] Improve toString of platform types created by typeOf In the stdlib implementation, render "!" if the type is only nullability-flexible. Otherwise, render "($lower..$upper)". Note that full kotlin-reflect has a much more complicated logic (see `DescriptorRendererImpl.renderFlexibleType`) that renders things like `(Mutable)List` and so on. It is not a goal of the stdlib implementation to replicate all of that, since it requires copying a large amount of code, namely the entirety of `JavaToKotlinClassMap` to map Java class names to Kotlin. --- .../FirBlackBoxCodegenTestGenerated.java | 12 ++++++ .../typeOf/noReflect/flexibleTypes_after.kt | 9 ++-- .../typeOf/noReflect/nothing_after.kt | 3 +- .../typeOf/noReflect/primitiveJavaTypes.kt | 43 +++++++++++++++++++ .../typeOf/noReflect/rawTypes_after.kt | 8 ++-- .../reflection/typeOf/primitiveJavaTypes.kt | 43 +++++++++++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 12 ++++++ .../IrBlackBoxCodegenTestGenerated.java | 12 ++++++ .../LightAnalysisModeTestGenerated.java | 10 +++++ .../kotlin/jvm/internal/TypeReference.kt | 17 +++++++- 10 files changed, 157 insertions(+), 12 deletions(-) create mode 100644 compiler/testData/codegen/box/reflection/typeOf/noReflect/primitiveJavaTypes.kt create mode 100644 compiler/testData/codegen/box/reflection/typeOf/primitiveJavaTypes.kt diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index 05fac697bd2..2493d067c95 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -37521,6 +37521,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/reflection/typeOf/nothing_before.kt"); } + @Test + @TestMetadata("primitiveJavaTypes.kt") + public void testPrimitiveJavaTypes() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/primitiveJavaTypes.kt"); + } + @Test @TestMetadata("rawTypes_after.kt") public void testRawTypes_after() throws Exception { @@ -37606,6 +37612,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/nothing_before.kt"); } + @Test + @TestMetadata("primitiveJavaTypes.kt") + public void testPrimitiveJavaTypes() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/primitiveJavaTypes.kt"); + } + @Test @TestMetadata("rawTypes_after.kt") public void testRawTypes_after() throws Exception { diff --git a/compiler/testData/codegen/box/reflection/typeOf/noReflect/flexibleTypes_after.kt b/compiler/testData/codegen/box/reflection/typeOf/noReflect/flexibleTypes_after.kt index 01813d9b504..27b6a73508d 100644 --- a/compiler/testData/codegen/box/reflection/typeOf/noReflect/flexibleTypes_after.kt +++ b/compiler/testData/codegen/box/reflection/typeOf/noReflect/flexibleTypes_after.kt @@ -15,12 +15,11 @@ fun check(expected: String, actual: KType) { } fun box(): String { - // String representation of platform types does not differ from non-nullable types in the stdlib implementation (without reflect). - check("java.lang.String", returnTypeOf { J.nullabilityFlexible() }) - check("java.util.List", returnTypeOf { J.mutabilityFlexible() }) - check("java.util.List", returnTypeOf { J.bothFlexible() }) + check("java.lang.String!", returnTypeOf { J.nullabilityFlexible() }) + check("java.util.List", returnTypeOf { J.mutabilityFlexible() }) + check("java.util.List!", returnTypeOf { J.bothFlexible() }) - // However, type flexibility affects equality! Platform type is neither nullable nor non-nullable. + // Type flexibility affects equality! Platform type is neither nullable nor non-nullable. val platform = returnTypeOf { J.nullabilityFlexible() } if (platform == returnTypeOf { J.nullable() }) return "Fail: platform type should not be equal to nullable type" if (platform == returnTypeOf { J.notNull() }) return "Fail: platform type should not be equal to non-nullable type" diff --git a/compiler/testData/codegen/box/reflection/typeOf/noReflect/nothing_after.kt b/compiler/testData/codegen/box/reflection/typeOf/noReflect/nothing_after.kt index be540ca5128..aa2704bf331 100644 --- a/compiler/testData/codegen/box/reflection/typeOf/noReflect/nothing_after.kt +++ b/compiler/testData/codegen/box/reflection/typeOf/noReflect/nothing_after.kt @@ -26,8 +26,7 @@ fun box(): String { check("test.C", typeOf>()) check("kotlin.Nothing?", nothingBound()) - // String representation of platform types does not differ from non-nullable types in the stdlib implementation (without reflect). - check("test.C", returnTypeOf { C(J.platformType()) }) + check("test.C", returnTypeOf { C(J.platformType()) }) // Such type's classifier is still Void::class, until KT-15518 is fixed. // TODO: support a special KClass instance for Nothing (KT-15518). diff --git a/compiler/testData/codegen/box/reflection/typeOf/noReflect/primitiveJavaTypes.kt b/compiler/testData/codegen/box/reflection/typeOf/noReflect/primitiveJavaTypes.kt new file mode 100644 index 00000000000..33a583db13f --- /dev/null +++ b/compiler/testData/codegen/box/reflection/typeOf/noReflect/primitiveJavaTypes.kt @@ -0,0 +1,43 @@ +// !API_VERSION: LATEST +// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi +// TARGET_BACKEND: JVM +// WITH_RUNTIME +// FILE: box.kt + +package test + +import kotlin.reflect.KType +import kotlin.reflect.typeOf +import kotlin.test.assertEquals + +fun check(expected: String, actual: KType) { + assertEquals(expected + " (Kotlin reflection is not available)", actual.toString()) +} + +fun box(): String { + check("int", returnTypeOf { J.primitive() }) + check("java.lang.Integer!", returnTypeOf { J.wrapper() }) + check("java.lang.Integer?", returnTypeOf { J.nullableWrapper() }) + check("java.lang.Integer", returnTypeOf { J.notNullWrapper() }) + + return "OK" +} + +inline fun returnTypeOf(block: () -> T) = + typeOf() + +// FILE: J.java + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class J { + public static int primitive() { return 0; } + public static Integer wrapper() { return 0; } + + @Nullable + public static Integer nullableWrapper() { return 0; } + + @NotNull + public static Integer notNullWrapper() { return 0; } +} diff --git a/compiler/testData/codegen/box/reflection/typeOf/noReflect/rawTypes_after.kt b/compiler/testData/codegen/box/reflection/typeOf/noReflect/rawTypes_after.kt index a2fbf880423..49ee8b9b222 100644 --- a/compiler/testData/codegen/box/reflection/typeOf/noReflect/rawTypes_after.kt +++ b/compiler/testData/codegen/box/reflection/typeOf/noReflect/rawTypes_after.kt @@ -18,10 +18,10 @@ fun check(expected: String, actual: KType) { class C fun box(): String { - check("test.C", returnTypeOf { J.raw() }) - check("test.C", returnTypeOf { J.rawNotNull() }) - check("java.util.List", returnTypeOf { J.rawList() }) - check("java.util.Map", returnTypeOf { J.rawNotNullMap() }) + check("(test.C..test.C<*, *>?)", returnTypeOf { J.raw() }) + check("(test.C..test.C<*, *>)", returnTypeOf { J.rawNotNull() }) + check("(java.util.List..java.util.List<*>?)", returnTypeOf { J.rawList() }) + check("(java.util.Map..java.util.Map<*, *>)", returnTypeOf { J.rawNotNullMap() }) return "OK" } diff --git a/compiler/testData/codegen/box/reflection/typeOf/primitiveJavaTypes.kt b/compiler/testData/codegen/box/reflection/typeOf/primitiveJavaTypes.kt new file mode 100644 index 00000000000..6a27d2a3f4e --- /dev/null +++ b/compiler/testData/codegen/box/reflection/typeOf/primitiveJavaTypes.kt @@ -0,0 +1,43 @@ +// !API_VERSION: LATEST +// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi +// TARGET_BACKEND: JVM +// WITH_REFLECT +// FILE: box.kt + +package test + +import kotlin.reflect.KType +import kotlin.reflect.typeOf +import kotlin.test.assertEquals + +fun check(expected: String, actual: KType) { + assertEquals(expected, actual.toString()) +} + +fun box(): String { + check("kotlin.Int", returnTypeOf { J.primitive() }) + check("kotlin.Int!", returnTypeOf { J.wrapper() }) + check("kotlin.Int?", returnTypeOf { J.nullableWrapper() }) + check("kotlin.Int", returnTypeOf { J.notNullWrapper() }) + + return "OK" +} + +inline fun returnTypeOf(block: () -> T) = + typeOf() + +// FILE: J.java + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class J { + public static int primitive() { return 0; } + public static Integer wrapper() { return 0; } + + @Nullable + public static Integer nullableWrapper() { return 0; } + + @NotNull + public static Integer notNullWrapper() { return 0; } +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java index 472ada91239..1368d28c7f5 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java @@ -37479,6 +37479,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/reflection/typeOf/nothing_before.kt"); } + @Test + @TestMetadata("primitiveJavaTypes.kt") + public void testPrimitiveJavaTypes() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/primitiveJavaTypes.kt"); + } + @Test @TestMetadata("rawTypes_after.kt") public void testRawTypes_after() throws Exception { @@ -37564,6 +37570,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/nothing_before.kt"); } + @Test + @TestMetadata("primitiveJavaTypes.kt") + public void testPrimitiveJavaTypes() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/primitiveJavaTypes.kt"); + } + @Test @TestMetadata("rawTypes_after.kt") public void testRawTypes_after() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index 60f9648f936..5a161aed253 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -37521,6 +37521,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/reflection/typeOf/nothing_before.kt"); } + @Test + @TestMetadata("primitiveJavaTypes.kt") + public void testPrimitiveJavaTypes() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/primitiveJavaTypes.kt"); + } + @Test @TestMetadata("rawTypes_after.kt") public void testRawTypes_after() throws Exception { @@ -37606,6 +37612,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/nothing_before.kt"); } + @Test + @TestMetadata("primitiveJavaTypes.kt") + public void testPrimitiveJavaTypes() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/primitiveJavaTypes.kt"); + } + @Test @TestMetadata("rawTypes_after.kt") public void testRawTypes_after() throws Exception { diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index dd49722c38f..dc6d3eae9dc 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -29840,6 +29840,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/reflection/typeOf/nothing_before.kt"); } + @TestMetadata("primitiveJavaTypes.kt") + public void testPrimitiveJavaTypes() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/primitiveJavaTypes.kt"); + } + @TestMetadata("rawTypes_after.kt") public void testRawTypes_after() throws Exception { runTest("compiler/testData/codegen/box/reflection/typeOf/rawTypes_after.kt"); @@ -29920,6 +29925,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/nothing_before.kt"); } + @TestMetadata("primitiveJavaTypes.kt") + public void testPrimitiveJavaTypes() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/primitiveJavaTypes.kt"); + } + @TestMetadata("rawTypes_after.kt") public void testRawTypes_after() throws Exception { runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/rawTypes_after.kt"); diff --git a/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/TypeReference.kt b/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/TypeReference.kt index f593b919b89..659ef9f0b4b 100644 --- a/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/TypeReference.kt +++ b/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/TypeReference.kt @@ -53,7 +53,22 @@ public class TypeReference /* @SinceKotlin("1.6") constructor */( else arguments.joinToString(", ", "<", ">") { it.asString() } val nullable = if (isMarkedNullable) "?" else "" - return klass + args + nullable + val result = klass + args + nullable + + return when (val upper = platformTypeUpperBound) { + is TypeReference -> { + when (val renderedUpper = upper.asString(true)) { + result -> { + // Rendered upper bound can be the same as rendered lower bound in cases when the type is mutability-flexible, + // but not nullability-flexible, since both MutableCollection and Collection are rendered as "java.util.Collection". + result + } + "$result?" -> "$result!" + else -> "($result..$renderedUpper)" + } + } + else -> result + } } private val Class<*>.arrayClassName