Implement more precise control flow in ir interpreter

"More precise" in terms of the number of passing tests.
In this case ControlStructures test block was used.
This commit is contained in:
Ivan Kylchik
2021-03-12 13:30:29 +03:00
committed by TeamCityServer
parent c3ad319c13
commit fe99b235d3
3 changed files with 28 additions and 17 deletions
@@ -496,13 +496,9 @@ class IrInterpreter(val irBuiltIns: IrBuiltIns, private val bodyMap: Map<IdSigna
return executionResult
}
private fun interpretBlock(block: IrBlock): ExecutionResult {
return stack.newFrame(asSubFrame = true) { interpretStatements(block.statements) }
}
private fun interpretBody(body: IrBody): ExecutionResult {
private fun List<IrStatement>.withUnitIfNoResult(): ExecutionResult {
return stack.newFrame(asSubFrame = true) {
val executionResult = interpretStatements(body.statements).check { return@newFrame it }
val executionResult = interpretStatements(this).check { return@newFrame it }
when {
!stack.hasReturnValue() -> getOrCreateObjectValue(irBuiltIns.unitClass.owner)
else -> executionResult
@@ -510,6 +506,14 @@ class IrInterpreter(val irBuiltIns: IrBuiltIns, private val bodyMap: Map<IdSigna
}
}
private fun interpretBlock(block: IrBlock): ExecutionResult {
return block.statements.withUnitIfNoResult()
}
private fun interpretBody(body: IrBody): ExecutionResult {
return body.statements.withUnitIfNoResult()
}
private fun interpretReturn(expression: IrReturn): ExecutionResult {
expression.value.interpret().check { return it }
return Return.addOwnerInfo(expression.returnTargetSymbol.owner)
@@ -528,7 +532,7 @@ class IrInterpreter(val irBuiltIns: IrBuiltIns, private val bodyMap: Map<IdSigna
do {
// pool from body must be seen to condition expression, so must create temp frame here
stack.newFrame(asSubFrame = true) {
expression.body?.interpret()?.check { return@newFrame it }
expression.body?.interpret()?.check(ReturnLabel.REGULAR, ReturnLabel.CONTINUE) { return@newFrame it }
expression.condition.interpret().check { return@newFrame it }
Next
}.check { return it }
@@ -705,8 +709,10 @@ class IrInterpreter(val irBuiltIns: IrBuiltIns, private val bodyMap: Map<IdSigna
val typeOperand = if (isReified) (stack.getVariable(typeClassifier).state as KTypeState).irType else expression.typeOperand
when (expression.operator) {
// coercion to unit means that return value isn't used
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> stack.popReturnValue()
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> {
stack.popReturnValue()
getOrCreateObjectValue(irBuiltIns.unitClass.owner).check { return it }
}
IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST -> {
if (!isErased && !stack.peekReturnValue().isSubtypeOf(typeOperand)) {
val convertibleClassName = stack.popReturnValue().irClass.fqNameWhenAvailable
@@ -802,7 +808,8 @@ class IrInterpreter(val irBuiltIns: IrBuiltIns, private val bodyMap: Map<IdSigna
}
return Exception
} finally {
expression.finallyExpression?.takeIf { (it as? IrBlock)?.statements?.isEmpty() != true }?.interpret()?.check { return it }
expression.finallyExpression?.interpret()
?.let { if (it.returnLabel == ReturnLabel.REGULAR) stack.popReturnValue() else return it }
}
}
@@ -10,10 +10,7 @@ import org.jetbrains.kotlin.ir.interpreter.state.Primitive
import org.jetbrains.kotlin.ir.interpreter.state.isSubtypeOf
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrReturnableBlock
import org.jetbrains.kotlin.ir.expressions.IrWhen
import org.jetbrains.kotlin.ir.expressions.IrWhileLoop
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.interpreter.exceptions.throwAsUserException
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.IrType
@@ -38,11 +35,15 @@ open class ExecutionResult(val returnLabel: ReturnLabel, private val owner: IrEl
else -> this
}
ReturnLabel.BREAK_LOOP -> when (irElement) {
is IrWhileLoop -> if (owner == irElement) Next else this
is IrWhileLoop, is IrDoWhileLoop -> if (owner == irElement) Next else this
else -> this
}
ReturnLabel.CONTINUE -> when (irElement) {
is IrWhileLoop -> if (owner == irElement) irElement.interpret() else this
is IrDoWhileLoop -> {
if (owner != irElement) return this
error("Continue must be handled inside DoWhile interpreter function")
}
else -> this
}
ReturnLabel.EXCEPTION -> Exception
@@ -55,8 +56,10 @@ open class ExecutionResult(val returnLabel: ReturnLabel, private val owner: IrEl
}
}
inline fun ExecutionResult.check(toCheckLabel: ReturnLabel = ReturnLabel.REGULAR, returnBlock: (ExecutionResult) -> Unit): ExecutionResult {
if (this.returnLabel != toCheckLabel) returnBlock(this)
inline fun ExecutionResult.check(
vararg toCheckLabel: ReturnLabel = arrayOf(ReturnLabel.REGULAR), returnBlock: (ExecutionResult) -> Unit
): ExecutionResult {
if (this.returnLabel !in toCheckLabel) returnBlock(this)
return this
}
@@ -43,6 +43,7 @@ internal class ExceptionState private constructor(
}
constructor(common: Common, stackTrace: List<String>) : this(common.irClass, common.fields, stackTrace) {
(common.superWrapperClass?.value as? Throwable)?.let { setMessage(it.message) }
setUpCauseIfNeeded(common.superWrapperClass)
}