Fixed bug: finally blocks are not always inside IrFunction

For instance: package level field initializers.
This commit is contained in:
Igor Chevdar
2017-05-24 17:08:37 +03:00
parent 48f5962645
commit 7eee76f0f5
2 changed files with 194 additions and 198 deletions
@@ -82,7 +82,7 @@ internal class KonanLower(val context: Context) {
TailrecLowering(context).runOnFilePostfix(irFile)
}
phaser.phase(KonanPhase.LOWER_FINALLY) {
FinallyBlocksLowering(context).runOnFilePostfix(irFile)
FinallyBlocksLowering(context).lower(irFile)
}
phaser.phase(KonanPhase.LOWER_DEFAULT_PARAMETER_EXTENT) {
DefaultArgumentStubGenerator(context).runOnFilePostfix(irFile)
@@ -1,6 +1,5 @@
package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.FunctionLoweringPass
import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.konan.Context
@@ -12,22 +11,23 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isUnit
internal class FinallyBlocksLowering(val context: Context): FunctionLoweringPass {
internal class FinallyBlocksLowering(val context: Context): FileLoweringPass, IrElementTransformerVoidWithContext() {
private interface HighLevelJump {
fun toIr(context: Context, startOffset: Int, endOffset: Int, value: IrExpression): IrExpression
@@ -79,204 +79,200 @@ internal class FinallyBlocksLowering(val context: Context): FunctionLoweringPass
}
}
override fun lower(irFunction: IrFunction) {
val functionDescriptor = irFunction.descriptor
using(ReturnableScope(functionDescriptor)) {
irFunction.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(this)
}
override fun visitContainerExpression(expression: IrContainerExpression): IrExpression {
if (expression !is IrReturnableBlockImpl)
return super.visitContainerExpression(expression)
using(ReturnableScope(expression.descriptor)) {
return super.visitContainerExpression(expression)
}
}
override fun visitLoop(loop: IrLoop): IrExpression {
using(LoopScope(loop)) {
return super.visitLoop(loop)
}
}
override fun visitBreak(jump: IrBreak): IrExpression {
val startOffset = jump.startOffset
val endOffset = jump.endOffset
val irBuilder = context.createIrBuilder(irFunction.symbol, startOffset, endOffset)
return performHighLevelJump(
targetScopePredicate = { it is LoopScope && it.loop == jump.loop },
jump = Break(jump.loop),
startOffset = startOffset,
endOffset = endOffset,
value = irBuilder.irGetObject(context.ir.symbols.unit)
) ?: jump
}
override fun visitContinue(jump: IrContinue): IrExpression {
val startOffset = jump.startOffset
val endOffset = jump.endOffset
val irBuilder = context.createIrBuilder(irFunction.symbol, startOffset, endOffset)
return performHighLevelJump(
targetScopePredicate = { it is LoopScope && it.loop == jump.loop },
jump = Continue(jump.loop),
startOffset = startOffset,
endOffset = endOffset,
value = irBuilder.irGetObject(context.ir.symbols.unit)
) ?: jump
}
override fun visitReturn(expression: IrReturn): IrExpression {
expression.transformChildrenVoid(this)
return performHighLevelJump(
targetScopePredicate = { it is ReturnableScope && it.descriptor == expression.returnTarget },
jump = Return(expression.returnTargetSymbol),
startOffset = expression.startOffset,
endOffset = expression.endOffset,
value = expression.value
) ?: expression
}
private fun performHighLevelJump(targetScopePredicate: (Scope) -> Boolean,
jump: HighLevelJump,
startOffset: Int,
endOffset: Int,
value: IrExpression): IrExpression? {
val tryScopes = scopeStack.reversed()
.takeWhile { !targetScopePredicate(it) }
.filterIsInstance<TryScope>()
.toList()
if (tryScopes.isEmpty())
return null
return performHighLevelJump(tryScopes, 0, jump, startOffset, endOffset, value)
}
private fun performHighLevelJump(tryScopes: List<TryScope>,
index: Int,
jump: HighLevelJump,
startOffset: Int,
endOffset: Int,
value: IrExpression): IrExpression {
if (index == tryScopes.size)
return jump.toIr(context, startOffset, endOffset, value)
val currentTryScope = tryScopes[index]
currentTryScope.jumps.getOrPut(jump) {
val symbol = getIrReturnableBlockSymbol(jump.toString(), value.type)
with(currentTryScope) {
irBuilder.run {
val inlinedFinally = irInlineFinally(symbol, expression, finallyExpression)
expression = performHighLevelJump(
tryScopes = tryScopes,
index = index + 1,
jump = jump,
startOffset = startOffset,
endOffset = endOffset,
value = inlinedFinally)
}
}
symbol
}.let {
return IrReturnImpl(
startOffset = startOffset,
endOffset = endOffset,
returnTargetSymbol = it,
value = value)
}
}
override fun visitTry(aTry: IrTry): IrExpression {
val finallyExpression = aTry.finallyExpression
if (finallyExpression == null)
return super.visitTry(aTry)
val startOffset = aTry.startOffset
val endOffset = aTry.endOffset
val irBuilder = context.createIrBuilder(irFunction.symbol, startOffset, endOffset)
val transformer = this
irBuilder.run {
val transformedTry = IrTryImpl(
startOffset = startOffset,
endOffset = endOffset,
type = context.builtIns.nothingType
)
val transformedFinallyExpression = finallyExpression.transform(transformer, null)
val parameter = IrTemporaryVariableDescriptorImpl(
containingDeclaration = irFunction.descriptor,
name = Name.identifier("t"),
outType = context.builtIns.throwable.defaultType
)
val catchParameter = IrVariableImpl(
startOffset, endOffset, IrDeclarationOrigin.CATCH_PARAMETER, parameter)
val syntheticTry = IrTryImpl(
startOffset = startOffset,
endOffset = endOffset,
type = context.builtIns.nothingType,
tryResult = transformedTry,
catches = listOf(
irCatch(catchParameter).apply {
result = irBlock {
+finallyExpression.copy()
+irThrow(irGet(catchParameter.symbol))
}
}),
finallyExpression = null
)
using(TryScope(syntheticTry, transformedFinallyExpression, this)) {
val fallThroughSymbol = getIrReturnableBlockSymbol("fallThrough", aTry.type)
val transformedResult = aTry.tryResult.transform(transformer, null)
transformedTry.tryResult = irReturn(fallThroughSymbol, transformedResult)
for (aCatch in aTry.catches) {
val transformedCatch = aCatch.transform(transformer, null)
transformedCatch.result = irReturn(fallThroughSymbol, transformedCatch.result)
transformedTry.catches.add(transformedCatch)
}
return irInlineFinally(fallThroughSymbol, it.expression, it.finallyExpression)
}
}
}
private fun IrBuilderWithScope.irInlineFinally(symbol: IrReturnableBlockSymbol,
value: IrExpression,
finallyExpression: IrExpression): IrExpression {
val returnType = symbol.descriptor.returnType!!
return when {
returnType.isUnit() || returnType.isNothing() -> irBlock(value, null, returnType) {
+irReturnableBlock(symbol) {
+value
}
+finallyExpression.copy()
}
else -> irBlock(value, null, returnType) {
val tmp = irTemporary(irReturnableBlock(symbol) {
+irReturn(symbol, value)
})
+finallyExpression.copy()
+irGet(tmp.symbol)
}
}
}
private fun getFakeFunctionDescriptor(name: String, returnType: KotlinType): FunctionDescriptor {
return SimpleFunctionDescriptorImpl.create(
functionDescriptor,
Annotations.EMPTY,
name.synthesizedName,
CallableMemberDescriptor.Kind.SYNTHESIZED,
SourceElement.NO_SOURCE).apply {
initialize(null, null, emptyList(), emptyList(), returnType, Modality.ABSTRACT, Visibilities.PRIVATE)
}
}
private fun getIrReturnableBlockSymbol(name: String, returnType: KotlinType): IrReturnableBlockSymbol =
IrReturnableBlockSymbolImpl(getFakeFunctionDescriptor(name, returnType))
private inline fun <reified T : IrElement> T.copy() = this.deepCopyWithVariables()
})
override fun visitFunctionNew(declaration: IrFunction): IrStatement {
using(ReturnableScope(declaration.descriptor)) {
return super.visitFunctionNew(declaration)
}
}
override fun visitContainerExpression(expression: IrContainerExpression): IrExpression {
if (expression !is IrReturnableBlockImpl)
return super.visitContainerExpression(expression)
using(ReturnableScope(expression.descriptor)) {
return super.visitContainerExpression(expression)
}
}
override fun visitLoop(loop: IrLoop): IrExpression {
using(LoopScope(loop)) {
return super.visitLoop(loop)
}
}
override fun visitBreak(jump: IrBreak): IrExpression {
val startOffset = jump.startOffset
val endOffset = jump.endOffset
val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, startOffset, endOffset)
return performHighLevelJump(
targetScopePredicate = { it is LoopScope && it.loop == jump.loop },
jump = Break(jump.loop),
startOffset = startOffset,
endOffset = endOffset,
value = irBuilder.irGetObject(context.ir.symbols.unit)
) ?: jump
}
override fun visitContinue(jump: IrContinue): IrExpression {
val startOffset = jump.startOffset
val endOffset = jump.endOffset
val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, startOffset, endOffset)
return performHighLevelJump(
targetScopePredicate = { it is LoopScope && it.loop == jump.loop },
jump = Continue(jump.loop),
startOffset = startOffset,
endOffset = endOffset,
value = irBuilder.irGetObject(context.ir.symbols.unit)
) ?: jump
}
override fun visitReturn(expression: IrReturn): IrExpression {
expression.transformChildrenVoid(this)
return performHighLevelJump(
targetScopePredicate = { it is ReturnableScope && it.descriptor == expression.returnTarget },
jump = Return(expression.returnTargetSymbol),
startOffset = expression.startOffset,
endOffset = expression.endOffset,
value = expression.value
) ?: expression
}
private fun performHighLevelJump(targetScopePredicate: (Scope) -> Boolean,
jump: HighLevelJump,
startOffset: Int,
endOffset: Int,
value: IrExpression): IrExpression? {
val tryScopes = scopeStack.reversed()
.takeWhile { !targetScopePredicate(it) }
.filterIsInstance<TryScope>()
.toList()
if (tryScopes.isEmpty())
return null
return performHighLevelJump(tryScopes, 0, jump, startOffset, endOffset, value)
}
private fun performHighLevelJump(tryScopes: List<TryScope>,
index: Int,
jump: HighLevelJump,
startOffset: Int,
endOffset: Int,
value: IrExpression): IrExpression {
if (index == tryScopes.size)
return jump.toIr(context, startOffset, endOffset, value)
val currentTryScope = tryScopes[index]
currentTryScope.jumps.getOrPut(jump) {
val symbol = getIrReturnableBlockSymbol(jump.toString(), value.type)
with(currentTryScope) {
irBuilder.run {
val inlinedFinally = irInlineFinally(symbol, expression, finallyExpression)
expression = performHighLevelJump(
tryScopes = tryScopes,
index = index + 1,
jump = jump,
startOffset = startOffset,
endOffset = endOffset,
value = inlinedFinally)
}
}
symbol
}.let {
return IrReturnImpl(
startOffset = startOffset,
endOffset = endOffset,
returnTargetSymbol = it,
value = value)
}
}
override fun visitTry(aTry: IrTry): IrExpression {
val finallyExpression = aTry.finallyExpression
?: return super.visitTry(aTry)
val startOffset = aTry.startOffset
val endOffset = aTry.endOffset
val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, startOffset, endOffset)
val transformer = this
irBuilder.run {
val transformedTry = IrTryImpl(
startOffset = startOffset,
endOffset = endOffset,
type = context.builtIns.nothingType
)
val transformedFinallyExpression = finallyExpression.transform(transformer, null)
val parameter = IrTemporaryVariableDescriptorImpl(
containingDeclaration = currentScope!!.scope.scopeOwner,
name = Name.identifier("t"),
outType = context.builtIns.throwable.defaultType
)
val catchParameter = IrVariableImpl(
startOffset, endOffset, IrDeclarationOrigin.CATCH_PARAMETER, parameter)
val syntheticTry = IrTryImpl(
startOffset = startOffset,
endOffset = endOffset,
type = context.builtIns.nothingType,
tryResult = transformedTry,
catches = listOf(
irCatch(catchParameter).apply {
result = irBlock {
+finallyExpression.copy()
+irThrow(irGet(catchParameter.symbol))
}
}),
finallyExpression = null
)
using(TryScope(syntheticTry, transformedFinallyExpression, this)) {
val fallThroughSymbol = getIrReturnableBlockSymbol("fallThrough", aTry.type)
val transformedResult = aTry.tryResult.transform(transformer, null)
transformedTry.tryResult = irReturn(fallThroughSymbol, transformedResult)
for (aCatch in aTry.catches) {
val transformedCatch = aCatch.transform(transformer, null)
transformedCatch.result = irReturn(fallThroughSymbol, transformedCatch.result)
transformedTry.catches.add(transformedCatch)
}
return irInlineFinally(fallThroughSymbol, it.expression, it.finallyExpression)
}
}
}
private fun IrBuilderWithScope.irInlineFinally(symbol: IrReturnableBlockSymbol,
value: IrExpression,
finallyExpression: IrExpression): IrExpression {
val returnType = symbol.descriptor.returnType!!
return when {
returnType.isUnit() || returnType.isNothing() -> irBlock(value, null, returnType) {
+irReturnableBlock(symbol) {
+value
}
+finallyExpression.copy()
}
else -> irBlock(value, null, returnType) {
val tmp = irTemporary(irReturnableBlock(symbol) {
+irReturn(symbol, value)
})
+finallyExpression.copy()
+irGet(tmp.symbol)
}
}
}
private fun getFakeFunctionDescriptor(name: String, returnType: KotlinType) =
SimpleFunctionDescriptorImpl.create(currentScope!!.scope.scopeOwner, Annotations.EMPTY, name.synthesizedName,
CallableMemberDescriptor.Kind.SYNTHESIZED, SourceElement.NO_SOURCE).apply {
initialize(null, null, emptyList(), emptyList(), returnType, Modality.ABSTRACT, Visibilities.PRIVATE)
}
private fun getIrReturnableBlockSymbol(name: String, returnType: KotlinType): IrReturnableBlockSymbol =
IrReturnableBlockSymbolImpl(getFakeFunctionDescriptor(name, returnType))
private inline fun <reified T : IrElement> T.copy() = this.deepCopyWithVariables()
fun IrBuilderWithScope.irReturn(target: IrFunctionSymbol, value: IrExpression) =
IrReturnImpl(startOffset, endOffset, target, value)