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.
This commit is contained in:
Alexander Udalov
2021-03-30 18:30:48 +02:00
parent 54befa769f
commit da06faa57e
8 changed files with 22 additions and 68 deletions
@@ -247,9 +247,6 @@ class JvmSymbols(
private val resultClassStub: IrClassSymbol = private val resultClassStub: IrClassSymbol =
createClass(StandardNames.RESULT_FQ_NAME, classIsInline = true) { klass -> createClass(StandardNames.RESULT_FQ_NAME, classIsInline = true) { klass ->
klass.addTypeParameter("T", irBuiltIns.anyNType, Variance.OUT_VARIANCE) 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) klass.inlineClassRepresentation = InlineClassRepresentation(Name.identifier("value"), irBuiltIns.anyNType as IrSimpleType)
} }
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.backend.jvm.codegen
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.ir.eraseTypeParameters 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.backend.jvm.lower.inlineclasses.*
import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.StackValue 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.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.* 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.isTypeParameter
import org.jetbrains.kotlin.ir.util.parentAsClass import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.resolve.jvm.AsmTypes 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) { open fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) {
val erasedSourceType = irType.eraseTypeParameters() val erasedSourceType = irType.eraseTypeParameters()
val erasedTargetType = irTarget.eraseTypeParameters() val erasedTargetType = irTarget.eraseTypeParameters()
val isFromTypeInlineClass = erasedSourceType.classOrNull!!.owner.isInline val fromTypeRepresentation = erasedSourceType.getClass()!!.inlineClassRepresentation
val isToTypeInlineClass = erasedTargetType.classOrNull!!.owner.isInline val toTypeRepresentation = erasedTargetType.getClass()!!.inlineClassRepresentation
// Boxing and unboxing kotlin.Result leads to CCE in generated code // Boxing and unboxing kotlin.Result leads to CCE in generated code
val doNotCoerceKotlinResultInContinuation = val doNotCoerceKotlinResultInContinuation =
@@ -42,9 +40,9 @@ abstract class PromisedValue(val codegen: ExpressionCodegen, val type: Type, val
&& (irType.isKotlinResult() || irTarget.isKotlinResult()) && (irType.isKotlinResult() || irTarget.isKotlinResult())
// Coerce inline classes // Coerce inline classes
if ((isFromTypeInlineClass || isToTypeInlineClass) && !doNotCoerceKotlinResultInContinuation) { if ((fromTypeRepresentation != null || toTypeRepresentation != null) && !doNotCoerceKotlinResultInContinuation) {
val isFromTypeUnboxed = isFromTypeInlineClass && typeMapper.mapType(erasedSourceType.unboxed) == type val isFromTypeUnboxed = fromTypeRepresentation?.underlyingType?.let(typeMapper::mapType) == type
val isToTypeUnboxed = isToTypeInlineClass && typeMapper.mapType(erasedTargetType.unboxed) == target val isToTypeUnboxed = toTypeRepresentation?.underlyingType?.let(typeMapper::mapType) == target
when { when {
isFromTypeUnboxed && !isToTypeUnboxed -> { isFromTypeUnboxed && !isToTypeUnboxed -> {
@@ -181,9 +179,6 @@ fun PromisedValue.materializeAtBoxed(irTarget: IrType) {
materializeAt(typeMapper.boxType(irTarget), irTarget) 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. // A Non-materialized value of Unit type that is only materialized through coercion.
val ExpressionCodegen.unitValue: PromisedValue val ExpressionCodegen.unitValue: PromisedValue
get() = MaterialValue(this, Type.VOID_TYPE, context.irBuiltIns.unitType) get() = MaterialValue(this, Type.VOID_TYPE, context.irBuiltIns.unitType)
@@ -112,7 +112,7 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
} }
private fun transformFunctionFlat(function: IrFunction): List<IrDeclaration>? { private fun transformFunctionFlat(function: IrFunction): List<IrDeclaration>? {
if (function.isPrimaryInlineClassConstructor) if (function is IrConstructor && function.isPrimary && function.constructedClass.isInline)
return null return null
val replacement = context.inlineClassReplacements.getReplacementFunction(function) val replacement = context.inlineClassReplacements.getReplacementFunction(function)
@@ -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.erasedUpperBound
import org.jetbrains.kotlin.backend.jvm.ir.isInlineClassType 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.builtins.StandardNames
import org.jetbrains.kotlin.codegen.state.InfoForMangling import org.jetbrains.kotlin.codegen.state.InfoForMangling
import org.jetbrains.kotlin.codegen.state.collectFunctionSignatureForManglingSuffix import org.jetbrains.kotlin.codegen.state.collectFunctionSignatureForManglingSuffix
@@ -40,10 +39,10 @@ object InlineClassAbi {
*/ */
internal fun unboxType(type: IrType): IrType? { internal fun unboxType(type: IrType): IrType? {
val klass = type.classOrNull?.owner ?: return null val klass = type.classOrNull?.owner ?: return null
if (!klass.isInline) return null val representation = klass.inlineClassRepresentation ?: return null
// TODO: Apply type substitutions // TODO: Apply type substitutions
val underlyingType = getUnderlyingType(klass).unboxInlineClass() val underlyingType = representation.underlyingType.unboxInlineClass()
if (!type.isNullable()) if (!type.isNullable())
return underlyingType return underlyingType
if (underlyingType.isNullable() || underlyingType.isPrimitiveType()) if (underlyingType.isNullable() || underlyingType.isPrimitiveType())
@@ -51,16 +50,6 @@ object InlineClassAbi {
return underlyingType.makeNullable() 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 * Returns a mangled name for a function taking inline class arguments
* to avoid clashes between overloaded methods. * to avoid clashes between overloaded methods.
@@ -157,20 +146,9 @@ internal val IrFunction.hasMangledParameters: Boolean
internal val IrFunction.hasMangledReturnType: Boolean internal val IrFunction.hasMangledReturnType: Boolean
get() = returnType.isInlineClassType() && parentClassOrNull?.isFileClass != true 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 internal val IrClass.inlineClassFieldName: Name
get() = singlePrimaryConstructorParameter.name get() = (inlineClassRepresentation ?: error("Not an inline class: ${render()}")).underlyingPropertyName
val IrFunction.isInlineClassFieldGetter: Boolean val IrFunction.isInlineClassFieldGetter: Boolean
get() = (parent as? IrClass)?.isInline == true && this is IrSimpleFunction && extensionReceiverParameter == null && get() = (parent as? IrClass)?.isInline == true && this is IrSimpleFunction && extensionReceiverParameter == null &&
correspondingPropertySymbol?.let { it.owner.getter == this && it.owner.name == parentAsClass.inlineClassFieldName } == true correspondingPropertySymbol?.let { it.owner.getter == this && it.owner.name == parentAsClass.inlineClassFieldName } == true
val IrFunction.isPrimaryInlineClassConstructor: Boolean
get() = this is IrConstructor && isPrimary && constructedClass.isInline
@@ -131,7 +131,7 @@ class MemoizedInlineClassReplacements(
copyTypeParametersFrom(irClass) copyTypeParametersFrom(irClass)
addValueParameter { addValueParameter {
name = InlineClassDescriptorResolver.BOXING_VALUE_PARAMETER_NAME name = InlineClassDescriptorResolver.BOXING_VALUE_PARAMETER_NAME
type = InlineClassAbi.getUnderlyingType(irClass) type = irClass.inlineClassRepresentation!!.underlyingType
} }
} }
} }
@@ -146,7 +146,7 @@ class MemoizedInlineClassReplacements(
irFactory.buildFun { irFactory.buildFun {
name = Name.identifier(KotlinTypeMapper.UNBOX_JVM_METHOD_NAME) name = Name.identifier(KotlinTypeMapper.UNBOX_JVM_METHOD_NAME)
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_INLINE_CLASS_MEMBER origin = JvmLoweredDeclarationOrigin.SYNTHETIC_INLINE_CLASS_MEMBER
returnType = InlineClassAbi.getUnderlyingType(irClass) returnType = irClass.inlineClassRepresentation!!.underlyingType
}.apply { }.apply {
parent = irClass parent = irClass
createDispatchReceiverParameter() createDispatchReceiverParameter()
@@ -615,7 +615,8 @@ open class IrBasedClassDescriptor(owner: IrClass) : ClassDescriptor, IrBasedDecl
TODO("not implemented") TODO("not implemented")
} }
override fun getInlineClassRepresentation(): InlineClassRepresentation<SimpleType>? = TODO("not implemented") override fun getInlineClassRepresentation(): InlineClassRepresentation<SimpleType>? =
owner.inlineClassRepresentation?.mapUnderlyingType { it.toIrBasedKotlinType() as SimpleType }
override fun getOriginal() = this override fun getOriginal() = this
@@ -435,12 +435,8 @@ interface IrTypeSystemContext : TypeSystemContext, TypeSystemCommonSuperTypesCon
irClass.kind != ClassKind.INTERFACE && irClass.kind != ClassKind.ANNOTATION_CLASS irClass.kind != ClassKind.INTERFACE && irClass.kind != ClassKind.ANNOTATION_CLASS
} ?: owner.superTypes.first() } ?: owner.superTypes.first()
override fun KotlinTypeMarker.getUnsubstitutedUnderlyingType(): KotlinTypeMarker? { 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 as IrType).classOrNull?.owner?.inlineClassRepresentation?.underlyingType
// 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.getSubstitutedUnderlyingType(): KotlinTypeMarker? = override fun KotlinTypeMarker.getSubstitutedUnderlyingType(): KotlinTypeMarker? =
getUnsubstitutedUnderlyingType()?.let { type -> getUnsubstitutedUnderlyingType()?.let { type ->
@@ -7,37 +7,24 @@ package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
val JVM_INLINE_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmInline") 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: DeserializedClassDescriptor in reflection do not have @JvmInline annotation, that we
// FIXME: would like to check as well. // 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? { fun KotlinType.unsubstitutedUnderlyingType(): KotlinType? =
return constructor.declarationDescriptor.safeAs<ClassDescriptor>()?.underlyingRepresentation() constructor.declarationDescriptor.safeAs<ClassDescriptor>()?.inlineClassRepresentation?.underlyingType
}
fun KotlinType.unsubstitutedUnderlyingType(): KotlinType? = unsubstitutedUnderlyingParameter()?.type
fun KotlinType.isInlineClassType(): Boolean = constructor.declarationDescriptor?.isInlineClass() ?: false fun KotlinType.isInlineClassType(): Boolean = constructor.declarationDescriptor?.isInlineClass() ?: false
fun KotlinType.substitutedUnderlyingType(): KotlinType? { fun KotlinType.substitutedUnderlyingType(): KotlinType? =
val parameter = unsubstitutedUnderlyingParameter() ?: return null unsubstitutedUnderlyingType()?.let { TypeSubstitutor.create(this).substitute(it, Variance.INVARIANT) }
return TypeSubstitutor.create(this).substitute(parameter.type, Variance.INVARIANT)
}
fun KotlinType.isRecursiveInlineClassType() = fun KotlinType.isRecursiveInlineClassType(): Boolean =
isRecursiveInlineClassTypeInner(hashSetOf()) isRecursiveInlineClassTypeInner(hashSetOf())
private fun KotlinType.isRecursiveInlineClassTypeInner(visited: HashSet<ClassifierDescriptor>): Boolean { private fun KotlinType.isRecursiveInlineClassTypeInner(visited: HashSet<ClassifierDescriptor>): Boolean {