diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index de8827e3d99..d7375c44195 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -34964,6 +34964,18 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/properties/const/interfaceCompanion.kt"); } + @Test + @TestMetadata("intermoduleInlineConst.kt") + public void testIntermoduleInlineConst() throws Exception { + runTest("compiler/testData/codegen/box/properties/const/intermoduleInlineConst.kt"); + } + + @Test + @TestMetadata("kt52970.kt") + public void testKt52970() throws Exception { + runTest("compiler/testData/codegen/box/properties/const/kt52970.kt"); + } + @Test @TestMetadata("nonConstValsAreProperlyInitialized.kt") public void testNonConstValsAreProperlyInitialized() throws Exception { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InlineConstTransformer.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InlineConstTransformer.kt new file mode 100644 index 00000000000..0f150c3ebc0 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InlineConstTransformer.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.common.lower + +import org.jetbrains.kotlin.ir.declarations.IrField +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl +import org.jetbrains.kotlin.ir.expressions.impl.copyWithOffsets +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid + +abstract class InlineConstTransformer : IrElementTransformerVoid() { + abstract val IrField.constantInitializer: IrConst<*>? + private fun IrExpression.lowerConstRead(receiver: IrExpression?, field: IrField?): IrExpression? { + val value = field?.constantInitializer ?: return null + transformChildrenVoid() + reportInlineConst(field, value) + + val resultExpression = value.copyWithOffsets(startOffset, endOffset) + + return if (receiver == null || receiver.shouldDropConstReceiver()) + resultExpression + else + IrCompositeImpl( + startOffset, endOffset, resultExpression.type, null, + listOf(receiver, resultExpression) + ) + } + + abstract fun reportInlineConst(field: IrField, value: IrConst<*>) + + abstract fun IrExpression.shouldDropConstReceiver(): Boolean + + override fun visitCall(expression: IrCall): IrExpression { + val function = (expression.symbol.owner as? IrSimpleFunction) ?: return super.visitCall(expression) + val property = function.correspondingPropertySymbol?.owner ?: return super.visitCall(expression) + // If `constantValue` is not null, `function` can only be the getter because the property is immutable. + return expression.lowerConstRead(expression.dispatchReceiver, property.backingField) ?: super.visitCall(expression) + } + + override fun visitGetField(expression: IrGetField): IrExpression = + expression.lowerConstRead(expression.receiver, expression.symbol.owner) ?: super.visitGetField(expression) +} \ No newline at end of file diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ConstLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ConstLowering.kt index 130af6eb5e8..3dd6d6d7306 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ConstLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ConstLowering.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.backend.jvm.lower import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.lower.InlineConstTransformer import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.ir.constantValue @@ -13,12 +14,8 @@ import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.incremental.components.InlineConstTracker import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl -import org.jetbrains.kotlin.ir.expressions.impl.copyWithOffsets import org.jetbrains.kotlin.ir.util.classId import org.jetbrains.kotlin.ir.util.parentAsClass -import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid internal val constPhase1 = makeIrFilePhase( @@ -37,34 +34,12 @@ class ConstLowering(val context: JvmBackendContext) : FileLoweringPass { val inlineConstTracker = context.state.configuration[CommonConfigurationKeys.INLINE_CONST_TRACKER] - override fun lower(irFile: IrFile) = irFile.transformChildrenVoid(ConstTransformer(irFile, context, inlineConstTracker)) + override fun lower(irFile: IrFile) = irFile.transformChildrenVoid(JvmInlineConstTransformer(irFile, inlineConstTracker)) } -private class ConstTransformer( - val irFile: IrFile, - val context: JvmBackendContext, - val inlineConstTracker: InlineConstTracker? -) : IrElementTransformerVoid() { - private fun IrExpression.lowerConstRead(receiver: IrExpression?, field: IrField?): IrExpression? { - val value = field?.constantValue() ?: return null - transformChildrenVoid() - reportInlineConst(field, value) - - val resultExpression = if (context.state.shouldInlineConstVals) - value.copyWithOffsets(startOffset, endOffset) - else - IrGetFieldImpl(startOffset, endOffset, field.symbol, field.type) - - return if (receiver == null || receiver.shouldDropConstReceiver()) - resultExpression - else - IrCompositeImpl( - startOffset, endOffset, resultExpression.type, null, - listOf(receiver, resultExpression) - ) - } - - private fun reportInlineConst(field: IrField, value: IrConst<*>) { +private class JvmInlineConstTransformer(val irFile: IrFile, val inlineConstTracker: InlineConstTracker?) : InlineConstTransformer() { + override val IrField.constantInitializer get() = constantValue() + override fun reportInlineConst(field: IrField, value: IrConst<*>) { if (inlineConstTracker == null) return if (field.origin != IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB) return @@ -76,17 +51,5 @@ private class ConstTransformer( inlineConstTracker.report(path, owner, name, constType) } - private fun IrExpression.shouldDropConstReceiver() = - this is IrConst<*> || this is IrGetValue || - this is IrGetObjectValue - - override fun visitCall(expression: IrCall): IrExpression { - val function = (expression.symbol.owner as? IrSimpleFunction) ?: return super.visitCall(expression) - val property = function.correspondingPropertySymbol?.owner ?: return super.visitCall(expression) - // If `constantValue` is not null, `function` can only be the getter because the property is immutable. - return expression.lowerConstRead(expression.dispatchReceiver, property.backingField) ?: super.visitCall(expression) - } - - override fun visitGetField(expression: IrGetField): IrExpression = - expression.lowerConstRead(expression.receiver, expression.symbol.owner) ?: super.visitGetField(expression) + override fun IrExpression.shouldDropConstReceiver() = this is IrConst<*> || this is IrGetValue || this is IrGetObjectValue } \ No newline at end of file diff --git a/compiler/testData/codegen/box/properties/const/intermoduleInlineConst.kt b/compiler/testData/codegen/box/properties/const/intermoduleInlineConst.kt new file mode 100644 index 00000000000..5bb6575209b --- /dev/null +++ b/compiler/testData/codegen/box/properties/const/intermoduleInlineConst.kt @@ -0,0 +1,22 @@ +// MODULE: lib1 +// FILE: lib1.kt +object L1 { + lateinit var block: () -> Unit +} +// MODULE: lib2(lib1) +// FILE: lib2.kt +object L2 { + val sideEffect = L1.block() + const val x = 42 + fun triggerInitialization() {} +} + +// MODULE: main(lib1, lib2) +// FILE: main.kt +fun box(): String { + var result = 0 + L1.block = { result = L2.x } + L2.triggerInitialization() + if (result != 42) return "FAIL: $result" + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/properties/const/kt52970.kt b/compiler/testData/codegen/box/properties/const/kt52970.kt new file mode 100644 index 00000000000..254bc617028 --- /dev/null +++ b/compiler/testData/codegen/box/properties/const/kt52970.kt @@ -0,0 +1,7 @@ +open class A(val a: String = DEFAULT_A){ + companion object: A(){ + const val DEFAULT_A = "OK" + } +} + +fun box() = A().a \ No newline at end of file diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java index f18c7ca4511..71ea565c94c 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java @@ -34388,6 +34388,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/properties/const/interfaceCompanion.kt"); } + @Test + @TestMetadata("intermoduleInlineConst.kt") + public void testIntermoduleInlineConst() throws Exception { + runTest("compiler/testData/codegen/box/properties/const/intermoduleInlineConst.kt"); + } + + @Test + @TestMetadata("kt52970.kt") + public void testKt52970() throws Exception { + runTest("compiler/testData/codegen/box/properties/const/kt52970.kt"); + } + @Test @TestMetadata("nonConstValsAreProperlyInitialized.kt") public void testNonConstValsAreProperlyInitialized() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index 8ba6dffdb8b..c3d7161ad35 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -34964,6 +34964,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/properties/const/interfaceCompanion.kt"); } + @Test + @TestMetadata("intermoduleInlineConst.kt") + public void testIntermoduleInlineConst() throws Exception { + runTest("compiler/testData/codegen/box/properties/const/intermoduleInlineConst.kt"); + } + + @Test + @TestMetadata("kt52970.kt") + public void testKt52970() throws Exception { + runTest("compiler/testData/codegen/box/properties/const/kt52970.kt"); + } + @Test @TestMetadata("nonConstValsAreProperlyInitialized.kt") public void testNonConstValsAreProperlyInitialized() throws Exception { diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index bb18a2ab3ec..846538460e3 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -29340,6 +29340,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/properties/const/interfaceCompanion.kt"); } + @TestMetadata("intermoduleInlineConst.kt") + public void testIntermoduleInlineConst() throws Exception { + runTest("compiler/testData/codegen/box/properties/const/intermoduleInlineConst.kt"); + } + + @TestMetadata("kt52970.kt") + public void testKt52970() throws Exception { + runTest("compiler/testData/codegen/box/properties/const/kt52970.kt"); + } + @TestMetadata("nonConstValsAreProperlyInitialized.kt") public void testNonConstValsAreProperlyInitialized() throws Exception { runTest("compiler/testData/codegen/box/properties/const/nonConstValsAreProperlyInitialized.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java index 3b97544f323..48130810329 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java @@ -25251,6 +25251,18 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { public void testConstPropertyAccessor() throws Exception { runTest("compiler/testData/codegen/box/properties/const/constPropertyAccessor.kt"); } + + @Test + @TestMetadata("intermoduleInlineConst.kt") + public void testIntermoduleInlineConst() throws Exception { + runTest("compiler/testData/codegen/box/properties/const/intermoduleInlineConst.kt"); + } + + @Test + @TestMetadata("kt52970.kt") + public void testKt52970() throws Exception { + runTest("compiler/testData/codegen/box/properties/const/kt52970.kt"); + } } @Nested diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java index 47a85817fd6..161675f22c3 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java @@ -25245,6 +25245,18 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { public void testConstPropertyAccessor() throws Exception { runTest("compiler/testData/codegen/box/properties/const/constPropertyAccessor.kt"); } + + @Test + @TestMetadata("intermoduleInlineConst.kt") + public void testIntermoduleInlineConst() throws Exception { + runTest("compiler/testData/codegen/box/properties/const/intermoduleInlineConst.kt"); + } + + @Test + @TestMetadata("kt52970.kt") + public void testKt52970() throws Exception { + runTest("compiler/testData/codegen/box/properties/const/kt52970.kt"); + } } @Nested diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java index 941399f5a89..79b234c277b 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java @@ -22532,6 +22532,16 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest public void testConstPropertyAccessor() throws Exception { runTest("compiler/testData/codegen/box/properties/const/constPropertyAccessor.kt"); } + + @TestMetadata("intermoduleInlineConst.kt") + public void testIntermoduleInlineConst() throws Exception { + runTest("compiler/testData/codegen/box/properties/const/intermoduleInlineConst.kt"); + } + + @TestMetadata("kt52970.kt") + public void testKt52970() throws Exception { + runTest("compiler/testData/codegen/box/properties/const/kt52970.kt"); + } } @TestMetadata("compiler/testData/codegen/box/properties/lateinit") diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt index d0dbfb90e75..b39b2e1144a 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt @@ -407,6 +407,12 @@ internal val expressionBodyTransformPhase = makeKonanFileLoweringPhase( description = "Replace IrExpressionBody with IrBlockBody" ) +internal val constantInliningPhase = makeKonanFileLoweringPhase( + ::ConstLowering, + name = "ConstantInlining", + description = "Inline const fields reads", +) + internal val fileInitializersPhase = makeKonanFileLoweringPhase( ::FileInitializersLowering, name = "FileInitializers", diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index cefdec5792f..9c58eaedbe7 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -302,6 +302,7 @@ internal val allLoweringsPhase = NamedCompilerPhase( coroutinesPhase, typeOperatorPhase, expressionBodyTransformPhase, + constantInliningPhase, fileInitializersPhase, bridgesPhase, autoboxPhase, diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ConstLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ConstLowering.kt new file mode 100644 index 00000000000..3f068b2a18c --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ConstLowering.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.konan.lower + +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.lower.InlineConstTransformer +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.ir.declarations.IrField +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.expressions.IrConst +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrGetValue +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid + +internal class ConstLowering(val context: Context) : FileLoweringPass { + override fun lower(irFile: IrFile) { + irFile.transformChildrenVoid(NativeInlineConstTransformer()) + } +} + +private class NativeInlineConstTransformer : InlineConstTransformer() { + override val IrField.constantInitializer get() = + (initializer?.expression as? IrConst<*>)?.takeIf { correspondingPropertySymbol?.owner?.isConst == true } + override fun reportInlineConst(field: IrField, value: IrConst<*>) {} + // on jvm IrGetObjectValue is also dropped. This would be breaking change for native. + // Some design work is required to decide what is correct, let just keep current behaviour for now + override fun IrExpression.shouldDropConstReceiver() = this is IrConst<*> || this is IrGetValue +} \ No newline at end of file diff --git a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java index 750836f7856..e379662a944 100644 --- a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java +++ b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java @@ -28056,6 +28056,18 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest public void testConstPropertyAccessor() throws Exception { runTest("compiler/testData/codegen/box/properties/const/constPropertyAccessor.kt"); } + + @Test + @TestMetadata("intermoduleInlineConst.kt") + public void testIntermoduleInlineConst() throws Exception { + runTest("compiler/testData/codegen/box/properties/const/intermoduleInlineConst.kt"); + } + + @Test + @TestMetadata("kt52970.kt") + public void testKt52970() throws Exception { + runTest("compiler/testData/codegen/box/properties/const/kt52970.kt"); + } } @Nested