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:
+9
@@ -3,7 +3,9 @@ package org.jetbrains.kotlin.backend.common.lower
|
|||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
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.IrValueAccessExpression
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrSetterCallImpl
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||||
@@ -82,6 +84,13 @@ abstract class AbstractClosureAnnotator : IrElementVisitorVoid {
|
|||||||
closuresStack.peek()?.addNested(closure)
|
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) {
|
override fun visitFunction(declaration: IrFunction) {
|
||||||
val functionDescriptor = declaration.descriptor
|
val functionDescriptor = declaration.descriptor
|
||||||
val closureBuilder = FunctionClosureBuilder(functionDescriptor)
|
val closureBuilder = FunctionClosureBuilder(functionDescriptor)
|
||||||
|
|||||||
+24
-11
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.backend.common.lower
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.BackendContext
|
import org.jetbrains.kotlin.backend.common.BackendContext
|
||||||
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
|
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.*
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
|
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 :
|
private object DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE :
|
||||||
IrDeclarationOriginImpl("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) {
|
private fun rewriteClassMembers(irClass: IrClass, localClassContext: LocalClassContext) {
|
||||||
irClass.transformChildrenVoid(FunctionBodiesRewriter(localClassContext))
|
irClass.transformChildrenVoid(FunctionBodiesRewriter(localClassContext))
|
||||||
|
|
||||||
@@ -334,23 +338,25 @@ class LocalDeclarationsLowering(val context: BackendContext) : DeclarationContai
|
|||||||
TODO("local classes without primary constructor")
|
TODO("local classes without primary constructor")
|
||||||
|
|
||||||
val primaryConstructorContext = localClassConstructors[primaryConstructor]!!
|
val primaryConstructorContext = localClassConstructors[primaryConstructor]!!
|
||||||
|
val primaryConstructorBody = primaryConstructorContext.declaration.body as? IrBlockBody
|
||||||
|
?: throw AssertionError("Unexpected constructor body: ${primaryConstructorContext.declaration.body}")
|
||||||
|
|
||||||
localClassContext.capturedValueToField.forEach { capturedValue, fieldDescriptor ->
|
localClassContext.capturedValueToField.forEach { capturedValue, fieldDescriptor ->
|
||||||
|
val startOffset = irClass.startOffset
|
||||||
val capturedValueExpression =
|
val endOffset = irClass.endOffset
|
||||||
primaryConstructorContext.irGet(irClass.startOffset, irClass.endOffset, capturedValue)!!
|
|
||||||
|
|
||||||
irClass.declarations.add(
|
irClass.declarations.add(
|
||||||
IrFieldImpl(
|
IrFieldImpl(
|
||||||
irClass.startOffset, irClass.endOffset,
|
startOffset, endOffset,
|
||||||
DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE,
|
DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE,
|
||||||
fieldDescriptor,
|
fieldDescriptor
|
||||||
IrExpressionBodyImpl(
|
|
||||||
irClass.startOffset, irClass.endOffset,
|
|
||||||
capturedValueExpression
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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 =
|
private fun suggestNameForCapturedValue(valueDescriptor: ValueDescriptor): Name =
|
||||||
if (valueDescriptor.name.isSpecial) {
|
if (valueDescriptor.name.isSpecial) {
|
||||||
val oldNameStr = valueDescriptor.name.asString()
|
val oldNameStr = valueDescriptor.name.asString()
|
||||||
Name.identifier("$" + oldNameStr.substring(1, oldNameStr.length - 1))
|
oldNameStr.substring(1, oldNameStr.length - 1).synthesizedName
|
||||||
} else
|
} else
|
||||||
valueDescriptor.name
|
valueDescriptor.name
|
||||||
|
|
||||||
@@ -639,6 +645,13 @@ class LocalDeclarationsLowering(val context: BackendContext) : DeclarationContai
|
|||||||
element.acceptChildrenVoid(this)
|
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) {
|
private fun DeclarationDescriptor.declaredInFunction() = when (this.containingDeclaration) {
|
||||||
is CallableDescriptor -> true
|
is CallableDescriptor -> true
|
||||||
is ClassDescriptor -> false
|
is ClassDescriptor -> false
|
||||||
|
|||||||
+12
-9
@@ -22,7 +22,6 @@ internal class InitializersLowering(val context: Context) : ClassLoweringPass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private inner class InitializersTransformer(val irClass: IrClass) {
|
private inner class InitializersTransformer(val irClass: IrClass) {
|
||||||
val fieldInitializers = mutableListOf<IrStatement>()
|
|
||||||
val initializers = mutableListOf<IrStatement>()
|
val initializers = mutableListOf<IrStatement>()
|
||||||
|
|
||||||
fun lowerInitializers() {
|
fun lowerInitializers() {
|
||||||
@@ -34,34 +33,38 @@ internal class InitializersLowering(val context: Context) : ClassLoweringPass {
|
|||||||
IrStatementOriginImpl("ANONYMOUS_INITIALIZER")
|
IrStatementOriginImpl("ANONYMOUS_INITIALIZER")
|
||||||
|
|
||||||
private fun collectAndRemoveInitializers() {
|
private fun collectAndRemoveInitializers() {
|
||||||
|
// Do with one traversal in order to preserve initializers order.
|
||||||
irClass.transformChildrenVoid(object : IrElementTransformerVoid() {
|
irClass.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||||
override fun visitClass(declaration: IrClass): IrStatement {
|
override fun visitClass(declaration: IrClass): IrStatement {
|
||||||
// Skip nested.
|
// Skip nested.
|
||||||
return declaration
|
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 {
|
override fun visitField(declaration: IrField): IrStatement {
|
||||||
val initializer = declaration.initializer ?: return declaration
|
val initializer = declaration.initializer ?: return declaration
|
||||||
val propertyDescriptor = declaration.descriptor
|
val propertyDescriptor = declaration.descriptor
|
||||||
val startOffset = initializer.startOffset
|
val startOffset = initializer.startOffset
|
||||||
val endOffset = initializer.endOffset
|
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(
|
listOf(
|
||||||
IrSetFieldImpl(startOffset, endOffset, propertyDescriptor,
|
IrSetFieldImpl(startOffset, endOffset, propertyDescriptor,
|
||||||
IrGetValueImpl(startOffset, endOffset, irClass.descriptor.thisAsReceiverParameter),
|
IrGetValueImpl(startOffset, endOffset, irClass.descriptor.thisAsReceiverParameter),
|
||||||
initializer.expression, STATEMENT_ORIGIN_ANONYMOUS_INITIALIZER))))
|
initializer.expression, STATEMENT_ORIGIN_ANONYMOUS_INITIALIZER))))
|
||||||
return IrFieldImpl(declaration.startOffset, declaration.endOffset, declaration.origin, propertyDescriptor)
|
declaration.initializer = null
|
||||||
|
return declaration
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
irClass.declarations.transformFlat {
|
irClass.declarations.transformFlat {
|
||||||
if (it !is IrAnonymousInitializer)
|
if (it !is IrAnonymousInitializer)
|
||||||
null
|
null
|
||||||
else {
|
else listOf()
|
||||||
initializers.add(IrBlockImpl(it.startOffset, it.endOffset,
|
|
||||||
context.builtIns.unitType, STATEMENT_ORIGIN_ANONYMOUS_INITIALIZER, it.body.statements))
|
|
||||||
listOf()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +80,7 @@ internal class InitializersLowering(val context: Context) : ClassLoweringPass {
|
|||||||
|
|
||||||
blockBody.statements.transformFlat {
|
blockBody.statements.transformFlat {
|
||||||
when {
|
when {
|
||||||
it is IrInstanceInitializerCall -> fieldInitializers + initializers
|
it is IrInstanceInitializerCall -> initializers
|
||||||
/**
|
/**
|
||||||
* IR for kotlin.Any is:
|
* IR for kotlin.Any is:
|
||||||
* BLOCK_BODY
|
* BLOCK_BODY
|
||||||
|
|||||||
@@ -404,6 +404,21 @@ task localClass_innerTakesCapturedFromOuter(type: RunKonanTest) {
|
|||||||
source = "codegen/localClass/innerTakesCapturedFromOuter.kt"
|
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) {
|
task array0(type: RunKonanTest) {
|
||||||
goldValue = "5\n6\n7\n8\n9\n10\n11\n12\n13\n"
|
goldValue = "5\n6\n7\n8\n9\n10\n11\n12\n13\n"
|
||||||
source = "runtime/collections/array0.kt"
|
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()
|
return u()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
|
||||||
foo()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
return foo()
|
return foo()
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user