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 return executionResult
} }
private fun interpretBlock(block: IrBlock): ExecutionResult { private fun List<IrStatement>.withUnitIfNoResult(): ExecutionResult {
return stack.newFrame(asSubFrame = true) { interpretStatements(block.statements) }
}
private fun interpretBody(body: IrBody): ExecutionResult {
return stack.newFrame(asSubFrame = true) { return stack.newFrame(asSubFrame = true) {
val executionResult = interpretStatements(body.statements).check { return@newFrame it } val executionResult = interpretStatements(this).check { return@newFrame it }
when { when {
!stack.hasReturnValue() -> getOrCreateObjectValue(irBuiltIns.unitClass.owner) !stack.hasReturnValue() -> getOrCreateObjectValue(irBuiltIns.unitClass.owner)
else -> executionResult 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 { private fun interpretReturn(expression: IrReturn): ExecutionResult {
expression.value.interpret().check { return it } expression.value.interpret().check { return it }
return Return.addOwnerInfo(expression.returnTargetSymbol.owner) return Return.addOwnerInfo(expression.returnTargetSymbol.owner)
@@ -528,7 +532,7 @@ class IrInterpreter(val irBuiltIns: IrBuiltIns, private val bodyMap: Map<IdSigna
do { do {
// pool from body must be seen to condition expression, so must create temp frame here // pool from body must be seen to condition expression, so must create temp frame here
stack.newFrame(asSubFrame = true) { 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 } expression.condition.interpret().check { return@newFrame it }
Next Next
}.check { return it } }.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 val typeOperand = if (isReified) (stack.getVariable(typeClassifier).state as KTypeState).irType else expression.typeOperand
when (expression.operator) { when (expression.operator) {
// coercion to unit means that return value isn't used IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> {
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> stack.popReturnValue() stack.popReturnValue()
getOrCreateObjectValue(irBuiltIns.unitClass.owner).check { return it }
}
IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST -> { IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST -> {
if (!isErased && !stack.peekReturnValue().isSubtypeOf(typeOperand)) { if (!isErased && !stack.peekReturnValue().isSubtypeOf(typeOperand)) {
val convertibleClassName = stack.popReturnValue().irClass.fqNameWhenAvailable val convertibleClassName = stack.popReturnValue().irClass.fqNameWhenAvailable
@@ -802,7 +808,8 @@ class IrInterpreter(val irBuiltIns: IrBuiltIns, private val bodyMap: Map<IdSigna
} }
return Exception return Exception
} finally { } 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.interpreter.state.isSubtypeOf
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.*
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.interpreter.exceptions.throwAsUserException import org.jetbrains.kotlin.ir.interpreter.exceptions.throwAsUserException
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
@@ -38,11 +35,15 @@ open class ExecutionResult(val returnLabel: ReturnLabel, private val owner: IrEl
else -> this else -> this
} }
ReturnLabel.BREAK_LOOP -> when (irElement) { 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 else -> this
} }
ReturnLabel.CONTINUE -> when (irElement) { ReturnLabel.CONTINUE -> when (irElement) {
is IrWhileLoop -> if (owner == irElement) irElement.interpret() else this 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 else -> this
} }
ReturnLabel.EXCEPTION -> Exception 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 { inline fun ExecutionResult.check(
if (this.returnLabel != toCheckLabel) returnBlock(this) vararg toCheckLabel: ReturnLabel = arrayOf(ReturnLabel.REGULAR), returnBlock: (ExecutionResult) -> Unit
): ExecutionResult {
if (this.returnLabel !in toCheckLabel) returnBlock(this)
return this return this
} }
@@ -43,6 +43,7 @@ internal class ExceptionState private constructor(
} }
constructor(common: Common, stackTrace: List<String>) : this(common.irClass, common.fields, stackTrace) { constructor(common: Common, stackTrace: List<String>) : this(common.irClass, common.fields, stackTrace) {
(common.superWrapperClass?.value as? Throwable)?.let { setMessage(it.message) }
setUpCauseIfNeeded(common.superWrapperClass) setUpCauseIfNeeded(common.superWrapperClass)
} }