Support non-reified type parameters in typeOf in JVM and JVM_IR
#KT-30279 Fixed
This commit is contained in:
committed by
Alexander Udalov
parent
6fb40878c4
commit
0ce16b9d8c
@@ -643,59 +643,61 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
|
||||
|
||||
if (!state.getClassBuilderMode().generateBodies) return;
|
||||
|
||||
boolean generateClassIntCtorCall = state.getGenerateOptimizedCallableReferenceSuperClasses();
|
||||
|
||||
InstructionAdapter iv = createOrGetClInitCodegen().v;
|
||||
iv.iconst(delegatedProperties.size());
|
||||
iv.newarray(K_PROPERTY_TYPE);
|
||||
|
||||
for (int i = 0, size = delegatedProperties.size(); i < size; i++) {
|
||||
VariableDescriptorWithAccessors property = delegatedProperties.get(i);
|
||||
|
||||
iv.dup();
|
||||
iv.iconst(i);
|
||||
|
||||
int receiverCount = (property.getDispatchReceiverParameter() != null ? 1 : 0) +
|
||||
(property.getExtensionReceiverParameter() != null ? 1 : 0);
|
||||
Type implType = property.isVar() ? MUTABLE_PROPERTY_REFERENCE_IMPL[receiverCount] : PROPERTY_REFERENCE_IMPL[receiverCount];
|
||||
iv.anew(implType);
|
||||
iv.dup();
|
||||
|
||||
List<Type> superCtorArgTypes = new ArrayList<>();
|
||||
if (generateClassIntCtorCall) {
|
||||
CallableReferenceUtilKt.generateCallableReferenceDeclarationContainerClass(iv, property, state);
|
||||
superCtorArgTypes.add(JAVA_CLASS_TYPE);
|
||||
} else {
|
||||
// TODO: generate the container once and save to a local field instead (KT-10495)
|
||||
CallableReferenceUtilKt.generateCallableReferenceDeclarationContainer(iv, property, state);
|
||||
superCtorArgTypes.add(K_DECLARATION_CONTAINER_TYPE);
|
||||
}
|
||||
|
||||
iv.aconst(property.getName().asString());
|
||||
CallableReferenceUtilKt.generateCallableReferenceSignature(iv, property, state);
|
||||
superCtorArgTypes.add(JAVA_STRING_TYPE);
|
||||
superCtorArgTypes.add(JAVA_STRING_TYPE);
|
||||
|
||||
if (generateClassIntCtorCall) {
|
||||
iv.aconst(CallableReferenceUtilKt.getCallableReferenceTopLevelFlag(property));
|
||||
superCtorArgTypes.add(Type.INT_TYPE);
|
||||
}
|
||||
|
||||
iv.invokespecial(
|
||||
implType.getInternalName(), "<init>",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, superCtorArgTypes.toArray(new Type[0])), false
|
||||
);
|
||||
Method wrapper = PropertyReferenceCodegen.getWrapperMethodForPropertyReference(property, receiverCount);
|
||||
iv.invokestatic(REFLECTION, wrapper.getName(), wrapper.getDescriptor(), false);
|
||||
|
||||
StackValue.onStack(implType).put(K_PROPERTY_TYPE, iv);
|
||||
|
||||
generatePropertyReference(iv, delegatedProperties.get(i), state);
|
||||
iv.astore(K_PROPERTY_TYPE);
|
||||
}
|
||||
|
||||
iv.putstatic(thisAsmType.getInternalName(), JvmAbi.DELEGATED_PROPERTIES_ARRAY_NAME, "[" + K_PROPERTY_TYPE);
|
||||
}
|
||||
|
||||
public static void generatePropertyReference(
|
||||
@NotNull InstructionAdapter iv,
|
||||
@NotNull VariableDescriptorWithAccessors property,
|
||||
@NotNull GenerationState state
|
||||
) {
|
||||
int receiverCount = (property.getDispatchReceiverParameter() != null ? 1 : 0) +
|
||||
(property.getExtensionReceiverParameter() != null ? 1 : 0);
|
||||
Type implType = property.isVar() ? MUTABLE_PROPERTY_REFERENCE_IMPL[receiverCount] : PROPERTY_REFERENCE_IMPL[receiverCount];
|
||||
iv.anew(implType);
|
||||
iv.dup();
|
||||
|
||||
List<Type> superCtorArgTypes = new ArrayList<>();
|
||||
if (state.getGenerateOptimizedCallableReferenceSuperClasses()) {
|
||||
CallableReferenceUtilKt.generateCallableReferenceDeclarationContainerClass(iv, property, state);
|
||||
superCtorArgTypes.add(JAVA_CLASS_TYPE);
|
||||
} else {
|
||||
// TODO: generate the container once and save to a local field instead (KT-10495)
|
||||
CallableReferenceUtilKt.generateCallableReferenceDeclarationContainer(iv, property, state);
|
||||
superCtorArgTypes.add(K_DECLARATION_CONTAINER_TYPE);
|
||||
}
|
||||
|
||||
iv.aconst(property.getName().asString());
|
||||
CallableReferenceUtilKt.generateCallableReferenceSignature(iv, property, state);
|
||||
superCtorArgTypes.add(JAVA_STRING_TYPE);
|
||||
superCtorArgTypes.add(JAVA_STRING_TYPE);
|
||||
|
||||
if (state.getGenerateOptimizedCallableReferenceSuperClasses()) {
|
||||
iv.aconst(CallableReferenceUtilKt.getCallableReferenceTopLevelFlag(property));
|
||||
superCtorArgTypes.add(Type.INT_TYPE);
|
||||
}
|
||||
|
||||
iv.invokespecial(
|
||||
implType.getInternalName(), "<init>",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, superCtorArgTypes.toArray(new Type[0])), false
|
||||
);
|
||||
Method wrapper = PropertyReferenceCodegen.getWrapperMethodForPropertyReference(property, receiverCount);
|
||||
iv.invokestatic(REFLECTION, wrapper.getName(), wrapper.getDescriptor(), false);
|
||||
|
||||
StackValue.onStack(implType).put(K_PROPERTY_TYPE, iv);
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return v.getThisName();
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
class PsiInlineCodegen(
|
||||
@@ -42,15 +41,8 @@ class PsiInlineCodegen(
|
||||
private val actualDispatchReceiver: Type = methodOwner
|
||||
) : InlineCodegen<ExpressionCodegen>(
|
||||
codegen, state, function, methodOwner, signature, typeParameterMappings, sourceCompiler,
|
||||
ReifiedTypeInliner(typeParameterMappings, object : ReifiedTypeInliner.IntrinsicsSupport<KotlinType> {
|
||||
override fun putClassInstance(v: InstructionAdapter, type: KotlinType) {
|
||||
AsmUtil.putJavaLangClassInstance(v, state.typeMapper.mapType(type), type, state.typeMapper)
|
||||
}
|
||||
|
||||
override fun toKotlinType(type: KotlinType): KotlinType = type
|
||||
}, codegen.typeSystem, state.languageVersionSettings)
|
||||
ReifiedTypeInliner(typeParameterMappings, PsiInlineIntrinsicsSupport(state), codegen.typeSystem, state.languageVersionSettings),
|
||||
), CallGenerator {
|
||||
|
||||
override fun generateAssertFieldIfNeeded(info: RootInliningContext) {
|
||||
if (info.generateAssertField) {
|
||||
codegen.parentCodegen.generateAssertField()
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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 org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.*
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.model.TypeParameterMarker
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
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> {
|
||||
override fun putClassInstance(v: InstructionAdapter, type: KotlinType) {
|
||||
AsmUtil.putJavaLangClassInstance(v, state.typeMapper.mapType(type), type, state.typeMapper)
|
||||
}
|
||||
|
||||
override fun generateTypeParameterContainer(v: InstructionAdapter, typeParameter: TypeParameterMarker) {
|
||||
require(typeParameter is TypeParameterDescriptor)
|
||||
|
||||
when (val container = typeParameter.containingDeclaration) {
|
||||
is ClassDescriptor -> putClassInstance(v, container.defaultType).also { AsmUtil.wrapJavaClassIntoKClass(v) }
|
||||
is FunctionDescriptor -> generateFunctionReference(v, container)
|
||||
is PropertyDescriptor -> MemberCodegen.generatePropertyReference(v, container, state)
|
||||
else -> error("Unknown container of type parameter: $container (${typeParameter.name})")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateFunctionReference(v: InstructionAdapter, descriptor: FunctionDescriptor) {
|
||||
check(state.generateOptimizedCallableReferenceSuperClasses) {
|
||||
"typeOf() of a non-reified type parameter is only allowed if optimized callable references are enabled.\n" +
|
||||
"Please make sure API version is set to 1.4, and -Xno-optimized-callable-references is NOT used.\n" +
|
||||
"Container: $descriptor"
|
||||
}
|
||||
|
||||
v.anew(FUNCTION_REFERENCE_IMPL)
|
||||
v.dup()
|
||||
v.aconst(descriptor.arity)
|
||||
generateCallableReferenceDeclarationContainerClass(v, descriptor, state)
|
||||
v.aconst(descriptor.name.asString())
|
||||
generateCallableReferenceSignature(v, descriptor, state)
|
||||
v.aconst(getCallableReferenceTopLevelFlag(descriptor))
|
||||
v.invokespecial(
|
||||
FUNCTION_REFERENCE_IMPL.internalName, "<init>",
|
||||
Type.getMethodDescriptor(VOID_TYPE, INT_TYPE, JAVA_CLASS_TYPE, JAVA_STRING_TYPE, JAVA_STRING_TYPE, INT_TYPE),
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
override fun toKotlinType(type: KotlinType): KotlinType = type
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.intConstant
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
|
||||
@@ -63,6 +64,8 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
|
||||
interface IntrinsicsSupport<KT : KotlinTypeMarker> {
|
||||
fun putClassInstance(v: InstructionAdapter, type: KT)
|
||||
|
||||
fun generateTypeParameterContainer(v: InstructionAdapter, typeParameter: TypeParameterMarker)
|
||||
|
||||
fun toKotlinType(type: KT): KotlinType
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
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)
|
||||
@@ -39,7 +40,18 @@ private fun TypeSystemCommonBackendContext.putTypeOfReifiedTypeParameter(
|
||||
internal fun <KT : KotlinTypeMarker> TypeSystemCommonBackendContext.generateTypeOf(
|
||||
v: InstructionAdapter, type: KT, intrinsicsSupport: ReifiedTypeInliner.IntrinsicsSupport<KT>
|
||||
) {
|
||||
intrinsicsSupport.putClassInstance(v, type)
|
||||
val typeParameter = type.typeConstructor().getTypeParameterClassifier()
|
||||
if (typeParameter != null) {
|
||||
if (!doesTypeContainTypeParametersWithRecursiveBounds(type)) {
|
||||
throw UnsupportedOperationException(
|
||||
"Non-reified type parameters with recursive bounds are not supported yet: ${typeParameter.getName()}"
|
||||
)
|
||||
}
|
||||
|
||||
generateNonReifiedTypeParameter(v, typeParameter, intrinsicsSupport)
|
||||
} else {
|
||||
intrinsicsSupport.putClassInstance(v, type)
|
||||
}
|
||||
|
||||
val argumentsSize = type.argumentsCount()
|
||||
val useArray = argumentsSize >= 3
|
||||
@@ -64,17 +76,87 @@ internal fun <KT : KotlinTypeMarker> TypeSystemCommonBackendContext.generateType
|
||||
|
||||
val methodName = if (type.isMarkedNullable()) "nullableTypeOf" else "typeOf"
|
||||
|
||||
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))
|
||||
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, JAVA_CLASS_TYPE, *projections)
|
||||
|
||||
v.invokestatic(REFLECTION, methodName, signature, false)
|
||||
}
|
||||
|
||||
private fun <KT : KotlinTypeMarker> TypeSystemCommonBackendContext.generateNonReifiedTypeParameter(
|
||||
v: InstructionAdapter, typeParameter: TypeParameterMarker, intrinsicsSupport: ReifiedTypeInliner.IntrinsicsSupport<KT>
|
||||
) {
|
||||
intrinsicsSupport.generateTypeParameterContainer(v, typeParameter)
|
||||
|
||||
v.aconst(typeParameter.getName().asString())
|
||||
val variance = when (typeParameter.getVariance()) {
|
||||
TypeVariance.INV -> KVariance.INVARIANT
|
||||
TypeVariance.IN -> KVariance.IN
|
||||
TypeVariance.OUT -> KVariance.OUT
|
||||
}
|
||||
v.getstatic(K_VARIANCE.internalName, variance.name, K_VARIANCE.descriptor)
|
||||
v.aconst(typeParameter.isReified())
|
||||
v.invokestatic(
|
||||
REFLECTION, "typeParameter",
|
||||
Type.getMethodDescriptor(K_TYPE_PARAMETER, OBJECT_TYPE, JAVA_STRING_TYPE, K_VARIANCE, Type.BOOLEAN_TYPE),
|
||||
false,
|
||||
)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val bounds = (0 until typeParameter.upperBoundCount()).map { typeParameter.getUpperBound(it) as KT }
|
||||
if (bounds.isEmpty()) return
|
||||
|
||||
v.dup()
|
||||
|
||||
if (bounds.size == 1) {
|
||||
generateTypeOf(v, bounds.single(), intrinsicsSupport)
|
||||
} else {
|
||||
v.aconst(bounds.size)
|
||||
v.newarray(K_TYPE)
|
||||
for ((i, bound) in bounds.withIndex()) {
|
||||
v.dup()
|
||||
v.aconst(i)
|
||||
generateTypeOf(v, bound, intrinsicsSupport)
|
||||
v.astore(K_TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
v.invokestatic(
|
||||
REFLECTION, "setUpperBounds", Type.getMethodDescriptor(
|
||||
Type.VOID_TYPE, K_TYPE_PARAMETER,
|
||||
if (bounds.size == 1) K_TYPE else AsmUtil.getArrayType(K_TYPE)
|
||||
),
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
private fun TypeSystemCommonBackendContext.doesTypeContainTypeParametersWithRecursiveBounds(
|
||||
type: KotlinTypeMarker,
|
||||
used: MutableSet<TypeParameterMarker> = linkedSetOf()
|
||||
): Boolean {
|
||||
val typeParameter = type.typeConstructor().getTypeParameterClassifier()
|
||||
if (typeParameter != null) {
|
||||
if (!used.add(typeParameter)) return false
|
||||
for (i in 0 until typeParameter.upperBoundCount()) {
|
||||
if (!doesTypeContainTypeParametersWithRecursiveBounds(typeParameter.getUpperBound(i), used)) return false
|
||||
}
|
||||
used.remove(typeParameter)
|
||||
} else {
|
||||
for (i in 0 until type.argumentsCount()) {
|
||||
if (!doesTypeContainTypeParametersWithRecursiveBounds(type.getArgument(i).getType(), used)) return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun <KT : KotlinTypeMarker> TypeSystemCommonBackendContext.doGenerateTypeProjection(
|
||||
v: InstructionAdapter,
|
||||
projection: TypeArgumentMarker,
|
||||
@@ -91,14 +173,8 @@ private fun <KT : KotlinTypeMarker> TypeSystemCommonBackendContext.doGenerateTyp
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val type = projection.getType() as KT
|
||||
val typeParameterClassifier = type.typeConstructor().getTypeParameterClassifier()
|
||||
if (typeParameterClassifier != null) {
|
||||
if (typeParameterClassifier.isReified()) {
|
||||
putTypeOfReifiedTypeParameter(v, typeParameterClassifier, type.isMarkedNullable())
|
||||
} else {
|
||||
// TODO: support non-reified type parameters in typeOf
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
generateTypeOf(v, nullableAnyType() as KT, intrinsicsSupport)
|
||||
}
|
||||
if (typeParameterClassifier != null && typeParameterClassifier.isReified()) {
|
||||
putTypeOfReifiedTypeParameter(v, typeParameterClassifier, type.isMarkedNullable())
|
||||
} else {
|
||||
generateTypeOf(v, type, intrinsicsSupport)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user