[IR] Unite inline class and multi-field value class representation
#KT-1179
This commit is contained in:
committed by
teamcity
parent
282ab398c6
commit
28bf83ceac
+1
-1
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.backend.jvm.isMultiFieldValueClassFieldGetter
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
|
||||
private class JvmInlineMultiFieldValueClassLowering(context: JvmBackendContext) : JvmValueClassAbstractLowering(context) {
|
||||
private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmValueClassAbstractLowering(context) {
|
||||
override val replacements: MemoizedValueClassAbstractReplacements
|
||||
get() = context.multiFieldValueClassReplacements
|
||||
|
||||
-8
@@ -121,14 +121,6 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
|
||||
|
||||
protected abstract fun addJvmInlineAnnotation(valueClass: IrClass)
|
||||
|
||||
// abstract override fun visitFunctionReference(expression: IrFunctionReference): IrExpression
|
||||
|
||||
// abstract override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression
|
||||
|
||||
// abstract override fun visitCall(expression: IrCall): IrExpression
|
||||
|
||||
// abstract override fun visitGetField(expression: IrGetField): IrExpression
|
||||
|
||||
final override fun visitReturn(expression: IrReturn): IrExpression {
|
||||
expression.returnTargetSymbol.owner.safeAs<IrFunction>()?.let { target ->
|
||||
val suffix = target.hashSuffix()
|
||||
|
||||
@@ -129,8 +129,7 @@ class JvmBackendContext(
|
||||
|
||||
val inlineClassReplacements = MemoizedInlineClassReplacements(state.functionsWithInlineClassReturnTypesMangled, irFactory, this)
|
||||
|
||||
val multiFieldValueClassReplacements =
|
||||
MemoizedMultiFieldValueClassReplacements(state.functionsWithInlineClassReturnTypesMangled, irFactory, this)
|
||||
val multiFieldValueClassReplacements = MemoizedMultiFieldValueClassReplacements(irFactory, this)
|
||||
|
||||
val continuationClassesVarsCountByType: MutableMap<IrAttributeContainer, Map<Type, Int>> = hashMapOf()
|
||||
|
||||
|
||||
@@ -273,7 +273,7 @@ class JvmSymbols(
|
||||
private val resultClassStub: IrClassSymbol =
|
||||
createClass(StandardNames.RESULT_FQ_NAME, classIsValue = true) { klass ->
|
||||
klass.addTypeParameter("T", irBuiltIns.anyNType, Variance.OUT_VARIANCE)
|
||||
klass.inlineClassRepresentation = InlineClassRepresentation(Name.identifier("value"), irBuiltIns.anyNType as IrSimpleType)
|
||||
klass.valueClassRepresentation = InlineClassRepresentation(Name.identifier("value"), irBuiltIns.anyNType as IrSimpleType)
|
||||
}
|
||||
|
||||
val resultOfAnyType: IrType = resultClassStub.typeWith(irBuiltIns.anyNType)
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ class MemoizedInlineClassReplacements(
|
||||
private val storageManager = LockBasedStorageManager("inline-class-replacements")
|
||||
private val propertyMap = ConcurrentHashMap<IrPropertySymbol, IrProperty>()
|
||||
|
||||
val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
|
||||
override val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
|
||||
internal val originalFunctionForMethodReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
|
||||
|
||||
/**
|
||||
|
||||
+6
-276
@@ -5,298 +5,28 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTypeParameters
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.*
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildProperty
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
|
||||
import org.jetbrains.kotlin.ir.types.isInt
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFactory
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/**
|
||||
* Keeps track of replacement functions and multi-field value class box/unbox functions.
|
||||
*/
|
||||
class MemoizedMultiFieldValueClassReplacements(
|
||||
private val mangleReturnTypes: Boolean, // todo always false
|
||||
irFactory: IrFactory,
|
||||
context: JvmBackendContext
|
||||
) : MemoizedValueClassAbstractReplacements(irFactory, context) { // There is only sample logic yet
|
||||
) : MemoizedValueClassAbstractReplacements(irFactory, context) {
|
||||
private val storageManager = LockBasedStorageManager("multi-field-value-class-replacements")
|
||||
private val propertyMap = ConcurrentHashMap<IrPropertySymbol, IrProperty>()
|
||||
|
||||
val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
|
||||
override val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
|
||||
internal val originalFunctionForMethodReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
|
||||
|
||||
/**
|
||||
* Get a replacement for a function or a constructor.
|
||||
*/
|
||||
override val getReplacementFunction: (IrFunction) -> IrSimpleFunction? = storageManager.createMemoizedFunctionWithNullableValues {
|
||||
when {
|
||||
// Don't mangle anonymous or synthetic functions, except for generated SAM wrapper methods
|
||||
(it.isLocal && it is IrSimpleFunction && it.overriddenSymbols.isEmpty()) ||
|
||||
(it.origin == IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR && it.visibility == DescriptorVisibilities.LOCAL) ||
|
||||
it.isStaticInlineClassReplacement ||
|
||||
it.origin.isSynthetic && it.origin != IrDeclarationOrigin.SYNTHETIC_GENERATED_SAM_IMPLEMENTATION ->
|
||||
null
|
||||
|
||||
it.isMultiFieldValueClassFieldGetter ->
|
||||
if (it.hasMangledReturnType)
|
||||
createMethodReplacement(it)
|
||||
else
|
||||
null
|
||||
|
||||
// Mangle all functions in the body of an inline class
|
||||
it.parent.safeAs<IrClass>()?.isMultiFieldValueClass == true ->
|
||||
when {
|
||||
it.isRemoveAtSpecialBuiltinStub() ->
|
||||
null
|
||||
it.isInlineClassMemberFakeOverriddenFromJvmDefaultInterfaceMethod() ||
|
||||
it.origin == IrDeclarationOrigin.IR_BUILTINS_STUB ->
|
||||
createMethodReplacement(it)
|
||||
else ->
|
||||
createStaticReplacement(it)
|
||||
}
|
||||
|
||||
// Otherwise, mangle functions with mangled parameters, ignoring constructors
|
||||
it is IrSimpleFunction && !it.isFromJava() && (it.hasMangledParameters || mangleReturnTypes && it.hasMangledReturnType) ->
|
||||
createMethodReplacement(it)
|
||||
|
||||
else ->
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrFunction.isRemoveAtSpecialBuiltinStub() =
|
||||
origin == IrDeclarationOrigin.IR_BUILTINS_STUB &&
|
||||
name.asString() == "remove" &&
|
||||
valueParameters.size == 1 &&
|
||||
valueParameters[0].type.isInt()
|
||||
|
||||
private fun IrFunction.isInlineClassMemberFakeOverriddenFromJvmDefaultInterfaceMethod(): Boolean {
|
||||
if (this !is IrSimpleFunction) return false
|
||||
if (!this.isFakeOverride) return false
|
||||
val parentClass = parentClassOrNull ?: return false
|
||||
if (!parentClass.isMultiFieldValueClass) return false
|
||||
|
||||
val overridden = resolveFakeOverride() ?: return false
|
||||
if (!overridden.parentAsClass.isJvmInterface) return false
|
||||
if (overridden.modality == Modality.ABSTRACT) return false
|
||||
|
||||
// We have a non-abstract interface member.
|
||||
// It is a JVM default interface method if one of the following conditions are true:
|
||||
// - it is a Java method,
|
||||
// - it is a Kotlin function compiled to JVM default interface method.
|
||||
return overridden.isFromJava() || overridden.isCompiledToJvmDefault(context.state.jvmDefaultMode)
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Get the box function for an inline class. Concretely, this is a synthetic
|
||||
// * static function named "box-impl" which takes an unboxed value and returns
|
||||
// * a boxed value.
|
||||
// */
|
||||
// val getBoxFunction: (IrClass) -> IrSimpleFunction =
|
||||
// storageManager.createMemoizedFunction { irClass ->
|
||||
// require(irClass.isSingleFieldValueClass)
|
||||
// irFactory.buildFun {
|
||||
// name = Name.identifier(KotlinTypeMapper.BOX_JVM_METHOD_NAME)
|
||||
// origin = JvmLoweredDeclarationOrigin.SYNTHETIC_INLINE_CLASS_MEMBER
|
||||
// returnType = irClass.defaultType
|
||||
// }.apply {
|
||||
// parent = irClass
|
||||
// copyTypeParametersFrom(irClass)
|
||||
// addValueParameter {
|
||||
// name = InlineClassDescriptorResolver.BOXING_VALUE_PARAMETER_NAME
|
||||
// type = irClass.inlineClassRepresentation!!.underlyingType
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Get the unbox function for an inline class. Concretely, this is a synthetic
|
||||
// * member function named "unbox-impl" which returns an unboxed result.
|
||||
// */
|
||||
// val getUnboxFunction: (IrClass) -> IrSimpleFunction =
|
||||
// storageManager.createMemoizedFunction { irClass ->
|
||||
// require(irClass.isSingleFieldValueClass)
|
||||
// irFactory.buildFun {
|
||||
// name = Name.identifier(KotlinTypeMapper.UNBOX_JVM_METHOD_NAME)
|
||||
// origin = JvmLoweredDeclarationOrigin.SYNTHETIC_INLINE_CLASS_MEMBER
|
||||
// returnType = irClass.inlineClassRepresentation!!.underlyingType
|
||||
// }.apply {
|
||||
// parent = irClass
|
||||
// createDispatchReceiverParameter()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private val specializedEqualsCache = storageManager.createCacheWithNotNullValues<IrClass, IrSimpleFunction>()
|
||||
// fun getSpecializedEqualsMethod(irClass: IrClass, irBuiltIns: IrBuiltIns): IrSimpleFunction {
|
||||
// require(irClass.isSingleFieldValueClass)
|
||||
// return specializedEqualsCache.computeIfAbsent(irClass) {
|
||||
// irFactory.buildFun {
|
||||
// name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_NAME
|
||||
// // TODO: Revisit this once we allow user defined equals methods in inline classes.
|
||||
// origin = JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD
|
||||
// returnType = irBuiltIns.booleanType
|
||||
// }.apply {
|
||||
// parent = irClass
|
||||
// // We ignore type arguments here, since there is no good way to go from type arguments to types in the IR anyway.
|
||||
// val typeArgument =
|
||||
// IrSimpleTypeImpl(null, irClass.symbol, false, List(irClass.typeParameters.size) { IrStarProjectionImpl }, listOf())
|
||||
// addValueParameter {
|
||||
// name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_FIRST_PARAMETER_NAME
|
||||
// type = typeArgument
|
||||
// }
|
||||
// addValueParameter {
|
||||
// name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_SECOND_PARAMETER_NAME
|
||||
// type = typeArgument
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
private fun createMethodReplacement(function: IrFunction): IrSimpleFunction =
|
||||
buildReplacement(function, function.origin) {
|
||||
originalFunctionForMethodReplacement[this] = function
|
||||
dispatchReceiverParameter = function.dispatchReceiverParameter?.copyTo(this, index = -1)
|
||||
extensionReceiverParameter = function.extensionReceiverParameter?.copyTo(
|
||||
// The function's name will be mangled, so preserve the old receiver name.
|
||||
this, index = -1, name = Name.identifier(function.extensionReceiverName(context.state))
|
||||
)
|
||||
contextReceiverParametersCount = function.contextReceiverParametersCount
|
||||
valueParameters = function.valueParameters.mapIndexed { index, parameter ->
|
||||
parameter.copyTo(this, index = index, defaultValue = null).also {
|
||||
// Assuming that constructors and non-override functions are always replaced with the unboxed
|
||||
// equivalent, deep-copying the value here is unnecessary. See `JvmInlineClassLowering`.
|
||||
it.defaultValue = parameter.defaultValue?.patchDeclarationParents(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createStaticReplacement(function: IrFunction): IrSimpleFunction =
|
||||
buildReplacement(function, JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT, noFakeOverride = true) {
|
||||
originalFunctionForStaticReplacement[this] = function
|
||||
|
||||
val newValueParameters = mutableListOf<IrValueParameter>()
|
||||
if (function.dispatchReceiverParameter != null) {
|
||||
// FAKE_OVERRIDEs have broken dispatch receivers
|
||||
newValueParameters += function.parentAsClass.thisReceiver!!.copyTo(
|
||||
this, index = newValueParameters.size, name = Name.identifier("arg${newValueParameters.size}"),
|
||||
type = function.parentAsClass.defaultType, origin = IrDeclarationOrigin.MOVED_DISPATCH_RECEIVER
|
||||
)
|
||||
}
|
||||
if (function.contextReceiverParametersCount != 0) {
|
||||
function.valueParameters.take(function.contextReceiverParametersCount).forEachIndexed { i, contextReceiver ->
|
||||
newValueParameters += contextReceiver.copyTo(
|
||||
this, index = newValueParameters.size, name = Name.identifier("contextReceiver$i"),
|
||||
origin = IrDeclarationOrigin.MOVED_CONTEXT_RECEIVER
|
||||
)
|
||||
}
|
||||
}
|
||||
function.extensionReceiverParameter?.let {
|
||||
newValueParameters += it.copyTo(
|
||||
this, index = newValueParameters.size, name = Name.identifier(function.extensionReceiverName(context.state)),
|
||||
origin = IrDeclarationOrigin.MOVED_EXTENSION_RECEIVER
|
||||
)
|
||||
}
|
||||
for (parameter in function.valueParameters.drop(function.contextReceiverParametersCount)) {
|
||||
newValueParameters += parameter.copyTo(this, index = newValueParameters.size, defaultValue = null).also {
|
||||
// See comment next to a similar line above.
|
||||
it.defaultValue = parameter.defaultValue?.patchDeclarationParents(this)
|
||||
}
|
||||
}
|
||||
valueParameters = newValueParameters
|
||||
}
|
||||
|
||||
private fun buildReplacement(
|
||||
function: IrFunction,
|
||||
replacementOrigin: IrDeclarationOrigin,
|
||||
noFakeOverride: Boolean = false,
|
||||
body: IrFunction.() -> Unit
|
||||
): IrSimpleFunction {
|
||||
val useOldManglingScheme = context.state.useOldManglingSchemeForFunctionsWithInlineClassesInSignatures
|
||||
val replacement = buildReplacementInner(function, replacementOrigin, noFakeOverride, useOldManglingScheme, body)
|
||||
// When using the new mangling scheme we might run into dependencies using the old scheme
|
||||
// for which we will fall back to the old mangling scheme as well.
|
||||
if (
|
||||
!useOldManglingScheme &&
|
||||
replacement.name.asString().contains("-") &&
|
||||
function.parentClassId?.let { classFileContainsMethod(it, replacement, context) } == false
|
||||
) {
|
||||
return buildReplacementInner(function, replacementOrigin, noFakeOverride, true, body)
|
||||
}
|
||||
return replacement
|
||||
}
|
||||
|
||||
private fun buildReplacementInner(
|
||||
function: IrFunction,
|
||||
replacementOrigin: IrDeclarationOrigin,
|
||||
noFakeOverride: Boolean,
|
||||
useOldManglingScheme: Boolean,
|
||||
body: IrFunction.() -> Unit,
|
||||
): IrSimpleFunction = irFactory.buildFun {
|
||||
updateFrom(function)
|
||||
if (function is IrConstructor) {
|
||||
// The [updateFrom] call will set the modality to FINAL for constructors, while the JVM backend would use OPEN here.
|
||||
modality = Modality.OPEN
|
||||
}
|
||||
origin = when {
|
||||
function.origin == IrDeclarationOrigin.GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER ->
|
||||
JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD
|
||||
function is IrConstructor && function.constructedClass.isSingleFieldValueClass ->
|
||||
JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_CONSTRUCTOR
|
||||
else ->
|
||||
replacementOrigin
|
||||
}
|
||||
if (noFakeOverride) {
|
||||
isFakeOverride = false
|
||||
}
|
||||
name = InlineClassAbi.mangledNameFor(function, mangleReturnTypes, useOldManglingScheme)
|
||||
returnType = function.returnType
|
||||
}.apply {
|
||||
parent = function.parent
|
||||
annotations = function.annotations
|
||||
copyTypeParameters(function.allTypeParameters)
|
||||
if (function.metadata != null) {
|
||||
metadata = function.metadata
|
||||
function.metadata = null
|
||||
}
|
||||
copyAttributes(function as? IrAttributeContainer)
|
||||
|
||||
if (function is IrSimpleFunction) {
|
||||
val propertySymbol = function.correspondingPropertySymbol
|
||||
if (propertySymbol != null) {
|
||||
val property = propertyMap.getOrPut(propertySymbol) {
|
||||
irFactory.buildProperty {
|
||||
name = propertySymbol.owner.name
|
||||
updateFrom(propertySymbol.owner)
|
||||
}.apply {
|
||||
parent = propertySymbol.owner.parent
|
||||
copyAttributes(propertySymbol.owner)
|
||||
annotations = propertySymbol.owner.annotations
|
||||
backingField = propertySymbol.owner.backingField
|
||||
}
|
||||
}
|
||||
correspondingPropertySymbol = property.symbol
|
||||
when (function) {
|
||||
propertySymbol.owner.getter -> property.getter = this
|
||||
propertySymbol.owner.setter -> property.setter = this
|
||||
else -> error("Orphaned property getter/setter: ${function.render()}")
|
||||
}
|
||||
}
|
||||
|
||||
overriddenSymbols = function.overriddenSymbols.map {
|
||||
getReplacementFunction(it.owner)?.symbol ?: it
|
||||
}
|
||||
}
|
||||
|
||||
body()
|
||||
TODO()
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -14,4 +14,7 @@ abstract class MemoizedValueClassAbstractReplacements(protected val irFactory: I
|
||||
* Get a replacement for a function or a constructor.
|
||||
*/
|
||||
abstract val getReplacementFunction: (IrFunction) -> IrSimpleFunction?
|
||||
|
||||
abstract val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction>
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user