From da06faa57e81005cf27aa147cd1462abeff99773 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Tue, 30 Mar 2021 18:30:48 +0200 Subject: [PATCH] JVM IR: use inlineClassRepresentation in more utilities Remove any logic related to finding the single parameter of the primary constructor, and use inlineClassRepresentaton from IrClass or ClassDescriptor instead. --- .../kotlin/backend/jvm/JvmSymbols.kt | 3 -- .../backend/jvm/codegen/PromisedValue.kt | 15 ++++------ .../jvm/lower/JvmInlineClassLowering.kt | 2 +- .../jvm/lower/inlineclasses/InlineClassAbi.kt | 28 ++----------------- .../MemoizedInlineClassReplacements.kt | 4 +-- .../ir/descriptors/IrBasedDescriptors.kt | 3 +- .../kotlin/ir/types/IrTypeSystemContext.kt | 8 ++---- .../kotlin/resolve/inlineClassesUtils.kt | 27 +++++------------- 8 files changed, 22 insertions(+), 68 deletions(-) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt index bba2dbb84a2..0636bfe71e9 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt @@ -247,9 +247,6 @@ class JvmSymbols( private val resultClassStub: IrClassSymbol = createClass(StandardNames.RESULT_FQ_NAME, classIsInline = true) { klass -> klass.addTypeParameter("T", irBuiltIns.anyNType, Variance.OUT_VARIANCE) - klass.addConstructor { isPrimary = true }.apply { - addValueParameter("value", irBuiltIns.anyNType) - } klass.inlineClassRepresentation = InlineClassRepresentation(Name.identifier("value"), irBuiltIns.anyNType as IrSimpleType) } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/PromisedValue.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/PromisedValue.kt index 93e34a2398f..43322addaed 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/PromisedValue.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/PromisedValue.kt @@ -7,7 +7,6 @@ package org.jetbrains.kotlin.backend.jvm.codegen import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.ir.eraseTypeParameters -import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.* import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.StackValue @@ -15,7 +14,6 @@ import org.jetbrains.kotlin.codegen.inline.v import org.jetbrains.kotlin.ir.declarations.IrTypeParameter import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.types.* -import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.isTypeParameter import org.jetbrains.kotlin.ir.util.parentAsClass import org.jetbrains.kotlin.resolve.jvm.AsmTypes @@ -31,8 +29,8 @@ abstract class PromisedValue(val codegen: ExpressionCodegen, val type: Type, val open fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) { val erasedSourceType = irType.eraseTypeParameters() val erasedTargetType = irTarget.eraseTypeParameters() - val isFromTypeInlineClass = erasedSourceType.classOrNull!!.owner.isInline - val isToTypeInlineClass = erasedTargetType.classOrNull!!.owner.isInline + val fromTypeRepresentation = erasedSourceType.getClass()!!.inlineClassRepresentation + val toTypeRepresentation = erasedTargetType.getClass()!!.inlineClassRepresentation // Boxing and unboxing kotlin.Result leads to CCE in generated code val doNotCoerceKotlinResultInContinuation = @@ -42,9 +40,9 @@ abstract class PromisedValue(val codegen: ExpressionCodegen, val type: Type, val && (irType.isKotlinResult() || irTarget.isKotlinResult()) // Coerce inline classes - if ((isFromTypeInlineClass || isToTypeInlineClass) && !doNotCoerceKotlinResultInContinuation) { - val isFromTypeUnboxed = isFromTypeInlineClass && typeMapper.mapType(erasedSourceType.unboxed) == type - val isToTypeUnboxed = isToTypeInlineClass && typeMapper.mapType(erasedTargetType.unboxed) == target + if ((fromTypeRepresentation != null || toTypeRepresentation != null) && !doNotCoerceKotlinResultInContinuation) { + val isFromTypeUnboxed = fromTypeRepresentation?.underlyingType?.let(typeMapper::mapType) == type + val isToTypeUnboxed = toTypeRepresentation?.underlyingType?.let(typeMapper::mapType) == target when { isFromTypeUnboxed && !isToTypeUnboxed -> { @@ -181,9 +179,6 @@ fun PromisedValue.materializeAtBoxed(irTarget: IrType) { materializeAt(typeMapper.boxType(irTarget), irTarget) } -val IrType.unboxed: IrType - get() = InlineClassAbi.getUnderlyingType(erasedUpperBound) - // A Non-materialized value of Unit type that is only materialized through coercion. val ExpressionCodegen.unitValue: PromisedValue get() = MaterialValue(this, Type.VOID_TYPE, context.irBuiltIns.unitType) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt index d7ce8143340..b031a8b409b 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt @@ -112,7 +112,7 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F } private fun transformFunctionFlat(function: IrFunction): List? { - if (function.isPrimaryInlineClassConstructor) + if (function is IrConstructor && function.isPrimary && function.constructedClass.isInline) return null val replacement = context.inlineClassReplacements.getReplacementFunction(function) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/InlineClassAbi.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/InlineClassAbi.kt index a4b006bb96b..62d2db7dcd4 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/InlineClassAbi.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/InlineClassAbi.kt @@ -7,7 +7,6 @@ package org.jetbrains.kotlin.backend.jvm.lower.inlineclasses import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound import org.jetbrains.kotlin.backend.jvm.ir.isInlineClassType -import org.jetbrains.kotlin.backend.jvm.lower.STUB_FOR_INLINING import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.codegen.state.InfoForMangling import org.jetbrains.kotlin.codegen.state.collectFunctionSignatureForManglingSuffix @@ -40,10 +39,10 @@ object InlineClassAbi { */ internal fun unboxType(type: IrType): IrType? { val klass = type.classOrNull?.owner ?: return null - if (!klass.isInline) return null + val representation = klass.inlineClassRepresentation ?: return null // TODO: Apply type substitutions - val underlyingType = getUnderlyingType(klass).unboxInlineClass() + val underlyingType = representation.underlyingType.unboxInlineClass() if (!type.isNullable()) return underlyingType if (underlyingType.isNullable() || underlyingType.isPrimitiveType()) @@ -51,16 +50,6 @@ object InlineClassAbi { return underlyingType.makeNullable() } - /** - * Get the underlying type of an inline class based on the single argument to its - * primary constructor. This is what the current jvm backend does. - * - * Looking for a backing field does not work for unsigned types, which don't - * contain a field. - */ - fun getUnderlyingType(irClass: IrClass): IrType = - irClass.singlePrimaryConstructorParameter.type - /** * Returns a mangled name for a function taking inline class arguments * to avoid clashes between overloaded methods. @@ -157,20 +146,9 @@ internal val IrFunction.hasMangledParameters: Boolean internal val IrFunction.hasMangledReturnType: Boolean get() = returnType.isInlineClassType() && parentClassOrNull?.isFileClass != true -private val IrClass.singlePrimaryConstructorParameter: IrValueParameter - get() { - require(isInline) { "Not an inline class: ${render()} "} - val primaryConstructor = primaryConstructor ?: error("Inline class has no primary constructor: ${render()}") - return primaryConstructor.valueParameters.singleOrNull() - ?: error("Inline class primary constructor should have one parameter: ${primaryConstructor.render()}") - } - internal val IrClass.inlineClassFieldName: Name - get() = singlePrimaryConstructorParameter.name + get() = (inlineClassRepresentation ?: error("Not an inline class: ${render()}")).underlyingPropertyName val IrFunction.isInlineClassFieldGetter: Boolean get() = (parent as? IrClass)?.isInline == true && this is IrSimpleFunction && extensionReceiverParameter == null && correspondingPropertySymbol?.let { it.owner.getter == this && it.owner.name == parentAsClass.inlineClassFieldName } == true - -val IrFunction.isPrimaryInlineClassConstructor: Boolean - get() = this is IrConstructor && isPrimary && constructedClass.isInline diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/MemoizedInlineClassReplacements.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/MemoizedInlineClassReplacements.kt index 77c40a00971..725f6347302 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/MemoizedInlineClassReplacements.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/MemoizedInlineClassReplacements.kt @@ -131,7 +131,7 @@ class MemoizedInlineClassReplacements( copyTypeParametersFrom(irClass) addValueParameter { name = InlineClassDescriptorResolver.BOXING_VALUE_PARAMETER_NAME - type = InlineClassAbi.getUnderlyingType(irClass) + type = irClass.inlineClassRepresentation!!.underlyingType } } } @@ -146,7 +146,7 @@ class MemoizedInlineClassReplacements( irFactory.buildFun { name = Name.identifier(KotlinTypeMapper.UNBOX_JVM_METHOD_NAME) origin = JvmLoweredDeclarationOrigin.SYNTHETIC_INLINE_CLASS_MEMBER - returnType = InlineClassAbi.getUnderlyingType(irClass) + returnType = irClass.inlineClassRepresentation!!.underlyingType }.apply { parent = irClass createDispatchReceiverParameter() diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBasedDescriptors.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBasedDescriptors.kt index 0db34acd287..0db03861746 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBasedDescriptors.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBasedDescriptors.kt @@ -615,7 +615,8 @@ open class IrBasedClassDescriptor(owner: IrClass) : ClassDescriptor, IrBasedDecl TODO("not implemented") } - override fun getInlineClassRepresentation(): InlineClassRepresentation? = TODO("not implemented") + override fun getInlineClassRepresentation(): InlineClassRepresentation? = + owner.inlineClassRepresentation?.mapUnderlyingType { it.toIrBasedKotlinType() as SimpleType } override fun getOriginal() = this diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt index 1329c8d1400..2507660487b 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt @@ -435,12 +435,8 @@ interface IrTypeSystemContext : TypeSystemContext, TypeSystemCommonSuperTypesCon irClass.kind != ClassKind.INTERFACE && irClass.kind != ClassKind.ANNOTATION_CLASS } ?: owner.superTypes.first() - override fun KotlinTypeMarker.getUnsubstitutedUnderlyingType(): KotlinTypeMarker? { - // Code in inlineClassesUtils.kt loads the property with the same name from the scope of the substituted type and takes its type. - // This code below should have the same effect. - val irClass = (this as? IrType)?.classOrNull?.owner?.takeIf { it.isInline } - return irClass?.primaryConstructor?.valueParameters?.singleOrNull()?.type - } + override fun KotlinTypeMarker.getUnsubstitutedUnderlyingType(): KotlinTypeMarker? = + (this as IrType).classOrNull?.owner?.inlineClassRepresentation?.underlyingType override fun KotlinTypeMarker.getSubstitutedUnderlyingType(): KotlinTypeMarker? = getUnsubstitutedUnderlyingType()?.let { type -> diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt index 341a233db7e..dcfd76adbb0 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt @@ -7,37 +7,24 @@ package org.jetbrains.kotlin.resolve import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.TypeSubstitutor -import org.jetbrains.kotlin.types.TypeUtils -import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.utils.addToStdlib.safeAs val JVM_INLINE_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmInline") -private fun ClassDescriptor.underlyingRepresentation(): ValueParameterDescriptor? { - if (!isInlineClass()) return null - return unsubstitutedPrimaryConstructor?.valueParameters?.singleOrNull() -} - // FIXME: DeserializedClassDescriptor in reflection do not have @JvmInline annotation, that we // FIXME: would like to check as well. -fun DeclarationDescriptor.isInlineClass() = this is ClassDescriptor && (isInline || isValue) +fun DeclarationDescriptor.isInlineClass(): Boolean = this is ClassDescriptor && (isInline || isValue) -private fun KotlinType.unsubstitutedUnderlyingParameter(): ValueParameterDescriptor? { - return constructor.declarationDescriptor.safeAs()?.underlyingRepresentation() -} - -fun KotlinType.unsubstitutedUnderlyingType(): KotlinType? = unsubstitutedUnderlyingParameter()?.type +fun KotlinType.unsubstitutedUnderlyingType(): KotlinType? = + constructor.declarationDescriptor.safeAs()?.inlineClassRepresentation?.underlyingType fun KotlinType.isInlineClassType(): Boolean = constructor.declarationDescriptor?.isInlineClass() ?: false -fun KotlinType.substitutedUnderlyingType(): KotlinType? { - val parameter = unsubstitutedUnderlyingParameter() ?: return null - return TypeSubstitutor.create(this).substitute(parameter.type, Variance.INVARIANT) -} +fun KotlinType.substitutedUnderlyingType(): KotlinType? = + unsubstitutedUnderlyingType()?.let { TypeSubstitutor.create(this).substitute(it, Variance.INVARIANT) } -fun KotlinType.isRecursiveInlineClassType() = +fun KotlinType.isRecursiveInlineClassType(): Boolean = isRecursiveInlineClassTypeInner(hashSetOf()) private fun KotlinType.isRecursiveInlineClassTypeInner(visited: HashSet): Boolean {