Support flexible types internally in typeOf

#KT-45066 Fixed
This commit is contained in:
Alexander Udalov
2021-05-21 19:27:59 +02:00
parent 26cdb2f928
commit 6e975b3498
21 changed files with 358 additions and 42 deletions
@@ -19,7 +19,7 @@ import org.jetbrains.org.objectweb.asm.Type.INT_TYPE
import org.jetbrains.org.objectweb.asm.Type.VOID_TYPE
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
class PsiInlineIntrinsicsSupport(private val state: GenerationState) : ReifiedTypeInliner.IntrinsicsSupport<KotlinType> {
class PsiInlineIntrinsicsSupport(override val state: GenerationState) : ReifiedTypeInliner.IntrinsicsSupport<KotlinType> {
override fun putClassInstance(v: InstructionAdapter, type: KotlinType) {
DescriptorAsmUtil.putJavaLangClassInstance(v, state.typeMapper.mapType(type), type, state.typeMapper)
}
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.codegen.generateAsCast
import org.jetbrains.kotlin.codegen.generateIsCheck
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
import org.jetbrains.kotlin.codegen.optimization.common.intConstant
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.config.isReleaseCoroutines
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
@@ -63,6 +64,8 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
}
interface IntrinsicsSupport<KT : KotlinTypeMarker> {
val state: GenerationState
fun putClassInstance(v: InstructionAdapter, type: KT)
fun generateTypeParameterContainer(v: InstructionAdapter, typeParameter: TypeParameterMarker)
@@ -89,6 +89,15 @@ fun <KT : KotlinTypeMarker> TypeSystemCommonBackendContext.generateTypeOf(
}
v.invokestatic(REFLECTION, methodName, signature, false)
if (type.isFlexible() && intrinsicsSupport.state.stableTypeOf) {
// If this is a flexible type, we've just generated its lower bound and have it on the stack.
// Let's generate the upper bound now and call the method that takes lower and upper bound and constructs a flexible KType.
@Suppress("UNCHECKED_CAST")
generateTypeOf(v, type.upperBoundIfFlexible() as KT, intrinsicsSupport)
v.invokestatic(REFLECTION, "platformType", Type.getMethodDescriptor(K_TYPE, K_TYPE, K_TYPE), false)
}
}
private fun <KT : KotlinTypeMarker> TypeSystemCommonBackendContext.generateNonReifiedTypeParameter(
@@ -260,6 +260,10 @@ class GenerationState private constructor(
val useKotlinNothingValueException =
languageVersionSettings.apiVersion >= ApiVersion.KOTLIN_1_4 &&
!configuration.getBoolean(JVMConfigurationKeys.NO_KOTLIN_NOTHING_VALUE_EXCEPTION)
// In 1.6, `typeOf` became stable and started to rely on a few internal stdlib functions which were missing before 1.6.
val stableTypeOf = languageVersionSettings.apiVersion >= ApiVersion.KOTLIN_1_6
val samWrapperClasses: SamWrapperClasses = SamWrapperClasses(this)
val globalInlineContext: GlobalInlineContext = GlobalInlineContext(diagnostics)
val mappingsClassesForWhenByEnum: MappingsClassesForWhenByEnum = MappingsClassesForWhenByEnum(this)
@@ -37404,9 +37404,15 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
}
@Test
@TestMetadata("flexibleType.kt")
public void testFlexibleType() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/flexibleType.kt");
@TestMetadata("flexibleTypes_after.kt")
public void testFlexibleTypes_after() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/flexibleTypes_after.kt");
}
@Test
@TestMetadata("flexibleTypes_before.kt")
public void testFlexibleTypes_before() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/flexibleTypes_before.kt");
}
@Test
@@ -37458,6 +37464,18 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/classes.kt");
}
@Test
@TestMetadata("flexibleTypes_after.kt")
public void testFlexibleTypes_after() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/flexibleTypes_after.kt");
}
@Test
@TestMetadata("flexibleTypes_before.kt")
public void testFlexibleTypes_before() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/flexibleTypes_before.kt");
}
@Test
@TestMetadata("inlineClasses.kt")
public void testInlineClasses() throws Exception {
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.jvm.intrinsics.SignatureString
import org.jetbrains.kotlin.backend.jvm.lower.FunctionReferenceLowering
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.toIrBasedKotlinType
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
@@ -29,6 +30,9 @@ class IrInlineIntrinsicsSupport(
private val context: JvmBackendContext,
private val typeMapper: IrTypeMapper
) : ReifiedTypeInliner.IntrinsicsSupport<IrType> {
override val state: GenerationState
get() = context.state
override fun putClassInstance(v: InstructionAdapter, type: IrType) {
ExpressionCodegen.generateClassInstance(v, type, typeMapper)
}
@@ -1,26 +0,0 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// TARGET_BACKEND: JVM
// WITH_REFLECT
// FILE: box.kt
package test
import kotlin.reflect.*
fun box(): String {
val v = returnTypeOf { J.get() }.toString()
if (v != "J") return "Fail: $v"
return "OK"
}
inline fun <reified T: Any> returnTypeOf(block: () -> T) =
typeOf<T>()
// FILE: J.java
public class J {
public static J get() {
return null;
}
}
@@ -0,0 +1,48 @@
// !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
fun box(): String {
val v1 = returnTypeOf { J.nullabilityFlexible() }.toString()
if (v1 != "kotlin.String!") return "Fail 1: $v1"
val v2 = returnTypeOf { J.mutabilityFlexible() }.toString()
// TODO: should be (Mutable)List after KT-35877.
if (v2 != "kotlin.collections.List<kotlin.String!>") return "Fail 2: $v2"
val v3 = returnTypeOf { J.bothFlexible() }.toString()
// TODO: should be (Mutable)List after KT-35877.
if (v3 != "kotlin.collections.List<kotlin.String!>!") return "Fail 3: $v3"
return "OK"
}
inline fun <reified T : Any> returnTypeOf(block: () -> T) =
typeOf<T>()
// FILE: J.java
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class J {
public static String nullabilityFlexible() {
return null;
}
@NotNull
public static List<String> mutabilityFlexible() {
return null;
}
public static List<String> bothFlexible() {
return null;
}
}
@@ -0,0 +1,46 @@
// !API_VERSION: 1.5
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// TARGET_BACKEND: JVM
// WITH_REFLECT
// FILE: box.kt
package test
import kotlin.reflect.KType
import kotlin.reflect.typeOf
fun box(): String {
val v1 = returnTypeOf { J.nullabilityFlexible() }.toString()
if (v1 != "kotlin.String") return "Fail 1: $v1"
val v2 = returnTypeOf { J.mutabilityFlexible() }.toString()
if (v2 != "kotlin.collections.List<kotlin.String>") return "Fail 2: $v2"
val v3 = returnTypeOf { J.bothFlexible() }.toString()
if (v3 != "kotlin.collections.List<kotlin.String>") return "Fail 3: $v3"
return "OK"
}
inline fun <reified T : Any> returnTypeOf(block: () -> T) =
typeOf<T>()
// FILE: J.java
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class J {
public static String nullabilityFlexible() {
return null;
}
@NotNull
public static List<String> mutabilityFlexible() {
return null;
}
public static List<String> bothFlexible() {
return null;
}
}
@@ -0,0 +1,53 @@
// !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 {
// 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<java.lang.String>", returnTypeOf { J.mutabilityFlexible() })
check("java.util.List<java.lang.String>", returnTypeOf { J.bothFlexible() })
// However, 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"
return "OK"
}
inline fun <reified T> returnTypeOf(block: () -> T) =
typeOf<T>()
// FILE: J.java
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class J {
public static String nullabilityFlexible() { return null; }
@NotNull
public static List<String> mutabilityFlexible() { return null; }
public static List<String> bothFlexible() { return null; }
@Nullable
public static String nullable() { return null; }
@NotNull
public static String notNull() { return null; }
}
@@ -0,0 +1,52 @@
// !API_VERSION: 1.5
// !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("java.lang.String", returnTypeOf { J.nullabilityFlexible() })
check("java.util.List<java.lang.String>", returnTypeOf { J.mutabilityFlexible() })
check("java.util.List<java.lang.String>", returnTypeOf { J.bothFlexible() })
// Before 1.6, typeOf of a flexible type was represented as non-nullable type in stdlib implementation (without reflect).
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 be equal to non-nullable type"
return "OK"
}
inline fun <reified T> returnTypeOf(block: () -> T) =
typeOf<T>()
// FILE: J.java
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class J {
public static String nullabilityFlexible() { return null; }
@NotNull
public static List<String> mutabilityFlexible() { return null; }
public static List<String> bothFlexible() { return null; }
@Nullable
public static String nullable() { return null; }
@NotNull
public static String notNull() { return null; }
}
@@ -37362,9 +37362,15 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
}
@Test
@TestMetadata("flexibleType.kt")
public void testFlexibleType() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/flexibleType.kt");
@TestMetadata("flexibleTypes_after.kt")
public void testFlexibleTypes_after() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/flexibleTypes_after.kt");
}
@Test
@TestMetadata("flexibleTypes_before.kt")
public void testFlexibleTypes_before() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/flexibleTypes_before.kt");
}
@Test
@@ -37416,6 +37422,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/classes.kt");
}
@Test
@TestMetadata("flexibleTypes_after.kt")
public void testFlexibleTypes_after() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/flexibleTypes_after.kt");
}
@Test
@TestMetadata("flexibleTypes_before.kt")
public void testFlexibleTypes_before() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/flexibleTypes_before.kt");
}
@Test
@TestMetadata("inlineClasses.kt")
public void testInlineClasses() throws Exception {
@@ -37404,9 +37404,15 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
}
@Test
@TestMetadata("flexibleType.kt")
public void testFlexibleType() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/flexibleType.kt");
@TestMetadata("flexibleTypes_after.kt")
public void testFlexibleTypes_after() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/flexibleTypes_after.kt");
}
@Test
@TestMetadata("flexibleTypes_before.kt")
public void testFlexibleTypes_before() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/flexibleTypes_before.kt");
}
@Test
@@ -37458,6 +37464,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/classes.kt");
}
@Test
@TestMetadata("flexibleTypes_after.kt")
public void testFlexibleTypes_after() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/flexibleTypes_after.kt");
}
@Test
@TestMetadata("flexibleTypes_before.kt")
public void testFlexibleTypes_before() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/flexibleTypes_before.kt");
}
@Test
@TestMetadata("inlineClasses.kt")
public void testInlineClasses() throws Exception {
@@ -29737,9 +29737,14 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/reflection/typeOf/classes.kt");
}
@TestMetadata("flexibleType.kt")
public void testFlexibleType() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/flexibleType.kt");
@TestMetadata("flexibleTypes_after.kt")
public void testFlexibleTypes_after() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/flexibleTypes_after.kt");
}
@TestMetadata("flexibleTypes_before.kt")
public void testFlexibleTypes_before() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/flexibleTypes_before.kt");
}
@TestMetadata("inlineClasses.kt")
@@ -29792,6 +29797,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/classes.kt");
}
@TestMetadata("flexibleTypes_after.kt")
public void testFlexibleTypes_after() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/flexibleTypes_after.kt");
}
@TestMetadata("flexibleTypes_before.kt")
public void testFlexibleTypes_before() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/flexibleTypes_before.kt");
}
@TestMetadata("inlineClasses.kt")
public void testInlineClasses() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/inlineClasses.kt");
@@ -58,6 +58,9 @@ class ApiVersion private constructor(
@JvmField
val KOTLIN_1_5 = createByLanguageVersion(LanguageVersion.KOTLIN_1_5)
@JvmField
val KOTLIN_1_6 = createByLanguageVersion(LanguageVersion.KOTLIN_1_6)
@JvmField
val LATEST: ApiVersion = createByLanguageVersion(LanguageVersion.values().last())
@@ -146,6 +146,11 @@ public class ReflectionFactoryImpl extends ReflectionFactory {
// Do nothing. KTypeParameterImpl implementation will load upper bounds from the metadata.
}
// @Override // JPS
public KType platformType(KType lowerBound, KType upperBound) {
return TypeOfImplKt.createPlatformKType(lowerBound, upperBound);
}
// Misc
public static void clearCaches() {
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.types.KotlinTypeFactory
import org.jetbrains.kotlin.types.SimpleType
import kotlin.reflect.KType
internal fun createPlatformKType(lowerBound: KType, upperBound: KType): KType {
// lowerBound/upperBound are guaranteed to be KTypeImpl over SimpleType, as long as this function is only used from the bytecode
// generated by the compiler for `typeOf`.
return KTypeImpl(
KotlinTypeFactory.flexibleType(
(lowerBound as KTypeImpl).type as SimpleType,
(upperBound as KTypeImpl).type as SimpleType,
)
)
}
@@ -183,4 +183,11 @@ public class Reflection {
public static void setUpperBounds(KTypeParameter typeParameter, KType... bounds) {
factory.setUpperBounds(typeParameter, ArraysKt.toList(bounds));
}
// Features of stable typeOf
@SinceKotlin(version = "1.6")
public static KType platformType(KType lowerBound, KType upperBound) {
return factory.platformType(lowerBound, upperBound);
}
}
@@ -93,4 +93,9 @@ public class ReflectionFactory {
public void setUpperBounds(KTypeParameter typeParameter, List<KType> bounds) {
((TypeParameterReference) typeParameter).setUpperBounds(bounds);
}
@SinceKotlin(version = "1.6")
public KType platformType(KType lowerBound, KType upperBound) {
return new TypeReference(lowerBound.getClassifier(), lowerBound.getArguments(), lowerBound.isMarkedNullable(), upperBound);
}
}
@@ -3,22 +3,32 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:Suppress("NEWER_VERSION_IN_SINCE_KOTLIN")
package kotlin.jvm.internal
import kotlin.reflect.*
@SinceKotlin("1.4")
public class TypeReference(
public class TypeReference /* @SinceKotlin("1.6") constructor */(
override val classifier: KClassifier,
override val arguments: List<KTypeProjection>,
override val isMarkedNullable: Boolean
override val isMarkedNullable: Boolean,
private val platformTypeUpperBound: KType?,
) : KType {
constructor(
classifier: KClassifier,
arguments: List<KTypeProjection>,
isMarkedNullable: Boolean,
) : this(classifier, arguments, isMarkedNullable, null)
override val annotations: List<Annotation>
get() = emptyList()
override fun equals(other: Any?): Boolean =
other is TypeReference &&
classifier == other.classifier && arguments == other.arguments && isMarkedNullable == other.isMarkedNullable
classifier == other.classifier && arguments == other.arguments && isMarkedNullable == other.isMarkedNullable &&
platformTypeUpperBound == other.platformTypeUpperBound
override fun hashCode(): Int =
(classifier.hashCode() * 31 + arguments.hashCode()) * 31 + isMarkedNullable.hashCode()
@@ -3995,6 +3995,7 @@ public class kotlin/jvm/internal/Reflection {
public static fun nullableTypeOf (Ljava/lang/Class;Lkotlin/reflect/KTypeProjection;Lkotlin/reflect/KTypeProjection;)Lkotlin/reflect/KType;
public static fun nullableTypeOf (Ljava/lang/Class;[Lkotlin/reflect/KTypeProjection;)Lkotlin/reflect/KType;
public static fun nullableTypeOf (Lkotlin/reflect/KClassifier;)Lkotlin/reflect/KType;
public static fun platformType (Lkotlin/reflect/KType;Lkotlin/reflect/KType;)Lkotlin/reflect/KType;
public static fun property0 (Lkotlin/jvm/internal/PropertyReference0;)Lkotlin/reflect/KProperty0;
public static fun property1 (Lkotlin/jvm/internal/PropertyReference1;)Lkotlin/reflect/KProperty1;
public static fun property2 (Lkotlin/jvm/internal/PropertyReference2;)Lkotlin/reflect/KProperty2;
@@ -4021,6 +4022,7 @@ public class kotlin/jvm/internal/ReflectionFactory {
public fun mutableProperty0 (Lkotlin/jvm/internal/MutablePropertyReference0;)Lkotlin/reflect/KMutableProperty0;
public fun mutableProperty1 (Lkotlin/jvm/internal/MutablePropertyReference1;)Lkotlin/reflect/KMutableProperty1;
public fun mutableProperty2 (Lkotlin/jvm/internal/MutablePropertyReference2;)Lkotlin/reflect/KMutableProperty2;
public fun platformType (Lkotlin/reflect/KType;Lkotlin/reflect/KType;)Lkotlin/reflect/KType;
public fun property0 (Lkotlin/jvm/internal/PropertyReference0;)Lkotlin/reflect/KProperty0;
public fun property1 (Lkotlin/jvm/internal/PropertyReference1;)Lkotlin/reflect/KProperty1;
public fun property2 (Lkotlin/jvm/internal/PropertyReference2;)Lkotlin/reflect/KProperty2;
@@ -4120,6 +4122,7 @@ public final class kotlin/jvm/internal/TypeParameterReference$Companion {
public final class kotlin/jvm/internal/TypeReference : kotlin/reflect/KType {
public fun <init> (Lkotlin/reflect/KClassifier;Ljava/util/List;Z)V
public fun <init> (Lkotlin/reflect/KClassifier;Ljava/util/List;ZLkotlin/reflect/KType;)V
public fun equals (Ljava/lang/Object;)Z
public fun getAnnotations ()Ljava/util/List;
public fun getArguments ()Ljava/util/List;