From b2e95eebabc54a62abea74651ab603b874ef07f0 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Tue, 27 Aug 2019 16:13:17 +0300 Subject: [PATCH] [FIR] Add processing of loops with constant boolean value in condition --- .../fir/resolve/dfa/cfg/ControlFlowGraph.kt | 4 +- .../dfa/cfg/ControlFlowGraphBuilder.kt | 31 +- .../dfa/cfg/ControlFlowGraphNodeBuilder.kt | 4 +- .../resolve/testData/resolve/cfg/jumps.dot | 71 +-- .../fir/resolve/testData/resolve/cfg/jumps.kt | 2 +- .../resolve/testData/resolve/cfg/jumps.txt | 6 +- .../resolve/testData/resolve/cfg/loops.dot | 274 ++++++++++ .../fir/resolve/testData/resolve/cfg/loops.kt | 47 ++ .../resolve/testData/resolve/cfg/loops.txt | 56 ++ .../resolve/testData/resolve/cfg/tryCatch.dot | 30 +- .../resolve/smartcasts/endlessLoops.dot | 513 ++++++++++++++++++ .../resolve/smartcasts/endlessLoops.kt | 74 +++ .../resolve/smartcasts/endlessLoops.txt | 111 ++++ .../fir/FirCfgBuildingTestGenerated.java | 5 + 14 files changed, 1158 insertions(+), 70 deletions(-) create mode 100644 compiler/fir/resolve/testData/resolve/smartcasts/endlessLoops.dot create mode 100644 compiler/fir/resolve/testData/resolve/smartcasts/endlessLoops.kt create mode 100644 compiler/fir/resolve/testData/resolve/smartcasts/endlessLoops.txt diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt index 5c159bf0816..887a0e6f78c 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt @@ -74,8 +74,8 @@ class WhenBranchResultExitNode(owner: ControlFlowGraph, override val fir: FirWhe class LoopEnterNode(owner: ControlFlowGraph, override val fir: FirLoop, level: Int) : CFGNode(owner, level) class LoopBlockEnterNode(owner: ControlFlowGraph, override val fir: FirLoop, level: Int) : CFGNode(owner, level) class LoopBlockExitNode(owner: ControlFlowGraph, override val fir: FirLoop, level: Int) : CFGNode(owner, level) -class LoopConditionEnterNode(owner: ControlFlowGraph, override val fir: FirLoop, level: Int) : CFGNode(owner, level) -class LoopConditionExitNode(owner: ControlFlowGraph, override val fir: FirLoop, level: Int) : CFGNode(owner, level) +class LoopConditionEnterNode(owner: ControlFlowGraph, override val fir: FirExpression, level: Int) : CFGNode(owner, level) +class LoopConditionExitNode(owner: ControlFlowGraph, override val fir: FirExpression, level: Int) : CFGNode(owner, level) class LoopExitNode(owner: ControlFlowGraph, override val fir: FirLoop, level: Int) : CFGNode(owner, level) // ----------------------------------- Try-catch-finally ----------------------------------- 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 0a9b269330b..c7fef99fdc6 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 @@ -217,7 +217,7 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() { addNewSimpleNode(createLoopEnterNode(loop)) loopExitNodes.push(createLoopExitNode(loop)) levelCounter++ - val node = createLoopConditionEnterNode(loop) + val node = createLoopConditionEnterNode(loop.condition) levelCounter++ addNewSimpleNode(node) // put conditional node twice so we can refer it after exit from loop block @@ -228,11 +228,12 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() { fun exitWhileLoopCondition(loop: FirLoop): LoopConditionExitNode { levelCounter-- - val conditionExitNode = createLoopConditionExitNode(loop) + val conditionExitNode = createLoopConditionExitNode(loop.condition) addNewSimpleNode(conditionExitNode) - // TODO: here we can check that condition is always true - addEdge(conditionExitNode, loopExitNodes.top()) - addNewSimpleNode(createLoopBlockEnterNode(loop)) + val conditionConstBooleanValue = conditionExitNode.booleanConstValue + addEdge(conditionExitNode, loopExitNodes.top(), isDead = conditionConstBooleanValue == true) + val loopBlockEnterNode = createLoopBlockEnterNode(loop) + addNewSimpleNode(loopBlockEnterNode, conditionConstBooleanValue == false) levelCounter++ return conditionExitNode } @@ -272,7 +273,7 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() { fun enterDoWhileLoopCondition(loop: FirLoop): Pair { levelCounter-- val blockExitNode = createLoopBlockExitNode(loop).also { addNewSimpleNode(it) } - val conditionEnterNode = createLoopConditionEnterNode(loop).also { addNewSimpleNode(it) } + val conditionEnterNode = createLoopConditionEnterNode(loop.condition).also { addNewSimpleNode(it) } levelCounter++ return blockExitNode to conditionEnterNode } @@ -280,14 +281,14 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() { fun exitDoWhileLoop(loop: FirLoop): LoopExitNode { loopEnterNodes.pop() levelCounter-- - val conditionExitNode = createLoopConditionExitNode(loop) - // TODO: here we can check that condition is always false + val conditionExitNode = createLoopConditionExitNode(loop.condition) + val conditionBooleanValue = conditionExitNode.booleanConstValue addEdge(lastNodes.pop(), conditionExitNode) val blockEnterNode = lastNodes.pop() require(blockEnterNode is LoopBlockEnterNode) - addEdge(conditionExitNode, blockEnterNode, propagateDeadness = false) + addEdge(conditionExitNode, blockEnterNode, propagateDeadness = false, isDead = conditionBooleanValue == false) val loopExit = loopExitNodes.pop() - addEdge(conditionExitNode, loopExit, propagateDeadness = false) + addEdge(conditionExitNode, loopExit, propagateDeadness = false, isDead = conditionBooleanValue == true) loopExit.markAsDeadIfNecessary() lastNodes.push(loopExit) levelCounter-- @@ -531,22 +532,22 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() { lastNodes.push(stub) } - private fun addNewSimpleNode(newNode: CFGNode<*>): CFGNode<*> { + private fun addNewSimpleNode(newNode: CFGNode<*>, isDead: Boolean = false): CFGNode<*> { val oldNode = lastNodes.pop() - addEdge(oldNode, newNode) + addEdge(oldNode, newNode, isDead = isDead) lastNodes.push(newNode) return oldNode } - private fun addDeadEdge(from: CFGNode<*>, to: CFGNode<*>) { + private fun addDeadEdge(from: CFGNode<*>, to: CFGNode<*>, propagateDeadness: Boolean) { val stub = createStubNode() addEdge(from, stub) - addEdge(stub, to) + addEdge(stub, to, propagateDeadness = propagateDeadness) } private fun addEdge(from: CFGNode<*>, to: CFGNode<*>, propagateDeadness: Boolean = true, isDead: Boolean = false) { if (isDead) { - addDeadEdge(from, to) + addDeadEdge(from, to, propagateDeadness) return } if (propagateDeadness && from.isDead) { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphNodeBuilder.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphNodeBuilder.kt index 4e04b5d5973..e8a53ebd289 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphNodeBuilder.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphNodeBuilder.kt @@ -95,10 +95,10 @@ abstract class ControlFlowGraphNodeBuilder { protected fun createWhenBranchResultExitNode(fir: FirWhenBranch): WhenBranchResultExitNode = WhenBranchResultExitNode(graph, fir, levelCounter) - protected fun createLoopConditionExitNode(fir: FirLoop): LoopConditionExitNode = + protected fun createLoopConditionExitNode(fir: FirExpression): LoopConditionExitNode = LoopConditionExitNode(graph, fir, levelCounter) - protected fun createLoopConditionEnterNode(fir: FirLoop): LoopConditionEnterNode = + protected fun createLoopConditionEnterNode(fir: FirExpression): LoopConditionEnterNode = LoopConditionEnterNode(graph, fir, levelCounter) protected fun createLoopBlockEnterNode(fir: FirLoop): LoopBlockEnterNode = diff --git a/compiler/fir/resolve/testData/resolve/cfg/jumps.dot b/compiler/fir/resolve/testData/resolve/cfg/jumps.dot index e07ccc333ef..097c59e714c 100644 --- a/compiler/fir/resolve/testData/resolve/cfg/jumps.dot +++ b/compiler/fir/resolve/testData/resolve/cfg/jumps.dot @@ -121,73 +121,78 @@ subgraph test_3 { 61 [shape=box label="Stub[DEAD]"]; 62 [shape=box label="Exit block[DEAD]"]; 63 [shape=box label="Exit loop block[DEAD]"]; - 64 [shape=box label="Exit whileloop"]; - 65 [shape=box label="Access variable R|/x|"]; - 66 [shape=box label="Function call: R|/x|.#()"]; - 67 [shape=box label="Exit block"]; - 68 [shape=box label="Exit function test_3"]; + 64 [shape=box label="Stub[DEAD]"]; + 65 [shape=box label="Exit whileloop"]; + 66 [shape=box label="Access variable R|/x|"]; + 67 [shape=box label="Function call: R|/x|.R|kotlin/Int.inc|()"]; + 68 [shape=box label="Exit block"]; + 69 [shape=box label="Exit function test_3"]; 50 -> {51}; 51 -> {52}; 52 -> {53}; 53 -> {54}; 54 -> {55}; - 55 -> {64 56}; + 55 -> {56}; + 55 -> {64} [style=dotted]; 56 -> {57}; 57 -> {58}; 58 -> {59}; 59 -> {60}; - 60 -> {64}; + 60 -> {65}; 60 -> {61} [style=dotted]; 61 -> {62} [style=dotted]; 62 -> {63} [style=dotted]; 63 -> {53} [style=dotted]; - 64 -> {65}; + 64 -> {65} [style=dotted]; 65 -> {66}; 66 -> {67}; 67 -> {68}; + 68 -> {69}; } subgraph test_4 { - 69 [shape=box label="Enter function test_4"]; - 70 [shape=box label="Enter block"]; - 71 [shape=box label="Enter do-while loop"]; - 72 [shape=box label="Enter loop block"]; - 73 [shape=box label="Enter block"]; - 74 [shape=box label="Access variable R|/x|"]; - 75 [shape=box label="Type operator: x as int"]; - 76 [shape=box label="Jump: break@@@[Boolean(true)] "]; - 77 [shape=box label="Stub[DEAD]"]; - 78 [shape=box label="Exit block[DEAD]"]; - 79 [shape=box label="Exit loop block[DEAD]"]; - 80 [shape=box label="Enter loop condition[DEAD]"]; - 81 [shape=box label="Const: Boolean(true)[DEAD]"]; - 82 [shape=box label="Exit loop condition[DEAD]"]; - 83 [shape=box label="Exit do-whileloop"]; - 84 [shape=box label="Access variable R|/x|"]; - 85 [shape=box label="Function call: R|/x|.#()"]; - 86 [shape=box label="Exit block"]; - 87 [shape=box label="Exit function test_4"]; + 70 [shape=box label="Enter function test_4"]; + 71 [shape=box label="Enter block"]; + 72 [shape=box label="Enter do-while loop"]; + 73 [shape=box label="Enter loop block"]; + 74 [shape=box label="Enter block"]; + 75 [shape=box label="Access variable R|/x|"]; + 76 [shape=box label="Type operator: x as Int"]; + 77 [shape=box label="Jump: break@@@[Boolean(true)] "]; + 78 [shape=box label="Stub[DEAD]"]; + 79 [shape=box label="Exit block[DEAD]"]; + 80 [shape=box label="Exit loop block[DEAD]"]; + 81 [shape=box label="Enter loop condition[DEAD]"]; + 82 [shape=box label="Const: Boolean(true)[DEAD]"]; + 83 [shape=box label="Exit loop condition[DEAD]"]; + 84 [shape=box label="Stub[DEAD]"]; + 85 [shape=box label="Exit do-whileloop"]; + 86 [shape=box label="Access variable R|/x|"]; + 87 [shape=box label="Function call: R|/x|.R|kotlin/Int.inc|()"]; + 88 [shape=box label="Exit block"]; + 89 [shape=box label="Exit function test_4"]; - 69 -> {70}; 70 -> {71}; 71 -> {72}; 72 -> {73}; 73 -> {74}; 74 -> {75}; 75 -> {76}; - 76 -> {83}; - 76 -> {77} [style=dotted]; + 76 -> {77}; + 77 -> {85}; 77 -> {78} [style=dotted]; 78 -> {79} [style=dotted]; 79 -> {80} [style=dotted]; 80 -> {81} [style=dotted]; 81 -> {82} [style=dotted]; - 82 -> {72 83} [style=dotted]; - 83 -> {84}; - 84 -> {85}; + 82 -> {83} [style=dotted]; + 83 -> {73 84} [style=dotted]; + 84 -> {85} [style=dotted]; 85 -> {86}; 86 -> {87}; + 87 -> {88}; + 88 -> {89}; } } diff --git a/compiler/fir/resolve/testData/resolve/cfg/jumps.kt b/compiler/fir/resolve/testData/resolve/cfg/jumps.kt index 7c073e5afd7..db6ae0af89a 100644 --- a/compiler/fir/resolve/testData/resolve/cfg/jumps.kt +++ b/compiler/fir/resolve/testData/resolve/cfg/jumps.kt @@ -26,7 +26,7 @@ fun test_3(x: Int?) { fun test_4(x: Int?) { do { - x as int + x as Int break } while (true) x.inc() diff --git a/compiler/fir/resolve/testData/resolve/cfg/jumps.txt b/compiler/fir/resolve/testData/resolve/cfg/jumps.txt index 52048d3559b..61de8e960fd 100644 --- a/compiler/fir/resolve/testData/resolve/cfg/jumps.txt +++ b/compiler/fir/resolve/testData/resolve/cfg/jumps.txt @@ -29,13 +29,13 @@ FILE: jumps.kt break@@@[Boolean(true)] } - R|/x|.#() + R|/x|.R|kotlin/Int.inc|() } public final fun test_4(x: R|kotlin/Int?|): R|kotlin/Unit| { do { - (R|/x| as R|class error: Symbol not found, for `int`|) + (R|/x| as R|kotlin/Int|) break@@@[Boolean(true)] } while(Boolean(true)) - R|/x|.#() + R|/x|.R|kotlin/Int.inc|() } diff --git a/compiler/fir/resolve/testData/resolve/cfg/loops.dot b/compiler/fir/resolve/testData/resolve/cfg/loops.dot index 33fd3b89a64..01083fbb028 100644 --- a/compiler/fir/resolve/testData/resolve/cfg/loops.dot +++ b/compiler/fir/resolve/testData/resolve/cfg/loops.dot @@ -138,4 +138,278 @@ subgraph testFor { 63 -> {64}; } +subgraph testWhileTrue { + 65 [shape=box label="Enter function testWhileTrue"]; + 66 [shape=box label="Enter block"]; + 67 [shape=box label="Enter while loop"]; + 68 [shape=box label="Enter loop condition"]; + 69 [shape=box label="Const: Boolean(true)"]; + 70 [shape=box label="Exit loop condition"]; + 71 [shape=box label="Enter loop block"]; + 72 [shape=box label="Enter block"]; + 73 [shape=box label="Const: Int(1)"]; + 74 [shape=box label="Exit block"]; + 75 [shape=box label="Exit loop block"]; + 76 [shape=box label="Stub[DEAD]"]; + 77 [shape=box label="Exit whileloop[DEAD]"]; + 78 [shape=box label="Const: Int(1)[DEAD]"]; + 79 [shape=box label="Exit block[DEAD]"]; + 80 [shape=box label="Exit function testWhileTrue[DEAD]"]; + + 65 -> {66}; + 66 -> {67}; + 67 -> {68}; + 68 -> {69}; + 69 -> {70}; + 70 -> {71}; + 70 -> {76} [style=dotted]; + 71 -> {72}; + 72 -> {73}; + 73 -> {74}; + 74 -> {75}; + 75 -> {68}; + 76 -> {77} [style=dotted]; + 77 -> {78} [style=dotted]; + 78 -> {79} [style=dotted]; + 79 -> {80} [style=dotted]; +} + +subgraph testWhileTrueWithBreak { + 81 [shape=box label="Enter function testWhileTrueWithBreak"]; + 82 [shape=box label="Enter block"]; + 83 [shape=box label="Enter while loop"]; + 84 [shape=box label="Enter loop condition"]; + 85 [shape=box label="Const: Boolean(true)"]; + 86 [shape=box label="Exit loop condition"]; + 87 [shape=box label="Enter loop block"]; + 88 [shape=box label="Enter block"]; + 89 [shape=box label="Enter when"]; + 90 [shape=box label="Enter when branch condition "]; + 91 [shape=box label="Access variable R|/b|"]; + 92 [shape=box label="Exit when branch condition"]; + 93 [shape=box label="Enter block"]; + 94 [shape=box label="Jump: break@@@[Boolean(true)] "]; + 95 [shape=box label="Stub[DEAD]"]; + 96 [shape=box label="Exit block[DEAD]"]; + 97 [shape=box label="Exit when branch result[DEAD]"]; + 98 [shape=box label="Enter when branch condition else"]; + 99 [shape=box label="Exit when branch condition"]; + 100 [shape=box label="Enter block"]; + 101 [shape=box label="Exit block"]; + 102 [shape=box label="Exit when branch result"]; + 103 [shape=box label="Exit when"]; + 104 [shape=box label="Exit block"]; + 105 [shape=box label="Exit loop block"]; + 106 [shape=box label="Stub[DEAD]"]; + 107 [shape=box label="Exit whileloop"]; + 108 [shape=box label="Const: Int(1)"]; + 109 [shape=box label="Exit block"]; + 110 [shape=box label="Exit function testWhileTrueWithBreak"]; + + 81 -> {82}; + 82 -> {83}; + 83 -> {84}; + 84 -> {85}; + 85 -> {86}; + 86 -> {87}; + 86 -> {106} [style=dotted]; + 87 -> {88}; + 88 -> {89}; + 89 -> {90}; + 90 -> {91}; + 91 -> {92}; + 92 -> {93 98}; + 93 -> {94}; + 94 -> {107}; + 94 -> {95} [style=dotted]; + 95 -> {96} [style=dotted]; + 96 -> {97} [style=dotted]; + 97 -> {103} [style=dotted]; + 98 -> {99}; + 99 -> {100}; + 100 -> {101}; + 101 -> {102}; + 102 -> {103}; + 103 -> {104}; + 104 -> {105}; + 105 -> {84}; + 106 -> {107} [style=dotted]; + 107 -> {108}; + 108 -> {109}; + 109 -> {110}; +} + +subgraph testWhileFalse { + 111 [shape=box label="Enter function testWhileFalse"]; + 112 [shape=box label="Enter block"]; + 113 [shape=box label="Enter while loop"]; + 114 [shape=box label="Enter loop condition"]; + 115 [shape=box label="Const: Boolean(false)"]; + 116 [shape=box label="Exit loop condition"]; + 117 [shape=box label="Stub[DEAD]"]; + 118 [shape=box label="Enter loop block[DEAD]"]; + 119 [shape=box label="Enter block[DEAD]"]; + 120 [shape=box label="Const: Int(1)[DEAD]"]; + 121 [shape=box label="Exit block[DEAD]"]; + 122 [shape=box label="Exit loop block[DEAD]"]; + 123 [shape=box label="Exit whileloop"]; + 124 [shape=box label="Const: Int(1)"]; + 125 [shape=box label="Exit block"]; + 126 [shape=box label="Exit function testWhileFalse"]; + + 111 -> {112}; + 112 -> {113}; + 113 -> {114}; + 114 -> {115}; + 115 -> {116}; + 116 -> {123}; + 116 -> {117} [style=dotted]; + 117 -> {118} [style=dotted]; + 118 -> {119} [style=dotted]; + 119 -> {120} [style=dotted]; + 120 -> {121} [style=dotted]; + 121 -> {122} [style=dotted]; + 122 -> {114} [style=dotted]; + 123 -> {124}; + 124 -> {125}; + 125 -> {126}; +} + +subgraph testDoWhileTrue { + 127 [shape=box label="Enter function testDoWhileTrue"]; + 128 [shape=box label="Enter block"]; + 129 [shape=box label="Enter do-while loop"]; + 130 [shape=box label="Enter loop block"]; + 131 [shape=box label="Enter block"]; + 132 [shape=box label="Const: Int(1)"]; + 133 [shape=box label="Exit block"]; + 134 [shape=box label="Exit loop block"]; + 135 [shape=box label="Enter loop condition"]; + 136 [shape=box label="Const: Boolean(true)"]; + 137 [shape=box label="Exit loop condition"]; + 138 [shape=box label="Stub[DEAD]"]; + 139 [shape=box label="Exit do-whileloop[DEAD]"]; + 140 [shape=box label="Const: Int(1)[DEAD]"]; + 141 [shape=box label="Exit block[DEAD]"]; + 142 [shape=box label="Exit function testDoWhileTrue[DEAD]"]; + + 127 -> {128}; + 128 -> {129}; + 129 -> {130}; + 130 -> {131}; + 131 -> {132}; + 132 -> {133}; + 133 -> {134}; + 134 -> {135}; + 135 -> {136}; + 136 -> {137}; + 137 -> {130}; + 137 -> {138} [style=dotted]; + 138 -> {139} [style=dotted]; + 139 -> {140} [style=dotted]; + 140 -> {141} [style=dotted]; + 141 -> {142} [style=dotted]; +} + +subgraph testDoWhileTrueWithBreak { + 143 [shape=box label="Enter function testDoWhileTrueWithBreak"]; + 144 [shape=box label="Enter block"]; + 145 [shape=box label="Enter do-while loop"]; + 146 [shape=box label="Enter loop block"]; + 147 [shape=box label="Enter block"]; + 148 [shape=box label="Enter when"]; + 149 [shape=box label="Enter when branch condition "]; + 150 [shape=box label="Access variable R|/b|"]; + 151 [shape=box label="Exit when branch condition"]; + 152 [shape=box label="Enter block"]; + 153 [shape=box label="Jump: break@@@[Boolean(true)] "]; + 154 [shape=box label="Stub[DEAD]"]; + 155 [shape=box label="Exit block[DEAD]"]; + 156 [shape=box label="Exit when branch result[DEAD]"]; + 157 [shape=box label="Enter when branch condition else"]; + 158 [shape=box label="Exit when branch condition"]; + 159 [shape=box label="Enter block"]; + 160 [shape=box label="Exit block"]; + 161 [shape=box label="Exit when branch result"]; + 162 [shape=box label="Exit when"]; + 163 [shape=box label="Exit block"]; + 164 [shape=box label="Exit loop block"]; + 165 [shape=box label="Enter loop condition"]; + 166 [shape=box label="Const: Boolean(true)"]; + 167 [shape=box label="Exit loop condition"]; + 168 [shape=box label="Stub[DEAD]"]; + 169 [shape=box label="Exit do-whileloop"]; + 170 [shape=box label="Const: Int(1)"]; + 171 [shape=box label="Exit block"]; + 172 [shape=box label="Exit function testDoWhileTrueWithBreak"]; + + 143 -> {144}; + 144 -> {145}; + 145 -> {146}; + 146 -> {147}; + 147 -> {148}; + 148 -> {149}; + 149 -> {150}; + 150 -> {151}; + 151 -> {152 157}; + 152 -> {153}; + 153 -> {169}; + 153 -> {154} [style=dotted]; + 154 -> {155} [style=dotted]; + 155 -> {156} [style=dotted]; + 156 -> {162} [style=dotted]; + 157 -> {158}; + 158 -> {159}; + 159 -> {160}; + 160 -> {161}; + 161 -> {162}; + 162 -> {163}; + 163 -> {164}; + 164 -> {165}; + 165 -> {166}; + 166 -> {167}; + 167 -> {146}; + 167 -> {168} [style=dotted]; + 168 -> {169} [style=dotted]; + 169 -> {170}; + 170 -> {171}; + 171 -> {172}; +} + +subgraph testDoWhileFalse { + 173 [shape=box label="Enter function testDoWhileFalse"]; + 174 [shape=box label="Enter block"]; + 175 [shape=box label="Enter do-while loop"]; + 176 [shape=box label="Enter loop block"]; + 177 [shape=box label="Enter block"]; + 178 [shape=box label="Const: Int(1)"]; + 179 [shape=box label="Exit block"]; + 180 [shape=box label="Exit loop block"]; + 181 [shape=box label="Enter loop condition"]; + 182 [shape=box label="Const: Boolean(false)"]; + 183 [shape=box label="Exit loop condition"]; + 184 [shape=box label="Exit do-whileloop"]; + 185 [shape=box label="Const: Int(1)"]; + 186 [shape=box label="Exit block"]; + 187 [shape=box label="Exit function testDoWhileFalse"]; + 188 [shape=box label="Stub[DEAD]"]; + + 173 -> {174}; + 174 -> {175}; + 175 -> {176}; + 176 -> {177}; + 177 -> {178}; + 178 -> {179}; + 179 -> {180}; + 180 -> {181}; + 181 -> {182}; + 182 -> {183}; + 183 -> {184}; + 183 -> {188} [style=dotted]; + 184 -> {185}; + 185 -> {186}; + 186 -> {187}; + 188 -> {176} [style=dotted]; +} + } diff --git a/compiler/fir/resolve/testData/resolve/cfg/loops.kt b/compiler/fir/resolve/testData/resolve/cfg/loops.kt index a939f97fe25..b52562e0edc 100644 --- a/compiler/fir/resolve/testData/resolve/cfg/loops.kt +++ b/compiler/fir/resolve/testData/resolve/cfg/loops.kt @@ -17,4 +17,51 @@ fun testFor(x: Any?) { val y = x is String } x is String +} + +fun testWhileTrue() { + while (true) { + 1 + } + 1 +} + +fun testWhileTrueWithBreak(b: Boolean) { + while (true) { + if (b) { + break + } + } + 1 +} + + +fun testWhileFalse() { + while (false) { + 1 + } + 1 +} + +fun testDoWhileTrue() { + do { + 1 + } while (true) + 1 +} + +fun testDoWhileTrueWithBreak(b: Boolean) { + do { + if (b) { + break + } + } while (true) + 1 +} + +fun testDoWhileFalse() { + do { + 1 + } while (false) + 1 } \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/cfg/loops.txt b/compiler/fir/resolve/testData/resolve/cfg/loops.txt index f13ada73e89..f60a2790fef 100644 --- a/compiler/fir/resolve/testData/resolve/cfg/loops.txt +++ b/compiler/fir/resolve/testData/resolve/cfg/loops.txt @@ -23,3 +23,59 @@ FILE: loops.kt (R|/x| is R|kotlin/String|) } + public final fun testWhileTrue(): R|kotlin/Unit| { + while(Boolean(true)) { + Int(1) + } + + Int(1) + } + public final fun testWhileTrueWithBreak(b: R|kotlin/Boolean|): R|kotlin/Unit| { + while(Boolean(true)) { + when () { + R|/b| -> { + break@@@[Boolean(true)] + } + else -> { + } + } + + } + + Int(1) + } + public final fun testWhileFalse(): R|kotlin/Unit| { + while(Boolean(false)) { + Int(1) + } + + Int(1) + } + public final fun testDoWhileTrue(): R|kotlin/Unit| { + do { + Int(1) + } + while(Boolean(true)) + Int(1) + } + public final fun testDoWhileTrueWithBreak(b: R|kotlin/Boolean|): R|kotlin/Unit| { + do { + when () { + R|/b| -> { + break@@@[Boolean(true)] + } + else -> { + } + } + + } + while(Boolean(true)) + Int(1) + } + public final fun testDoWhileFalse(): R|kotlin/Unit| { + do { + Int(1) + } + while(Boolean(false)) + Int(1) + } diff --git a/compiler/fir/resolve/testData/resolve/cfg/tryCatch.dot b/compiler/fir/resolve/testData/resolve/cfg/tryCatch.dot index 6c0e64362c0..e4511921d9f 100644 --- a/compiler/fir/resolve/testData/resolve/cfg/tryCatch.dot +++ b/compiler/fir/resolve/testData/resolve/cfg/tryCatch.dot @@ -151,29 +151,31 @@ subgraph test_3 { 101 [shape=box label="Variable declaration: lval y: R|kotlin/Int|"]; 102 [shape=box label="Exit block"]; 103 [shape=box label="Exit loop block"]; - 104 [shape=box label="Exit whileloop"]; - 105 [shape=box label="Const: Int(3)"]; - 106 [shape=box label="Variable declaration: lval z: R|kotlin/Int|"]; - 107 [shape=box label="Exit block"]; - 108 [shape=box label="Exit function test_3"]; + 104 [shape=box label="Stub[DEAD]"]; + 105 [shape=box label="Exit whileloop"]; + 106 [shape=box label="Const: Int(3)"]; + 107 [shape=box label="Variable declaration: lval z: R|kotlin/Int|"]; + 108 [shape=box label="Exit block"]; + 109 [shape=box label="Exit function test_3"]; 41 -> {42}; 42 -> {43}; 43 -> {44}; 44 -> {45}; 45 -> {46}; - 46 -> {104 47}; + 46 -> {47}; + 46 -> {104} [style=dotted]; 47 -> {48}; 48 -> {49}; 49 -> {50}; - 50 -> {108 93 87 51}; + 50 -> {109 93 87 51}; 51 -> {52}; 52 -> {53}; 53 -> {54}; 54 -> {55}; 55 -> {56 61}; 56 -> {57}; - 57 -> {108}; + 57 -> {109}; 57 -> {58} [style=dotted]; 58 -> {59} [style=dotted]; 59 -> {60} [style=dotted]; @@ -192,7 +194,7 @@ subgraph test_3 { 72 -> {73}; 73 -> {74 79}; 74 -> {75}; - 75 -> {104}; + 75 -> {105}; 75 -> {76} [style=dotted]; 76 -> {77} [style=dotted]; 77 -> {78} [style=dotted]; @@ -205,16 +207,15 @@ subgraph test_3 { 84 -> {85}; 85 -> {86}; 86 -> {99}; - 87 -> {108 88}; + 87 -> {109 88}; 88 -> {89}; - 89 -> {104}; + 89 -> {105}; 89 -> {90} [style=dotted]; 90 -> {91} [style=dotted]; 91 -> {92} [style=dotted]; 92 -> {99} [style=dotted]; - 93 -> {108 94}; + 93 -> {109 94}; 94 -> {95}; - 95 -> {44}; 95 -> {96} [style=dotted]; 96 -> {97} [style=dotted]; 97 -> {98} [style=dotted]; @@ -224,10 +225,11 @@ subgraph test_3 { 101 -> {102}; 102 -> {103}; 103 -> {44}; - 104 -> {105}; + 104 -> {105} [style=dotted]; 105 -> {106}; 106 -> {107}; 107 -> {108}; + 108 -> {109}; } } diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/endlessLoops.dot b/compiler/fir/resolve/testData/resolve/smartcasts/endlessLoops.dot new file mode 100644 index 00000000000..c1d2f007071 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/endlessLoops.dot @@ -0,0 +1,513 @@ +digraph endlessLoops_kt { +graph [splines=ortho] + +subgraph foo { + 0 [shape=box label="Enter function foo"]; + 1 [shape=box label="Exit function foo"]; + + 0 -> {1}; +} + +subgraph test_1 { + 2 [shape=box label="Enter function test_1"]; + 3 [shape=box label="Enter block"]; + 4 [shape=box label="Enter while loop"]; + 5 [shape=box label="Enter loop condition"]; + 6 [shape=box label="Const: Boolean(true)"]; + 7 [shape=box label="Exit loop condition"]; + 8 [shape=box label="Enter loop block"]; + 9 [shape=box label="Enter block"]; + 10 [shape=box label="Access variable R|/x|"]; + 11 [shape=box label="Type operator: x as A"]; + 12 [shape=box label="Enter when"]; + 13 [shape=box label="Enter when branch condition "]; + 14 [shape=box label="Access variable R|/b|"]; + 15 [shape=box label="Exit when branch condition"]; + 16 [shape=box label="Enter block"]; + 17 [shape=box label="Jump: break@@@[Boolean(true)] "]; + 18 [shape=box label="Stub[DEAD]"]; + 19 [shape=box label="Exit block[DEAD]"]; + 20 [shape=box label="Exit when branch result[DEAD]"]; + 21 [shape=box label="Enter when branch condition else"]; + 22 [shape=box label="Exit when branch condition"]; + 23 [shape=box label="Enter block"]; + 24 [shape=box label="Exit block"]; + 25 [shape=box label="Exit when branch result"]; + 26 [shape=box label="Exit when"]; + 27 [shape=box label="Exit block"]; + 28 [shape=box label="Exit loop block"]; + 29 [shape=box label="Stub[DEAD]"]; + 30 [shape=box label="Exit whileloop"]; + 31 [shape=box label="Access variable R|/x|"]; + 32 [shape=box label="Function call: R|/x|.R|/A.foo|()"]; + 33 [shape=box label="Exit block"]; + 34 [shape=box label="Exit function test_1"]; + + 2 -> {3}; + 3 -> {4}; + 4 -> {5}; + 5 -> {6}; + 6 -> {7}; + 7 -> {8}; + 7 -> {29} [style=dotted]; + 8 -> {9}; + 9 -> {10}; + 10 -> {11}; + 11 -> {12}; + 12 -> {13}; + 13 -> {14}; + 14 -> {15}; + 15 -> {16 21}; + 16 -> {17}; + 17 -> {30}; + 17 -> {18} [style=dotted]; + 18 -> {19} [style=dotted]; + 19 -> {20} [style=dotted]; + 20 -> {26} [style=dotted]; + 21 -> {22}; + 22 -> {23}; + 23 -> {24}; + 24 -> {25}; + 25 -> {26}; + 26 -> {27}; + 27 -> {28}; + 28 -> {5}; + 29 -> {30} [style=dotted]; + 30 -> {31}; + 31 -> {32}; + 32 -> {33}; + 33 -> {34}; +} + +subgraph test_2 { + 35 [shape=box label="Enter function test_2"]; + 36 [shape=box label="Enter block"]; + 37 [shape=box label="Enter while loop"]; + 38 [shape=box label="Enter loop condition"]; + 39 [shape=box label="Const: Boolean(true)"]; + 40 [shape=box label="Exit loop condition"]; + 41 [shape=box label="Enter loop block"]; + 42 [shape=box label="Enter block"]; + 43 [shape=box label="Enter when"]; + 44 [shape=box label="Enter when branch condition "]; + 45 [shape=box label="Access variable R|/b|"]; + 46 [shape=box label="Exit when branch condition"]; + 47 [shape=box label="Enter block"]; + 48 [shape=box label="Access variable R|/x|"]; + 49 [shape=box label="Type operator: x as A"]; + 50 [shape=box label="Jump: break@@@[Boolean(true)] "]; + 51 [shape=box label="Stub[DEAD]"]; + 52 [shape=box label="Exit block[DEAD]"]; + 53 [shape=box label="Exit when branch result[DEAD]"]; + 54 [shape=box label="Enter when branch condition else"]; + 55 [shape=box label="Exit when branch condition"]; + 56 [shape=box label="Enter block"]; + 57 [shape=box label="Exit block"]; + 58 [shape=box label="Exit when branch result"]; + 59 [shape=box label="Exit when"]; + 60 [shape=box label="Exit block"]; + 61 [shape=box label="Exit loop block"]; + 62 [shape=box label="Stub[DEAD]"]; + 63 [shape=box label="Exit whileloop"]; + 64 [shape=box label="Access variable R|/x|"]; + 65 [shape=box label="Function call: R|/x|.R|/A.foo|()"]; + 66 [shape=box label="Exit block"]; + 67 [shape=box label="Exit function test_2"]; + + 35 -> {36}; + 36 -> {37}; + 37 -> {38}; + 38 -> {39}; + 39 -> {40}; + 40 -> {41}; + 40 -> {62} [style=dotted]; + 41 -> {42}; + 42 -> {43}; + 43 -> {44}; + 44 -> {45}; + 45 -> {46}; + 46 -> {47 54}; + 47 -> {48}; + 48 -> {49}; + 49 -> {50}; + 50 -> {63}; + 50 -> {51} [style=dotted]; + 51 -> {52} [style=dotted]; + 52 -> {53} [style=dotted]; + 53 -> {59} [style=dotted]; + 54 -> {55}; + 55 -> {56}; + 56 -> {57}; + 57 -> {58}; + 58 -> {59}; + 59 -> {60}; + 60 -> {61}; + 61 -> {38}; + 62 -> {63} [style=dotted]; + 63 -> {64}; + 64 -> {65}; + 65 -> {66}; + 66 -> {67}; +} + +subgraph test_3 { + 68 [shape=box label="Enter function test_3"]; + 69 [shape=box label="Enter block"]; + 70 [shape=box label="Enter while loop"]; + 71 [shape=box label="Enter loop condition"]; + 72 [shape=box label="Const: Boolean(true)"]; + 73 [shape=box label="Exit loop condition"]; + 74 [shape=box label="Enter loop block"]; + 75 [shape=box label="Enter block"]; + 76 [shape=box label="Access variable R|/x|"]; + 77 [shape=box label="Type operator: x as A"]; + 78 [shape=box label="Enter when"]; + 79 [shape=box label="Enter when branch condition "]; + 80 [shape=box label="Access variable R|/b|"]; + 81 [shape=box label="Exit when branch condition"]; + 82 [shape=box label="Enter block"]; + 83 [shape=box label="Jump: break@@@[Boolean(true)] "]; + 84 [shape=box label="Stub[DEAD]"]; + 85 [shape=box label="Exit block[DEAD]"]; + 86 [shape=box label="Exit when branch result[DEAD]"]; + 87 [shape=box label="Enter when branch condition else"]; + 88 [shape=box label="Exit when branch condition"]; + 89 [shape=box label="Enter block"]; + 90 [shape=box label="Exit block"]; + 91 [shape=box label="Exit when branch result"]; + 92 [shape=box label="Exit when"]; + 93 [shape=box label="Enter when"]; + 94 [shape=box label="Enter when branch condition "]; + 95 [shape=box label="Access variable R|/b|"]; + 96 [shape=box label="Exit when branch condition"]; + 97 [shape=box label="Enter block"]; + 98 [shape=box label="Jump: break@@@[Boolean(true)] "]; + 99 [shape=box label="Stub[DEAD]"]; + 100 [shape=box label="Exit block[DEAD]"]; + 101 [shape=box label="Exit when branch result[DEAD]"]; + 102 [shape=box label="Enter when branch condition else"]; + 103 [shape=box label="Exit when branch condition"]; + 104 [shape=box label="Enter block"]; + 105 [shape=box label="Exit block"]; + 106 [shape=box label="Exit when branch result"]; + 107 [shape=box label="Exit when"]; + 108 [shape=box label="Exit block"]; + 109 [shape=box label="Exit loop block"]; + 110 [shape=box label="Stub[DEAD]"]; + 111 [shape=box label="Exit whileloop"]; + 112 [shape=box label="Access variable R|/x|"]; + 113 [shape=box label="Function call: R|/x|.R|/A.foo|()"]; + 114 [shape=box label="Exit block"]; + 115 [shape=box label="Exit function test_3"]; + + 68 -> {69}; + 69 -> {70}; + 70 -> {71}; + 71 -> {72}; + 72 -> {73}; + 73 -> {74}; + 73 -> {110} [style=dotted]; + 74 -> {75}; + 75 -> {76}; + 76 -> {77}; + 77 -> {78}; + 78 -> {79}; + 79 -> {80}; + 80 -> {81}; + 81 -> {82 87}; + 82 -> {83}; + 83 -> {111}; + 83 -> {84} [style=dotted]; + 84 -> {85} [style=dotted]; + 85 -> {86} [style=dotted]; + 86 -> {92} [style=dotted]; + 87 -> {88}; + 88 -> {89}; + 89 -> {90}; + 90 -> {91}; + 91 -> {92}; + 92 -> {93}; + 93 -> {94}; + 94 -> {95}; + 95 -> {96}; + 96 -> {97 102}; + 97 -> {98}; + 98 -> {111}; + 98 -> {99} [style=dotted]; + 99 -> {100} [style=dotted]; + 100 -> {101} [style=dotted]; + 101 -> {107} [style=dotted]; + 102 -> {103}; + 103 -> {104}; + 104 -> {105}; + 105 -> {106}; + 106 -> {107}; + 107 -> {108}; + 108 -> {109}; + 109 -> {71}; + 110 -> {111} [style=dotted]; + 111 -> {112}; + 112 -> {113}; + 113 -> {114}; + 114 -> {115}; +} + +subgraph test_4 { + 116 [shape=box label="Enter function test_4"]; + 117 [shape=box label="Enter block"]; + 118 [shape=box label="Enter while loop"]; + 119 [shape=box label="Enter loop condition"]; + 120 [shape=box label="Const: Boolean(true)"]; + 121 [shape=box label="Exit loop condition"]; + 122 [shape=box label="Enter loop block"]; + 123 [shape=box label="Enter block"]; + 124 [shape=box label="Enter when"]; + 125 [shape=box label="Enter when branch condition "]; + 126 [shape=box label="Access variable R|/b|"]; + 127 [shape=box label="Exit when branch condition"]; + 128 [shape=box label="Enter block"]; + 129 [shape=box label="Access variable R|/x|"]; + 130 [shape=box label="Type operator: x as A"]; + 131 [shape=box label="Jump: break@@@[Boolean(true)] "]; + 132 [shape=box label="Stub[DEAD]"]; + 133 [shape=box label="Exit block[DEAD]"]; + 134 [shape=box label="Exit when branch result[DEAD]"]; + 135 [shape=box label="Enter when branch condition else"]; + 136 [shape=box label="Exit when branch condition"]; + 137 [shape=box label="Enter block"]; + 138 [shape=box label="Exit block"]; + 139 [shape=box label="Exit when branch result"]; + 140 [shape=box label="Exit when"]; + 141 [shape=box label="Jump: break@@@[Boolean(true)] "]; + 142 [shape=box label="Stub[DEAD]"]; + 143 [shape=box label="Exit block[DEAD]"]; + 144 [shape=box label="Exit loop block[DEAD]"]; + 145 [shape=box label="Stub[DEAD]"]; + 146 [shape=box label="Exit whileloop"]; + 147 [shape=box label="Access variable R|/x|"]; + 148 [shape=box label="Function call: R|/x|.#()"]; + 149 [shape=box label="Exit block"]; + 150 [shape=box label="Exit function test_4"]; + + 116 -> {117}; + 117 -> {118}; + 118 -> {119}; + 119 -> {120}; + 120 -> {121}; + 121 -> {122}; + 121 -> {145} [style=dotted]; + 122 -> {123}; + 123 -> {124}; + 124 -> {125}; + 125 -> {126}; + 126 -> {127}; + 127 -> {128 135}; + 128 -> {129}; + 129 -> {130}; + 130 -> {131}; + 131 -> {146}; + 131 -> {132} [style=dotted]; + 132 -> {133} [style=dotted]; + 133 -> {134} [style=dotted]; + 134 -> {140} [style=dotted]; + 135 -> {136}; + 136 -> {137}; + 137 -> {138}; + 138 -> {139}; + 139 -> {140}; + 140 -> {141}; + 141 -> {146}; + 141 -> {142} [style=dotted]; + 142 -> {143} [style=dotted]; + 143 -> {144} [style=dotted]; + 144 -> {119} [style=dotted]; + 145 -> {146} [style=dotted]; + 146 -> {147}; + 147 -> {148}; + 148 -> {149}; + 149 -> {150}; +} + +subgraph test_5 { + 151 [shape=box label="Enter function test_5"]; + 152 [shape=box label="Enter block"]; + 153 [shape=box label="Enter do-while loop"]; + 154 [shape=box label="Enter loop block"]; + 155 [shape=box label="Enter block"]; + 156 [shape=box label="Enter when"]; + 157 [shape=box label="Enter when branch condition "]; + 158 [shape=box label="Access variable R|/b|"]; + 159 [shape=box label="Exit when branch condition"]; + 160 [shape=box label="Enter block"]; + 161 [shape=box label="Access variable R|/x|"]; + 162 [shape=box label="Type operator: x as A"]; + 163 [shape=box label="Jump: break@@@[Boolean(true)] "]; + 164 [shape=box label="Stub[DEAD]"]; + 165 [shape=box label="Exit block[DEAD]"]; + 166 [shape=box label="Exit when branch result[DEAD]"]; + 167 [shape=box label="Enter when branch condition else"]; + 168 [shape=box label="Exit when branch condition"]; + 169 [shape=box label="Enter block"]; + 170 [shape=box label="Exit block"]; + 171 [shape=box label="Exit when branch result"]; + 172 [shape=box label="Exit when"]; + 173 [shape=box label="Exit block"]; + 174 [shape=box label="Exit loop block"]; + 175 [shape=box label="Enter loop condition"]; + 176 [shape=box label="Const: Boolean(true)"]; + 177 [shape=box label="Exit loop condition"]; + 178 [shape=box label="Stub[DEAD]"]; + 179 [shape=box label="Exit do-whileloop"]; + 180 [shape=box label="Access variable R|/x|"]; + 181 [shape=box label="Function call: R|/x|.R|/A.foo|()"]; + 182 [shape=box label="Exit block"]; + 183 [shape=box label="Exit function test_5"]; + + 151 -> {152}; + 152 -> {153}; + 153 -> {154}; + 154 -> {155}; + 155 -> {156}; + 156 -> {157}; + 157 -> {158}; + 158 -> {159}; + 159 -> {160 167}; + 160 -> {161}; + 161 -> {162}; + 162 -> {163}; + 163 -> {179}; + 163 -> {164} [style=dotted]; + 164 -> {165} [style=dotted]; + 165 -> {166} [style=dotted]; + 166 -> {172} [style=dotted]; + 167 -> {168}; + 168 -> {169}; + 169 -> {170}; + 170 -> {171}; + 171 -> {172}; + 172 -> {173}; + 173 -> {174}; + 174 -> {175}; + 175 -> {176}; + 176 -> {177}; + 177 -> {154}; + 177 -> {178} [style=dotted]; + 178 -> {179} [style=dotted]; + 179 -> {180}; + 180 -> {181}; + 181 -> {182}; + 182 -> {183}; +} + +subgraph test_6 { + 184 [shape=box label="Enter function test_6"]; + 185 [shape=box label="Enter block"]; + 186 [shape=box label="Enter do-while loop"]; + 187 [shape=box label="Enter loop block"]; + 188 [shape=box label="Enter block"]; + 189 [shape=box label="Access variable R|/x|"]; + 190 [shape=box label="Type operator: x as A"]; + 191 [shape=box label="Enter when"]; + 192 [shape=box label="Enter when branch condition "]; + 193 [shape=box label="Access variable R|/b|"]; + 194 [shape=box label="Exit when branch condition"]; + 195 [shape=box label="Enter block"]; + 196 [shape=box label="Jump: break@@@[Boolean(true)] "]; + 197 [shape=box label="Stub[DEAD]"]; + 198 [shape=box label="Exit block[DEAD]"]; + 199 [shape=box label="Exit when branch result[DEAD]"]; + 200 [shape=box label="Enter when branch condition else"]; + 201 [shape=box label="Exit when branch condition"]; + 202 [shape=box label="Enter block"]; + 203 [shape=box label="Exit block"]; + 204 [shape=box label="Exit when branch result"]; + 205 [shape=box label="Exit when"]; + 206 [shape=box label="Exit block"]; + 207 [shape=box label="Exit loop block"]; + 208 [shape=box label="Enter loop condition"]; + 209 [shape=box label="Const: Boolean(true)"]; + 210 [shape=box label="Exit loop condition"]; + 211 [shape=box label="Stub[DEAD]"]; + 212 [shape=box label="Exit do-whileloop"]; + 213 [shape=box label="Access variable R|/x|"]; + 214 [shape=box label="Function call: R|/x|.R|/A.foo|()"]; + 215 [shape=box label="Exit block"]; + 216 [shape=box label="Exit function test_6"]; + + 184 -> {185}; + 185 -> {186}; + 186 -> {187}; + 187 -> {188}; + 188 -> {189}; + 189 -> {190}; + 190 -> {191}; + 191 -> {192}; + 192 -> {193}; + 193 -> {194}; + 194 -> {195 200}; + 195 -> {196}; + 196 -> {212}; + 196 -> {197} [style=dotted]; + 197 -> {198} [style=dotted]; + 198 -> {199} [style=dotted]; + 199 -> {205} [style=dotted]; + 200 -> {201}; + 201 -> {202}; + 202 -> {203}; + 203 -> {204}; + 204 -> {205}; + 205 -> {206}; + 206 -> {207}; + 207 -> {208}; + 208 -> {209}; + 209 -> {210}; + 210 -> {187}; + 210 -> {211} [style=dotted]; + 211 -> {212} [style=dotted]; + 212 -> {213}; + 213 -> {214}; + 214 -> {215}; + 215 -> {216}; +} + +subgraph test_7 { + 217 [shape=box label="Enter function test_7"]; + 218 [shape=box label="Enter block"]; + 219 [shape=box label="Enter do-while loop"]; + 220 [shape=box label="Enter loop block"]; + 221 [shape=box label="Enter block"]; + 222 [shape=box label="Access variable R|/x|"]; + 223 [shape=box label="Type operator: x as A"]; + 224 [shape=box label="Exit block"]; + 225 [shape=box label="Exit loop block"]; + 226 [shape=box label="Enter loop condition"]; + 227 [shape=box label="Const: Boolean(true)"]; + 228 [shape=box label="Exit loop condition"]; + 229 [shape=box label="Stub[DEAD]"]; + 230 [shape=box label="Exit do-whileloop[DEAD]"]; + 231 [shape=box label="Access variable R|/x|[DEAD]"]; + 232 [shape=box label="Function call: R|/x|.#()[DEAD]"]; + 233 [shape=box label="Exit block[DEAD]"]; + 234 [shape=box label="Exit function test_7[DEAD]"]; + + 217 -> {218}; + 218 -> {219}; + 219 -> {220}; + 220 -> {221}; + 221 -> {222}; + 222 -> {223}; + 223 -> {224}; + 224 -> {225}; + 225 -> {226}; + 226 -> {227}; + 227 -> {228}; + 228 -> {220}; + 228 -> {229} [style=dotted]; + 229 -> {230} [style=dotted]; + 230 -> {231} [style=dotted]; + 231 -> {232} [style=dotted]; + 232 -> {233} [style=dotted]; + 233 -> {234} [style=dotted]; +} + +} diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/endlessLoops.kt b/compiler/fir/resolve/testData/resolve/smartcasts/endlessLoops.kt new file mode 100644 index 00000000000..32ee9a395cf --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/endlessLoops.kt @@ -0,0 +1,74 @@ +interface A { + fun foo() +} + +fun test_1(x: Any, b: Boolean) { + while (true) { + x as A + if (b) { + break + } + } + x.foo() +} + +fun test_2(x: Any, b: Boolean) { + while (true) { + if (b) { + x as A + break + } + } + x.foo() +} + +fun test_3(x: Any, b: Boolean) { + while (true) { + x as A + if (b) { + break + } + if (b) { + break + } + } + x.foo() +} + +fun test_4(x: Any, b: Boolean) { + while (true) { + if (b) { + x as A + break + } + break + } + x.foo() // No smartcast +} + +fun test_5(x: Any, b: Boolean) { + do { + if (b) { + x as A + break + } + } while (true) + x.foo() +} + +fun test_6(x: Any, b: Boolean) { + do { + x as A + if (b) { + break + } + } while (true) + x.foo() +} + +fun test_7(x: Any) { + do { + x as A + } while (true) + x.foo() // No smartcast +} diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/endlessLoops.txt b/compiler/fir/resolve/testData/resolve/smartcasts/endlessLoops.txt new file mode 100644 index 00000000000..80fc13dc5ad --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/endlessLoops.txt @@ -0,0 +1,111 @@ +FILE: endlessLoops.kt + public abstract interface A : R|kotlin/Any| { + public abstract fun foo(): R|kotlin/Unit| + + } + public final fun test_1(x: R|kotlin/Any|, b: R|kotlin/Boolean|): R|kotlin/Unit| { + while(Boolean(true)) { + (R|/x| as R|A|) + when () { + R|/b| -> { + break@@@[Boolean(true)] + } + else -> { + } + } + + } + + R|/x|.R|/A.foo|() + } + public final fun test_2(x: R|kotlin/Any|, b: R|kotlin/Boolean|): R|kotlin/Unit| { + while(Boolean(true)) { + when () { + R|/b| -> { + (R|/x| as R|A|) + break@@@[Boolean(true)] + } + else -> { + } + } + + } + + R|/x|.R|/A.foo|() + } + public final fun test_3(x: R|kotlin/Any|, b: R|kotlin/Boolean|): R|kotlin/Unit| { + while(Boolean(true)) { + (R|/x| as R|A|) + when () { + R|/b| -> { + break@@@[Boolean(true)] + } + else -> { + } + } + + when () { + R|/b| -> { + break@@@[Boolean(true)] + } + else -> { + } + } + + } + + R|/x|.R|/A.foo|() + } + public final fun test_4(x: R|kotlin/Any|, b: R|kotlin/Boolean|): R|kotlin/Unit| { + while(Boolean(true)) { + when () { + R|/b| -> { + (R|/x| as R|A|) + break@@@[Boolean(true)] + } + else -> { + } + } + + break@@@[Boolean(true)] + } + + R|/x|.#() + } + public final fun test_5(x: R|kotlin/Any|, b: R|kotlin/Boolean|): R|kotlin/Unit| { + do { + when () { + R|/b| -> { + (R|/x| as R|A|) + break@@@[Boolean(true)] + } + else -> { + } + } + + } + while(Boolean(true)) + R|/x|.R|/A.foo|() + } + public final fun test_6(x: R|kotlin/Any|, b: R|kotlin/Boolean|): R|kotlin/Unit| { + do { + (R|/x| as R|A|) + when () { + R|/b| -> { + break@@@[Boolean(true)] + } + else -> { + } + } + + } + while(Boolean(true)) + R|/x|.R|/A.foo|() + } + public final fun test_7(x: R|kotlin/Any|): R|kotlin/Unit| { + do { + (R|/x| as R|A|) + } + while(Boolean(true)) + R|/x|.#() + } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirCfgBuildingTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirCfgBuildingTestGenerated.java index 856f966fbaf..ce2596e0541 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirCfgBuildingTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirCfgBuildingTestGenerated.java @@ -119,6 +119,11 @@ public class FirCfgBuildingTestGenerated extends AbstractFirCfgBuildingTest { runTest("compiler/fir/resolve/testData/resolve/smartcasts/elvis.kt"); } + @TestMetadata("endlessLoops.kt") + public void testEndlessLoops() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/smartcasts/endlessLoops.kt"); + } + @TestMetadata("equalsAndIdentity.kt") public void testEqualsAndIdentity() throws Exception { runTest("compiler/fir/resolve/testData/resolve/smartcasts/equalsAndIdentity.kt");