JVM: partially reify typeOf and signatures as soon as possible

E.g. when substituting T -> Array<T>, write the bytecode for the
Array<...> part for typeOf.

This fixes various issues where either Array nesting levels, nullability
information (for typeOf), or entire reification markers were missing,
causing incorrect outputs ranging from missing `?`s to missing `[]`s to
just reified types not really being reified.

^KT-53761 Fixed
This commit is contained in:
pyos
2022-08-30 12:15:40 +02:00
committed by Alexander Udalov
parent 2e6817042c
commit 8d33de4297
20 changed files with 363 additions and 223 deletions
@@ -2928,33 +2928,12 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
@NotNull
CallGenerator getOrCreateCallGenerator(@NotNull ResolvedCall<?> resolvedCall, @NotNull CallableDescriptor descriptor) {
Map<TypeParameterDescriptor, KotlinType> typeArguments = getTypeArgumentsForResolvedCall(resolvedCall, descriptor);
TypeParameterMappings<KotlinType> mappings = new TypeParameterMappings<>();
for (Map.Entry<TypeParameterDescriptor, KotlinType> entry : typeArguments.entrySet()) {
TypeParameterDescriptor key = entry.getKey();
KotlinType type = entry.getValue();
boolean isReified = key.isReified() || InlineUtil.isArrayConstructorWithLambda(resolvedCall.getResultingDescriptor());
Pair<TypeParameterMarker, ReificationArgument> typeParameterAndReificationArgument =
extractReificationArgument(typeSystem, type);
if (typeParameterAndReificationArgument == null) {
KotlinType approximatedType = approximateCapturedType(type);
JvmSignatureWriter signatureWriter = new BothSignatureWriter(BothSignatureWriter.Mode.TYPE);
Type asmType = typeMapper.mapTypeArgument(approximatedType, signatureWriter);
mappings.addParameterMappingToType(
key.getName().getIdentifier(), approximatedType, asmType, signatureWriter.toString(), isReified
);
}
else {
mappings.addParameterMappingForFurtherReification(
key.getName().getIdentifier(), type, typeParameterAndReificationArgument.getSecond(), isReified
);
}
}
TypeParameterMappings<KotlinType> mappings = new TypeParameterMappings<>(
typeSystem,
getTypeArgumentsForResolvedCall(resolvedCall, descriptor),
InlineUtil.isArrayConstructorWithLambda(resolvedCall.getResultingDescriptor()),
(KotlinType type, BothSignatureWriter sw) -> typeMapper.mapTypeArgument(approximateCapturedType(type), sw)
);
return getOrCreateCallGenerator(descriptor, resolvedCall.getCall().getCallElement(), mappings, false);
}
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.codegen.coroutines.CONTINUATION_ASM_TYPE
import org.jetbrains.kotlin.codegen.coroutines.unwrapInitialDescriptorForSuspendFunction
import org.jetbrains.kotlin.codegen.inline.NUMBERED_FUNCTION_PREFIX
import org.jetbrains.kotlin.codegen.inline.ReificationArgument
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeParametersUsages
import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
@@ -386,6 +387,7 @@ fun TypeSystemCommonBackendContext.extractReificationArgument(initialType: Kotli
val isNullable = type.isMarkedNullable()
while (type.isArrayOrNullableArray()) {
arrayDepth++
// TODO: warn that nullability info on argument will be lost?
val argument = type.getArgument(0)
if (argument.isStarProjection()) return null
type = argument.getType()
@@ -396,6 +398,24 @@ fun TypeSystemCommonBackendContext.extractReificationArgument(initialType: Kotli
return Pair(typeParameter, ReificationArgument(typeParameter.getName().asString(), isNullable, arrayDepth))
}
fun TypeSystemCommonBackendContext.extractUsedReifiedParameters(type: KotlinTypeMarker): ReifiedTypeParametersUsages =
ReifiedTypeParametersUsages().apply {
fun KotlinTypeMarker.visit() {
val typeParameter = typeConstructor().getTypeParameterClassifier()
if (typeParameter == null) {
for (argument in getArguments()) {
if (!argument.isStarProjection()) {
argument.getType().visit()
}
}
} else if (typeParameter.isReified()) {
addUsedReifiedParameter(typeParameter.getName().asString())
}
}
type.visit()
}
fun unwrapInitialSignatureDescriptor(function: FunctionDescriptor): FunctionDescriptor =
function.initialSignatureDescriptor ?: function
@@ -30,16 +30,16 @@ class AsmTypeRemapper(val typeRemapper: TypeRemapper, val result: InlineResult)
return object : SignatureRemapper(v, this) {
override fun visitTypeVariable(name: String) {
/*TODO try to erase absent type variable*/
val mapping = typeRemapper.mapTypeParameter(name) ?: return super.visitTypeVariable(name)
if (mapping.newName != null) {
val mapping = typeRemapper.mapTypeParameter(name)
if (mapping != null) {
// TODO: what is this condition
if (mapping.isReified) {
result.reifiedTypeParametersUsages.addUsedReifiedParameter(mapping.newName)
result.reifiedTypeParametersUsages.mergeAll(mapping.reifiedTypeParametersUsages)
}
return super.visitTypeVariable(mapping.newName)
SignatureReader(mapping.signature).accept(v)
return
}
// else TypeVariable is replaced by concrete type
SignatureReader(mapping.signature).accept(v)
return super.visitTypeVariable(name)
}
override fun visitFormalTypeParameter(name: String) {
@@ -5,10 +5,13 @@
package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.kotlin.codegen.extractReificationArgument
import org.jetbrains.kotlin.codegen.extractUsedReifiedParameters
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.signature.BothSignatureWriter
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.name.Name
@@ -147,9 +150,9 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
val result = ReifiedTypeParametersUsages()
for (insn in instructions.toArray()) {
if (isOperationReifiedMarker(insn)) {
val newName: String? = processReifyMarker(insn as MethodInsnNode, instructions)
if (newName != null) {
result.addUsedReifiedParameter(newName)
val newNames = processReifyMarker(insn as MethodInsnNode, instructions)
if (newNames != null) {
result.mergeAll(newNames)
}
}
}
@@ -158,33 +161,34 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
return result
}
/**
* @return new type parameter identifier if this marker should be reified further
* or null if it shouldn't
*/
private fun processReifyMarker(insn: MethodInsnNode, instructions: InsnList): String? {
private fun processReifyMarker(insn: MethodInsnNode, instructions: InsnList): ReifiedTypeParametersUsages? {
val operationKind = insn.operationKind ?: return null
val reificationArgument = insn.reificationArgument ?: return null
val mapping = parametersMapping?.get(reificationArgument.parameterName) ?: return null
if (mapping.asmType != null) {
// process* methods return false if marker should be reified further
// or it's invalid (may be emitted explicitly in code)
// they return true if instruction is reified and marker can be deleted
val (asmType, type) = reify(reificationArgument, mapping.asmType, mapping.type)
val kotlinType = intrinsicsSupport.toKotlinType(type)
var processed = if (isPluginNext(insn)) processPlugin(insn, instructions, type) else false
if (!processed) processed = when (operationKind) {
val asmType = mapping.asmType.reify(reificationArgument)
val type = mapping.type.reify(reificationArgument)
// Runtime-available types on the JVM are:
// 1. Array<T?> for some runtime-available type T
// 2. C<*, ...> and C<*, ...>? for some classifier C
// `typeOf`-like intrinsics are special in that they can handle a bigger set of types, including
// 1. Array<T> where T is not nullable
// 2. C<A, B, ...> with non-star projections A, B, ...
// To properly support those cases, we need to partially reify them even if we don't know the entire type yet.
// Otherwise nullability on all but the innermost dimension of a multidimensional array will be lost,
// and reified type parameters used as arguments to classifier types will never be reified.
if (mapping.reificationArgument == null || operationKind == OperationKind.TYPE_OF) {
val processed = (isPluginNext(insn) && processPlugin(insn, instructions, type)) || when (operationKind) {
// TODO: if `process*` returns false, then the marked sequence is invalid - simply leaving the marker in place
// will lead to an exception at runtime. What to do instead? Possible that the bytecode has been removed by
// dead code elimination (e.g. result of `T::class.java` was unused) and now we only need to erase the marker.
OperationKind.NEW_ARRAY -> processNewArray(insn, asmType)
OperationKind.AS -> processAs(insn, instructions, kotlinType, asmType, safe = false)
OperationKind.SAFE_AS -> processAs(insn, instructions, kotlinType, asmType, safe = true)
OperationKind.IS -> processIs(insn, instructions, kotlinType, asmType)
OperationKind.AS -> processAs(insn, instructions, type, asmType, safe = false)
OperationKind.SAFE_AS -> processAs(insn, instructions, type, asmType, safe = true)
OperationKind.IS -> processIs(insn, instructions, type, asmType)
OperationKind.JAVA_CLASS -> processJavaClass(insn, asmType)
OperationKind.ENUM_REIFIED -> processSpecialEnumFunction(insn, instructions, asmType)
OperationKind.TYPE_OF -> processTypeOfOrPlugin(insn, instructions, type)
OperationKind.TYPE_OF -> processTypeOf(insn, instructions, type)
}
if (processed) {
@@ -192,24 +196,22 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
instructions.remove(insn.previous!!) // PUSH type parameter
instructions.remove(insn) // INVOKESTATIC marker method
}
return null
} else {
val newReificationArgument = reificationArgument.combine(mapping.reificationArgument!!)
val newReificationArgument = reificationArgument.combine(mapping.reificationArgument)
instructions.set(insn.previous!!, LdcInsnNode(newReificationArgument.asString()))
return mapping.reificationArgument.parameterName
}
return mapping.reifiedTypeParametersUsages
}
private fun reify(argument: ReificationArgument, replacementAsmType: Type, type: KT): Pair<Type, KT> =
@Suppress("UNCHECKED_CAST")
private fun KT.reify(argument: ReificationArgument): KT =
with(typeSystem) {
val arrayType = type.arrayOf(argument.arrayDepth)
@Suppress("UNCHECKED_CAST")
Pair(
Type.getType("[".repeat(argument.arrayDepth) + replacementAsmType),
(if (argument.nullable) arrayType.makeNullable() else arrayType) as KT
)
}
val withArrays = arrayOf(argument.arrayDepth)
if (argument.nullable) withArrays.makeNullable() else withArrays
} as KT
private fun Type.reify(argument: ReificationArgument): Type =
Type.getType("[".repeat(argument.arrayDepth) + this)
private fun KotlinTypeMarker.arrayOf(arrayDepth: Int): KotlinTypeMarker {
var currentType = this
@@ -228,14 +230,14 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
private fun processAs(
insn: MethodInsnNode,
instructions: InsnList,
kotlinType: KotlinType,
type: KT,
asmType: Type,
safe: Boolean
) = rewriteNextTypeInsn(insn, Opcodes.CHECKCAST) { stubCheckcast: AbstractInsnNode ->
if (stubCheckcast !is TypeInsnNode) return false
val newMethodNode = MethodNode(Opcodes.API_VERSION)
generateAsCast(InstructionAdapter(newMethodNode), kotlinType, asmType, safe, unifiedNullChecks)
generateAsCast(InstructionAdapter(newMethodNode), intrinsicsSupport.toKotlinType(type), asmType, safe, unifiedNullChecks)
instructions.insert(insn, newMethodNode.instructions)
// Keep stubCheckcast to avoid VerifyErrors on 1.8+ bytecode,
@@ -253,13 +255,13 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
private fun processIs(
insn: MethodInsnNode,
instructions: InsnList,
kotlinType: KotlinType,
type: KT,
asmType: Type
) = rewriteNextTypeInsn(insn, Opcodes.INSTANCEOF) { stubInstanceOf: AbstractInsnNode ->
if (stubInstanceOf !is TypeInsnNode) return false
val newMethodNode = MethodNode(Opcodes.API_VERSION)
generateIsCheck(InstructionAdapter(newMethodNode), kotlinType, asmType)
generateIsCheck(InstructionAdapter(newMethodNode), intrinsicsSupport.toKotlinType(type), asmType)
instructions.insert(insn, newMethodNode.instructions)
instructions.remove(stubInstanceOf)
@@ -269,7 +271,7 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
return true
}
private fun processTypeOfOrPlugin(
private fun processTypeOf(
insn: MethodInsnNode,
instructions: InsnList,
type: KT
@@ -385,37 +387,43 @@ val MethodInsnNode.operationKind: ReifiedTypeInliner.OperationKind?
ReifiedTypeInliner.OperationKind.values().getOrNull(it)
}
class TypeParameterMappings<KT : KotlinTypeMarker> {
class TypeParameterMappings<KT : KotlinTypeMarker>(
typeSystem: TypeSystemCommonBackendContext,
typeArguments: Map<out TypeParameterMarker, KT>,
allReified: Boolean,
mapType: (KT, BothSignatureWriter) -> Type
) {
private val mappingsByName = hashMapOf<String, TypeParameterMapping<KT>>()
fun addParameterMappingToType(name: String, type: KT, asmType: Type, signature: String, isReified: Boolean) {
mappingsByName[name] = TypeParameterMapping(
name, type, asmType, reificationArgument = null, signature = signature, isReified = isReified
)
}
fun addParameterMappingForFurtherReification(name: String, type: KT, reificationArgument: ReificationArgument, isReified: Boolean) {
mappingsByName[name] = TypeParameterMapping(
name, type, asmType = null, reificationArgument = reificationArgument, signature = null, isReified = isReified
)
init {
with(typeSystem) {
for ((parameter, type) in typeArguments.entries) {
val name = parameter.getName().identifier
val sw = BothSignatureWriter(BothSignatureWriter.Mode.TYPE)
mappingsByName[name] = TypeParameterMapping(
type, mapType(type, sw), sw.toString(), allReified || parameter.isReified(),
typeSystem.extractReificationArgument(type)?.second,
typeSystem.extractUsedReifiedParameters(type)
)
}
}
}
operator fun get(name: String): TypeParameterMapping<KT>? = mappingsByName[name]
fun hasReifiedParameters() = mappingsByName.values.any { it.isReified }
internal inline fun forEach(l: (TypeParameterMapping<KT>) -> Unit) {
mappingsByName.values.forEach(l)
}
internal inline fun forEach(block: (String, TypeParameterMapping<KT>) -> Unit) =
mappingsByName.entries.forEach { (name, mapping) -> block(name, mapping) }
}
class TypeParameterMapping<KT : KotlinTypeMarker>(
val name: String,
val type: KT,
val asmType: Type?,
val asmType: Type,
val signature: String,
val isReified: Boolean,
val reificationArgument: ReificationArgument?,
val signature: String?,
val isReified: Boolean
val reifiedTypeParametersUsages: ReifiedTypeParametersUsages,
)
class ReifiedTypeParametersUsages {
@@ -18,8 +18,6 @@ package org.jetbrains.kotlin.codegen.inline
import java.util.*
class TypeParameter(val oldName: String, val newName: String?, val isReified: Boolean, val signature: String?)
//typeMapping data could be changed outside through method processing
class TypeRemapper private constructor(
private val typeMapping: MutableMap<String, String?>,
@@ -27,7 +25,7 @@ class TypeRemapper private constructor(
private val isRootInlineLambda: Boolean = false
) {
private val additionalMappings = hashMapOf<String, String>()
private val typeParametersMapping = hashMapOf<String, TypeParameter>()
private val typeParametersMapping = hashMapOf<String, TypeParameterMapping<*>?>()
fun addMapping(type: String, newType: String) {
typeMapping[type] = newType
@@ -50,26 +48,24 @@ class TypeRemapper private constructor(
// assert(typeParametersMapping[name] == null) {
// "Type parameter already registered $name"
// }
typeParametersMapping[name] = TypeParameter(name, name, false, null)
typeParametersMapping[name] = null
}
fun registerTypeParameter(mapping: TypeParameterMapping<*>) {
typeParametersMapping[mapping.name] = TypeParameter(
mapping.name, mapping.reificationArgument?.parameterName, mapping.isReified, mapping.signature
)
fun registerTypeParameter(name: String, mapping: TypeParameterMapping<*>) {
typeParametersMapping[name] = mapping
}
fun mapTypeParameter(name: String): TypeParameter? {
return typeParametersMapping[name] ?: if (!isRootInlineLambda) parent?.mapTypeParameter(name) else null
fun mapTypeParameter(name: String): TypeParameterMapping<*>? = when {
name in typeParametersMapping -> typeParametersMapping[name]
!isRootInlineLambda -> parent?.mapTypeParameter(name)
else -> null
}
companion object {
@JvmStatic
fun createRoot(formalTypeParameters: TypeParameterMappings<*>?): TypeRemapper {
fun createRoot(formalTypeParameters: TypeParameterMappings<*>): TypeRemapper {
return TypeRemapper(HashMap()).apply {
formalTypeParameters?.forEach {
registerTypeParameter(it)
}
formalTypeParameters.forEach(::registerTypeParameter)
}
}
@@ -22,73 +22,62 @@ import kotlin.reflect.KVariance
internal fun TypeSystemCommonBackendContext.createTypeOfMethodBody(typeParameter: TypeParameterMarker): MethodNode {
val node = MethodNode(Opcodes.API_VERSION, Opcodes.ACC_STATIC, "fake", Type.getMethodDescriptor(K_TYPE), null, null)
val v = InstructionAdapter(node)
putTypeOfReifiedTypeParameter(v, typeParameter, false)
val argument = ReificationArgument(typeParameter.getName().asString(), false, 0)
ReifiedTypeInliner.putReifiedOperationMarker(ReifiedTypeInliner.OperationKind.TYPE_OF, argument, v)
v.aconst(null)
v.areturn(K_TYPE)
v.visitMaxs(2, 0)
return node
}
private fun TypeSystemCommonBackendContext.putTypeOfReifiedTypeParameter(
v: InstructionAdapter, typeParameter: TypeParameterMarker, isNullable: Boolean
) {
ReifiedTypeInliner.putReifiedOperationMarkerIfNeeded(typeParameter, isNullable, ReifiedTypeInliner.OperationKind.TYPE_OF, v, this)
v.aconst(null)
private inline fun InstructionAdapter.unrollArrayIfFewerThan(n: Int, limit: Int, type: Type, element: (Int) -> Unit): Array<Type> {
if (n < limit) {
return Array(n) { i ->
element(i)
type
}
}
iconst(n)
newarray(type)
for (i in 0 until n) {
dup()
iconst(i)
element(i)
astore(type)
}
return arrayOf(AsmUtil.getArrayType(type))
}
fun <KT : KotlinTypeMarker> TypeSystemCommonBackendContext.generateTypeOf(
v: InstructionAdapter, type: KT, intrinsicsSupport: ReifiedTypeInliner.IntrinsicsSupport<KT>
) = generateTypeOf(v, type, intrinsicsSupport, isTypeParameterBound = false)
private fun <KT : KotlinTypeMarker> TypeSystemCommonBackendContext.generateTypeOf(
v: InstructionAdapter, type: KT, intrinsicsSupport: ReifiedTypeInliner.IntrinsicsSupport<KT>, isTypeParameterBound: Boolean
) {
val typeParameter = type.typeConstructor().getTypeParameterClassifier()
if (typeParameter != null) {
if (!doesTypeContainTypeParametersWithRecursiveBounds(type)) {
intrinsicsSupport.reportNonReifiedTypeParameterWithRecursiveBoundUnsupported(typeParameter.getName())
v.aconst(null)
return
}
generateNonReifiedTypeParameter(v, typeParameter, intrinsicsSupport)
} else {
val methodArguments = if (typeParameter == null) {
intrinsicsSupport.putClassInstance(v, type)
}
val argumentsSize = type.argumentsCount()
val useArray = argumentsSize >= 3
if (useArray) {
v.iconst(argumentsSize)
v.newarray(K_TYPE_PROJECTION)
}
for (i in 0 until argumentsSize) {
if (useArray) {
v.dup()
v.iconst(i)
}
doGenerateTypeProjection(v, type.getArgument(i), intrinsicsSupport)
if (useArray) {
v.astore(K_TYPE_PROJECTION)
val arguments = v.unrollArrayIfFewerThan(type.argumentsCount(), 3, K_TYPE_PROJECTION) { i ->
generateTypeOfArgument(v, type.getArgument(i), intrinsicsSupport, isTypeParameterBound)
}
arrayOf(JAVA_CLASS_TYPE, *arguments)
} else if (!isTypeParameterBound && typeParameter.isReified()) {
val argument = ReificationArgument(typeParameter.getName().asString(), type.isMarkedNullable(), 0)
ReifiedTypeInliner.putReifiedOperationMarker(ReifiedTypeInliner.OperationKind.TYPE_OF, argument, v)
v.aconst(null)
return
} else if (typeReferencesParameterWithRecursiveBound(type)) {
intrinsicsSupport.reportNonReifiedTypeParameterWithRecursiveBoundUnsupported(typeParameter.getName())
v.aconst(null)
return
} else {
generateNonReifiedTypeParameter(v, typeParameter, intrinsicsSupport)
arrayOf(K_CLASSIFIER_TYPE)
}
val methodName = if (type.isMarkedNullable()) "nullableTypeOf" else "typeOf"
val signature = if (typeParameter != null) {
Type.getMethodDescriptor(K_TYPE, K_CLASSIFIER_TYPE)
} else {
val projections = when (argumentsSize) {
0 -> emptyArray()
1 -> arrayOf(K_TYPE_PROJECTION)
2 -> arrayOf(K_TYPE_PROJECTION, K_TYPE_PROJECTION)
else -> arrayOf(AsmUtil.getArrayType(K_TYPE_PROJECTION))
}
Type.getMethodDescriptor(K_TYPE, JAVA_CLASS_TYPE, *projections)
}
val signature = Type.getMethodDescriptor(K_TYPE, *methodArguments)
v.invokestatic(REFLECTION, methodName, signature, false)
if (intrinsicsSupport.toKotlinType(type).isSuspendFunctionType) {
@@ -106,7 +95,7 @@ fun <KT : KotlinTypeMarker> TypeSystemCommonBackendContext.generateTypeOf(
// 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)
generateTypeOf(v, type.upperBoundIfFlexible() as KT, intrinsicsSupport, isTypeParameterBound)
v.invokestatic(REFLECTION, "platformType", Type.getMethodDescriptor(K_TYPE, K_TYPE, K_TYPE), false)
}
@@ -132,76 +121,54 @@ private fun <KT : KotlinTypeMarker> TypeSystemCommonBackendContext.generateNonRe
false,
)
@Suppress("UNCHECKED_CAST")
val bounds = (0 until typeParameter.upperBoundCount()).map { typeParameter.getUpperBound(it) as KT }
if (bounds.isEmpty()) return
if (typeParameter.upperBoundCount() == 0) return
v.dup()
if (bounds.size == 1) {
generateTypeOf(v, bounds.single(), intrinsicsSupport)
} else {
v.iconst(bounds.size)
v.newarray(K_TYPE)
for ((i, bound) in bounds.withIndex()) {
v.dup()
v.iconst(i)
generateTypeOf(v, bound, intrinsicsSupport)
v.astore(K_TYPE)
}
val argumentsForBounds = v.unrollArrayIfFewerThan(typeParameter.upperBoundCount(), 2, K_TYPE) { i ->
@Suppress("UNCHECKED_CAST")
generateTypeOf(v, typeParameter.getUpperBound(i) as KT, intrinsicsSupport, isTypeParameterBound = true)
}
v.invokestatic(
REFLECTION, "setUpperBounds", Type.getMethodDescriptor(
Type.VOID_TYPE, K_TYPE_PARAMETER,
if (bounds.size == 1) K_TYPE else AsmUtil.getArrayType(K_TYPE)
),
REFLECTION, "setUpperBounds", Type.getMethodDescriptor(Type.VOID_TYPE, K_TYPE_PARAMETER, *argumentsForBounds),
false
)
}
private fun TypeSystemCommonBackendContext.doesTypeContainTypeParametersWithRecursiveBounds(
private fun TypeSystemCommonBackendContext.typeReferencesParameterWithRecursiveBound(
type: KotlinTypeMarker,
used: MutableSet<TypeParameterMarker> = linkedSetOf()
): Boolean {
val typeParameter = type.typeConstructor().getTypeParameterClassifier()
if (typeParameter != null) {
if (!used.add(typeParameter)) return false
if (!used.add(typeParameter)) return true
for (i in 0 until typeParameter.upperBoundCount()) {
if (!doesTypeContainTypeParametersWithRecursiveBounds(typeParameter.getUpperBound(i), used)) return false
if (typeReferencesParameterWithRecursiveBound(typeParameter.getUpperBound(i), used)) return true
}
used.remove(typeParameter)
} else {
for (i in 0 until type.argumentsCount()) {
val argument = type.getArgument(i)
if (!argument.isStarProjection() && !doesTypeContainTypeParametersWithRecursiveBounds(argument.getType(), used)) return false
if (!argument.isStarProjection() && typeReferencesParameterWithRecursiveBound(argument.getType(), used)) return true
}
}
return true
return false
}
private fun <KT : KotlinTypeMarker> TypeSystemCommonBackendContext.doGenerateTypeProjection(
private fun <KT : KotlinTypeMarker> TypeSystemCommonBackendContext.generateTypeOfArgument(
v: InstructionAdapter,
projection: TypeArgumentMarker,
intrinsicsSupport: ReifiedTypeInliner.IntrinsicsSupport<KT>
intrinsicsSupport: ReifiedTypeInliner.IntrinsicsSupport<KT>,
isTypeParameterBound: Boolean,
) {
// KTypeProjection members could be static, see KT-30083 and KT-30084
// KTypeProjection companion members could be made `@JvmStatic`, see KT-30083 and KT-30084
v.getstatic(K_TYPE_PROJECTION.internalName, "Companion", K_TYPE_PROJECTION_COMPANION.descriptor)
if (projection.isStarProjection()) {
v.invokevirtual(K_TYPE_PROJECTION_COMPANION.internalName, "getSTAR", Type.getMethodDescriptor(K_TYPE_PROJECTION), false)
return
}
@Suppress("UNCHECKED_CAST")
val type = projection.getType() as KT
val typeParameterClassifier = type.typeConstructor().getTypeParameterClassifier()
if (typeParameterClassifier != null && typeParameterClassifier.isReified()) {
putTypeOfReifiedTypeParameter(v, typeParameterClassifier, type.isMarkedNullable())
} else {
generateTypeOf(v, type, intrinsicsSupport)
}
generateTypeOf(v, projection.getType() as KT, intrinsicsSupport, isTypeParameterBound)
val methodName = when (projection.getVariance()) {
TypeVariance.INV -> "invariant"
TypeVariance.IN -> "contravariant"
@@ -44586,6 +44586,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/reflection/typeOf/annotatedType.kt");
}
@Test
@TestMetadata("arrayOfNullableReified.kt")
public void testArrayOfNullableReified() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/arrayOfNullableReified.kt");
}
@Test
@TestMetadata("caching.kt")
public void testCaching() throws Exception {
@@ -44676,6 +44682,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/reflection/typeOf/rawTypes_before.kt");
}
@Test
@TestMetadata("reifiedAsNestedArgument.kt")
public void testReifiedAsNestedArgument() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/reifiedAsNestedArgument.kt");
}
@Test
@TestMetadata("typeOfCapturedStar.kt")
public void testTypeOfCapturedStar() throws Exception {
@@ -45989,6 +46001,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/reified/spreads.kt");
}
@Test
@TestMetadata("typeTokenWrapper.kt")
public void testTypeTokenWrapper() throws Exception {
runTest("compiler/testData/codegen/box/reified/typeTokenWrapper.kt");
}
@Test
@TestMetadata("varargs.kt")
public void testVarargs() throws Exception {
@@ -57,7 +57,6 @@ import org.jetbrains.kotlin.types.computeExpandedTypeForInlineClass
import org.jetbrains.kotlin.types.model.TypeParameterMarker
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.kotlin.utils.keysToMap
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
@@ -1455,30 +1454,12 @@ class ExpressionCodegen(
if (element.typeArgumentsCount == 0) {
//avoid ambiguity with type constructor type parameters
emptyMap()
} else typeArgumentContainer.typeParameters.keysToMap {
element.getTypeArgumentOrDefault(it)
} else typeArgumentContainer.typeParameters.associate {
it.symbol to element.getTypeArgumentOrDefault(it)
}
val mappings = TypeParameterMappings<IrType>()
for ((key, type) in typeArguments.entries) {
val reificationArgument = typeMapper.typeSystem.extractReificationArgument(type)
if (reificationArgument == null) {
// type is not generic
val signatureWriter = BothSignatureWriter(BothSignatureWriter.Mode.TYPE)
val asmType = typeMapper.mapTypeParameter(type, signatureWriter)
mappings.addParameterMappingToType(
key.name.identifier, type, asmType, signatureWriter.toString(), key.isReified
)
} else {
mappings.addParameterMappingForFurtherReification(
key.name.identifier, type, reificationArgument.second, key.isReified
)
}
}
val mappings = TypeParameterMappings(typeMapper.typeSystem, typeArguments, allReified = false, typeMapper::mapTypeParameter)
val sourceCompiler = IrSourceCompilerForInline(state, element, callee, this, data)
val reifiedTypeInliner = ReifiedTypeInliner(
mappings,
IrInlineIntrinsicsSupport(classCodegen, element, irFunction.fileParent),
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
import org.jetbrains.kotlin.backend.jvm.codegen.IrInlineIntrinsicsSupport
import org.jetbrains.kotlin.backend.jvm.ir.fileParent
import org.jetbrains.kotlin.codegen.extractUsedReifiedParameters
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner
import org.jetbrains.kotlin.codegen.inline.generateTypeOf
import org.jetbrains.kotlin.codegen.putReifiedOperationMarkerIfTypeIsReifiedParameter
@@ -17,12 +18,9 @@ import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
object TypeOf : IntrinsicMethod() {
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) = with(codegen) {
val type = expression.getTypeArgument(0)!!
if (putReifiedOperationMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.OperationKind.TYPE_OF)) {
mv.aconst(null) // see ReifiedTypeInliner.processTypeOf
} else {
val support = IrInlineIntrinsicsSupport(codegen.classCodegen, expression, codegen.irFunction.fileParent)
typeMapper.typeSystem.generateTypeOf(mv, type, support)
}
val support = IrInlineIntrinsicsSupport(codegen.classCodegen, expression, codegen.irFunction.fileParent)
typeMapper.typeSystem.generateTypeOf(mv, type, support)
codegen.propagateChildReifiedTypeParametersUsages(codegen.typeMapper.typeSystem.extractUsedReifiedParameters(type))
expression.onStack
}
}
@@ -0,0 +1,21 @@
// IGNORE_BACKEND: JS_IR, JS_IR_ES6
// WITH_REFLECT
import kotlin.reflect.typeOf
import kotlin.test.assertEquals
inline fun <reified T> typeOfArrayOfNArrayOf() =
typeOf<Array<Array<T>?>>()
inline fun <reified T> myTypeOf() =
typeOf<T>()
inline fun <reified T> myTypeOfArrayOfNArrayOf() =
typeOf<Array<Array<T>?>>()
fun box(): String {
assertEquals(typeOf<Array<Array<String>?>>(), typeOfArrayOfNArrayOf<String>())
assertEquals(typeOf<Array<Array<String>?>>(), myTypeOf<Array<Array<String>?>>())
assertEquals(typeOf<Array<Array<String>?>>(), myTypeOfArrayOfNArrayOf<String>())
return "OK"
}
@@ -0,0 +1,20 @@
// IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6
// WITH_REFLECT
import kotlin.reflect.typeOf
import kotlin.reflect.KType
import kotlin.test.assertEquals
inline fun <reified T> foo() =
object { val x = typeOf<T>() }.x
inline fun <reified T> bar(expected: KType) {
assertEquals(expected, foo<List<T>>())
assertEquals(expected, object { val x = typeOf<List<T>>() }.x)
assertEquals(expected, typeOf<List<T>>())
}
fun box(): String {
bar<Int>(typeOf<List<Int>>())
return "OK"
}
@@ -0,0 +1,33 @@
// TARGET_BACKEND: JVM
// ISSUE: KT-53761
// WITH_STDLIB
// IGNORE_BACKEND: ANDROID
import kotlin.test.assertEquals
open class TypeToken<T> {
val type = javaClass.genericSuperclass
}
inline fun <reified E> myTypeOf() =
object : TypeToken<E>() {}.type
inline fun <reified T> myTypeOfArrayOf() =
object : TypeToken<Array<T>>() {}.type
inline fun <reified T> myTypeOfArrayOf2() =
myTypeOf<Array<T>>()
inline fun <reified T> myTypeOfListOf() =
object : TypeToken<List<T>>() {}.type
inline fun <reified T> myTypeOfListOf2() =
myTypeOf<List<T>>()
fun box(): String {
assertEquals(myTypeOf<Array<String>>(), myTypeOfArrayOf<String>())
assertEquals(myTypeOf<Array<String>>(), myTypeOfArrayOf2<String>())
assertEquals(myTypeOf<List<String>>(), myTypeOfListOf<String>())
assertEquals(myTypeOf<List<String>>(), myTypeOfListOf2<String>())
return "OK"
}
@@ -43104,6 +43104,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/reflection/typeOf/annotatedType.kt");
}
@Test
@TestMetadata("arrayOfNullableReified.kt")
public void testArrayOfNullableReified() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/arrayOfNullableReified.kt");
}
@Test
@TestMetadata("classes.kt")
public void testClasses() throws Exception {
@@ -43188,6 +43194,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/reflection/typeOf/rawTypes_before.kt");
}
@Test
@TestMetadata("reifiedAsNestedArgument.kt")
public void testReifiedAsNestedArgument() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/reifiedAsNestedArgument.kt");
}
@Test
@TestMetadata("typeOfCapturedStar.kt")
public void testTypeOfCapturedStar() throws Exception {
@@ -44501,6 +44513,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/reified/spreads.kt");
}
@Test
@TestMetadata("typeTokenWrapper.kt")
public void testTypeTokenWrapper() throws Exception {
runTest("compiler/testData/codegen/box/reified/typeTokenWrapper.kt");
}
@Test
@TestMetadata("varargs.kt")
public void testVarargs() throws Exception {
@@ -44586,6 +44586,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/reflection/typeOf/annotatedType.kt");
}
@Test
@TestMetadata("arrayOfNullableReified.kt")
public void testArrayOfNullableReified() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/arrayOfNullableReified.kt");
}
@Test
@TestMetadata("caching.kt")
public void testCaching() throws Exception {
@@ -44676,6 +44682,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/reflection/typeOf/rawTypes_before.kt");
}
@Test
@TestMetadata("reifiedAsNestedArgument.kt")
public void testReifiedAsNestedArgument() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/reifiedAsNestedArgument.kt");
}
@Test
@TestMetadata("typeOfCapturedStar.kt")
public void testTypeOfCapturedStar() throws Exception {
@@ -45989,6 +46001,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/reified/spreads.kt");
}
@Test
@TestMetadata("typeTokenWrapper.kt")
public void testTypeTokenWrapper() throws Exception {
runTest("compiler/testData/codegen/box/reified/typeTokenWrapper.kt");
}
@Test
@TestMetadata("varargs.kt")
public void testVarargs() throws Exception {
@@ -34660,6 +34660,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/reflection/typeOf/annotatedType.kt");
}
@TestMetadata("arrayOfNullableReified.kt")
public void testArrayOfNullableReified() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/arrayOfNullableReified.kt");
}
@TestMetadata("classes.kt")
public void testClasses() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/classes.kt");
@@ -34730,6 +34735,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/reflection/typeOf/rawTypes_before.kt");
}
@TestMetadata("reifiedAsNestedArgument.kt")
public void testReifiedAsNestedArgument() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/reifiedAsNestedArgument.kt");
}
@TestMetadata("typeOfCapturedStar.kt")
public void testTypeOfCapturedStar() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/typeOfCapturedStar.kt");
@@ -35871,6 +35881,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/reified/spreads.kt");
}
@TestMetadata("typeTokenWrapper.kt")
public void testTypeTokenWrapper() throws Exception {
runTest("compiler/testData/codegen/box/reified/typeTokenWrapper.kt");
}
@TestMetadata("varargs.kt")
public void testVarargs() throws Exception {
runTest("compiler/testData/codegen/box/reified/varargs.kt");
@@ -32486,6 +32486,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/typeOf"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true);
}
@Test
@TestMetadata("arrayOfNullableReified.kt")
public void testArrayOfNullableReified() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/arrayOfNullableReified.kt");
}
@Test
@TestMetadata("classes.kt")
public void testClasses() throws Exception {
@@ -32516,6 +32522,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/reflection/typeOf/multipleLayers.kt");
}
@Test
@TestMetadata("reifiedAsNestedArgument.kt")
public void testReifiedAsNestedArgument() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/reifiedAsNestedArgument.kt");
}
@Test
@TestMetadata("typeOfCapturedStar.kt")
public void testTypeOfCapturedStar() throws Exception {
@@ -32666,6 +32666,12 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/typeOf"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
}
@Test
@TestMetadata("arrayOfNullableReified.kt")
public void testArrayOfNullableReified() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/arrayOfNullableReified.kt");
}
@Test
@TestMetadata("classes.kt")
public void testClasses() throws Exception {
@@ -32696,6 +32702,12 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/reflection/typeOf/multipleLayers.kt");
}
@Test
@TestMetadata("reifiedAsNestedArgument.kt")
public void testReifiedAsNestedArgument() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/reifiedAsNestedArgument.kt");
}
@Test
@TestMetadata("typeOfCapturedStar.kt")
public void testTypeOfCapturedStar() throws Exception {
@@ -29295,6 +29295,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/typeOf"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true);
}
@TestMetadata("arrayOfNullableReified.kt")
public void testArrayOfNullableReified() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/arrayOfNullableReified.kt");
}
@TestMetadata("classes.kt")
public void testClasses() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/classes.kt");
@@ -29320,6 +29325,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
runTest("compiler/testData/codegen/box/reflection/typeOf/multipleLayers.kt");
}
@TestMetadata("reifiedAsNestedArgument.kt")
public void testReifiedAsNestedArgument() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/reifiedAsNestedArgument.kt");
}
@TestMetadata("typeOfCapturedStar.kt")
public void testTypeOfCapturedStar() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/typeOfCapturedStar.kt");
@@ -35743,6 +35743,12 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/typeOf"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.NATIVE, true);
}
@Test
@TestMetadata("arrayOfNullableReified.kt")
public void testArrayOfNullableReified() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/arrayOfNullableReified.kt");
}
@Test
@TestMetadata("classes.kt")
public void testClasses() throws Exception {
@@ -35773,6 +35779,12 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest
runTest("compiler/testData/codegen/box/reflection/typeOf/multipleLayers.kt");
}
@Test
@TestMetadata("reifiedAsNestedArgument.kt")
public void testReifiedAsNestedArgument() throws Exception {
runTest("compiler/testData/codegen/box/reflection/typeOf/reifiedAsNestedArgument.kt");
}
@Test
@TestMetadata("typeOfCapturedStar.kt")
public void testTypeOfCapturedStar() throws Exception {
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.jvm.intrinsics.IntrinsicMethod
import org.jetbrains.kotlin.backend.jvm.ir.representativeUpperBound
import org.jetbrains.kotlin.backend.jvm.mapping.mapClass
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.extractUsedReifiedParameters
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.Companion.pluginIntrinsicsMarkerMethod
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.Companion.pluginIntrinsicsMarkerOwner
@@ -92,6 +93,7 @@ class SerializationJvmIrIntrinsicSupport(val jvmBackendContext: JvmBackendContex
mv,
intrinsicType
)
codegen.propagateChildReifiedTypeParametersUsages(codegen.typeMapper.typeSystem.extractUsedReifiedParameters(argument))
if (withModule) {
frameMap.leaveTemp(serializersModuleType)
}