diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InitializersLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InitializersLowering.kt index e92a4ab9802..f77be5e9aa6 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InitializersLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InitializersLowering.kt @@ -5,41 +5,64 @@ package org.jetbrains.kotlin.backend.common.lower -import org.jetbrains.kotlin.backend.common.ClassLoweringPass -import org.jetbrains.kotlin.backend.common.CommonBackendContext -import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext +import org.jetbrains.kotlin.backend.common.* +import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.IrBody import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrInstanceInitializerCall import org.jetbrains.kotlin.ir.expressions.IrStatementOriginImpl import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBodyImpl import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl +import org.jetbrains.kotlin.ir.util.constructedClass import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols -import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.ir.visitors.* object SYNTHESIZED_INIT_BLOCK : IrStatementOriginImpl("SYNTHESIZED_INIT_BLOCK") -open class InitializersLowering(context: CommonBackendContext) : InitializersLoweringBase(context) { - override fun lower(irClass: IrClass) { +open class InitializersLowering(context: CommonBackendContext) : InitializersLoweringBase(context), BodyLoweringPass { + + override fun lower(irFile: IrFile) { + runOnFilePostfix(irFile, true) + } + + override fun lower(irBody: IrBody, container: IrDeclaration) { + if (container !is IrConstructor) return + + val irClass = container.constructedClass + val instanceInitializerStatements = extractInitializers(irClass) { (it is IrField && !it.isStatic) || (it is IrAnonymousInitializer && !it.isStatic) } - irClass.transformChildrenVoid(object : IrElementTransformerVoidWithContext() { - // Only transform constructors of current class. - override fun visitClassNew(declaration: IrClass) = declaration - override fun visitSimpleFunction(declaration: IrSimpleFunction) = declaration + container.body?.transformChildrenVoid(object : IrElementTransformerVoid() { + override fun visitInstanceInitializerCall(expression: IrInstanceInitializerCall): IrExpression { + return IrBlockImpl(irClass.startOffset, irClass.endOffset, context.irBuiltIns.unitType, null, instanceInitializerStatements) + .deepCopyWithSymbols(container).also { + // Handle declarations, copied from initializers + // Otherwise local classes inside them won't get processed. + // Yes, there are such cases - see testData/codegen/box/properties/complexPropertyInitializer.kt + it.acceptVoid(object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } - override fun visitInstanceInitializerCall(expression: IrInstanceInitializerCall): IrExpression = - IrBlockImpl(irClass.startOffset, irClass.endOffset, context.irBuiltIns.unitType, null, instanceInitializerStatements) - .deepCopyWithSymbols(currentScope!!.scope.getLocalDeclarationParent()) + override fun visitConstructor(declaration: IrConstructor) { + super.visitConstructor(declaration) + + declaration.body?.let { lower(it, declaration) } + } + }) + } + } }) } } -abstract class InitializersLoweringBase(open val context: CommonBackendContext) : ClassLoweringPass { +abstract class InitializersLoweringBase(open val context: CommonBackendContext) { protected fun extractInitializers(irClass: IrClass, filter: (IrDeclaration) -> Boolean) = irClass.declarations.filter(filter).mapNotNull { when (it) { @@ -47,25 +70,15 @@ abstract class InitializersLoweringBase(open val context: CommonBackendContext) is IrAnonymousInitializer -> handleAnonymousInitializer(it) else -> null } - }.also { - irClass.declarations.removeAll { it is IrAnonymousInitializer && filter(it) } } - protected open fun shouldEraseFieldInitializer(irField: IrField): Boolean = irField.correspondingPropertySymbol?.owner?.isConst != true - private fun handleField(irClass: IrClass, declaration: IrField): IrStatement? = declaration.initializer?.run { val receiver = if (!declaration.isStatic) // TODO isStaticField IrGetValueImpl(startOffset, endOffset, irClass.thisReceiver!!.type, irClass.thisReceiver!!.symbol) else null - val value = if (shouldEraseFieldInitializer(declaration)) { - declaration.initializer = null - expression - } else { - expression.deepCopyWithSymbols() - } - IrSetFieldImpl(startOffset, endOffset, declaration.symbol, receiver, value, context.irBuiltIns.unitType) + IrSetFieldImpl(startOffset, endOffset, declaration.symbol, receiver, expression, context.irBuiltIns.unitType) } private fun handleAnonymousInitializer(declaration: IrAnonymousInitializer): IrStatement = @@ -73,3 +86,31 @@ abstract class InitializersLoweringBase(open val context: CommonBackendContext) IrBlockImpl(startOffset, endOffset, context.irBuiltIns.unitType, SYNTHESIZED_INIT_BLOCK, body.statements) } } + +// Remove anonymous initializers and set field initializers to `null` +class InitializersCleanupLowering( + val context: CommonBackendContext, + private val shouldEraseFieldInitializer: (IrField) -> Boolean = { it.correspondingPropertySymbol?.owner?.isConst != true } +) : DeclarationTransformer { + + override fun lower(irFile: IrFile) { + runPostfix(withLocalDeclarations = true).toFileLoweringPass().lower(irFile) + } + + override fun transformFlat(declaration: IrDeclaration): List? { + if (declaration is IrAnonymousInitializer) return emptyList() + + if (declaration is IrField && declaration.parent is IrClass) { + if (shouldEraseFieldInitializer(declaration)) { + declaration.initializer = null + } else { + declaration.initializer?.let { + declaration.initializer = + IrExpressionBodyImpl(it.startOffset, it.endOffset) { expression = it.expression.deepCopyWithSymbols() } + } + } + } + + return null + } +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index 59a2e47a791..b971d72e17c 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -293,6 +293,13 @@ private val initializersLoweringPhase = makeJsModulePhase( prerequisite = setOf(enumClassConstructorLoweringPhase, primaryConstructorLoweringPhase, annotationConstructorLowering) ) +private val initializersCleanupLoweringPhase = makeJsModulePhase( + ::InitializersCleanupLowering, + name = "InitializersCleanupLowering", + description = "Remove non-static anonymous initializers and field init expressions", + prerequisite = setOf(initializersLoweringPhase) +) + private val multipleCatchesLoweringPhase = makeJsModulePhase( ::MultipleCatchesLowering, name = "MultipleCatchesLowering", @@ -428,6 +435,7 @@ val jsPhases = namedIrModulePhase( primaryConstructorLoweringPhase then annotationConstructorLowering then initializersLoweringPhase then + initializersCleanupLoweringPhase then // Common prefix ends enumClassLoweringPhase then enumUsageLoweringPhase then diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 7b4878c7de7..f28f2f1889f 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -178,14 +178,21 @@ private val staticInitializersPhase = makeIrFilePhase( ) private val initializersPhase = makeIrFilePhase( - { context -> - object : InitializersLowering(context) { - override fun shouldEraseFieldInitializer(irField: IrField): Boolean = - irField.constantValue(context) == null - } - }, + ::InitializersLowering, name = "Initializers", description = "Merge init blocks and field initializers into constructors", + // Depends on local class extraction, because otherwise local classes in initializers will be copied into each constructor. + prerequisite = setOf(jvmLocalClassExtractionPhase) +) + +private val initializersCleanupPhase = makeIrFilePhase( + { context -> + InitializersCleanupLowering(context) { + it.constantValue(context) == null && (!it.isStatic || it.correspondingPropertySymbol?.owner?.isConst != true) + } + }, + name = "InitializersCleanup", + description = "Remove non-static anonymous initializers and non-constant non-static field init expressions", stickyPostconditions = setOf(fun(irFile: IrFile) { irFile.acceptVoid(object : IrElementVisitorVoid { override fun visitElement(element: IrElement) { @@ -197,8 +204,7 @@ private val initializersPhase = makeIrFilePhase( } }) }), - // Depends on local class extraction, because otherwise local classes in initializers will be copied into each constructor. - prerequisite = setOf(jvmLocalClassExtractionPhase) + prerequisite = setOf(initializersPhase) ) private val returnableBlocksPhase = makeIrFilePhase( @@ -293,6 +299,7 @@ private val jvmFilePhases = objectClassPhase then staticInitializersPhase then initializersPhase then + initializersCleanupPhase then collectionStubMethodLowering then functionNVarargBridgePhase then bridgePhase then diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/StaticInitializersLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/StaticInitializersLowering.kt index aed08433b97..68f36c5bfc2 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/StaticInitializersLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/StaticInitializersLowering.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.backend.jvm.lower +import org.jetbrains.kotlin.backend.common.ClassLoweringPass import org.jetbrains.kotlin.backend.common.lower.InitializersLoweringBase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin @@ -19,7 +20,7 @@ import org.jetbrains.kotlin.ir.util.patchDeclarationParents import org.jetbrains.kotlin.load.java.JavaVisibilities import org.jetbrains.kotlin.name.Name -class StaticInitializersLowering(override val context: JvmBackendContext) : InitializersLoweringBase(context) { +class StaticInitializersLowering(override val context: JvmBackendContext) : InitializersLoweringBase(context), ClassLoweringPass { override fun lower(irClass: IrClass) { val staticInitializerStatements = extractInitializers(irClass) { // JVM implementations are required to generate initializers for all static fields with ConstantValue, diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt index c3cf236fd20..4d3097adf1c 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt @@ -208,6 +208,13 @@ private val initializersLoweringPhase = makeWasmModulePhase( prerequisite = setOf(primaryConstructorLoweringPhase) ) +private val initializersCleanupLoweringPhase = makeWasmModulePhase( + ::InitializersCleanupLowering, + name = "InitializersCleanupLowering", + description = "Remove non-static anonymous initializers and field init expressions", + prerequisite = setOf(initializersLoweringPhase) +) + private val excludeDeclarationsFromCodegenPhase = makeCustomWasmModulePhase( { context, module -> excludeDeclarationsFromCodegen(context, module) @@ -336,6 +343,7 @@ val wasmPhases = namedIrModulePhase( propertiesLoweringPhase then primaryConstructorLoweringPhase then initializersLoweringPhase then + initializersCleanupLoweringPhase then // Common prefix ends builtInsLoweringPhase then