diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/jumps.dot b/compiler/fir/analysis-tests/testData/resolve/cfg/jumps.dot index 9955587b956..d108e0025b8 100644 --- a/compiler/fir/analysis-tests/testData/resolve/cfg/jumps.dot +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/jumps.dot @@ -354,7 +354,7 @@ digraph jumps_kt { 113 -> {114}; 114 -> {115}; 115 -> {116} [style=dotted]; - 115 -> {102} [color=green style=dashed]; + 115 -> {103} [color=green style=dashed]; 116 -> {117} [style=dotted]; 117 -> {118} [style=dotted]; 118 -> {119} [style=dotted]; diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/tryCatch.dot b/compiler/fir/analysis-tests/testData/resolve/cfg/tryCatch.dot index 0a0408483db..faf8d7f6da7 100644 --- a/compiler/fir/analysis-tests/testData/resolve/cfg/tryCatch.dot +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/tryCatch.dot @@ -324,7 +324,7 @@ digraph tryCatch_kt { 96 -> {97}; 97 -> {98}; 98 -> {99} [style=dotted]; - 98 -> {46} [color=green style=dashed]; + 98 -> {47} [color=green style=dashed]; 99 -> {100} [style=dotted]; 100 -> {101} [style=dotted]; 101 -> {102} [style=dotted]; diff --git a/compiler/fir/analysis-tests/testData/resolve/smartcasts/boundSmartcasts/boundSmartcastsInBranches.dot b/compiler/fir/analysis-tests/testData/resolve/smartcasts/boundSmartcasts/boundSmartcastsInBranches.dot index 2e7d30ef9ca..1b6b5adc9aa 100644 --- a/compiler/fir/analysis-tests/testData/resolve/smartcasts/boundSmartcasts/boundSmartcastsInBranches.dot +++ b/compiler/fir/analysis-tests/testData/resolve/smartcasts/boundSmartcasts/boundSmartcastsInBranches.dot @@ -136,7 +136,7 @@ digraph boundSmartcastsInBranches_kt { 36 -> {37}; 37 -> {38}; 38 -> {39} [style=dotted]; - 38 -> {17} [color=green style=dashed]; + 38 -> {18} [color=green style=dashed]; 39 -> {40} [style=dotted]; 40 -> {41} [style=dotted]; 41 -> {42} [style=dotted]; diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt index 43cb05efcc6..2d185f3b601 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt @@ -85,7 +85,7 @@ class ControlFlowGraphBuilder { private val exitsFromPostponedAnonymousFunctions: MutableMap, PostponedLambdaExitNode> = mutableMapOf() private val parentGraphForAnonymousFunctions: MutableMap, ControlFlowGraph> = mutableMapOf() - private val loopEnterNodes: NodeStorage> = NodeStorage() + private val loopConditionEnterNodes: NodeStorage = NodeStorage() private val loopExitNodes: NodeStorage = NodeStorage() private val exitsFromCompletedPostponedAnonymousFunctions: MutableList>> = mutableListOf() @@ -670,7 +670,7 @@ class ControlFlowGraphBuilder { val node = createJumpNode(jump) val nextNode = when (jump) { 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] else -> throw IllegalArgumentException("Unknown jump type: ${jump.render()}") } @@ -681,12 +681,17 @@ class ControlFlowGraphBuilder { 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( node, nextNode, - isBack = jump is FirContinueExpression, + isBack = isBack, trackJump = jump is FirReturnExpression, - label = NormalPath, + label = if (isBack) LoopBackPath else NormalPath, labelForFinallyBLock = labelForFinallyBLock ) return node @@ -758,14 +763,12 @@ class ControlFlowGraphBuilder { fun enterWhileLoop(loop: FirLoop): Pair { val loopEnterNode = createLoopEnterNode(loop).also { addNewSimpleNode(it) - loopEnterNodes.push(it) } loopExitNodes.push(createLoopExitNode(loop)) levelCounter++ val conditionEnterNode = createLoopConditionEnterNode(loop.condition, loop).also { addNewSimpleNode(it) - // put conditional node twice so we can refer it after exit from loop block - lastNodes.push(it) + loopConditionEnterNodes.push(it) } levelCounter++ return loopEnterNode to conditionEnterNode @@ -784,15 +787,11 @@ class ControlFlowGraphBuilder { } fun exitWhileLoop(loop: FirLoop): Pair { - loopEnterNodes.pop() levelCounter-- val loopBlockExitNode = createLoopBlockExitNode(loop) popAndAddEdge(loopBlockExitNode) - if (lastNodes.isNotEmpty) { - val conditionEnterNode = lastNodes.pop() - require(conditionEnterNode is LoopConditionEnterNode) { loop.render() } - addBackEdge(loopBlockExitNode, conditionEnterNode, label = LoopBackPath) - } + val conditionEnterNode = loopConditionEnterNodes.pop() + addBackEdge(loopBlockExitNode, conditionEnterNode, label = LoopBackPath) val loopExitNode = loopExitNodes.pop() loopExitNode.updateDeadStatus() lastNodes.push(loopExitNode) @@ -811,7 +810,7 @@ class ControlFlowGraphBuilder { addNewSimpleNode(blockEnterNode) // put block enter node twice so we can refer it after exit from loop condition lastNodes.push(blockEnterNode) - loopEnterNodes.push(blockEnterNode) + loopConditionEnterNodes.push(createLoopConditionEnterNode(loop.condition, loop)) levelCounter++ return loopEnterNode to blockEnterNode } @@ -819,13 +818,15 @@ class ControlFlowGraphBuilder { fun enterDoWhileLoopCondition(loop: FirLoop): Pair { levelCounter-- 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++ return blockExitNode to conditionEnterNode } fun exitDoWhileLoop(loop: FirLoop): Pair { - loopEnterNodes.pop() + loopConditionEnterNodes.pop() levelCounter-- val conditionExitNode = createLoopConditionExitNode(loop.condition) val conditionBooleanValue = conditionExitNode.booleanConstValue @@ -1556,12 +1557,7 @@ class ControlFlowGraphBuilder { popAndAddEdge(node, preferredKind) if (targetNode != null) { if (isBack) { - if (targetNode is LoopEnterNode) { - // `continue` to the loop header - addBackEdge(node, targetNode, label = LoopBackPath) - } else { - addBackEdge(node, targetNode, label = label) - } + addBackEdge(node, targetNode, label = label) } else { // go through all final nodes between node and target val finallyNodes = finallyBefore(targetNode) diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyContinue.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyContinue.fir.kt deleted file mode 100644 index 00461db0c84..00000000000 --- a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyContinue.fir.kt +++ /dev/null @@ -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 -} diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyContinue.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyContinue.kt index dc1d3eff4d7..c7f8f09ecf6 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyContinue.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileEarlyContinue.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL fun x(): Boolean { return true } fun y(): Boolean { return false } diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhileWithLongContinue.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhileWithLongContinue.fir.kt index 9d4e2a83221..7b369acf752 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhileWithLongContinue.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhileWithLongContinue.fir.kt @@ -5,10 +5,10 @@ public fun foo(p: String?, r: String?): Int { do { p!!.length if (!x()) continue@outer - } while (r == null) + } while (r == null) } while (!x()) // Auto cast NOT possible due to long continue - r.length + r.length // Auto cast possible return p.length } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhileWithLongContinue.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhileWithLongContinue.kt index 03414db1732..283a0baaed3 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhileWithLongContinue.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/nestedDoWhileWithLongContinue.kt @@ -5,7 +5,7 @@ public fun foo(p: String?, r: String?): Int { do { p!!.length if (!x()) continue@outer - } while (r == null) + } while (r == null) } while (!x()) // Auto cast NOT possible due to long continue r.length diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/35.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/35.fir.kt index 914d629a7b6..51eebccae47 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/35.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/35.fir.kt @@ -70,7 +70,7 @@ fun case_6(x: Any?) { x.equals(10) } while (false) - x + x } // TESTCASE NUMBER: 7