From ee2d3272436743e7117352ea956b3fa2dddfec9d Mon Sep 17 00:00:00 2001 From: "Evgeniy.Zhelenskiy" Date: Thu, 28 Apr 2022 21:12:30 +0300 Subject: [PATCH] [IR] Implement bridges generation for MFVC #KT-1179 --- .../backend/jvm/lower/BridgeLowering.kt | 72 ++++++++++++++--- .../lower/JvmDefaultConstructorLowering.kt | 2 +- .../jvm/lower/JvmInlineClassLowering.kt | 62 +-------------- .../lower/JvmMultiFieldValueClassLowering.kt | 75 ++++++++++++++---- .../jvm/lower/JvmPropertiesLowering.kt | 4 +- .../lower/JvmValueClassAbstractLowering.kt | 77 ++++++++++++++++--- .../jvm/lower/SyntheticAccessorLowering.kt | 2 +- .../kotlin/backend/jvm/InlineClassAbi.kt | 27 ++++--- .../jvm/MemoizedInlineClassReplacements.kt | 5 +- ...emoizedMultiFieldValueClassReplacements.kt | 49 ++++++++---- .../codegen/box/valueClasses/simple.kt | 27 ++++++- 11 files changed, 269 insertions(+), 133 deletions(-) diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt index 62382ec8ef8..4aab44fbd66 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt @@ -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 diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultConstructorLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultConstructorLowering.kt index f7abcb4d358..75f87f0a1e7 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultConstructorLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultConstructorLowering.kt @@ -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() }) diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt index 7ea5df3e87e..e0b8790b8fe 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt @@ -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) diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt index acc03c2a4b3..245e70e7b99 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt @@ -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>): Unit = - remapSymbol(original, replacements.getDeclarations(original.owner.type.erasedUpperBound)!!.ImplementationAgnostic(unboxed)) + fun remapSymbol( + original: IrValueSymbol, + unboxed: List>, + declarations: MultiFieldValueClassSpecificDeclarations + ): Unit = + remapSymbol(original, declarations.ImplementationAgnostic(unboxed)) fun remapSymbol(original: IrValueSymbol, unboxed: ImplementationAgnostic) { 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 = 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 -> 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().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 = makeNewArguments(parameter2expression.map { (_, argument) -> argument }, structure) + require(structure.sumOf { it.valueParameters.size } == replacement.explicitParametersCount) + val newArguments: List = + 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) diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmPropertiesLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmPropertiesLowering.kt index 71f054ba1ba..04acfabadee 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmPropertiesLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmPropertiesLowering.kt @@ -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) diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassAbstractLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassAbstractLowering.kt index 760e784994f..08d7305cd30 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassAbstractLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassAbstractLowering.kt @@ -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) } \ No newline at end of file diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt index 95fb7ccdf76..b3f5ae9776d 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt @@ -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 diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/InlineClassAbi.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/InlineClassAbi.kt index 8e0484fd8e0..e0c73ec613c 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/InlineClassAbi.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/InlineClassAbi.kt @@ -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 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 diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedInlineClassReplacements.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedInlineClassReplacements.kt index 88b71bc1f40..32adac0c30c 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedInlineClassReplacements.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedInlineClassReplacements.kt @@ -40,7 +40,7 @@ class MemoizedInlineClassReplacements( private val storageManager = LockBasedStorageManager("inline-class-replacements") val originalFunctionForStaticReplacement: MutableMap = ConcurrentHashMap() - internal val originalFunctionForMethodReplacement: MutableMap = ConcurrentHashMap() + val originalFunctionForMethodReplacement: MutableMap = 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 -> diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedMultiFieldValueClassReplacements.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedMultiFieldValueClassReplacements.kt index 4d34bbf7d53..3ad32b01c06 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedMultiFieldValueClassReplacements.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedMultiFieldValueClassReplacements.kt @@ -35,9 +35,9 @@ class MemoizedMultiFieldValueClassReplacements( ) : MemoizedValueClassAbstractReplacements(irFactory, context) { private val storageManager = LockBasedStorageManager("multi-field-value-class-replacements") - internal val originalFunctionForStaticReplacement: MutableMap = ConcurrentHashMap() - internal val originalFunctionForMethodReplacement: MutableMap = ConcurrentHashMap() - internal val originalConstructorForConstructorReplacement: MutableMap = ConcurrentHashMap() + val originalFunctionForStaticReplacement: MutableMap = ConcurrentHashMap() + val originalFunctionForMethodReplacement: MutableMap = ConcurrentHashMap() + private val originalConstructorForConstructorReplacement: MutableMap = ConcurrentHashMap() val getDeclarations: (IrClass) -> MultiFieldValueClassSpecificDeclarations? = storageManager.createMemoizedFunctionWithNullableValues { @@ -77,24 +77,24 @@ class MemoizedMultiFieldValueClassReplacements( } } - private fun IrFunction.makeValueParametersFromTemplate(newFlattenedParameters: List>) = - newFlattenedParameters.flatten().mapIndexed { index: Int, template -> template.toParameter(this, index) } + private fun IrFunction.makeValueParametersFromTemplate(newFlattenedParameters: List) = newFlattenedParameters + .flatMap { it.valueParameters }.mapIndexed { index: Int, template -> template.toParameter(this, index) } private fun List.grouped( originWhenFlattenedAndNotSpecified: IrDeclarationOrigin? = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_PARAMETER - ): List> = map { parameter -> - val declaration = parameter.type.takeIf { !it.isNullable() }?.getClass()?.let { getDeclarations(it) } - ?: return@map listOf(parameter) + ): List = 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> { - val newFlattenedParameters = mutableListOf>() + ): List { + val newFlattenedParameters = mutableListOf() if (function.dispatchReceiverParameter != null && includeDispatcherReceiver) { val template = ValueParameterTemplate( name = "\$dispatchReceiver", @@ -161,7 +161,24 @@ class MemoizedMultiFieldValueClassReplacements( return newFlattenedParameters } - val bindingParameterTemplateStructure: MutableMap>> = ConcurrentHashMap() + sealed class RemappedParameter { + data class RegularMapping(val valueParameter: ValueParameterTemplate) : RemappedParameter() { + override val valueParameters: List = listOf(valueParameter) + } + + data class MultiFieldValueClassMapping( + val declarations: MultiFieldValueClassSpecificDeclarations, + override val valueParameters: List, + ) : RemappedParameter() { + init { + require(valueParameters.size > 1) { "MFVC must have > 1 fields" } + } + } + + abstract val valueParameters: List + } + + val bindingParameterTemplateStructure: MutableMap> = 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 } diff --git a/compiler/testData/codegen/box/valueClasses/simple.kt b/compiler/testData/codegen/box/valueClasses/simple.kt index ef367151f23..526b33b88ce 100644 --- a/compiler/testData/codegen/box/valueClasses/simple.kt +++ b/compiler/testData/codegen/box/valueClasses/simple.kt @@ -35,8 +35,12 @@ value class E(val x: D) { set(_) = TODO() } +interface Base3 { + val z: E +} + @JvmInline -value class R(val x: Int, val y: UInt, val z: E, val t: A) +value class R(val x: Int, val y: UInt, override val z: E, val t: A): Base1, Base3 fun > f(r: R) { println(r) @@ -79,7 +83,20 @@ fun h1() { println(y) } -class NotInlined(var l: R>, var y: Int) { +interface Base1 { + val fakeOverrideMFVC: R> + get() = TODO() + val fakeOverrideRegular: Int + get() = TODO() +} +interface Base2 { + var l: R> +} +interface Base4 { + var l: T +} + +class NotInlined(override var l: R>, var y: Int): Base1, Base2, Base4>> { override fun toString(): String = l.toString() + l.z.x.x.z init { @@ -119,7 +136,7 @@ class NotInlined(var l: R>, 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>(x.l.x, x.l.y, x.l.z, x.l.t) } +fun reuseBoxed(list: MutableList>>) { + 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))))) }