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()