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 3c0634e4b92..89d23497c68 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 @@ -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) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FinallyBlocksLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FinallyBlocksLowering.kt index a9d618af710..fb6f6fde5cd 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FinallyBlocksLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FinallyBlocksLowering.kt @@ -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() - .toList() - if (tryScopes.isEmpty()) - return null - return performHighLevelJump(tryScopes, 0, jump, startOffset, endOffset, value) - } - - private fun performHighLevelJump(tryScopes: List, - 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 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() + .toList() + if (tryScopes.isEmpty()) + return null + return performHighLevelJump(tryScopes, 0, jump, startOffset, endOffset, value) + } + + private fun performHighLevelJump(tryScopes: List, + 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 T.copy() = this.deepCopyWithVariables() + fun IrBuilderWithScope.irReturn(target: IrFunctionSymbol, value: IrExpression) = IrReturnImpl(startOffset, endOffset, target, value)