From a856e16d2f7759ac61e345f7b9ac00fe48f4248d Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Wed, 8 Feb 2017 14:52:26 +0500 Subject: [PATCH] Local declarations and initializers fixes (#222) * captured values are stored in corresponding fields before call to super constructor * preserving the order of initializers * tests * refactoring * - test fix - workaround of IR bug * refactoring * review fix --- .../common/lower/AbstractClosureAnnotator.kt | 9 +++++ .../common/lower/LocalDeclarationsLowering.kt | 35 +++++++++++++------ .../konan/lower/InitializersLowering.kt | 21 ++++++----- backend.native/tests/build.gradle | 15 ++++++++ .../codegen/initializers/correctOrder1.kt | 13 +++++++ .../codegen/initializers/correctOrder2.kt | 13 +++++++ .../localClass/virtualCallFromConstructor.kt | 25 +++++++++++++ .../codegen/blackbox/localClasses/kt2873.kt | 4 --- 8 files changed, 111 insertions(+), 24 deletions(-) create mode 100644 backend.native/tests/codegen/initializers/correctOrder1.kt create mode 100644 backend.native/tests/codegen/initializers/correctOrder2.kt create mode 100644 backend.native/tests/codegen/localClass/virtualCallFromConstructor.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/AbstractClosureAnnotator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/AbstractClosureAnnotator.kt index 251b1746b4d..50e0194e055 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/AbstractClosureAnnotator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/AbstractClosureAnnotator.kt @@ -3,7 +3,9 @@ package org.jetbrains.kotlin.backend.common.lower import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrValueAccessExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrSetterCallImpl import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid @@ -82,6 +84,13 @@ abstract class AbstractClosureAnnotator : IrElementVisitorVoid { closuresStack.peek()?.addNested(closure) } + // TODO: remove as soon as bug in IrSetterCallImpl is fixed. + override fun visitCall(expression: IrCall) { + super.visitCall(expression) + if (expression is IrSetterCallImpl) + visitElement(expression.getValueArgument(0)!!) + } + override fun visitFunction(declaration: IrFunction) { val functionDescriptor = declaration.descriptor val closureBuilder = FunctionClosureBuilder(functionDescriptor) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt index a089e6b5fc2..896c309263c 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.backend.common.lower import org.jetbrains.kotlin.backend.common.BackendContext import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass +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.ClassConstructorDescriptorImpl @@ -327,6 +328,9 @@ class LocalDeclarationsLowering(val context: BackendContext) : DeclarationContai private object DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE : IrDeclarationOriginImpl("FIELD_FOR_CAPTURED_VALUE") {} + private object STATEMENT_ORIGIN_INITIALIZER_OF_FIELD_FOR_CAPTURED_VALUE : + IrStatementOriginImpl("INITIALIZER_OF_FIELD_FOR_CAPTURED_VALUE") {} + private fun rewriteClassMembers(irClass: IrClass, localClassContext: LocalClassContext) { irClass.transformChildrenVoid(FunctionBodiesRewriter(localClassContext)) @@ -334,23 +338,25 @@ class LocalDeclarationsLowering(val context: BackendContext) : DeclarationContai TODO("local classes without primary constructor") val primaryConstructorContext = localClassConstructors[primaryConstructor]!! + val primaryConstructorBody = primaryConstructorContext.declaration.body as? IrBlockBody + ?: throw AssertionError("Unexpected constructor body: ${primaryConstructorContext.declaration.body}") localClassContext.capturedValueToField.forEach { capturedValue, fieldDescriptor -> - - val capturedValueExpression = - primaryConstructorContext.irGet(irClass.startOffset, irClass.endOffset, capturedValue)!! - + val startOffset = irClass.startOffset + val endOffset = irClass.endOffset irClass.declarations.add( IrFieldImpl( - irClass.startOffset, irClass.endOffset, + startOffset, endOffset, DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE, - fieldDescriptor, - IrExpressionBodyImpl( - irClass.startOffset, irClass.endOffset, - capturedValueExpression - ) + fieldDescriptor ) ) + + val capturedValueExpression = primaryConstructorContext.irGet(startOffset, endOffset, capturedValue)!! + val capturedValueInitializer = IrSetFieldImpl(startOffset, endOffset, fieldDescriptor, + IrGetValueImpl(startOffset, endOffset, irClass.descriptor.thisAsReceiverParameter), + capturedValueExpression, STATEMENT_ORIGIN_INITIALIZER_OF_FIELD_FOR_CAPTURED_VALUE) + primaryConstructorBody.statements.add(0, capturedValueInitializer) } } @@ -595,7 +601,7 @@ class LocalDeclarationsLowering(val context: BackendContext) : DeclarationContai private fun suggestNameForCapturedValue(valueDescriptor: ValueDescriptor): Name = if (valueDescriptor.name.isSpecial) { val oldNameStr = valueDescriptor.name.asString() - Name.identifier("$" + oldNameStr.substring(1, oldNameStr.length - 1)) + oldNameStr.substring(1, oldNameStr.length - 1).synthesizedName } else valueDescriptor.name @@ -639,6 +645,13 @@ class LocalDeclarationsLowering(val context: BackendContext) : DeclarationContai element.acceptChildrenVoid(this) } + // TODO: remove as soon as bug in IrSetterCallImpl is fixed. + override fun visitCall(expression: IrCall) { + super.visitCall(expression) + if (expression is IrSetterCallImpl) + visitElement(expression.getValueArgument(0)!!) + } + private fun DeclarationDescriptor.declaredInFunction() = when (this.containingDeclaration) { is CallableDescriptor -> true is ClassDescriptor -> false 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 9abe0566dbc..246008e19b4 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 @@ -22,7 +22,6 @@ internal class InitializersLowering(val context: Context) : ClassLoweringPass { } private inner class InitializersTransformer(val irClass: IrClass) { - val fieldInitializers = mutableListOf() val initializers = mutableListOf() fun lowerInitializers() { @@ -34,34 +33,38 @@ internal class InitializersLowering(val context: Context) : ClassLoweringPass { IrStatementOriginImpl("ANONYMOUS_INITIALIZER") private fun collectAndRemoveInitializers() { + // Do with one traversal in order to preserve initializers order. irClass.transformChildrenVoid(object : IrElementTransformerVoid() { override fun visitClass(declaration: IrClass): IrStatement { // Skip nested. return declaration } + override fun visitAnonymousInitializer(declaration: IrAnonymousInitializer): IrStatement { + initializers.add(IrBlockImpl(declaration.startOffset, declaration.endOffset, + context.builtIns.unitType, STATEMENT_ORIGIN_ANONYMOUS_INITIALIZER, declaration.body.statements)) + return declaration + } + override fun visitField(declaration: IrField): IrStatement { val initializer = declaration.initializer ?: return declaration val propertyDescriptor = declaration.descriptor val startOffset = initializer.startOffset val endOffset = initializer.endOffset - fieldInitializers.add(IrBlockImpl(startOffset, endOffset, context.builtIns.unitType, STATEMENT_ORIGIN_ANONYMOUS_INITIALIZER, + initializers.add(IrBlockImpl(startOffset, endOffset, context.builtIns.unitType, STATEMENT_ORIGIN_ANONYMOUS_INITIALIZER, listOf( IrSetFieldImpl(startOffset, endOffset, propertyDescriptor, IrGetValueImpl(startOffset, endOffset, irClass.descriptor.thisAsReceiverParameter), initializer.expression, STATEMENT_ORIGIN_ANONYMOUS_INITIALIZER)))) - return IrFieldImpl(declaration.startOffset, declaration.endOffset, declaration.origin, propertyDescriptor) + declaration.initializer = null + return declaration } }) irClass.declarations.transformFlat { if (it !is IrAnonymousInitializer) null - else { - initializers.add(IrBlockImpl(it.startOffset, it.endOffset, - context.builtIns.unitType, STATEMENT_ORIGIN_ANONYMOUS_INITIALIZER, it.body.statements)) - listOf() - } + else listOf() } } @@ -77,7 +80,7 @@ internal class InitializersLowering(val context: Context) : ClassLoweringPass { blockBody.statements.transformFlat { when { - it is IrInstanceInitializerCall -> fieldInitializers + initializers + it is IrInstanceInitializerCall -> initializers /** * IR for kotlin.Any is: * BLOCK_BODY diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 37641c2cfb5..22333114e07 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -404,6 +404,21 @@ task localClass_innerTakesCapturedFromOuter(type: RunKonanTest) { source = "codegen/localClass/innerTakesCapturedFromOuter.kt" } +task localClass_virtualCallFromConstructor(type: RunKonanTest) { + goldValue = "OK\n" + source = "codegen/localClass/virtualCallFromConstructor.kt" +} + +task initializers_correctOrder1(type: RunKonanTest) { + goldValue = "42\n" + source = "codegen/initializers/correctOrder1.kt" +} + +task initializers_correctOrder2(type: RunKonanTest) { + goldValue = "42\n" + source = "codegen/initializers/correctOrder2.kt" +} + task array0(type: RunKonanTest) { goldValue = "5\n6\n7\n8\n9\n10\n11\n12\n13\n" source = "runtime/collections/array0.kt" diff --git a/backend.native/tests/codegen/initializers/correctOrder1.kt b/backend.native/tests/codegen/initializers/correctOrder1.kt new file mode 100644 index 00000000000..fd7645f3d05 --- /dev/null +++ b/backend.native/tests/codegen/initializers/correctOrder1.kt @@ -0,0 +1,13 @@ +class Test { + val x: Int + + init { + x = 42 + } + + val y = x +} + +fun main(args: Array) { + println(Test().y) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/initializers/correctOrder2.kt b/backend.native/tests/codegen/initializers/correctOrder2.kt new file mode 100644 index 00000000000..ddf63ade9a0 --- /dev/null +++ b/backend.native/tests/codegen/initializers/correctOrder2.kt @@ -0,0 +1,13 @@ +class Test { + val x: Int + + val y = 42 + + init { + x = y + } +} + +fun main(args: Array) { + println(Test().x) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/localClass/virtualCallFromConstructor.kt b/backend.native/tests/codegen/localClass/virtualCallFromConstructor.kt new file mode 100644 index 00000000000..230b5ebe0f3 --- /dev/null +++ b/backend.native/tests/codegen/localClass/virtualCallFromConstructor.kt @@ -0,0 +1,25 @@ +abstract class WaitFor { + init { + condition() + } + + abstract fun condition(): Boolean; +} + +fun box(): String { + val local = "" + var result = "fail" + val s = object : WaitFor() { + + override fun condition(): Boolean { + result = "OK" + return result.length == 2 + } + } + + return result; +} + +fun main(args: Array) { + println(box()) +} \ No newline at end of file diff --git a/backend.native/tests/external/codegen/blackbox/localClasses/kt2873.kt b/backend.native/tests/external/codegen/blackbox/localClasses/kt2873.kt index 6bb8dafadfd..ffc8ef112ed 100644 --- a/backend.native/tests/external/codegen/blackbox/localClasses/kt2873.kt +++ b/backend.native/tests/external/codegen/blackbox/localClasses/kt2873.kt @@ -6,10 +6,6 @@ fun foo() : String { return u() } -fun main(args: Array) { - foo() -} - fun box(): String { return foo() } \ No newline at end of file