From cc169613c164177ed7b56216bc9a960bf8514e15 Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Mon, 24 May 2021 21:03:05 +0300 Subject: [PATCH] Drop result of finally block after interpretation This change is needed due to using FIR as frontend for tests. In this case finally block doesn't has coercion to unit so it's result must be dropped manually. --- ...IrInterpreterAfterFir2IrTestGenerated.java | 6 ++++ .../ir/interpreter/InstructionsUnfolder.kt | 21 +++++++------ .../kotlin/ir/interpreter/IrInterpreter.kt | 18 ++++++++--- .../kotlin/ir/interpreter/stack/CallStack.kt | 5 ++-- .../ir/interpreter/exceptions/tryFinally.kt | 30 ++++++++++++------- .../testData/ir/interpreter/unitResult.kt | 12 ++++++++ 6 files changed, 67 insertions(+), 25 deletions(-) create mode 100644 compiler/testData/ir/interpreter/unitResult.kt diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/ir/interpreter/IrInterpreterAfterFir2IrTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/ir/interpreter/IrInterpreterAfterFir2IrTestGenerated.java index f77a83af90b..4db5ab448d6 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/ir/interpreter/IrInterpreterAfterFir2IrTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/ir/interpreter/IrInterpreterAfterFir2IrTestGenerated.java @@ -331,6 +331,12 @@ public class IrInterpreterAfterFir2IrTestGenerated extends AbstractIrInterpreter runTest("compiler/testData/ir/interpreter/superClass.kt"); } + @Test + @TestMetadata("unitResult.kt") + public void testUnitResult() throws Exception { + runTest("compiler/testData/ir/interpreter/unitResult.kt"); + } + @Test @TestMetadata("vararg.kt") public void testVararg() throws Exception { diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt index 6a2147e5d3a..e2b151dcb3e 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt @@ -17,9 +17,17 @@ import org.jetbrains.kotlin.ir.interpreter.stack.CallStack import org.jetbrains.kotlin.ir.interpreter.stack.Variable import org.jetbrains.kotlin.ir.interpreter.state.* import org.jetbrains.kotlin.ir.types.classOrNull -import org.jetbrains.kotlin.ir.types.classifierOrNull +import org.jetbrains.kotlin.ir.types.isUnit import org.jetbrains.kotlin.ir.util.* +internal fun IrExpression.handleAndDropResult(callStack: CallStack, dropOnlyUnit: Boolean = false) { + val dropResult = fun() { + if (!dropOnlyUnit && !this.type.isUnit() || callStack.peekState().isUnit()) callStack.popState() + } + callStack.addInstruction(CustomInstruction(dropResult)) + callStack.addInstruction(CompoundInstruction(this)) +} + internal fun unfoldInstruction(element: IrElement?, environment: IrInterpreterEnvironment) { val callStack = environment.callStack when (element) { @@ -178,6 +186,7 @@ private fun unfoldField(field: IrField, callStack: CallStack) { } private fun unfoldBody(body: IrBody, callStack: CallStack) { + callStack.addInstruction(SimpleInstruction(body)) unfoldStatements(body.statements, callStack) } @@ -188,18 +197,12 @@ private fun unfoldBlock(block: IrBlock, callStack: CallStack) { } private fun unfoldStatements(statements: List, callStack: CallStack) { - fun dropUnitResult() { - if (callStack.peekState().isUnit()) callStack.popState() - } - for (i in statements.indices.reversed()) { when (val statement = statements[i]) { is IrClass -> if (!statement.isLocal) TODO("Only local classes are supported") is IrFunction -> if (!statement.isLocal) TODO("Only local functions are supported") - else -> { - callStack.addInstruction(CustomInstruction(::dropUnitResult)) - callStack.addInstruction(CompoundInstruction(statement)) - } + is IrExpression -> statement.handleAndDropResult(callStack, dropOnlyUnit = true) + else -> callStack.addInstruction(CompoundInstruction(statement)) } } } diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt index a72ebb6e98a..99380064fa1 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt @@ -90,7 +90,6 @@ class IrInterpreter private constructor( incrementAndCheckCommands() } - if (callStack.peekState() == null) callStack.pushState(getUnitState()) callStack.popState().apply { callStack.dropFrame() } } } @@ -106,7 +105,8 @@ class IrInterpreter private constructor( is IrValueParameter -> interpretValueParameter(element) is IrInstanceInitializerCall -> interpretInstanceInitializerCall(element) is IrField -> interpretField(element) - is IrBlock -> callStack.dropSubFrame() + is IrBody -> interpretBody(element) + is IrBlock -> interpretBlock(element) is IrReturn -> interpretReturn(element) is IrSetField -> interpretSetField(element) is IrGetField -> interpretGetField(element) @@ -140,7 +140,6 @@ class IrInterpreter private constructor( private fun interpretFunction(function: IrSimpleFunction) { function.tryResetFunctionBody() - if (callStack.peekState() == null) callStack.pushState(getUnitState()) // implicit Unit return if (function.checkCast(environment)) callStack.dropFrameAndCopyResult() } @@ -237,6 +236,17 @@ class IrInterpreter private constructor( receiverState.setField(Variable(field.correspondingPropertySymbol!!, callStack.popState())) } + @Suppress("UNUSED_PARAMETER") + private fun interpretBody(body: IrBody) { + if (callStack.peekState() == null) callStack.pushState(getUnitState()) // implicit Unit result + } + + @Suppress("UNUSED_PARAMETER") + private fun interpretBlock(block: IrBlock) { + callStack.dropSubFrame() + if (callStack.peekState() == null) callStack.pushState(getUnitState()) // implicit Unit result + } + @Suppress("UNUSED_PARAMETER") private fun interpretConstructor(constructor: IrConstructor) { callStack.dropFrameAndCopyResult() @@ -533,7 +543,7 @@ class IrInterpreter private constructor( } callStack.addInstruction(CustomInstruction(checkUnhandledException)) } - callStack.addInstruction(CompoundInstruction(element.finallyExpression)) + element.finallyExpression?.handleAndDropResult(callStack) } private fun interpretThrow(expression: IrThrow) { diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/stack/CallStack.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/stack/CallStack.kt index 66f197d785f..d0f03115039 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/stack/CallStack.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/stack/CallStack.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.interpreter.CompoundInstruction import org.jetbrains.kotlin.ir.interpreter.Instruction import org.jetbrains.kotlin.ir.interpreter.SimpleInstruction +import org.jetbrains.kotlin.ir.interpreter.handleAndDropResult import org.jetbrains.kotlin.ir.interpreter.state.State import org.jetbrains.kotlin.ir.interpreter.state.StateWithClosure import org.jetbrains.kotlin.ir.symbols.IrSymbol @@ -61,7 +62,7 @@ internal class CallStack { dropSubFrame() pushState(result) addInstruction(SimpleInstruction(irReturn)) - addInstruction(CompoundInstruction(frameOwner.finallyExpression)) + frameOwner.finallyExpression?.handleAndDropResult(this) return } is IrCatch -> { @@ -69,7 +70,7 @@ internal class CallStack { dropSubFrame() pushState(result) addInstruction(SimpleInstruction(irReturn)) - addInstruction(CompoundInstruction(tryBlock.finallyExpression)) + tryBlock.finallyExpression?.handleAndDropResult(this) return } else -> { diff --git a/compiler/testData/ir/interpreter/exceptions/tryFinally.kt b/compiler/testData/ir/interpreter/exceptions/tryFinally.kt index d5ebadb753d..fbfd55b1340 100644 --- a/compiler/testData/ir/interpreter/exceptions/tryFinally.kt +++ b/compiler/testData/ir/interpreter/exceptions/tryFinally.kt @@ -29,6 +29,15 @@ fun outerReturnTryFinally(n: Int): Int { } } +@CompileTimeCalculation +fun outerReturnUnitTryFinally(n: Int): Int { + return try { + n + } finally { + Unit + } +} + @CompileTimeCalculation fun exceptionInFinally(divideBy: Int): Int { return try { @@ -191,25 +200,26 @@ fun tryTryFinally(): Int { const val a1 = tryFinally(0) const val a2 = tryFinally(10) -//const val a3 = outerReturnTryFinally(10) TODO -> `10` +const val a3 = outerReturnTryFinally(10) +const val a4 = outerReturnUnitTryFinally(10) const val b1 = tryFinally2() const val c1 = (tryFinally.kt:196)`!>tryCatchFinally() + at TryFinallyKt.tryCatchFinally(tryFinally.kt:57) + at TryFinallyKt.(tryFinally.kt:206)`!>tryCatchFinally() const val c2 = exceptionInFinally(10) const val c3 = (tryFinally.kt:198)`!>exceptionInFinally(0) -//const val d1 = returnTryFinally() TODO -> `OK` + at TryFinallyKt.exceptionInFinally(tryFinally.kt:46) + at TryFinallyKt.(tryFinally.kt:208)`!>exceptionInFinally(0) +const val d1 = returnTryFinally() const val d2 = tryCatchReturnFinally(10) const val d3 = tryCatchReturnFinally(0) const val e1 = tryFinallyContinue() const val e2 = tryFinallyBreak() const val e3 = tryCatchFinallyContinue(0) -//const val f1 = innerTryFinally(10) TODO -> `1` +const val f1 = innerTryFinally(10) const val f2 = innerTryFinallyReturn(10) -//const val g1 = tryCatch(10) TODO -> `0` -//const val g2 = tryCatch(0) TODO -> `-1` -//const val h1 = tryTryFinally() TODO -> `-1` +const val g1 = tryCatch(10) +const val g2 = tryCatch(0) +const val h1 = tryTryFinally() diff --git a/compiler/testData/ir/interpreter/unitResult.kt b/compiler/testData/ir/interpreter/unitResult.kt new file mode 100644 index 00000000000..f61eb2260f8 --- /dev/null +++ b/compiler/testData/ir/interpreter/unitResult.kt @@ -0,0 +1,12 @@ +@CompileTimeCalculation fun getUnitImplicit(): Unit {} +@CompileTimeCalculation fun getUnitExplicit(): Unit { return Unit } +@CompileTimeCalculation fun getUnitImplicitFromExpression(): Unit { if (true) {} else Unit } +@CompileTimeCalculation fun getUnitImplicitFromTry1(): Unit { try {} finally { 5 } } +@CompileTimeCalculation fun getUnitImplicitFromTry2(): Unit { try {} finally { } } + +const val unit1 = Unit.toString() +const val unit2 = getUnitImplicit().toString() +const val unit3 = getUnitExplicit().toString() +const val unit4 = getUnitImplicitFromExpression().toString() +const val unit5 = getUnitImplicitFromTry1().toString() +const val unit6 = getUnitImplicitFromTry2().toString()