FIR DFA: make continue jump to condition entry, not loop entry

It's also not a backwards jump in do-while, unless it's in the loop's
condition, which is a stupid "feature" IMO. As you can probably tell
from the comments added in this commit.
This commit is contained in:
pyos
2022-11-14 15:27:08 +01:00
committed by teamcity
parent 7e407585fc
commit f9745bd3f1
9 changed files with 26 additions and 42 deletions
+1 -1
View File
@@ -354,7 +354,7 @@ digraph jumps_kt {
113 -> {114}; 113 -> {114};
114 -> {115}; 114 -> {115};
115 -> {116} [style=dotted]; 115 -> {116} [style=dotted];
115 -> {102} [color=green style=dashed]; 115 -> {103} [color=green style=dashed];
116 -> {117} [style=dotted]; 116 -> {117} [style=dotted];
117 -> {118} [style=dotted]; 117 -> {118} [style=dotted];
118 -> {119} [style=dotted]; 118 -> {119} [style=dotted];
@@ -324,7 +324,7 @@ digraph tryCatch_kt {
96 -> {97}; 96 -> {97};
97 -> {98}; 97 -> {98};
98 -> {99} [style=dotted]; 98 -> {99} [style=dotted];
98 -> {46} [color=green style=dashed]; 98 -> {47} [color=green style=dashed];
99 -> {100} [style=dotted]; 99 -> {100} [style=dotted];
100 -> {101} [style=dotted]; 100 -> {101} [style=dotted];
101 -> {102} [style=dotted]; 101 -> {102} [style=dotted];
@@ -136,7 +136,7 @@ digraph boundSmartcastsInBranches_kt {
36 -> {37}; 36 -> {37};
37 -> {38}; 37 -> {38};
38 -> {39} [style=dotted]; 38 -> {39} [style=dotted];
38 -> {17} [color=green style=dashed]; 38 -> {18} [color=green style=dashed];
39 -> {40} [style=dotted]; 39 -> {40} [style=dotted];
40 -> {41} [style=dotted]; 40 -> {41} [style=dotted];
41 -> {42} [style=dotted]; 41 -> {42} [style=dotted];
@@ -85,7 +85,7 @@ class ControlFlowGraphBuilder {
private val exitsFromPostponedAnonymousFunctions: MutableMap<FirFunctionSymbol<*>, PostponedLambdaExitNode> = mutableMapOf() private val exitsFromPostponedAnonymousFunctions: MutableMap<FirFunctionSymbol<*>, PostponedLambdaExitNode> = mutableMapOf()
private val parentGraphForAnonymousFunctions: MutableMap<FirFunctionSymbol<*>, ControlFlowGraph> = mutableMapOf() private val parentGraphForAnonymousFunctions: MutableMap<FirFunctionSymbol<*>, ControlFlowGraph> = mutableMapOf()
private val loopEnterNodes: NodeStorage<FirElement, CFGNode<FirElement>> = NodeStorage() private val loopConditionEnterNodes: NodeStorage<FirElement, LoopConditionEnterNode> = NodeStorage()
private val loopExitNodes: NodeStorage<FirLoop, LoopExitNode> = NodeStorage() private val loopExitNodes: NodeStorage<FirLoop, LoopExitNode> = NodeStorage()
private val exitsFromCompletedPostponedAnonymousFunctions: MutableList<MutableList<CFGNode<*>>> = mutableListOf() private val exitsFromCompletedPostponedAnonymousFunctions: MutableList<MutableList<CFGNode<*>>> = mutableListOf()
@@ -670,7 +670,7 @@ class ControlFlowGraphBuilder {
val node = createJumpNode(jump) val node = createJumpNode(jump)
val nextNode = when (jump) { val nextNode = when (jump) {
is FirReturnExpression -> exitTargetsForReturn[jump.target.labeledElement.symbol] is FirReturnExpression -> exitTargetsForReturn[jump.target.labeledElement.symbol]
is FirContinueExpression -> loopEnterNodes[jump.target.labeledElement] is FirContinueExpression -> loopConditionEnterNodes[jump.target.labeledElement.condition]
is FirBreakExpression -> loopExitNodes[jump.target.labeledElement] is FirBreakExpression -> loopExitNodes[jump.target.labeledElement]
else -> throw IllegalArgumentException("Unknown jump type: ${jump.render()}") else -> throw IllegalArgumentException("Unknown jump type: ${jump.render()}")
} }
@@ -681,12 +681,17 @@ class ControlFlowGraphBuilder {
else -> NormalPath else -> NormalPath
} }
// while (...) continue // <- jump back
// do continue while (...) // <- jump forward (block exit node not created yet)
// do ... while (continue) // <- jump back (to itself), believe it or not
val isBack = nextNode is LoopConditionEnterNode &&
(nextNode.loop !is FirDoWhileLoop || nextNode.previousNodes.any { it is LoopBlockExitNode })
addNodeWithJump( addNodeWithJump(
node, node,
nextNode, nextNode,
isBack = jump is FirContinueExpression, isBack = isBack,
trackJump = jump is FirReturnExpression, trackJump = jump is FirReturnExpression,
label = NormalPath, label = if (isBack) LoopBackPath else NormalPath,
labelForFinallyBLock = labelForFinallyBLock labelForFinallyBLock = labelForFinallyBLock
) )
return node return node
@@ -758,14 +763,12 @@ class ControlFlowGraphBuilder {
fun enterWhileLoop(loop: FirLoop): Pair<LoopEnterNode, LoopConditionEnterNode> { fun enterWhileLoop(loop: FirLoop): Pair<LoopEnterNode, LoopConditionEnterNode> {
val loopEnterNode = createLoopEnterNode(loop).also { val loopEnterNode = createLoopEnterNode(loop).also {
addNewSimpleNode(it) addNewSimpleNode(it)
loopEnterNodes.push(it)
} }
loopExitNodes.push(createLoopExitNode(loop)) loopExitNodes.push(createLoopExitNode(loop))
levelCounter++ levelCounter++
val conditionEnterNode = createLoopConditionEnterNode(loop.condition, loop).also { val conditionEnterNode = createLoopConditionEnterNode(loop.condition, loop).also {
addNewSimpleNode(it) addNewSimpleNode(it)
// put conditional node twice so we can refer it after exit from loop block loopConditionEnterNodes.push(it)
lastNodes.push(it)
} }
levelCounter++ levelCounter++
return loopEnterNode to conditionEnterNode return loopEnterNode to conditionEnterNode
@@ -784,15 +787,11 @@ class ControlFlowGraphBuilder {
} }
fun exitWhileLoop(loop: FirLoop): Pair<LoopBlockExitNode, LoopExitNode> { fun exitWhileLoop(loop: FirLoop): Pair<LoopBlockExitNode, LoopExitNode> {
loopEnterNodes.pop()
levelCounter-- levelCounter--
val loopBlockExitNode = createLoopBlockExitNode(loop) val loopBlockExitNode = createLoopBlockExitNode(loop)
popAndAddEdge(loopBlockExitNode) popAndAddEdge(loopBlockExitNode)
if (lastNodes.isNotEmpty) { val conditionEnterNode = loopConditionEnterNodes.pop()
val conditionEnterNode = lastNodes.pop() addBackEdge(loopBlockExitNode, conditionEnterNode, label = LoopBackPath)
require(conditionEnterNode is LoopConditionEnterNode) { loop.render() }
addBackEdge(loopBlockExitNode, conditionEnterNode, label = LoopBackPath)
}
val loopExitNode = loopExitNodes.pop() val loopExitNode = loopExitNodes.pop()
loopExitNode.updateDeadStatus() loopExitNode.updateDeadStatus()
lastNodes.push(loopExitNode) lastNodes.push(loopExitNode)
@@ -811,7 +810,7 @@ class ControlFlowGraphBuilder {
addNewSimpleNode(blockEnterNode) addNewSimpleNode(blockEnterNode)
// put block enter node twice so we can refer it after exit from loop condition // put block enter node twice so we can refer it after exit from loop condition
lastNodes.push(blockEnterNode) lastNodes.push(blockEnterNode)
loopEnterNodes.push(blockEnterNode) loopConditionEnterNodes.push(createLoopConditionEnterNode(loop.condition, loop))
levelCounter++ levelCounter++
return loopEnterNode to blockEnterNode return loopEnterNode to blockEnterNode
} }
@@ -819,13 +818,15 @@ class ControlFlowGraphBuilder {
fun enterDoWhileLoopCondition(loop: FirLoop): Pair<LoopBlockExitNode, LoopConditionEnterNode> { fun enterDoWhileLoopCondition(loop: FirLoop): Pair<LoopBlockExitNode, LoopConditionEnterNode> {
levelCounter-- levelCounter--
val blockExitNode = createLoopBlockExitNode(loop).also { addNewSimpleNode(it) } val blockExitNode = createLoopBlockExitNode(loop).also { addNewSimpleNode(it) }
val conditionEnterNode = createLoopConditionEnterNode(loop.condition, loop).also { addNewSimpleNode(it) } // This may sound shocking, but `do...while` conditions can `continue` to themselves,
// so we can't pop the node off the stack here.
val conditionEnterNode = loopConditionEnterNodes.top().also { addNewSimpleNode(it) }
levelCounter++ levelCounter++
return blockExitNode to conditionEnterNode return blockExitNode to conditionEnterNode
} }
fun exitDoWhileLoop(loop: FirLoop): Pair<LoopConditionExitNode, LoopExitNode> { fun exitDoWhileLoop(loop: FirLoop): Pair<LoopConditionExitNode, LoopExitNode> {
loopEnterNodes.pop() loopConditionEnterNodes.pop()
levelCounter-- levelCounter--
val conditionExitNode = createLoopConditionExitNode(loop.condition) val conditionExitNode = createLoopConditionExitNode(loop.condition)
val conditionBooleanValue = conditionExitNode.booleanConstValue val conditionBooleanValue = conditionExitNode.booleanConstValue
@@ -1556,12 +1557,7 @@ class ControlFlowGraphBuilder {
popAndAddEdge(node, preferredKind) popAndAddEdge(node, preferredKind)
if (targetNode != null) { if (targetNode != null) {
if (isBack) { if (isBack) {
if (targetNode is LoopEnterNode) { addBackEdge(node, targetNode, label = label)
// `continue` to the loop header
addBackEdge(node, targetNode, label = LoopBackPath)
} else {
addBackEdge(node, targetNode, label = label)
}
} else { } else {
// go through all final nodes between node and target // go through all final nodes between node and target
val finallyNodes = finallyBefore(targetNode) val finallyNodes = finallyBefore(targetNode)
@@ -1,13 +0,0 @@
fun x(): Boolean { return true }
fun y(): Boolean { return false }
public fun foo(p: String?): Int {
do {
if (y()) continue
// We do not always reach this statement
p!!.length
} while (!x())
// Here we have do while loop but p is still nullable due to continue before
return p.length
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
fun x(): Boolean { return true } fun x(): Boolean { return true }
fun y(): Boolean { return false } fun y(): Boolean { return false }
@@ -5,10 +5,10 @@ public fun foo(p: String?, r: String?): Int {
do { do {
p!!.length p!!.length
if (!x()) continue@outer if (!x()) continue@outer
} while (r == null) } while (r == null)
} while (!x()) } while (!x())
// Auto cast NOT possible due to long continue // Auto cast NOT possible due to long continue
r.length r<!UNSAFE_CALL!>.<!>length
// Auto cast possible // Auto cast possible
return p.length return p.length
} }
@@ -5,7 +5,7 @@ public fun foo(p: String?, r: String?): Int {
do { do {
p!!.length p!!.length
if (!x()) continue@outer if (!x()) continue@outer
} while (r == null) } while (r == null)
} while (!x()) } while (!x())
// Auto cast NOT possible due to long continue // Auto cast NOT possible due to long continue
r<!UNSAFE_CALL!>.<!>length r<!UNSAFE_CALL!>.<!>length
@@ -70,7 +70,7 @@ fun case_6(x: Any?) {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Any")!>x<!>.equals(10) <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Any")!>x<!>.equals(10)
} while (false) } while (false)
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Any")!>x<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
} }
// TESTCASE NUMBER: 7 // TESTCASE NUMBER: 7