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
This commit is contained in:
Igor Chevdar
2017-02-08 14:52:26 +05:00
committed by GitHub
parent 7b0c0f5bfb
commit a856e16d2f
8 changed files with 111 additions and 24 deletions
@@ -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)
@@ -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
@@ -22,7 +22,6 @@ internal class InitializersLowering(val context: Context) : ClassLoweringPass {
}
private inner class InitializersTransformer(val irClass: IrClass) {
val fieldInitializers = mutableListOf<IrStatement>()
val initializers = mutableListOf<IrStatement>()
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
+15
View File
@@ -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"
@@ -0,0 +1,13 @@
class Test {
val x: Int
init {
x = 42
}
val y = x
}
fun main(args: Array<String>) {
println(Test().y)
}
@@ -0,0 +1,13 @@
class Test {
val x: Int
val y = 42
init {
x = y
}
}
fun main(args: Array<String>) {
println(Test().x)
}
@@ -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<String>) {
println(box())
}
@@ -6,10 +6,6 @@ fun foo() : String {
return u()
}
fun main(args: Array<String>) {
foo()
}
fun box(): String {
return foo()
}