[IR] Implement bridges generation for MFVC

#KT-1179
This commit is contained in:
Evgeniy.Zhelenskiy
2022-04-28 21:12:30 +03:00
committed by teamcity
parent 2896f5613d
commit ee2d327243
11 changed files with 269 additions and 133 deletions
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.Method
import kotlin.collections.set
/*
* Generate bridge methods to fix virtual dispatch after type erasure and to adapt Kotlin collections to
@@ -341,12 +342,14 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
for (override in irFunction.allOverridden()) {
if (override.isFakeOverride) continue
val signature = override.jvmMethod
val target = override.mangleFunctionIfNeeded()
val signature = target.jvmMethod
if (targetMethod != signature && signature !in blacklist) {
val bridge = generated.getOrPut(signature) {
Bridge(override, signature)
Bridge(target, signature)
}
bridge.overriddenSymbols += override.symbol
bridge.overriddenSymbols += target.symbol
}
}
@@ -358,6 +361,18 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
.forEach { irClass.addBridge(it, bridgeTarget) }
}
private fun IrSimpleFunction.mangleFunctionIfNeeded(): IrSimpleFunction {
if (!hasMangledReturnType && !hasMangledParameters()) return this
val replacement = context.multiFieldValueClassReplacements.getReplacementFunction(this)
?: context.inlineClassReplacements.getReplacementFunction(this)
?: return this
if (name.asString().substringAfterLast('-') == replacement.name.asString().substringAfterLast('-')) {
// function is already mangled
return this
}
return replacement
}
private fun IrSimpleFunction.isClashingWithPotentialBridge(name: Name, signature: Method): Boolean =
(!this.isFakeOverride || this.modality == Modality.FINAL) && this.name == name && this.jvmMethod == signature
@@ -612,16 +627,49 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
) =
irCastIfNeeded(
irCall(target, origin = IrStatementOrigin.BRIDGE_DELEGATION, superQualifierSymbol = superQualifierSymbol).apply {
for ((param, targetParam) in bridge.explicitParameters.zip(target.explicitParameters)) {
putArgument(
targetParam,
irGet(param).let { argument ->
if (param == bridge.dispatchReceiverParameter)
argument
else
irCastIfNeeded(argument, targetParam.type.upperBound)
val mfvcOrOriginal =
this@BridgeLowering.context.inlineClassReplacements.originalFunctionForMethodReplacement[target] ?: target
val structure = with(this@BridgeLowering.context.multiFieldValueClassReplacements) {
originalFunctionForMethodReplacement[mfvcOrOriginal]
?.let { bindingParameterTemplateStructure[it] }
?.also { structure ->
val errorMessage = { "Bad parameters structure: $structure" }
require(structure.size == bridge.explicitParametersCount) { errorMessage() }
require(structure.sumOf { it.valueParameters.size } == target.explicitParametersCount) { errorMessage() }
}
)
}
fun irGetOrCast(bridgeParameter: IrValueParameter, targetParameter: IrValueParameter) =
irGet(bridgeParameter).let { argument ->
if (bridgeParameter == bridge.dispatchReceiverParameter)
argument
else
irCastIfNeeded(argument, targetParameter.type.upperBound)
}
val targetParameters = target.explicitParameters
val flattenedBridgeArguments = when (structure) {
null -> bridge.explicitParameters.zip(target.explicitParameters, ::irGetOrCast)
else -> {
var index = 0
(structure zip bridge.explicitParameters).flatMap { (remappedParameter, bridgeParameter) ->
when (remappedParameter) {
is MultiFieldValueClassMapping -> remappedParameter.declarations.unboxMethods.map { unboxFunction ->
irCall(unboxFunction).apply {
dispatchReceiver =
irCastIfNeeded(irGet(bridgeParameter), remappedParameter.declarations.valueClass.defaultType)
}
}.also { index += it.size }
is RegularMapping -> listOf(irGetOrCast(bridgeParameter, targetParameters[index++]))
}
}
}
}
require(targetParameters.size == flattenedBridgeArguments.size) {
"Incorrect number of arguments: ${flattenedBridgeArguments.size} instead of ${targetParameters.size}"
}
for ((targetParam, argument) in target.explicitParameters zip flattenedBridgeArguments) {
putArgument(targetParam, argument)
}
},
bridge.returnType.upperBound
@@ -47,7 +47,7 @@ private class JvmDefaultConstructorLowering(val context: JvmBackendContext) : Cl
if (DescriptorVisibilities.isPrivate(primaryConstructor.visibility))
return
if (primaryConstructor.hasMangledParameters)
if (primaryConstructor.hasMangledParameters())
return
if (primaryConstructor.valueParameters.isEmpty() || !primaryConstructor.valueParameters.all { it.hasDefaultValue() })
@@ -18,7 +18,6 @@ import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
@@ -30,8 +29,6 @@ import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.types.makeNotNull
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.JVM_INLINE_ANNOTATION_FQ_NAME
val jvmInlineClassPhase = makeIrFilePhase(
@@ -117,64 +114,7 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
)
}
override fun createBridgeFunction(
function: IrSimpleFunction,
replacement: IrSimpleFunction
): IrSimpleFunction {
val bridgeFunction = createBridgeDeclaration(
function,
when {
// If the original function has signature which need mangling we still need to replace it with a mangled version.
(!function.isFakeOverride || function.findInterfaceImplementation(context.state.jvmDefaultMode) != null) &&
function.signatureRequiresMangling() ->
replacement.name
// Since we remove the corresponding property symbol from the bridge we need to resolve getter/setter
// names at this point.
replacement.isGetter ->
Name.identifier(JvmAbi.getterName(replacement.correspondingPropertySymbol!!.owner.name.asString()))
replacement.isSetter ->
Name.identifier(JvmAbi.setterName(replacement.correspondingPropertySymbol!!.owner.name.asString()))
else ->
function.name
}
)
// Update the overridden symbols to point to their inline class replacements
bridgeFunction.overriddenSymbols = replacement.overriddenSymbols
// Replace the function body with a wrapper
if (bridgeFunction.isFakeOverride && bridgeFunction.parentAsClass.isSingleFieldValueClass) {
// Fake overrides redirect from the replacement to the original function, which is in turn replaced during interfacePhase.
createBridgeBody(replacement, bridgeFunction)
} else {
createBridgeBody(bridgeFunction, replacement)
}
return bridgeFunction
}
private fun IrSimpleFunction.signatureRequiresMangling() =
fullValueParameterList.any { it.type.requiresMangling } ||
context.state.functionsWithInlineClassReturnTypesMangled && returnType.requiresMangling
// We may need to add a bridge method for inline class methods with static replacements. Ideally, we'd do this in BridgeLowering,
// but unfortunately this is a special case in the old backend. The bridge method is not marked as such and does not follow the normal
// visibility rules for bridge methods.
private fun createBridgeDeclaration(source: IrSimpleFunction, mangledName: Name) =
context.irFactory.buildFun {
updateFrom(source)
name = mangledName
returnType = source.returnType
}.apply {
copyParameterDeclarationsFrom(source)
annotations = source.annotations
parent = source.parent
// We need to ensure that this bridge has the same attribute owner as its static inline class replacement, since this
// is used in [CoroutineCodegen.isStaticInlineClassReplacementDelegatingCall] to identify the bridge and avoid generating
// a continuation class.
copyAttributes(source)
}
private fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction) {
override fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction, original: IrFunction, inverted: Boolean) {
source.body = context.createIrBuilder(source.symbol, source.startOffset, source.endOffset).run {
irExprBody(irCall(target).apply {
passTypeArgumentsFrom(source)
@@ -9,7 +9,12 @@ import org.jetbrains.kotlin.backend.common.ir.createDispatchReceiverParameter
import org.jetbrains.kotlin.backend.common.ir.passTypeArgumentsFrom
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.backend.jvm.*
import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements.RemappedParameter
import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements.RemappedParameter.MultiFieldValueClassMapping
import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements.RemappedParameter.RegularMapping
import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements.ValueParameterTemplate
import org.jetbrains.kotlin.backend.jvm.MultiFieldValueClassSpecificDeclarations.ImplementationAgnostic
import org.jetbrains.kotlin.backend.jvm.MultiFieldValueClassSpecificDeclarations.VirtualProperty
@@ -55,8 +60,12 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
symbol2setters[original] = listOf(if (replacement.isAssignable) { _, value -> irSet(replacement, value) } else null)
}
fun remapSymbol(original: IrValueSymbol, unboxed: List<VirtualProperty<Unit>>): Unit =
remapSymbol(original, replacements.getDeclarations(original.owner.type.erasedUpperBound)!!.ImplementationAgnostic(unboxed))
fun remapSymbol(
original: IrValueSymbol,
unboxed: List<VirtualProperty<Unit>>,
declarations: MultiFieldValueClassSpecificDeclarations
): Unit =
remapSymbol(original, declarations.ImplementationAgnostic(unboxed))
fun remapSymbol(original: IrValueSymbol, unboxed: ImplementationAgnostic<Unit>) {
symbol2getter[original] = {
@@ -314,9 +323,39 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
valueClass.declarations += properties.values.map { it.getter!!.apply { parent = valueClass } }
}
override fun createBridgeFunction(function: IrSimpleFunction, replacement: IrSimpleFunction): IrSimpleFunction? {
return null // todo
// todo change return type to non-nullable for base class
override fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction, original: IrFunction, inverted: Boolean) {
allScopes.push(createScope(source))
source.body = context.createIrBuilder(source.symbol, source.startOffset, source.endOffset).run {
if (inverted) {
irExprBody(irCall(target).apply {
passTypeArgumentsFrom(source)
val structure: List<RemappedParameter> = replacements.bindingParameterTemplateStructure[original]!!
var flattenedIndex = 0
for ((remappedParameter, targetParameter) in structure zip target.explicitParameters) {
when (remappedParameter) {
is MultiFieldValueClassMapping -> putArgument(
targetParameter,
irCall(remappedParameter.declarations.boxMethod).apply {
source.explicitParameters
.slice(flattenedIndex until flattenedIndex + remappedParameter.valueParameters.size)
.forEachIndexed { index, boxParameter -> putValueArgument(index, irGet(boxParameter)) }
.also { flattenedIndex += remappedParameter.valueParameters.size }
})
is RegularMapping -> putArgument(targetParameter, irGet(source.explicitParameters[flattenedIndex++]))
}
}
})
} else {
irExprBody(irCall(original).apply { // not target as it will be replaced during lowering
passTypeArgumentsFrom(source)
for ((parameter, newParameter) in source.explicitParameters.zip(original.explicitParameters)) {
putArgument(newParameter, irGet(parameter))
}
}).transform(this@JvmMultiFieldValueClassLowering, null)
}
}
allScopes.pop()
}
override fun addBindingsFor(original: IrFunction, replacement: IrFunction) {
@@ -324,19 +363,19 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
require(parametersStructure.size == original.explicitParameters.size) {
"Wrong value parameters structure: $parametersStructure"
}
require(parametersStructure.sumOf { it.size } == replacement.explicitParameters.size) {
require(parametersStructure.sumOf { it.valueParameters.size } == replacement.explicitParameters.size) {
"Wrong value parameters structure: $parametersStructure"
}
val old2newList = original.explicitParameters.zip(
parametersStructure.scan(0) { partial: Int, templates: List<ValueParameterTemplate> -> partial + templates.size }
parametersStructure.scan(0) { partial: Int, templates: RemappedParameter -> partial + templates.valueParameters.size }
.zipWithNext { start: Int, finish: Int -> replacement.explicitParameters.slice(start until finish) }
)
for ((param, newParamList) in old2newList) {
val single = newParamList.singleOrNull()
if (single != null) {
valueDeclarationsRemapper.remapSymbol(param.symbol, single)
} else {
valueDeclarationsRemapper.remapSymbol(param.symbol, newParamList.map { VirtualProperty(it) })
for (i in old2newList.indices) {
val (param, newParamList) = old2newList[i]
when (val structure = parametersStructure[i]) {
is MultiFieldValueClassMapping ->
valueDeclarationsRemapper.remapSymbol(param.symbol, newParamList.map { VirtualProperty(it) }, structure.declarations)
is RegularMapping -> valueDeclarationsRemapper.remapSymbol(param.symbol, newParamList.single())
}
}
}
@@ -352,7 +391,8 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
val initializersStatements = valueClass.declarations.filterIsInstance<IrAnonymousInitializer>().flatMap { it.body.statements }
valueDeclarationsRemapper.remapSymbol(
oldPrimaryConstructor.constructedClass.thisReceiver!!.symbol,
primaryConstructorImpl.valueParameters.map { VirtualProperty(it) }
primaryConstructorImpl.valueParameters.map { VirtualProperty(it) },
this,
)
primaryConstructorImpl.body = context.createIrBuilder(primaryConstructorImpl.symbol).irBlockBody {
for (stmt in initializersStatements) {
@@ -472,8 +512,9 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
val parameter2expression = typedArgumentList(originalFunction, original)
val structure = replacements.bindingParameterTemplateStructure[originalFunction]!!
require(parameter2expression.size == structure.size)
require(structure.sumOf { it.size } == replacement.explicitParametersCount)
val newArguments: List<IrExpression?> = makeNewArguments(parameter2expression.map { (_, argument) -> argument }, structure)
require(structure.sumOf { it.valueParameters.size } == replacement.explicitParametersCount)
val newArguments: List<IrExpression?> =
makeNewArguments(parameter2expression.map { (_, argument) -> argument }, structure.map { it.valueParameters })
+irCall(replacement.symbol).apply {
copyTypeArgumentsFrom(original)
for ((parameter, argument) in replacement.explicitParameters zip newArguments) {
@@ -586,7 +627,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
initializer?.let {
flattenExpressionTo(it, variables.toGettersAndSetters())
}
valueDeclarationsRemapper.remapSymbol(declaration.symbol, variables.map { VirtualProperty(it) })
valueDeclarationsRemapper.remapSymbol(declaration.symbol, variables.map { VirtualProperty(it) }, declarations)
}
}
return super.visitVariable(declaration)
@@ -10,10 +10,10 @@ import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.getRequiresMangling
import org.jetbrains.kotlin.backend.jvm.hasMangledReturnType
import org.jetbrains.kotlin.backend.jvm.ir.eraseTypeParameters
import org.jetbrains.kotlin.backend.jvm.ir.needsAccessor
import org.jetbrains.kotlin.backend.jvm.requiresMangling
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
@@ -222,7 +222,7 @@ class JvmPropertiesLowering(private val backendContext: JvmBackendContext) : IrE
val getter = property.getter
if (getter != null) {
val needsMangling =
getter.extensionReceiverParameter?.type?.requiresMangling == true ||
getter.extensionReceiverParameter?.type?.getRequiresMangling(includeInline = true, includeMFVC = false) == true ||
(state.functionsWithInlineClassReturnTypesMangled && getter.hasMangledReturnType)
val mangled = if (needsMangling) inlineClassReplacements.getReplacementFunction(getter) else null
defaultMethodSignatureMapper.mapFunctionName(mangled ?: getter)
@@ -7,21 +7,21 @@ package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.ir.copyParameterDeclarationsFrom
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.backend.jvm.InlineClassAbi
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.MemoizedValueClassAbstractReplacements
import org.jetbrains.kotlin.backend.jvm.*
import org.jetbrains.kotlin.backend.jvm.ir.isInlineClassType
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.transformStatement
import org.jetbrains.kotlin.ir.util.constructedClass
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.ir.util.transformFlat
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendContext) : FileLoweringPass,
@@ -121,7 +121,7 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
val bridgeFunction = createBridgeFunction(function, replacement)
return listOfNotNull(replacement, bridgeFunction)
return listOf(replacement, bridgeFunction)
}
final override fun visitReturn(expression: IrReturn): IrExpression {
@@ -166,10 +166,69 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
super.visitAnonymousInitializerNew(declaration)
protected abstract fun addBindingsFor(original: IrFunction, replacement: IrFunction)
protected abstract fun createBridgeFunction(function: IrSimpleFunction, replacement: IrSimpleFunction): IrSimpleFunction?
private fun createBridgeFunction(
function: IrSimpleFunction,
replacement: IrSimpleFunction
): IrSimpleFunction {
val bridgeFunction = createBridgeDeclaration(
function,
when {
// If the original function has signature which need mangling we still need to replace it with a mangled version.
(!function.isFakeOverride || function.findInterfaceImplementation(context.state.jvmDefaultMode) != null) &&
function.signatureRequiresMangling() ->
replacement.name
// Since we remove the corresponding property symbol from the bridge we need to resolve getter/setter
// names at this point.
replacement.isGetter ->
Name.identifier(JvmAbi.getterName(replacement.correspondingPropertySymbol!!.owner.name.asString()))
replacement.isSetter ->
Name.identifier(JvmAbi.setterName(replacement.correspondingPropertySymbol!!.owner.name.asString()))
else ->
function.name
}
)
// Update the overridden symbols to point to their inline class replacements
bridgeFunction.overriddenSymbols = replacement.overriddenSymbols
// Replace the function body with a wrapper
if (bridgeFunction.isFakeOverride && bridgeFunction.parentAsClass.isSpecificLoweringLogicApplicable()) {
// Fake overrides redirect from the replacement to the original function, which is in turn replaced during interfacePhase.
createBridgeBody(replacement, bridgeFunction, function, true)
} else {
createBridgeBody(bridgeFunction, replacement, function, false)
}
return bridgeFunction
}
private fun IrSimpleFunction.signatureRequiresMangling() =
fullValueParameterList.any { it.type.getRequiresMangling() } ||
context.state.functionsWithInlineClassReturnTypesMangled && returnType.getRequiresMangling() && returnType.isInlineClassType()
protected fun typedArgumentList(function: IrFunction, expression: IrMemberAccessExpression<*>) = listOfNotNull(
function.dispatchReceiverParameter?.let { it to expression.dispatchReceiver },
function.extensionReceiverParameter?.let { it to expression.extensionReceiver }
) + function.valueParameters.map { it to expression.getValueArgument(it.index) }
// We may need to add a bridge method for inline class methods with static replacements. Ideally, we'd do this in BridgeLowering,
// but unfortunately this is a special case in the old backend. The bridge method is not marked as such and does not follow the normal
// visibility rules for bridge methods.
private fun createBridgeDeclaration(source: IrSimpleFunction, mangledName: Name) =
context.irFactory.buildFun {
updateFrom(source)
name = mangledName
returnType = source.returnType
}.apply {
copyParameterDeclarationsFrom(source)
annotations = source.annotations
parent = source.parent
// We need to ensure that this bridge has the same attribute owner as its static inline class replacement, since this
// is used in [CoroutineCodegen.isStaticInlineClassReplacementDelegatingCall] to identify the bridge and avoid generating
// a continuation class.
copyAttributes(source)
}
protected abstract fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction, original: IrFunction, inverted: Boolean)
}
@@ -370,7 +370,7 @@ private class SyntheticAccessorTransformer(
val constructedClass = constructedClass
if (!DescriptorVisibilities.isPrivate(visibility) && !constructedClass.isSingleFieldValueClass && hasMangledParameters &&
if (!DescriptorVisibilities.isPrivate(visibility) && !constructedClass.isValue && hasMangledParameters() &&
!constructedClass.isAnonymousObject
) return true
@@ -101,7 +101,7 @@ object InlineClassAbi {
): String? =
collectFunctionSignatureForManglingSuffix(
useOldMangleRules,
valueParameters.any { it.requiresMangling },
valueParameters.any { it.getRequiresMangling() },
// The JVM backend computes mangled names after creating suspend function views, but before default argument
// stub insertion. It would be nice if this part of the continuation lowering happened earlier in the pipeline.
// TODO: Move suspend function view creation before JvmInlineClassLowering.
@@ -124,19 +124,28 @@ object InlineClassAbi {
get() = (this as IrSimpleFunction).correspondingPropertySymbol!!.owner.name
}
val IrType.requiresMangling: Boolean
get() {
val irClass = erasedUpperBound
return irClass.isValue && irClass.fqNameWhenAvailable != StandardNames.RESULT_FQ_NAME
fun IrType.getRequiresMangling(includeInline: Boolean = true, includeMFVC: Boolean = true): Boolean {
val irClass = erasedUpperBound
return irClass.fqNameWhenAvailable != StandardNames.RESULT_FQ_NAME && when {
irClass.isSingleFieldValueClass -> includeInline
irClass.isMultiFieldValueClass -> includeMFVC
else -> false
}
}
val IrFunction.fullValueParameterList: List<IrValueParameter>
get() = listOfNotNull(extensionReceiverParameter) + valueParameters
val IrFunction.hasMangledParameters: Boolean
get() = dispatchReceiverParameter != null && parentAsClass.isValue ||
fullValueParameterList.any { it.type.requiresMangling } ||
(this is IrConstructor && constructedClass.isValue)
fun IrFunction.hasMangledParameters(includeInline: Boolean = true, includeMFVC: Boolean = true): Boolean =
(dispatchReceiverParameter != null && when {
parentAsClass.isSingleFieldValueClass -> includeInline
parentAsClass.isMultiFieldValueClass -> includeMFVC
else -> false
}) || fullValueParameterList.any { it.type.getRequiresMangling(includeInline, includeMFVC) } || (this is IrConstructor && when {
constructedClass.isSingleFieldValueClass -> includeInline
constructedClass.isMultiFieldValueClass -> includeMFVC
else -> false
})
val IrFunction.hasMangledReturnType: Boolean
get() = returnType.isInlineClassType() && parentClassOrNull?.isFileClass != true
@@ -40,7 +40,7 @@ class MemoizedInlineClassReplacements(
private val storageManager = LockBasedStorageManager("inline-class-replacements")
val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
internal val originalFunctionForMethodReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
val originalFunctionForMethodReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
/**
* Get a replacement for a function or a constructor.
@@ -76,7 +76,8 @@ class MemoizedInlineClassReplacements(
}
// Otherwise, mangle functions with mangled parameters, ignoring constructors
it is IrSimpleFunction && !it.isFromJava() && (it.hasMangledParameters || mangleReturnTypes && it.hasMangledReturnType) ->
it is IrSimpleFunction && !it.isFromJava() &&
(it.hasMangledParameters(includeMFVC = false) || mangleReturnTypes && it.hasMangledReturnType) ->
createMethodReplacement(it)
else ->
@@ -35,9 +35,9 @@ class MemoizedMultiFieldValueClassReplacements(
) : MemoizedValueClassAbstractReplacements(irFactory, context) {
private val storageManager = LockBasedStorageManager("multi-field-value-class-replacements")
internal val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
internal val originalFunctionForMethodReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
internal val originalConstructorForConstructorReplacement: MutableMap<IrConstructor, IrConstructor> = ConcurrentHashMap()
val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
val originalFunctionForMethodReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
private val originalConstructorForConstructorReplacement: MutableMap<IrConstructor, IrConstructor> = ConcurrentHashMap()
val getDeclarations: (IrClass) -> MultiFieldValueClassSpecificDeclarations? =
storageManager.createMemoizedFunctionWithNullableValues {
@@ -77,24 +77,24 @@ class MemoizedMultiFieldValueClassReplacements(
}
}
private fun IrFunction.makeValueParametersFromTemplate(newFlattenedParameters: List<List<ValueParameterTemplate>>) =
newFlattenedParameters.flatten().mapIndexed { index: Int, template -> template.toParameter(this, index) }
private fun IrFunction.makeValueParametersFromTemplate(newFlattenedParameters: List<RemappedParameter>) = newFlattenedParameters
.flatMap { it.valueParameters }.mapIndexed { index: Int, template -> template.toParameter(this, index) }
private fun List<ValueParameterTemplate>.grouped(
originWhenFlattenedAndNotSpecified: IrDeclarationOrigin? = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER
): List<List<ValueParameterTemplate>> = map { parameter ->
val declaration = parameter.type.takeIf { !it.isNullable() }?.getClass()?.let { getDeclarations(it) }
?: return@map listOf(parameter)
): List<RemappedParameter> = map { parameter ->
val declarations = parameter.type.takeIf { !it.isNullable() }?.getClass()?.let { getDeclarations(it) }
?: return@map RemappedParameter.RegularMapping(parameter)
require(!parameter.original.hasDefaultValue()) { "Default parameters values are not supported for multi-field value classes" }
declaration.leaves.map { leaf ->
RemappedParameter.MultiFieldValueClassMapping(declarations, declarations.leaves.map { leaf ->
ValueParameterTemplate(
name = "${parameter.name}$${declaration.nodeFullNames[leaf]!!}",
name = "${parameter.name}$${declarations.nodeFullNames[leaf]!!}",
type = leaf.type,
origin = parameter.origin ?: originWhenFlattenedAndNotSpecified,
defaultValue = null,
original = parameter.original,
)
}
})
}
private fun buildReplacement(
@@ -115,8 +115,8 @@ class MemoizedMultiFieldValueClassReplacements(
private fun makeGroupedValueParametersFrom(
function: IrFunction, includeDispatcherReceiver: Boolean
): List<List<ValueParameterTemplate>> {
val newFlattenedParameters = mutableListOf<List<ValueParameterTemplate>>()
): List<RemappedParameter> {
val newFlattenedParameters = mutableListOf<RemappedParameter>()
if (function.dispatchReceiverParameter != null && includeDispatcherReceiver) {
val template = ValueParameterTemplate(
name = "\$dispatchReceiver",
@@ -161,7 +161,24 @@ class MemoizedMultiFieldValueClassReplacements(
return newFlattenedParameters
}
val bindingParameterTemplateStructure: MutableMap<IrFunction, List<List<ValueParameterTemplate>>> = ConcurrentHashMap()
sealed class RemappedParameter {
data class RegularMapping(val valueParameter: ValueParameterTemplate) : RemappedParameter() {
override val valueParameters: List<ValueParameterTemplate> = listOf(valueParameter)
}
data class MultiFieldValueClassMapping(
val declarations: MultiFieldValueClassSpecificDeclarations,
override val valueParameters: List<ValueParameterTemplate>,
) : RemappedParameter() {
init {
require(valueParameters.size > 1) { "MFVC must have > 1 fields" }
}
}
abstract val valueParameters: List<ValueParameterTemplate>
}
val bindingParameterTemplateStructure: MutableMap<IrFunction, List<RemappedParameter>> = ConcurrentHashMap()
override fun createStaticReplacement(function: IrFunction): IrSimpleFunction =
buildReplacement(function, JvmLoweredDeclarationOrigin.STATIC_MULTI_FIELD_VALUE_CLASS_REPLACEMENT, noFakeOverride = true) {
@@ -184,7 +201,7 @@ class MemoizedMultiFieldValueClassReplacements(
original = receiver,
) else null
bindingParameterTemplateStructure[function] =
if (receiverTemplate != null) listOf(listOf(receiverTemplate)) + newFlattenedParameters else newFlattenedParameters
if (receiverTemplate != null) listOf(RemappedParameter.RegularMapping(receiverTemplate)) + newFlattenedParameters else newFlattenedParameters
valueParameters = makeValueParametersFromTemplate(newFlattenedParameters)
}
@@ -217,7 +234,7 @@ class MemoizedMultiFieldValueClassReplacements(
defaultValue = receiver.defaultValue,
original = receiver
)
bindingParameterTemplateStructure[function] = listOf(listOf(receiverTemplate))
bindingParameterTemplateStructure[function] = listOf(RemappedParameter.RegularMapping(receiverTemplate))
return newGetter
}
+24 -3
View File
@@ -35,8 +35,12 @@ value class E(val x: D) {
set(_) = TODO()
}
interface Base3 {
val z: E
}
@JvmInline
value class R<T : Any>(val x: Int, val y: UInt, val z: E, val t: A<T>)
value class R<T : Any>(val x: Int, val y: UInt, override val z: E, val t: A<T>): Base1, Base3
fun <T : List<Int>> f(r: R<T>) {
println(r)
@@ -79,7 +83,20 @@ fun h1() {
println(y)
}
class NotInlined(var l: R<List<Int>>, var y: Int) {
interface Base1 {
val fakeOverrideMFVC: R<List<Int>>
get() = TODO()
val fakeOverrideRegular: Int
get() = TODO()
}
interface Base2 {
var l: R<List<Int>>
}
interface Base4<T> {
var l: T
}
class NotInlined(override var l: R<List<Int>>, var y: Int): Base1, Base2, Base4<R<List<Int>>> {
override fun toString(): String = l.toString() + l.z.x.x.z
init {
@@ -119,7 +136,7 @@ class NotInlined(var l: R<List<Int>>, var y: Int) {
}
}
fun ekeke(x: NotInlined) {
fun testVars(x: NotInlined) {
x.l.toString()
var y = x.l
y.toString()
@@ -129,6 +146,10 @@ fun ekeke(x: NotInlined) {
x.l = R<List<Int>>(x.l.x, x.l.y, x.l.z, x.l.t)
}
fun reuseBoxed(list: MutableList<R<List<Int>>>) {
list.add(list.last())
}
// todo add default parameters
fun box() = "bad"//.also { h(R(1, 2U, E(D(C(3, B(4U), "5"))), A(listOf(listOf(6))))) }