From ed2191b4a3f7be4d139a323d9f766a7b07ce496b Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Fri, 10 Mar 2017 19:02:02 +0500 Subject: [PATCH] Initializers lowering fix: clone init blocks. (#321) * Initializers lowering fix: clone init blocks. In order to let following phases to modify IR we need to clone init blocks for each constructor before inserting it. * Used DeepCopyIRTreeWithDescriptors. * Shared variables lowering must be performed before local functions lowering. Local functions lowering makes all variables non-shared, thus they must be transformed into Ref<> before this. * Moved all initializers into a method. In order not to deal with copying of IR at all it is easier to create a method 'initialize' which will be called from each constructor. * Used right parameters for call. * If class has primary constructor then move initializers to it. * review fixes --- .../kotlin/backend/konan/KonanLower.kt | 6 +- .../kotlin/backend/konan/KonanPhases.kt | 4 +- .../backend/konan/lower/EnumClassLowering.kt | 17 +++-- .../konan/lower/InitializersLowering.kt | 74 +++++++++++++++---- 4 files changed, 76 insertions(+), 25 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt index bce6b668332..fff242f3b39 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt @@ -25,12 +25,12 @@ internal class KonanLower(val context: Context) { phaser.phase(KonanPhase.LOWER_ENUMS) { EnumClassLowering(context).run(irFile) } - phaser.phase(KonanPhase.LOWER_SHARED_VARIABLES) { - SharedVariablesLowering(context).runOnFilePostfix(irFile) - } phaser.phase(KonanPhase.LOWER_INITIALIZERS) { InitializersLowering(context).runOnFilePostfix(irFile) } + phaser.phase(KonanPhase.LOWER_SHARED_VARIABLES) { + SharedVariablesLowering(context).runOnFilePostfix(irFile) + } phaser.phase(KonanPhase.LOWER_DELEGATION) { PropertyDelegationLowering(context).lower(irFile) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt index fe8232f0a9c..d39be6c8281 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt @@ -12,14 +12,14 @@ enum class KonanPhase(val description: String, /* ... */ LOWER("IR Lowering"), /* ... ... */ LOWER_INLINE("Functions inlining"), /* ... ... */ LOWER_INTEROP("Interop lowering"), - /* ... ... */ LOWER_SHARED_VARIABLES("Shared Variable Lowering"), /* ... ... */ LOWER_ENUMS("Enum classes lowering"), /* ... ... */ LOWER_DELEGATION("Delegation lowering"), /* ... ... */ LOWER_INITIALIZERS("Initializers lowering", setOf(LOWER_ENUMS)), + /* ... ... */ LOWER_SHARED_VARIABLES("Shared Variable Lowering", setOf(LOWER_INITIALIZERS)), /* ... ... */ LOWER_CALLABLES("Callable references Lowering", setOf( LOWER_INTEROP, LOWER_INITIALIZERS, LOWER_DELEGATION)), /* ... ... */ LOWER_VARARG("Vararg lowering", setOf(LOWER_CALLABLES)), - /* ... ... */ LOWER_LOCAL_FUNCTIONS("Local Function Lowering", setOf(LOWER_INITIALIZERS)), + /* ... ... */ LOWER_LOCAL_FUNCTIONS("Local Function Lowering", setOf(LOWER_SHARED_VARIABLES)), /* ... ... */ LOWER_TAILREC("tailrec lowering", setOf(LOWER_LOCAL_FUNCTIONS)), /* ... ... */ LOWER_DEFAULT_PARAMETER_EXTENT("Default Parameter Extent Lowering", setOf( LOWER_TAILREC, LOWER_ENUMS)), diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt index 0b20959026c..bc6a0801a5f 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt @@ -126,11 +126,13 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass { declaration.transformChildrenVoid(this) val blockBody = declaration.body as? IrBlockBody ?: throw AssertionError("Unexpected constructor body: ${declaration.body}") - blockBody.statements.transformFlat { - if (it is IrEnumConstructorCall) - listOf(it, IrInstanceInitializerCallImpl(declaration.startOffset, declaration.startOffset, - irClass.descriptor)) - else null + if (blockBody.statements.all { it !is IrInstanceInitializerCall }) { + blockBody.statements.transformFlat { + if (it is IrEnumConstructorCall) + listOf(it, IrInstanceInitializerCallImpl(declaration.startOffset, declaration.startOffset, + irClass.descriptor)) + else null + } } return declaration } @@ -173,6 +175,7 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass { val defaultClass = IrClassImpl(startOffset, endOffset, IrDeclarationOrigin.DEFINED, defaultClassDescriptor) val constructors = mutableSetOf() + var primaryConstructor: ClassConstructorDescriptor? = null descriptor.constructors.forEach { val loweredEnumConstructor = loweredEnumConstructors[it]!! @@ -181,6 +184,8 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass { constructors.add(constructorDescriptor) defaultClass.declarations.add(constructor) defaultEnumEntryConstructors.put(loweredEnumConstructor, constructorDescriptor) + if (loweredEnumConstructor.isPrimary) + primaryConstructor = constructorDescriptor val irConstructor = descriptorToIrConstructorWithDefaultArguments[loweredEnumConstructor] if (irConstructor != null) { @@ -194,7 +199,7 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass { } val memberScope = SimpleMemberScope(irClass.descriptor.unsubstitutedMemberScope.getContributedDescriptors().toList()) - defaultClassDescriptor.initialize(memberScope, constructors, null) + defaultClassDescriptor.initialize(memberScope, constructors, primaryConstructor) return defaultClass } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InitializersLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InitializersLowering.kt index c2330945e95..c95da0b2c9d 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InitializersLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InitializersLowering.kt @@ -2,18 +2,22 @@ package org.jetbrains.kotlin.backend.konan.lower import org.jetbrains.kotlin.backend.common.ClassLoweringPass import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.expressions.IrBlockBody import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall 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.IrGetValueImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl +import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.util.transformFlat import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.js.descriptorUtils.hasPrimaryConstructor internal class InitializersLowering(val context: Context) : ClassLoweringPass { override fun lower(irClass: IrClass) { @@ -25,12 +29,16 @@ internal class InitializersLowering(val context: Context) : ClassLoweringPass { fun lowerInitializers() { collectAndRemoveInitializers() - lowerConstructors() + val initializerMethodDescriptor = createInitializerMethod() + lowerConstructors(initializerMethodDescriptor) } object STATEMENT_ORIGIN_ANONYMOUS_INITIALIZER : IrStatementOriginImpl("ANONYMOUS_INITIALIZER") + object DECLARATION_ORIGIN_ANONYMOUS_INITIALIZER : + IrDeclarationOriginImpl("ANONYMOUS_INITIALIZER") + private fun collectAndRemoveInitializers() { // Do with one traversal in order to preserve initializers order. irClass.transformChildrenVoid(object : IrElementTransformerVoid() { @@ -67,8 +75,35 @@ internal class InitializersLowering(val context: Context) : ClassLoweringPass { } } - private fun lowerConstructors() { + private fun createInitializerMethod(): FunctionDescriptor? { + if (irClass.descriptor.hasPrimaryConstructor()) + return null // Place initializers in the primary constructor. + val initializerMethodDescriptor = SimpleFunctionDescriptorImpl.create( + /* containingDeclaration = */ irClass.descriptor, + /* annotations = */ Annotations.EMPTY, + /* name = */ "INITIALIZER".synthesizedName, + /* kind = */ CallableMemberDescriptor.Kind.DECLARATION, + /* source = */ SourceElement.NO_SOURCE) + initializerMethodDescriptor.initialize( + /* receiverParameterType = */ null, + /* dispatchReceiverParameter = */ irClass.descriptor.thisAsReceiverParameter, + /* typeParameters = */ listOf(), + /* unsubstitutedValueParameters = */ listOf(), + /* returnType = */ context.builtIns.unitType, + /* modality = */ Modality.FINAL, + /* visibility = */ Visibilities.PRIVATE) + val startOffset = irClass.startOffset + val endOffset = irClass.endOffset + val initializer = IrFunctionImpl(startOffset, endOffset, DECLARATION_ORIGIN_ANONYMOUS_INITIALIZER, + initializerMethodDescriptor, IrBlockBodyImpl(startOffset, endOffset, initializers)) + irClass.declarations.add(initializer) + + return initializerMethodDescriptor + } + + private fun lowerConstructors(initializerMethodDescriptor: FunctionDescriptor?) { irClass.transformChildrenVoid(object : IrElementTransformerVoid() { + override fun visitClass(declaration: IrClass): IrStatement { // Skip nested. return declaration @@ -79,15 +114,26 @@ internal class InitializersLowering(val context: Context) : ClassLoweringPass { blockBody.statements.transformFlat { when { - it is IrInstanceInitializerCall -> initializers - /** - * IR for kotlin.Any is: - * BLOCK_BODY - * DELEGATING_CONSTRUCTOR_CALL 'constructor Any()' - * INSTANCE_INITIALIZER_CALL classDescriptor='Any' - * - * to avoid possible recursion we manually reject body generation for Any. - */ + it is IrInstanceInitializerCall -> { + if (initializerMethodDescriptor == null) { + assert(declaration.descriptor.isPrimary) + initializers + } else { + val startOffset = it.startOffset + val endOffset = it.endOffset + listOf(IrCallImpl(startOffset, endOffset, initializerMethodDescriptor).apply { + dispatchReceiver = IrGetValueImpl(startOffset, endOffset, irClass.descriptor.thisAsReceiverParameter) + }) + } + } + /** + * IR for kotlin.Any is: + * BLOCK_BODY + * DELEGATING_CONSTRUCTOR_CALL 'constructor Any()' + * INSTANCE_INITIALIZER_CALL classDescriptor='Any' + * + * to avoid possible recursion we manually reject body generation for Any. + */ it is IrDelegatingConstructorCall && irClass.descriptor == context.builtIns.any && it.descriptor == declaration.descriptor -> listOf() else -> null