diff --git a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java index 1aed21bf4a3..14992dc6352 100644 --- a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java @@ -12958,6 +12958,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/inlineClasses/passInlineClassWithSpreadOperatorToVarargs.kt"); } + @TestMetadata("propertyLoweringOrder.kt") + public void testPropertyLoweringOrder() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/propertyLoweringOrder.kt"); + } + @TestMetadata("referToPropertyInCompanionObjectOfInlineClass.kt") public void testReferToPropertyInCompanionObjectOfInlineClass() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/referToPropertyInCompanionObjectOfInlineClass.kt"); diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt index 4f1a7492b15..296263d2ad5 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt @@ -9,20 +9,26 @@ 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.ir.passTypeArgumentsFrom +import org.jetbrains.kotlin.backend.common.lower.allOverridden import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.irBlockBody import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin +import org.jetbrains.kotlin.backend.jvm.codegen.MethodSignatureMapper +import org.jetbrains.kotlin.backend.jvm.ir.eraseTypeParameters import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.* import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.inlineClassFieldName import org.jetbrains.kotlin.config.ApiVersion +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibilities 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.addFunction +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.IrCallImpl @@ -37,6 +43,8 @@ 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.DescriptorUtils import org.jetbrains.kotlin.utils.addToStdlib.safeAs @@ -65,9 +73,7 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F } } - override fun lower(irFile: IrFile) { - irFile.transformChildrenVoid() - } + override fun lower(irFile: IrFile) = irFile.transformChildrenVoid() override fun visitClassNew(declaration: IrClass): IrStatement { // The arguments to the primary constructor are in scope in the initializers of IrFields. @@ -117,17 +123,6 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F } } - private fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction) { - source.body = context.createIrBuilder(source.symbol, source.startOffset, source.endOffset).run { - irExprBody(irCall(target).apply { - passTypeArgumentsFrom(source) - for ((parameter, newParameter) in source.explicitParameters.zip(target.explicitParameters)) { - putArgument(newParameter, irGet(parameter)) - } - }) - } - } - private fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List { replacement.valueParameters.forEach { it.transformChildrenVoid() } replacement.body = function.body?.transform(this, null)?.patchDeclarationParents(replacement) @@ -137,15 +132,26 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F if (function.overriddenSymbols.isEmpty() || replacement.dispatchReceiverParameter != null) return listOf(replacement) - // If the original function has value parameters which need mangling we still need to replace - // it with a mangled version. - val bridgeFunction = if (!function.isFakeOverride && function.fullValueParameterList.any { it.type.requiresMangling }) { - context.inlineClassReplacements.createMethodReplacement(function) - } else { - // Update the overridden symbols to point to their inline class replacements - function.overriddenSymbols = replacement.overriddenSymbols - function - } + val bridgeFunction = createBridgeDeclaration( + function, + when { + // If the original function has value parameters which need mangling we still need to replace + // it with a mangled version. + !function.isFakeOverride && function.fullValueParameterList.any { it.type.requiresMangling } -> + 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.isInline) { @@ -158,6 +164,35 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F return listOf(replacement, bridgeFunction) } + // 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) = + 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 { + irExprBody(irCall(target).apply { + passTypeArgumentsFrom(source) + for ((parameter, newParameter) in source.explicitParameters.zip(target.explicitParameters)) { + putArgument(newParameter, irGet(parameter)) + } + }) + } + } + // Secondary constructors for boxed types get translated to static functions returning // unboxed arguments. We remove the original constructor. private fun transformConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/MemoizedInlineClassReplacements.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/MemoizedInlineClassReplacements.kt index 349f0477ee2..4afaab8fe96 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/MemoizedInlineClassReplacements.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/MemoizedInlineClassReplacements.kt @@ -15,10 +15,12 @@ import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter import org.jetbrains.kotlin.ir.builders.declarations.buildFun import org.jetbrains.kotlin.ir.builders.declarations.buildFunWithDescriptorForInlining +import org.jetbrains.kotlin.ir.builders.declarations.buildProperty import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionBase import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns +import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl import org.jetbrains.kotlin.ir.util.* @@ -32,6 +34,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs */ class MemoizedInlineClassReplacements { private val storageManager = LockBasedStorageManager("inline-class-replacements") + private val propertyMap = mutableMapOf() /** * Get a replacement for a function or a constructor. @@ -124,7 +127,7 @@ class MemoizedInlineClassReplacements { } } - internal fun createMethodReplacement(function: IrFunction): IrSimpleFunction = + private fun createMethodReplacement(function: IrFunction): IrSimpleFunction = buildReplacement(function, function.origin) { require(function.dispatchReceiverParameter != null && function is IrSimpleFunction) val newValueParameters = ArrayList() @@ -176,33 +179,49 @@ class MemoizedInlineClassReplacements { replacementOrigin: IrDeclarationOrigin, noFakeOverride: Boolean = false, body: IrFunctionImpl.() -> Unit - ) = - buildFunWithDescriptorForInlining(function.descriptor) { - updateFrom(function) - origin = if (function.origin == IrDeclarationOrigin.GENERATED_INLINE_CLASS_MEMBER) { - JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD - } else { - replacementOrigin - } - if (noFakeOverride) { - isFakeOverride = false - } - name = mangledNameFor(function) - returnType = function.returnType - }.apply { - parent = function.parent - annotations += function.annotations - copyTypeParameters(function.allTypeParameters) - metadata = function.metadata - function.safeAs>()?.metadata = null + ) = buildFunWithDescriptorForInlining(function.descriptor) { + updateFrom(function) + origin = if (function.origin == IrDeclarationOrigin.GENERATED_INLINE_CLASS_MEMBER) { + JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD + } else { + replacementOrigin + } + if (noFakeOverride) { + isFakeOverride = false + } + name = mangledNameFor(function) + returnType = function.returnType + }.apply { + parent = function.parent + annotations += function.annotations + copyTypeParameters(function.allTypeParameters) + metadata = function.metadata + function.safeAs>()?.metadata = null - if (function is IrSimpleFunction) { - correspondingPropertySymbol = function.correspondingPropertySymbol - overriddenSymbols = function.overriddenSymbols.map { - getReplacementFunction(it.owner)?.symbol ?: it + if (function is IrSimpleFunction) { + val propertySymbol = function.correspondingPropertySymbol + if (propertySymbol != null) { + val property = propertyMap.getOrPut(propertySymbol) { + buildProperty(propertySymbol.descriptor) { + name = propertySymbol.owner.name + updateFrom(propertySymbol.owner) + }.apply { + parent = propertySymbol.owner.parent + } + } + correspondingPropertySymbol = property.symbol + when (function) { + propertySymbol.owner.getter -> property.getter = this + propertySymbol.owner.setter -> property.setter = this + else -> error("Orphaned property getter/setter: ${function.render()}") } } - body() + overriddenSymbols = function.overriddenSymbols.map { + getReplacementFunction(it.owner)?.symbol ?: it + } } + + body() + } } diff --git a/compiler/testData/codegen/box/inlineClasses/propertyLoweringOrder.kt b/compiler/testData/codegen/box/inlineClasses/propertyLoweringOrder.kt new file mode 100644 index 00000000000..e478bf76cbc --- /dev/null +++ b/compiler/testData/codegen/box/inlineClasses/propertyLoweringOrder.kt @@ -0,0 +1,14 @@ +// FILE: 1.kt + +inline class A(val x: String) + +fun accessProperty(y: B): A { + y.a = A("OK") + return y.a +} + +// FILE: 2.kt + +class B(var a: A) + +fun box(): String = accessProperty(B(A("Fail"))).x diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index d5afae6d5f9..b98c7025216 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -14173,6 +14173,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/inlineClasses/passInlineClassWithSpreadOperatorToVarargs.kt"); } + @TestMetadata("propertyLoweringOrder.kt") + public void testPropertyLoweringOrder() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/propertyLoweringOrder.kt"); + } + @TestMetadata("referToPropertyInCompanionObjectOfInlineClass.kt") public void testReferToPropertyInCompanionObjectOfInlineClass() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/referToPropertyInCompanionObjectOfInlineClass.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 79da70e2975..79217b1a1d3 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -14178,6 +14178,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/inlineClasses/passInlineClassWithSpreadOperatorToVarargs.kt"); } + @TestMetadata("propertyLoweringOrder.kt") + public void testPropertyLoweringOrder() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/propertyLoweringOrder.kt"); + } + @TestMetadata("referToPropertyInCompanionObjectOfInlineClass.kt") public void testReferToPropertyInCompanionObjectOfInlineClass() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/referToPropertyInCompanionObjectOfInlineClass.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 0515a82770b..4c34208f3d5 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -12958,6 +12958,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/inlineClasses/passInlineClassWithSpreadOperatorToVarargs.kt"); } + @TestMetadata("propertyLoweringOrder.kt") + public void testPropertyLoweringOrder() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/propertyLoweringOrder.kt"); + } + @TestMetadata("referToPropertyInCompanionObjectOfInlineClass.kt") public void testReferToPropertyInCompanionObjectOfInlineClass() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/referToPropertyInCompanionObjectOfInlineClass.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index b56514e4b10..9ba307ec715 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -11148,6 +11148,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/inlineClasses/passInlineClassWithSpreadOperatorToVarargs.kt"); } + @TestMetadata("propertyLoweringOrder.kt") + public void testPropertyLoweringOrder() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/propertyLoweringOrder.kt"); + } + @TestMetadata("referToPropertyInCompanionObjectOfInlineClass.kt") public void testReferToPropertyInCompanionObjectOfInlineClass() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/referToPropertyInCompanionObjectOfInlineClass.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index cb28757f05f..aacc2e07597 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -11213,6 +11213,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/inlineClasses/passInlineClassWithSpreadOperatorToVarargs.kt"); } + @TestMetadata("propertyLoweringOrder.kt") + public void testPropertyLoweringOrder() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/propertyLoweringOrder.kt"); + } + @TestMetadata("referToPropertyInCompanionObjectOfInlineClass.kt") public void testReferToPropertyInCompanionObjectOfInlineClass() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/referToPropertyInCompanionObjectOfInlineClass.kt");