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 =
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)
}
@@ -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)
@@ -112,7 +112,7 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
}
private fun transformFunctionFlat(function: IrFunction): List<IrDeclaration>? {
if (function.isPrimaryInlineClassConstructor)
if (function is IrConstructor && function.isPrimary && function.constructedClass.isInline)
return null
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.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
@@ -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()
@@ -615,7 +615,8 @@ open class IrBasedClassDescriptor(owner: IrClass) : ClassDescriptor, IrBasedDecl
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
@@ -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 ->
@@ -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<ClassDescriptor>()?.underlyingRepresentation()
}
fun KotlinType.unsubstitutedUnderlyingType(): KotlinType? = unsubstitutedUnderlyingParameter()?.type
fun KotlinType.unsubstitutedUnderlyingType(): KotlinType? =
constructor.declarationDescriptor.safeAs<ClassDescriptor>()?.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<ClassifierDescriptor>): Boolean {