[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.kotlin.utils.SmartList
import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.Method 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 * 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()) { for (override in irFunction.allOverridden()) {
if (override.isFakeOverride) continue if (override.isFakeOverride) continue
val signature = override.jvmMethod val target = override.mangleFunctionIfNeeded()
val signature = target.jvmMethod
if (targetMethod != signature && signature !in blacklist) { if (targetMethod != signature && signature !in blacklist) {
val bridge = generated.getOrPut(signature) { 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) } .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 = private fun IrSimpleFunction.isClashingWithPotentialBridge(name: Name, signature: Method): Boolean =
(!this.isFakeOverride || this.modality == Modality.FINAL) && this.name == name && this.jvmMethod == signature (!this.isFakeOverride || this.modality == Modality.FINAL) && this.name == name && this.jvmMethod == signature
@@ -612,16 +627,49 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
) = ) =
irCastIfNeeded( irCastIfNeeded(
irCall(target, origin = IrStatementOrigin.BRIDGE_DELEGATION, superQualifierSymbol = superQualifierSymbol).apply { irCall(target, origin = IrStatementOrigin.BRIDGE_DELEGATION, superQualifierSymbol = superQualifierSymbol).apply {
for ((param, targetParam) in bridge.explicitParameters.zip(target.explicitParameters)) { val mfvcOrOriginal =
putArgument( this@BridgeLowering.context.inlineClassReplacements.originalFunctionForMethodReplacement[target] ?: target
targetParam, val structure = with(this@BridgeLowering.context.multiFieldValueClassReplacements) {
irGet(param).let { argument -> originalFunctionForMethodReplacement[mfvcOrOriginal]
if (param == bridge.dispatchReceiverParameter) ?.let { bindingParameterTemplateStructure[it] }
argument ?.also { structure ->
else val errorMessage = { "Bad parameters structure: $structure" }
irCastIfNeeded(argument, targetParam.type.upperBound) 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 bridge.returnType.upperBound
@@ -47,7 +47,7 @@ private class JvmDefaultConstructorLowering(val context: JvmBackendContext) : Cl
if (DescriptorVisibilities.isPrivate(primaryConstructor.visibility)) if (DescriptorVisibilities.isPrivate(primaryConstructor.visibility))
return return
if (primaryConstructor.hasMangledParameters) if (primaryConstructor.hasMangledParameters())
return return
if (primaryConstructor.valueParameters.isEmpty() || !primaryConstructor.valueParameters.all { it.hasDefaultValue() }) 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.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor 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.declarations.*
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.* 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.types.makeNotNull
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid 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 import org.jetbrains.kotlin.resolve.JVM_INLINE_ANNOTATION_FQ_NAME
val jvmInlineClassPhase = makeIrFilePhase( val jvmInlineClassPhase = makeIrFilePhase(
@@ -117,64 +114,7 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
) )
} }
override fun createBridgeFunction( override fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction, original: IrFunction, inverted: Boolean) {
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) {
source.body = context.createIrBuilder(source.symbol, source.startOffset, source.endOffset).run { source.body = context.createIrBuilder(source.symbol, source.startOffset, source.endOffset).run {
irExprBody(irCall(target).apply { irExprBody(irCall(target).apply {
passTypeArgumentsFrom(source) 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.ir.passTypeArgumentsFrom
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase 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.*
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.MemoizedMultiFieldValueClassReplacements.ValueParameterTemplate
import org.jetbrains.kotlin.backend.jvm.MultiFieldValueClassSpecificDeclarations.ImplementationAgnostic import org.jetbrains.kotlin.backend.jvm.MultiFieldValueClassSpecificDeclarations.ImplementationAgnostic
import org.jetbrains.kotlin.backend.jvm.MultiFieldValueClassSpecificDeclarations.VirtualProperty 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) symbol2setters[original] = listOf(if (replacement.isAssignable) { _, value -> irSet(replacement, value) } else null)
} }
fun remapSymbol(original: IrValueSymbol, unboxed: List<VirtualProperty<Unit>>): Unit = fun remapSymbol(
remapSymbol(original, replacements.getDeclarations(original.owner.type.erasedUpperBound)!!.ImplementationAgnostic(unboxed)) original: IrValueSymbol,
unboxed: List<VirtualProperty<Unit>>,
declarations: MultiFieldValueClassSpecificDeclarations
): Unit =
remapSymbol(original, declarations.ImplementationAgnostic(unboxed))
fun remapSymbol(original: IrValueSymbol, unboxed: ImplementationAgnostic<Unit>) { fun remapSymbol(original: IrValueSymbol, unboxed: ImplementationAgnostic<Unit>) {
symbol2getter[original] = { symbol2getter[original] = {
@@ -314,9 +323,39 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
valueClass.declarations += properties.values.map { it.getter!!.apply { parent = valueClass } } valueClass.declarations += properties.values.map { it.getter!!.apply { parent = valueClass } }
} }
override fun createBridgeFunction(function: IrSimpleFunction, replacement: IrSimpleFunction): IrSimpleFunction? { override fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction, original: IrFunction, inverted: Boolean) {
return null // todo
// todo change return type to non-nullable for base class 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) { override fun addBindingsFor(original: IrFunction, replacement: IrFunction) {
@@ -324,19 +363,19 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
require(parametersStructure.size == original.explicitParameters.size) { require(parametersStructure.size == original.explicitParameters.size) {
"Wrong value parameters structure: $parametersStructure" "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" "Wrong value parameters structure: $parametersStructure"
} }
val old2newList = original.explicitParameters.zip( 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) } .zipWithNext { start: Int, finish: Int -> replacement.explicitParameters.slice(start until finish) }
) )
for ((param, newParamList) in old2newList) { for (i in old2newList.indices) {
val single = newParamList.singleOrNull() val (param, newParamList) = old2newList[i]
if (single != null) { when (val structure = parametersStructure[i]) {
valueDeclarationsRemapper.remapSymbol(param.symbol, single) is MultiFieldValueClassMapping ->
} else { valueDeclarationsRemapper.remapSymbol(param.symbol, newParamList.map { VirtualProperty(it) }, structure.declarations)
valueDeclarationsRemapper.remapSymbol(param.symbol, newParamList.map { VirtualProperty(it) }) 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 } val initializersStatements = valueClass.declarations.filterIsInstance<IrAnonymousInitializer>().flatMap { it.body.statements }
valueDeclarationsRemapper.remapSymbol( valueDeclarationsRemapper.remapSymbol(
oldPrimaryConstructor.constructedClass.thisReceiver!!.symbol, oldPrimaryConstructor.constructedClass.thisReceiver!!.symbol,
primaryConstructorImpl.valueParameters.map { VirtualProperty(it) } primaryConstructorImpl.valueParameters.map { VirtualProperty(it) },
this,
) )
primaryConstructorImpl.body = context.createIrBuilder(primaryConstructorImpl.symbol).irBlockBody { primaryConstructorImpl.body = context.createIrBuilder(primaryConstructorImpl.symbol).irBlockBody {
for (stmt in initializersStatements) { for (stmt in initializersStatements) {
@@ -472,8 +512,9 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
val parameter2expression = typedArgumentList(originalFunction, original) val parameter2expression = typedArgumentList(originalFunction, original)
val structure = replacements.bindingParameterTemplateStructure[originalFunction]!! val structure = replacements.bindingParameterTemplateStructure[originalFunction]!!
require(parameter2expression.size == structure.size) require(parameter2expression.size == structure.size)
require(structure.sumOf { it.size } == replacement.explicitParametersCount) require(structure.sumOf { it.valueParameters.size } == replacement.explicitParametersCount)
val newArguments: List<IrExpression?> = makeNewArguments(parameter2expression.map { (_, argument) -> argument }, structure) val newArguments: List<IrExpression?> =
makeNewArguments(parameter2expression.map { (_, argument) -> argument }, structure.map { it.valueParameters })
+irCall(replacement.symbol).apply { +irCall(replacement.symbol).apply {
copyTypeArgumentsFrom(original) copyTypeArgumentsFrom(original)
for ((parameter, argument) in replacement.explicitParameters zip newArguments) { for ((parameter, argument) in replacement.explicitParameters zip newArguments) {
@@ -586,7 +627,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
initializer?.let { initializer?.let {
flattenExpressionTo(it, variables.toGettersAndSetters()) 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) 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.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin 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.hasMangledReturnType
import org.jetbrains.kotlin.backend.jvm.ir.eraseTypeParameters import org.jetbrains.kotlin.backend.jvm.ir.eraseTypeParameters
import org.jetbrains.kotlin.backend.jvm.ir.needsAccessor 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.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
@@ -222,7 +222,7 @@ class JvmPropertiesLowering(private val backendContext: JvmBackendContext) : IrE
val getter = property.getter val getter = property.getter
if (getter != null) { if (getter != null) {
val needsMangling = val needsMangling =
getter.extensionReceiverParameter?.type?.requiresMangling == true || getter.extensionReceiverParameter?.type?.getRequiresMangling(includeInline = true, includeMFVC = false) == true ||
(state.functionsWithInlineClassReturnTypesMangled && getter.hasMangledReturnType) (state.functionsWithInlineClassReturnTypesMangled && getter.hasMangledReturnType)
val mangled = if (needsMangling) inlineClassReplacements.getReplacementFunction(getter) else null val mangled = if (needsMangling) inlineClassReplacements.getReplacementFunction(getter) else null
defaultMethodSignatureMapper.mapFunctionName(mangled ?: getter) 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.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext 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.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.pop import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.backend.jvm.InlineClassAbi import org.jetbrains.kotlin.backend.jvm.*
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.ir.isInlineClassType
import org.jetbrains.kotlin.backend.jvm.MemoizedValueClassAbstractReplacements
import org.jetbrains.kotlin.ir.IrStatement 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.builders.irReturn
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.transformStatement import org.jetbrains.kotlin.ir.transformStatement
import org.jetbrains.kotlin.ir.util.constructedClass import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.parentAsClass import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.ir.util.patchDeclarationParents import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.ir.util.transformFlat
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendContext) : FileLoweringPass, internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendContext) : FileLoweringPass,
@@ -121,7 +121,7 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
val bridgeFunction = createBridgeFunction(function, replacement) val bridgeFunction = createBridgeFunction(function, replacement)
return listOfNotNull(replacement, bridgeFunction) return listOf(replacement, bridgeFunction)
} }
final override fun visitReturn(expression: IrReturn): IrExpression { final override fun visitReturn(expression: IrReturn): IrExpression {
@@ -166,10 +166,69 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
super.visitAnonymousInitializerNew(declaration) super.visitAnonymousInitializerNew(declaration)
protected abstract fun addBindingsFor(original: IrFunction, replacement: IrFunction) 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( protected fun typedArgumentList(function: IrFunction, expression: IrMemberAccessExpression<*>) = listOfNotNull(
function.dispatchReceiverParameter?.let { it to expression.dispatchReceiver }, function.dispatchReceiverParameter?.let { it to expression.dispatchReceiver },
function.extensionReceiverParameter?.let { it to expression.extensionReceiver } function.extensionReceiverParameter?.let { it to expression.extensionReceiver }
) + function.valueParameters.map { it to expression.getValueArgument(it.index) } ) + 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 val constructedClass = constructedClass
if (!DescriptorVisibilities.isPrivate(visibility) && !constructedClass.isSingleFieldValueClass && hasMangledParameters && if (!DescriptorVisibilities.isPrivate(visibility) && !constructedClass.isValue && hasMangledParameters() &&
!constructedClass.isAnonymousObject !constructedClass.isAnonymousObject
) return true ) return true
@@ -101,7 +101,7 @@ object InlineClassAbi {
): String? = ): String? =
collectFunctionSignatureForManglingSuffix( collectFunctionSignatureForManglingSuffix(
useOldMangleRules, useOldMangleRules,
valueParameters.any { it.requiresMangling }, valueParameters.any { it.getRequiresMangling() },
// The JVM backend computes mangled names after creating suspend function views, but before default argument // 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. // 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. // TODO: Move suspend function view creation before JvmInlineClassLowering.
@@ -124,19 +124,28 @@ object InlineClassAbi {
get() = (this as IrSimpleFunction).correspondingPropertySymbol!!.owner.name get() = (this as IrSimpleFunction).correspondingPropertySymbol!!.owner.name
} }
val IrType.requiresMangling: Boolean fun IrType.getRequiresMangling(includeInline: Boolean = true, includeMFVC: Boolean = true): Boolean {
get() { val irClass = erasedUpperBound
val irClass = erasedUpperBound return irClass.fqNameWhenAvailable != StandardNames.RESULT_FQ_NAME && when {
return irClass.isValue && irClass.fqNameWhenAvailable != StandardNames.RESULT_FQ_NAME irClass.isSingleFieldValueClass -> includeInline
irClass.isMultiFieldValueClass -> includeMFVC
else -> false
} }
}
val IrFunction.fullValueParameterList: List<IrValueParameter> val IrFunction.fullValueParameterList: List<IrValueParameter>
get() = listOfNotNull(extensionReceiverParameter) + valueParameters get() = listOfNotNull(extensionReceiverParameter) + valueParameters
val IrFunction.hasMangledParameters: Boolean fun IrFunction.hasMangledParameters(includeInline: Boolean = true, includeMFVC: Boolean = true): Boolean =
get() = dispatchReceiverParameter != null && parentAsClass.isValue || (dispatchReceiverParameter != null && when {
fullValueParameterList.any { it.type.requiresMangling } || parentAsClass.isSingleFieldValueClass -> includeInline
(this is IrConstructor && constructedClass.isValue) 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 val IrFunction.hasMangledReturnType: Boolean
get() = returnType.isInlineClassType() && parentClassOrNull?.isFileClass != true get() = returnType.isInlineClassType() && parentClassOrNull?.isFileClass != true
@@ -40,7 +40,7 @@ class MemoizedInlineClassReplacements(
private val storageManager = LockBasedStorageManager("inline-class-replacements") private val storageManager = LockBasedStorageManager("inline-class-replacements")
val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap() 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. * Get a replacement for a function or a constructor.
@@ -76,7 +76,8 @@ class MemoizedInlineClassReplacements(
} }
// Otherwise, mangle functions with mangled parameters, ignoring constructors // 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) createMethodReplacement(it)
else -> else ->
@@ -35,9 +35,9 @@ class MemoizedMultiFieldValueClassReplacements(
) : MemoizedValueClassAbstractReplacements(irFactory, context) { ) : MemoizedValueClassAbstractReplacements(irFactory, context) {
private val storageManager = LockBasedStorageManager("multi-field-value-class-replacements") private val storageManager = LockBasedStorageManager("multi-field-value-class-replacements")
internal val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap() val originalFunctionForStaticReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
internal val originalFunctionForMethodReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap() val originalFunctionForMethodReplacement: MutableMap<IrFunction, IrFunction> = ConcurrentHashMap()
internal val originalConstructorForConstructorReplacement: MutableMap<IrConstructor, IrConstructor> = ConcurrentHashMap() private val originalConstructorForConstructorReplacement: MutableMap<IrConstructor, IrConstructor> = ConcurrentHashMap()
val getDeclarations: (IrClass) -> MultiFieldValueClassSpecificDeclarations? = val getDeclarations: (IrClass) -> MultiFieldValueClassSpecificDeclarations? =
storageManager.createMemoizedFunctionWithNullableValues { storageManager.createMemoizedFunctionWithNullableValues {
@@ -77,24 +77,24 @@ class MemoizedMultiFieldValueClassReplacements(
} }
} }
private fun IrFunction.makeValueParametersFromTemplate(newFlattenedParameters: List<List<ValueParameterTemplate>>) = private fun IrFunction.makeValueParametersFromTemplate(newFlattenedParameters: List<RemappedParameter>) = newFlattenedParameters
newFlattenedParameters.flatten().mapIndexed { index: Int, template -> template.toParameter(this, index) } .flatMap { it.valueParameters }.mapIndexed { index: Int, template -> template.toParameter(this, index) }
private fun List<ValueParameterTemplate>.grouped( private fun List<ValueParameterTemplate>.grouped(
originWhenFlattenedAndNotSpecified: IrDeclarationOrigin? = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER originWhenFlattenedAndNotSpecified: IrDeclarationOrigin? = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER
): List<List<ValueParameterTemplate>> = map { parameter -> ): List<RemappedParameter> = map { parameter ->
val declaration = parameter.type.takeIf { !it.isNullable() }?.getClass()?.let { getDeclarations(it) } val declarations = parameter.type.takeIf { !it.isNullable() }?.getClass()?.let { getDeclarations(it) }
?: return@map listOf(parameter) ?: return@map RemappedParameter.RegularMapping(parameter)
require(!parameter.original.hasDefaultValue()) { "Default parameters values are not supported for multi-field value classes" } 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( ValueParameterTemplate(
name = "${parameter.name}$${declaration.nodeFullNames[leaf]!!}", name = "${parameter.name}$${declarations.nodeFullNames[leaf]!!}",
type = leaf.type, type = leaf.type,
origin = parameter.origin ?: originWhenFlattenedAndNotSpecified, origin = parameter.origin ?: originWhenFlattenedAndNotSpecified,
defaultValue = null, defaultValue = null,
original = parameter.original, original = parameter.original,
) )
} })
} }
private fun buildReplacement( private fun buildReplacement(
@@ -115,8 +115,8 @@ class MemoizedMultiFieldValueClassReplacements(
private fun makeGroupedValueParametersFrom( private fun makeGroupedValueParametersFrom(
function: IrFunction, includeDispatcherReceiver: Boolean function: IrFunction, includeDispatcherReceiver: Boolean
): List<List<ValueParameterTemplate>> { ): List<RemappedParameter> {
val newFlattenedParameters = mutableListOf<List<ValueParameterTemplate>>() val newFlattenedParameters = mutableListOf<RemappedParameter>()
if (function.dispatchReceiverParameter != null && includeDispatcherReceiver) { if (function.dispatchReceiverParameter != null && includeDispatcherReceiver) {
val template = ValueParameterTemplate( val template = ValueParameterTemplate(
name = "\$dispatchReceiver", name = "\$dispatchReceiver",
@@ -161,7 +161,24 @@ class MemoizedMultiFieldValueClassReplacements(
return newFlattenedParameters 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 = override fun createStaticReplacement(function: IrFunction): IrSimpleFunction =
buildReplacement(function, JvmLoweredDeclarationOrigin.STATIC_MULTI_FIELD_VALUE_CLASS_REPLACEMENT, noFakeOverride = true) { buildReplacement(function, JvmLoweredDeclarationOrigin.STATIC_MULTI_FIELD_VALUE_CLASS_REPLACEMENT, noFakeOverride = true) {
@@ -184,7 +201,7 @@ class MemoizedMultiFieldValueClassReplacements(
original = receiver, original = receiver,
) else null ) else null
bindingParameterTemplateStructure[function] = bindingParameterTemplateStructure[function] =
if (receiverTemplate != null) listOf(listOf(receiverTemplate)) + newFlattenedParameters else newFlattenedParameters if (receiverTemplate != null) listOf(RemappedParameter.RegularMapping(receiverTemplate)) + newFlattenedParameters else newFlattenedParameters
valueParameters = makeValueParametersFromTemplate(newFlattenedParameters) valueParameters = makeValueParametersFromTemplate(newFlattenedParameters)
} }
@@ -217,7 +234,7 @@ class MemoizedMultiFieldValueClassReplacements(
defaultValue = receiver.defaultValue, defaultValue = receiver.defaultValue,
original = receiver original = receiver
) )
bindingParameterTemplateStructure[function] = listOf(listOf(receiverTemplate)) bindingParameterTemplateStructure[function] = listOf(RemappedParameter.RegularMapping(receiverTemplate))
return newGetter return newGetter
} }
+24 -3
View File
@@ -35,8 +35,12 @@ value class E(val x: D) {
set(_) = TODO() set(_) = TODO()
} }
interface Base3 {
val z: E
}
@JvmInline @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>) { fun <T : List<Int>> f(r: R<T>) {
println(r) println(r)
@@ -79,7 +83,20 @@ fun h1() {
println(y) 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 override fun toString(): String = l.toString() + l.z.x.x.z
init { 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() x.l.toString()
var y = x.l var y = x.l
y.toString() 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) 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 // todo add default parameters
fun box() = "bad"//.also { h(R(1, 2U, E(D(C(3, B(4U), "5"))), A(listOf(listOf(6))))) } fun box() = "bad"//.also { h(R(1, 2U, E(D(C(3, B(4U), "5"))), A(listOf(listOf(6))))) }