From 63b0708ed5f90fab1e9617ae79d1be3fa9691448 Mon Sep 17 00:00:00 2001 From: pyos Date: Mon, 13 Jun 2022 14:17:34 +0200 Subject: [PATCH] FIR CFG: join/unify data flow from postponed lambdas at each level For example: foo( // `if` joins A & B if (condition) run { ... } // A else run { ... }, // B run { ... } // C ) // `foo` unifies `A & B` and `C`, so if it is not resolved itself, // further `if`s, `when`s, safe calls outside it, etc. continue // building the correct type predicate until the next completed // call. ^KT-44512 Fixed --- .../resolve/cfg/flowFromInplaceLambda2.dot | 711 +++++++++--------- .../cfg/flowFromInplaceLambda2.fir.txt | 10 +- .../resolve/cfg/flowFromInplaceLambda2.kt | 10 +- .../inplaceLambdaInControlFlowExpressions.dot | 144 ++-- .../incorrectSmartcastToNothing.dot | 32 +- .../smartcasts/lambdas/lambdaInWhenBranch.dot | 22 +- .../fir/resolve/dfa/FirDataFlowAnalyzer.kt | 12 +- .../dfa/cfg/ControlFlowGraphBuilder.kt | 139 ++-- .../dfa/cfg/ControlFlowGraphNodeBuilder.kt | 3 + .../FirExpressionsResolveTransformer.kt | 1 + .../kotlin/fir/resolve/dfa/cfg/CFGNode.kt | 8 + .../fir/resolve/dfa/cfg/CFGNodeRenderer.kt | 1 + .../dfa/cfg/ControlFlowGraphVisitor.kt | 4 + .../dfa/cfg/ControlFlowGraphVisitorVoid.kt | 8 + .../codegen/box/smartCasts/kt44814.dot | 232 +++--- .../smartcasts/letChangesToNullComplex.fir.kt | 2 +- .../smartcasts/withChangesToNull.fir.kt | 4 +- 17 files changed, 720 insertions(+), 623 deletions(-) diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.dot b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.dot index 1f1c8378ef6..80e4fce43f0 100644 --- a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.dot +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.dot @@ -173,258 +173,257 @@ digraph flowFromInplaceLambda2_kt { 57 [label="Postponed enter to lambda"]; subgraph cluster_16 { color=blue - 73 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 72 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; subgraph cluster_17 { color=blue - 74 [label="Enter block"]; - 75 [label="Access variable R|/x|"]; - 76 [label="Type operator: (R|/x| as R|kotlin/String|)"]; - 77 [label="Function call: R|/n|()"]; - 78 [label="Exit block"]; + 73 [label="Enter block"]; + 74 [label="Access variable R|/x|"]; + 75 [label="Type operator: (R|/x| as R|kotlin/String|)"]; + 76 [label="Function call: R|/n|()"]; + 77 [label="Exit block"]; } - 79 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + 78 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } 58 [label="Postponed exit from lambda"]; 59 [label="Function call: R|kotlin/run|(...)"]; 60 [label="Function call: R|/id|(...)"]; 61 [label="Const: Int(1)"]; - 62 [label="Call arguments union" style="filled" fillcolor=yellow]; - 63 [label="Function call: R|/someCompletedCall|(...)"]; - 64 [label="Postponed enter to lambda"]; + 62 [label="Function call: R|/someCompletedCall|(...)"]; + 63 [label="Postponed enter to lambda"]; subgraph cluster_18 { color=blue - 80 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 79 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; subgraph cluster_19 { color=blue - 81 [label="Enter block"]; - 82 [label="Access variable R|/x|"]; - 83 [label="Access variable R|kotlin/String.length|"]; - 84 [label="Const: Int(123)"]; - 85 [label="Exit block"]; + 80 [label="Enter block"]; + 81 [label="Access variable R|/x|"]; + 82 [label="Access variable #"]; + 83 [label="Const: Int(123)"]; + 84 [label="Exit block"]; } - 86 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + 85 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } - 65 [label="Postponed exit from lambda"]; - 66 [label="Function call: R|kotlin/run|(...)"]; - 67 [label="Call arguments union" style="filled" fillcolor=yellow]; - 68 [label="Function call: R|/foo|(...)"]; - 69 [label="Access variable R|/x|"]; - 70 [label="Access variable R|kotlin/String.length|"]; - 71 [label="Exit block"]; + 64 [label="Postponed exit from lambda"]; + 65 [label="Function call: R|kotlin/run|(...)"]; + 66 [label="Call arguments union" style="filled" fillcolor=yellow]; + 67 [label="Function call: R|/foo|(...)"]; + 68 [label="Access variable R|/x|"]; + 69 [label="Access variable R|kotlin/String.length|"]; + 70 [label="Exit block"]; } - 72 [label="Exit function test2" style="filled" fillcolor=red]; + 71 [label="Exit function test2" style="filled" fillcolor=red]; } 55 -> {56}; 56 -> {57}; - 57 -> {73}; + 57 -> {72}; 57 -> {58} [color=red]; - 57 -> {73} [style=dashed]; + 57 -> {72} [style=dashed]; 58 -> {59}; 59 -> {60}; 60 -> {61}; 61 -> {62}; 62 -> {63}; - 63 -> {64}; - 64 -> {80}; - 64 -> {65} [color=red]; - 64 -> {80} [style=dashed]; + 63 -> {79}; + 63 -> {64} [color=red]; + 63 -> {79} [style=dashed]; + 64 -> {65}; 65 -> {66}; 66 -> {67}; 67 -> {68}; 68 -> {69}; 69 -> {70}; 70 -> {71}; - 71 -> {72}; + 72 -> {73}; 73 -> {74}; 74 -> {75}; 75 -> {76}; 76 -> {77}; 77 -> {78}; - 78 -> {79}; - 79 -> {62} [color=red]; - 79 -> {58} [color=green]; + 78 -> {66} [color=red]; + 78 -> {58} [color=green]; + 79 -> {80}; 80 -> {81}; 81 -> {82}; 82 -> {83}; 83 -> {84}; 84 -> {85}; - 85 -> {86}; - 86 -> {67} [color=red]; - 86 -> {65} [color=green]; + 85 -> {66} [color=red]; + 85 -> {64} [color=green]; subgraph cluster_20 { color=red - 87 [label="Enter function test3" style="filled" fillcolor=red]; + 86 [label="Enter function test3" style="filled" fillcolor=red]; subgraph cluster_21 { color=blue - 88 [label="Enter block"]; - 89 [label="Postponed enter to lambda"]; + 87 [label="Enter block"]; + 88 [label="Postponed enter to lambda"]; subgraph cluster_22 { color=blue - 119 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 118 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; subgraph cluster_23 { color=blue - 120 [label="Enter block"]; - 121 [label="Access variable R|/x|"]; - 122 [label="Type operator: (R|/x| as R|kotlin/String|)"]; - 123 [label="Function call: R|/n|()"]; - 124 [label="Exit block"]; + 119 [label="Enter block"]; + 120 [label="Access variable R|/x|"]; + 121 [label="Type operator: (R|/x| as R|kotlin/String|)"]; + 122 [label="Function call: R|/n|()"]; + 123 [label="Exit block"]; } - 125 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + 124 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } - 90 [label="Postponed exit from lambda"]; - 91 [label="Function call: R|kotlin/run|(...)"]; - 92 [label="Function call: R|/id|(...)"]; + 89 [label="Postponed exit from lambda"]; + 90 [label="Function call: R|kotlin/run|(...)"]; + 91 [label="Function call: R|/id|(...)"]; subgraph cluster_24 { color=blue - 93 [label="Enter when"]; + 92 [label="Enter when"]; subgraph cluster_25 { color=blue - 94 [label="Enter when branch condition "]; - 95 [label="Const: Boolean(true)"]; - 96 [label="Exit when branch condition"]; + 93 [label="Enter when branch condition "]; + 94 [label="Const: Boolean(true)"]; + 95 [label="Exit when branch condition"]; } subgraph cluster_26 { color=blue - 97 [label="Enter when branch condition else"]; - 98 [label="Exit when branch condition"]; + 96 [label="Enter when branch condition else"]; + 97 [label="Exit when branch condition"]; } - 99 [label="Enter when branch result"]; + 98 [label="Enter when branch result"]; subgraph cluster_27 { color=blue - 100 [label="Enter block"]; - 101 [label="Const: Int(2)"]; - 102 [label="Exit block"]; + 99 [label="Enter block"]; + 100 [label="Const: Int(2)"]; + 101 [label="Exit block"]; } - 103 [label="Exit when branch result"]; - 104 [label="Enter when branch result"]; + 102 [label="Exit when branch result"]; + 103 [label="Enter when branch result"]; subgraph cluster_28 { color=blue - 105 [label="Enter block"]; - 106 [label="Const: Int(1)"]; - 107 [label="Exit block"]; + 104 [label="Enter block"]; + 105 [label="Const: Int(1)"]; + 106 [label="Exit block"]; } - 108 [label="Exit when branch result"]; - 109 [label="Exit when"]; + 107 [label="Exit when branch result"]; + 108 [label="Exit when"]; } - 110 [label="Postponed enter to lambda"]; + 109 [label="Postponed enter to lambda"]; subgraph cluster_29 { color=blue - 126 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 125 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; subgraph cluster_30 { color=blue - 127 [label="Enter block"]; - 128 [label="Access variable R|/x|"]; - 129 [label="Access variable #"]; - 130 [label="Const: Int(123)"]; - 131 [label="Exit block"]; + 126 [label="Enter block"]; + 127 [label="Access variable R|/x|"]; + 128 [label="Access variable #"]; + 129 [label="Const: Int(123)"]; + 130 [label="Exit block"]; } - 132 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + 131 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } - 111 [label="Postponed exit from lambda"]; - 112 [label="Function call: R|kotlin/run|(...)"]; - 113 [label="Call arguments union" style="filled" fillcolor=yellow]; - 114 [label="Function call: R|/foo|(...)"]; - 115 [label="Access variable R|/x|"]; - 116 [label="Access variable #"]; - 117 [label="Exit block"]; + 110 [label="Postponed exit from lambda"]; + 111 [label="Function call: R|kotlin/run|(...)"]; + 112 [label="Call arguments union" style="filled" fillcolor=yellow]; + 113 [label="Function call: R|/foo|(...)"]; + 114 [label="Access variable R|/x|"]; + 115 [label="Access variable R|kotlin/String.length|"]; + 116 [label="Exit block"]; } - 118 [label="Exit function test3" style="filled" fillcolor=red]; + 117 [label="Exit function test3" style="filled" fillcolor=red]; } + 86 -> {87}; 87 -> {88}; - 88 -> {89}; - 89 -> {119}; - 89 -> {90} [color=red]; - 89 -> {119} [style=dashed]; + 88 -> {118}; + 88 -> {89} [color=red]; + 88 -> {118} [style=dashed]; + 89 -> {90}; 90 -> {91}; 91 -> {92}; 92 -> {93}; 93 -> {94}; 94 -> {95}; - 95 -> {96}; - 96 -> {104 97}; + 95 -> {103 96}; + 96 -> {97}; 97 -> {98}; 98 -> {99}; 99 -> {100}; 100 -> {101}; 101 -> {102}; - 102 -> {103}; - 103 -> {109}; + 102 -> {108}; + 103 -> {104}; 104 -> {105}; 105 -> {106}; 106 -> {107}; 107 -> {108}; 108 -> {109}; - 109 -> {110}; - 110 -> {126}; - 110 -> {111} [color=red]; - 110 -> {126} [style=dashed]; + 109 -> {125}; + 109 -> {110} [color=red]; + 109 -> {125} [style=dashed]; + 110 -> {111}; 111 -> {112}; 112 -> {113}; 113 -> {114}; 114 -> {115}; 115 -> {116}; 116 -> {117}; - 117 -> {118}; + 118 -> {119}; 119 -> {120}; 120 -> {121}; 121 -> {122}; 122 -> {123}; 123 -> {124}; - 124 -> {125}; - 125 -> {90} [color=green]; + 124 -> {112} [color=red]; + 124 -> {89} [color=green]; + 125 -> {126}; 126 -> {127}; 127 -> {128}; 128 -> {129}; 129 -> {130}; 130 -> {131}; - 131 -> {132}; - 132 -> {113} [color=red]; - 132 -> {111} [color=green]; + 131 -> {112} [color=red]; + 131 -> {110} [color=green]; subgraph cluster_31 { color=red - 133 [label="Enter function test4" style="filled" fillcolor=red]; + 132 [label="Enter function test4" style="filled" fillcolor=red]; subgraph cluster_32 { color=blue - 134 [label="Enter block"]; - 135 [label="Access variable R|/x|"]; - 136 [label="Variable declaration: lvar p: R|kotlin/String?|"]; + 133 [label="Enter block"]; + 134 [label="Access variable R|/x|"]; + 135 [label="Variable declaration: lvar p: R|kotlin/String?|"]; subgraph cluster_33 { color=blue - 137 [label="Enter when"]; + 136 [label="Enter when"]; subgraph cluster_34 { color=blue - 138 [label="Enter when branch condition "]; - 139 [label="Access variable R|/p|"]; - 140 [label="Const: Null(null)"]; - 141 [label="Equality operator !="]; - 142 [label="Exit when branch condition"]; + 137 [label="Enter when branch condition "]; + 138 [label="Access variable R|/p|"]; + 139 [label="Const: Null(null)"]; + 140 [label="Equality operator !="]; + 141 [label="Exit when branch condition"]; } - 143 [label="Synthetic else branch"]; - 144 [label="Enter when branch result"]; + 142 [label="Synthetic else branch"]; + 143 [label="Enter when branch result"]; subgraph cluster_35 { color=blue - 145 [label="Enter block"]; + 144 [label="Enter block"]; subgraph cluster_36 { color=blue - 146 [label="Enter when"]; + 145 [label="Enter when"]; subgraph cluster_37 { color=blue - 147 [label="Enter when branch condition "]; - 148 [label="Const: Boolean(true)"]; - 149 [label="Exit when branch condition"]; + 146 [label="Enter when branch condition "]; + 147 [label="Const: Boolean(true)"]; + 148 [label="Exit when branch condition"]; } subgraph cluster_38 { color=blue - 150 [label="Enter when branch condition else"]; - 151 [label="Exit when branch condition"]; + 149 [label="Enter when branch condition else"]; + 150 [label="Exit when branch condition"]; } - 152 [label="Enter when branch result"]; + 151 [label="Enter when branch result"]; subgraph cluster_39 { color=blue - 153 [label="Enter block"]; - 154 [label="Postponed enter to lambda"]; + 152 [label="Enter block"]; + 153 [label="Postponed enter to lambda"]; subgraph cluster_40 { color=blue 188 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; @@ -436,16 +435,16 @@ digraph flowFromInplaceLambda2_kt { } 192 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } - 155 [label="Postponed exit from lambda"]; - 156 [label="Function call: R|kotlin/run|(...)"]; - 157 [label="Exit block"]; + 154 [label="Postponed exit from lambda"]; + 155 [label="Function call: R|kotlin/run|(...)"]; + 156 [label="Exit block"]; } - 158 [label="Exit when branch result"]; - 159 [label="Enter when branch result"]; + 157 [label="Exit when branch result"]; + 158 [label="Enter when branch result"]; subgraph cluster_42 { color=blue - 160 [label="Enter block"]; - 161 [label="Postponed enter to lambda"]; + 159 [label="Enter block"]; + 160 [label="Postponed enter to lambda"]; subgraph cluster_43 { color=blue 181 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; @@ -459,16 +458,16 @@ digraph flowFromInplaceLambda2_kt { } 187 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } - 162 [label="Postponed exit from lambda"]; - 163 [label="Function call: R|kotlin/run|(...)"]; - 164 [label="Exit block"]; + 161 [label="Postponed exit from lambda"]; + 162 [label="Function call: R|kotlin/run|(...)"]; + 163 [label="Exit block"]; } - 165 [label="Exit when branch result"]; - 166 [label="Exit when"]; + 164 [label="Exit when branch result"]; + 165 [label="Exit when"]; } - 167 [label="Function call: R|/id|(...)"]; - 168 [label="Const: Int(1)"]; - 169 [label="Postponed enter to lambda"]; + 166 [label="Function call: R|/id|(...)"]; + 167 [label="Const: Int(1)"]; + 168 [label="Postponed enter to lambda"]; subgraph cluster_45 { color=blue 193 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; @@ -476,18 +475,19 @@ digraph flowFromInplaceLambda2_kt { color=blue 194 [label="Enter block"]; 195 [label="Access variable R|/p|"]; - 196 [label="Access variable R|kotlin/String.length|"]; + 196 [label="Access variable #"]; 197 [label="Const: Int(123)"]; 198 [label="Exit block"]; } 199 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } - 170 [label="Postponed exit from lambda"]; - 171 [label="Function call: R|kotlin/run|(...)"]; + 169 [label="Postponed exit from lambda"]; + 170 [label="Function call: R|kotlin/run|(...)"]; + 171 [label="Merge postponed lambda exits"]; 172 [label="Call arguments union" style="filled" fillcolor=yellow]; 173 [label="Function call: R|/foo|(...)"]; 174 [label="Access variable R|/p|"]; - 175 [label="Access variable R|kotlin/String.length|"]; + 175 [label="Access variable #"]; 176 [label="Exit block"]; } 177 [label="Exit when branch result"]; @@ -497,6 +497,7 @@ digraph flowFromInplaceLambda2_kt { } 180 [label="Exit function test4" style="filled" fillcolor=red]; } + 132 -> {133}; 133 -> {134}; 134 -> {135}; 135 -> {136}; @@ -505,43 +506,43 @@ digraph flowFromInplaceLambda2_kt { 138 -> {139}; 139 -> {140}; 140 -> {141}; - 141 -> {142}; - 142 -> {144 143}; - 143 -> {178}; + 141 -> {143 142}; + 142 -> {178}; + 143 -> {144}; 144 -> {145}; 145 -> {146}; 146 -> {147}; 147 -> {148}; - 148 -> {149}; - 149 -> {159 150}; + 148 -> {158 149}; + 149 -> {150}; 150 -> {151}; 151 -> {152}; 152 -> {153}; - 153 -> {154}; - 154 -> {188}; - 154 -> {155} [color=red]; - 154 -> {188} [style=dashed]; + 153 -> {188}; + 153 -> {154} [color=red]; + 153 -> {188} [style=dashed]; + 154 -> {155}; 155 -> {156}; 156 -> {157}; - 157 -> {158}; - 158 -> {166}; + 157 -> {165}; + 158 -> {159}; 159 -> {160}; - 160 -> {161}; - 161 -> {181}; - 161 -> {162} [color=red]; - 161 -> {181} [style=dashed]; + 160 -> {181}; + 160 -> {161} [color=red]; + 160 -> {181} [style=dashed]; + 161 -> {162}; 162 -> {163}; 163 -> {164}; 164 -> {165}; - 165 -> {166}; + 165 -> {171 166}; 166 -> {167}; 167 -> {168}; - 168 -> {169}; - 169 -> {193}; - 169 -> {170} [color=red]; - 169 -> {193} [style=dashed]; - 170 -> {171}; - 171 -> {172}; + 168 -> {193}; + 168 -> {169} [color=red]; + 168 -> {193} [style=dashed]; + 169 -> {170}; + 170 -> {172}; + 171 -> {172} [color=red]; 172 -> {173}; 173 -> {174}; 174 -> {175}; @@ -556,12 +557,14 @@ digraph flowFromInplaceLambda2_kt { 184 -> {185}; 185 -> {186}; 186 -> {187}; - 187 -> {162} [color=green]; + 187 -> {171} [color=red]; + 187 -> {161} [color=green]; 188 -> {189}; 189 -> {190}; 190 -> {191}; 191 -> {192}; - 192 -> {155} [color=green]; + 192 -> {171} [color=red]; + 192 -> {154} [color=green]; 193 -> {194}; 194 -> {195}; 195 -> {196}; @@ -569,7 +572,7 @@ digraph flowFromInplaceLambda2_kt { 197 -> {198}; 198 -> {199}; 199 -> {172} [color=red]; - 199 -> {170} [color=green]; + 199 -> {169} [color=green]; subgraph cluster_47 { color=red @@ -582,16 +585,16 @@ digraph flowFromInplaceLambda2_kt { 204 [label="Postponed enter to lambda"]; subgraph cluster_49 { color=blue - 218 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 219 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; subgraph cluster_50 { color=blue - 219 [label="Enter block"]; - 220 [label="Access variable R|/x|"]; - 221 [label="Type operator: (R|/x| as R|kotlin/String|)"]; - 222 [label="Function call: R|/n|()"]; - 223 [label="Exit block"]; + 220 [label="Enter block"]; + 221 [label="Access variable R|/x|"]; + 222 [label="Type operator: (R|/x| as R|kotlin/String|)"]; + 223 [label="Function call: R|/n|()"]; + 224 [label="Exit block"]; } - 224 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + 225 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } 205 [label="Postponed exit from lambda"]; 206 [label="Function call: $subj$.R|kotlin/let|(...)"]; @@ -600,291 +603,295 @@ digraph flowFromInplaceLambda2_kt { 209 [label="Postponed enter to lambda"]; subgraph cluster_51 { color=blue - 225 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 226 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; subgraph cluster_52 { color=blue - 226 [label="Enter block"]; - 227 [label="Const: String()"]; - 228 [label="Exit block"]; + 227 [label="Enter block"]; + 228 [label="Const: String()"]; + 229 [label="Exit block"]; } - 229 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + 230 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } 210 [label="Postponed exit from lambda"]; 211 [label="Function call: R|kotlin/run|(...)"]; - 212 [label="Call arguments union" style="filled" fillcolor=yellow]; - 213 [label="Function call: R|/foo|(...)"]; - 214 [label="Access variable R|/x|"]; - 215 [label="Access variable R|kotlin/String.length|"]; - 216 [label="Exit block"]; + 212 [label="Merge postponed lambda exits"]; + 213 [label="Call arguments union" style="filled" fillcolor=yellow]; + 214 [label="Function call: R|/foo|(...)"]; + 215 [label="Access variable R|/x|"]; + 216 [label="Access variable #"]; + 217 [label="Exit block"]; } - 217 [label="Exit function test5" style="filled" fillcolor=red]; + 218 [label="Exit function test5" style="filled" fillcolor=red]; } 200 -> {201}; 201 -> {202}; 202 -> {203 207}; 203 -> {204}; - 204 -> {218}; + 204 -> {219}; 204 -> {205} [color=red]; - 204 -> {218} [style=dashed]; + 204 -> {219} [style=dashed]; 205 -> {206}; 206 -> {207}; - 207 -> {208}; + 207 -> {212 208}; 208 -> {209}; - 209 -> {225}; + 209 -> {226}; 209 -> {210} [color=red]; - 209 -> {225} [style=dashed]; + 209 -> {226} [style=dashed]; 210 -> {211}; - 211 -> {212}; - 212 -> {213}; + 211 -> {213}; + 212 -> {213} [color=red]; 213 -> {214}; 214 -> {215}; 215 -> {216}; 216 -> {217}; - 218 -> {219}; + 217 -> {218}; 219 -> {220}; 220 -> {221}; 221 -> {222}; 222 -> {223}; 223 -> {224}; - 224 -> {212} [color=red]; - 224 -> {205} [color=green]; - 225 -> {226}; + 224 -> {225}; + 225 -> {212} [color=red]; + 225 -> {205} [color=green]; 226 -> {227}; 227 -> {228}; 228 -> {229}; - 229 -> {212} [color=red]; - 229 -> {210} [color=green]; + 229 -> {230}; + 230 -> {213} [color=red]; + 230 -> {210} [color=green]; subgraph cluster_53 { color=red - 230 [label="Enter function test6" style="filled" fillcolor=red]; + 231 [label="Enter function test6" style="filled" fillcolor=red]; subgraph cluster_54 { color=blue - 231 [label="Enter block"]; + 232 [label="Enter block"]; subgraph cluster_55 { color=blue - 232 [label="Enter when"]; + 233 [label="Enter when"]; subgraph cluster_56 { color=blue - 233 [label="Enter when branch condition "]; - 234 [label="Const: Boolean(true)"]; - 235 [label="Exit when branch condition"]; + 234 [label="Enter when branch condition "]; + 235 [label="Const: Boolean(true)"]; + 236 [label="Exit when branch condition"]; } subgraph cluster_57 { color=blue - 236 [label="Enter when branch condition else"]; - 237 [label="Exit when branch condition"]; + 237 [label="Enter when branch condition else"]; + 238 [label="Exit when branch condition"]; } - 238 [label="Enter when branch result"]; + 239 [label="Enter when branch result"]; subgraph cluster_58 { color=blue - 239 [label="Enter block"]; - 240 [label="Postponed enter to lambda"]; + 240 [label="Enter block"]; + 241 [label="Postponed enter to lambda"]; subgraph cluster_59 { color=blue - 271 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 273 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; subgraph cluster_60 { color=blue - 272 [label="Enter block"]; - 273 [label="Access variable R|/x|"]; - 274 [label="Type operator: (R|/x| as R|kotlin/String|)"]; - 275 [label="Function call: R|/n|()"]; - 276 [label="Exit block"]; + 274 [label="Enter block"]; + 275 [label="Access variable R|/x|"]; + 276 [label="Type operator: (R|/x| as R|kotlin/String|)"]; + 277 [label="Function call: R|/n|()"]; + 278 [label="Exit block"]; } - 277 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + 279 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } - 241 [label="Postponed exit from lambda"]; - 242 [label="Function call: R|kotlin/run|(...)"]; - 243 [label="Exit block"]; + 242 [label="Postponed exit from lambda"]; + 243 [label="Function call: R|kotlin/run|(...)"]; + 244 [label="Exit block"]; } - 244 [label="Exit when branch result"]; - 245 [label="Enter when branch result"]; + 245 [label="Exit when branch result"]; + 246 [label="Enter when branch result"]; subgraph cluster_61 { color=blue - 246 [label="Enter block"]; - 247 [label="Postponed enter to lambda"]; + 247 [label="Enter block"]; + 248 [label="Postponed enter to lambda"]; subgraph cluster_62 { color=blue - 264 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 266 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; subgraph cluster_63 { color=blue - 265 [label="Enter block"]; - 266 [label="Access variable R|/x|"]; - 267 [label="Type operator: (R|/x| as R|kotlin/String|)"]; - 268 [label="Function call: R|/n|()"]; - 269 [label="Exit block"]; + 267 [label="Enter block"]; + 268 [label="Access variable R|/x|"]; + 269 [label="Type operator: (R|/x| as R|kotlin/String|)"]; + 270 [label="Function call: R|/n|()"]; + 271 [label="Exit block"]; } - 270 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + 272 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } - 248 [label="Postponed exit from lambda"]; - 249 [label="Function call: R|kotlin/run|(...)"]; - 250 [label="Exit block"]; + 249 [label="Postponed exit from lambda"]; + 250 [label="Function call: R|kotlin/run|(...)"]; + 251 [label="Exit block"]; } - 251 [label="Exit when branch result"]; - 252 [label="Exit when"]; + 252 [label="Exit when branch result"]; + 253 [label="Exit when"]; } - 253 [label="Function call: R|/id|(...)"]; - 254 [label="Const: Int(1)"]; - 255 [label="Postponed enter to lambda"]; + 254 [label="Function call: R|/id|(...)"]; + 255 [label="Const: Int(1)"]; + 256 [label="Postponed enter to lambda"]; subgraph cluster_64 { color=blue - 278 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 280 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; subgraph cluster_65 { color=blue - 279 [label="Enter block"]; - 280 [label="Access variable R|/x|"]; - 281 [label="Access variable #"]; - 282 [label="Const: Int(123)"]; - 283 [label="Exit block"]; + 281 [label="Enter block"]; + 282 [label="Access variable R|/x|"]; + 283 [label="Access variable #"]; + 284 [label="Const: Int(123)"]; + 285 [label="Exit block"]; } - 284 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + 286 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } - 256 [label="Postponed exit from lambda"]; - 257 [label="Function call: R|kotlin/run|(...)"]; - 258 [label="Call arguments union" style="filled" fillcolor=yellow]; - 259 [label="Function call: R|/foo|(...)"]; - 260 [label="Access variable R|/x|"]; - 261 [label="Access variable #"]; - 262 [label="Exit block"]; + 257 [label="Postponed exit from lambda"]; + 258 [label="Function call: R|kotlin/run|(...)"]; + 259 [label="Merge postponed lambda exits"]; + 260 [label="Call arguments union" style="filled" fillcolor=yellow]; + 261 [label="Function call: R|/foo|(...)"]; + 262 [label="Access variable R|/x|"]; + 263 [label="Access variable #"]; + 264 [label="Exit block"]; } - 263 [label="Exit function test6" style="filled" fillcolor=red]; + 265 [label="Exit function test6" style="filled" fillcolor=red]; } - 230 -> {231}; 231 -> {232}; 232 -> {233}; 233 -> {234}; 234 -> {235}; - 235 -> {245 236}; - 236 -> {237}; + 235 -> {236}; + 236 -> {246 237}; 237 -> {238}; 238 -> {239}; 239 -> {240}; - 240 -> {271}; - 240 -> {241} [color=red]; - 240 -> {271} [style=dashed]; - 241 -> {242}; + 240 -> {241}; + 241 -> {273}; + 241 -> {242} [color=red]; + 241 -> {273} [style=dashed]; 242 -> {243}; 243 -> {244}; - 244 -> {252}; - 245 -> {246}; + 244 -> {245}; + 245 -> {253}; 246 -> {247}; - 247 -> {264}; - 247 -> {248} [color=red]; - 247 -> {264} [style=dashed]; - 248 -> {249}; + 247 -> {248}; + 248 -> {266}; + 248 -> {249} [color=red]; + 248 -> {266} [style=dashed]; 249 -> {250}; 250 -> {251}; 251 -> {252}; 252 -> {253}; - 253 -> {254}; + 253 -> {259 254}; 254 -> {255}; - 255 -> {278}; - 255 -> {256} [color=red]; - 255 -> {278} [style=dashed]; - 256 -> {257}; + 255 -> {256}; + 256 -> {280}; + 256 -> {257} [color=red]; + 256 -> {280} [style=dashed]; 257 -> {258}; - 258 -> {259}; - 259 -> {260}; + 258 -> {260}; + 259 -> {260} [color=red]; 260 -> {261}; 261 -> {262}; 262 -> {263}; + 263 -> {264}; 264 -> {265}; - 265 -> {266}; 266 -> {267}; 267 -> {268}; 268 -> {269}; 269 -> {270}; - 270 -> {248} [color=green]; + 270 -> {271}; 271 -> {272}; - 272 -> {273}; + 272 -> {259} [color=red]; + 272 -> {249} [color=green]; 273 -> {274}; 274 -> {275}; 275 -> {276}; 276 -> {277}; - 277 -> {241} [color=green]; + 277 -> {278}; 278 -> {279}; - 279 -> {280}; + 279 -> {259} [color=red]; + 279 -> {242} [color=green]; 280 -> {281}; 281 -> {282}; 282 -> {283}; 283 -> {284}; - 284 -> {258} [color=red]; - 284 -> {256} [color=green]; + 284 -> {285}; + 285 -> {286}; + 286 -> {260} [color=red]; + 286 -> {257} [color=green]; subgraph cluster_66 { color=red - 285 [label="Enter function test7" style="filled" fillcolor=red]; + 287 [label="Enter function test7" style="filled" fillcolor=red]; subgraph cluster_67 { color=blue - 286 [label="Enter block"]; - 287 [label="Access variable R|/x|"]; - 288 [label="Variable declaration: lvar p: R|kotlin/String?|"]; + 288 [label="Enter block"]; + 289 [label="Access variable R|/x|"]; + 290 [label="Variable declaration: lvar p: R|kotlin/String?|"]; subgraph cluster_68 { color=blue - 289 [label="Enter when"]; + 291 [label="Enter when"]; subgraph cluster_69 { color=blue - 290 [label="Enter when branch condition "]; - 291 [label="Access variable R|/p|"]; - 292 [label="Const: Null(null)"]; - 293 [label="Equality operator !="]; - 294 [label="Exit when branch condition"]; + 292 [label="Enter when branch condition "]; + 293 [label="Access variable R|/p|"]; + 294 [label="Const: Null(null)"]; + 295 [label="Equality operator !="]; + 296 [label="Exit when branch condition"]; } - 295 [label="Synthetic else branch"]; - 296 [label="Enter when branch result"]; + 297 [label="Synthetic else branch"]; + 298 [label="Enter when branch result"]; subgraph cluster_70 { color=blue - 297 [label="Enter block"]; - 298 [label="Postponed enter to lambda"]; + 299 [label="Enter block"]; + 300 [label="Postponed enter to lambda"]; subgraph cluster_71 { color=blue - 316 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 318 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; subgraph cluster_72 { color=blue - 317 [label="Enter block"]; - 318 [label="Const: Null(null)"]; - 319 [label="Assignment: R|/p|"]; - 320 [label="Function call: R|/n|()"]; - 321 [label="Exit block"]; + 319 [label="Enter block"]; + 320 [label="Const: Null(null)"]; + 321 [label="Assignment: R|/p|"]; + 322 [label="Function call: R|/n|()"]; + 323 [label="Exit block"]; } - 322 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + 324 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } - 299 [label="Postponed exit from lambda"]; - 300 [label="Function call: R|kotlin/run|(...)"]; - 301 [label="Function call: R|/id|(...)"]; - 302 [label="Const: Int(1)"]; - 303 [label="Postponed enter to lambda"]; + 301 [label="Postponed exit from lambda"]; + 302 [label="Function call: R|kotlin/run|(...)"]; + 303 [label="Function call: R|/id|(...)"]; + 304 [label="Const: Int(1)"]; + 305 [label="Postponed enter to lambda"]; subgraph cluster_73 { color=blue - 323 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 325 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; subgraph cluster_74 { color=blue - 324 [label="Enter block"]; - 325 [label="Access variable R|/p|"]; - 326 [label="Access variable R|kotlin/String.length|"]; - 327 [label="Const: Int(123)"]; - 328 [label="Exit block"]; + 326 [label="Enter block"]; + 327 [label="Access variable R|/p|"]; + 328 [label="Access variable R|kotlin/String.length|"]; + 329 [label="Const: Int(123)"]; + 330 [label="Exit block"]; } - 329 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + 331 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } - 304 [label="Postponed exit from lambda"]; - 305 [label="Function call: R|kotlin/run|(...)"]; - 306 [label="Call arguments union" style="filled" fillcolor=yellow]; - 307 [label="Function call: R|/foo|(...)"]; - 308 [label="Access variable R|/p|"]; - 309 [label="Stub" style="filled" fillcolor=gray]; - 310 [label="Access variable R|kotlin/String.length|" style="filled" fillcolor=gray]; - 311 [label="Exit block" style="filled" fillcolor=gray]; + 306 [label="Postponed exit from lambda"]; + 307 [label="Function call: R|kotlin/run|(...)"]; + 308 [label="Call arguments union" style="filled" fillcolor=yellow]; + 309 [label="Function call: R|/foo|(...)"]; + 310 [label="Access variable R|/p|"]; + 311 [label="Stub" style="filled" fillcolor=gray]; + 312 [label="Access variable R|kotlin/String.length|" style="filled" fillcolor=gray]; + 313 [label="Exit block" style="filled" fillcolor=gray]; } - 312 [label="Exit when branch result" style="filled" fillcolor=gray]; - 313 [label="Exit when"]; + 314 [label="Exit when branch result" style="filled" fillcolor=gray]; + 315 [label="Exit when"]; } - 314 [label="Exit block"]; + 316 [label="Exit block"]; } - 315 [label="Exit function test7" style="filled" fillcolor=red]; + 317 [label="Exit function test7" style="filled" fillcolor=red]; } - 285 -> {286}; - 286 -> {287}; 287 -> {288}; 288 -> {289}; 289 -> {290}; @@ -892,47 +899,49 @@ digraph flowFromInplaceLambda2_kt { 291 -> {292}; 292 -> {293}; 293 -> {294}; - 294 -> {296 295}; - 295 -> {313}; - 296 -> {297}; - 297 -> {298}; - 298 -> {316}; - 298 -> {299} [color=red]; - 298 -> {316} [style=dashed]; + 294 -> {295}; + 295 -> {296}; + 296 -> {298 297}; + 297 -> {315}; + 298 -> {299}; 299 -> {300}; - 300 -> {301}; + 300 -> {318}; + 300 -> {301} [color=red]; + 300 -> {318} [style=dashed]; 301 -> {302}; 302 -> {303}; - 303 -> {323}; - 303 -> {304} [color=red]; - 303 -> {323} [style=dashed]; + 303 -> {304}; 304 -> {305}; - 305 -> {306}; + 305 -> {325}; + 305 -> {306} [color=red]; + 305 -> {325} [style=dashed]; 306 -> {307}; 307 -> {308}; - 308 -> {315} [label=onUncaughtException]; - 308 -> {309} [style=dotted]; - 309 -> {310} [style=dotted]; + 308 -> {309}; + 309 -> {310}; + 310 -> {317} [label=onUncaughtException]; 310 -> {311} [style=dotted]; 311 -> {312} [style=dotted]; 312 -> {313} [style=dotted]; - 313 -> {314}; - 314 -> {315}; + 313 -> {314} [style=dotted]; + 314 -> {315} [style=dotted]; + 315 -> {316}; 316 -> {317}; - 317 -> {318}; 318 -> {319}; 319 -> {320}; 320 -> {321}; 321 -> {322}; - 322 -> {306} [color=red]; - 322 -> {299} [color=green]; + 322 -> {323}; 323 -> {324}; - 324 -> {325}; + 324 -> {308} [color=red]; + 324 -> {301} [color=green]; 325 -> {326}; 326 -> {327}; 327 -> {328}; 328 -> {329}; - 329 -> {306} [color=red]; - 329 -> {304} [color=green]; + 329 -> {330}; + 330 -> {331}; + 331 -> {308} [color=red]; + 331 -> {306} [color=green]; } diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.fir.txt b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.fir.txt index defc6d88aa3..422e9f49941 100644 --- a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.fir.txt @@ -28,7 +28,7 @@ FILE: flowFromInplaceLambda2.kt ^ R|/n|() } )), R|/someCompletedCall|(Int(1)), R|kotlin/run|( = run@fun (): R|kotlin/Int| { - R|/x|.R|kotlin/String.length| + R|/x|.# ^ Int(123) } )) @@ -52,7 +52,7 @@ FILE: flowFromInplaceLambda2.kt ^ Int(123) } )) - R|/x|.# + R|/x|.R|kotlin/String.length| } public final fun test4(x: R|kotlin/String?|): R|kotlin/Unit| { lvar p: R|kotlin/String?| = R|/x| @@ -74,11 +74,11 @@ FILE: flowFromInplaceLambda2.kt } } ), Int(1), R|kotlin/run|( = run@fun (): R|kotlin/Int| { - R|/p|.R|kotlin/String.length| + R|/p|.# ^ Int(123) } )) - R|/p|.R|kotlin/String.length| + R|/p|.# } } @@ -92,7 +92,7 @@ FILE: flowFromInplaceLambda2.kt ^ String() } )) - R|/x|.R|kotlin/String.length| + R|/x|.# } public final fun test6(x: R|kotlin/String?|): R|kotlin/Unit| { R|/foo|(R|/id|(when () { diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.kt b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.kt index b7fb98dd00b..240e533712f 100644 --- a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.kt +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.kt @@ -21,7 +21,7 @@ fun test2(x: String?) { foo( id(run { x as String; n() }), someCompletedCall(1), - run { x.length; 123 } // Bad (resolution order undefined) + run { x.length; 123 } // Bad (resolution order undefined) ) x.length // OK (x as String unconditional) } @@ -32,7 +32,7 @@ fun test3(x: String?) { if (true) 1 else 2, run { x.length; 123 } // Bad (resolution order undefined) ) - x.length // OK (x as String unconditional) + x.length // OK (x as String unconditional) } fun test4(x: String?) { @@ -41,9 +41,9 @@ fun test4(x: String?) { foo( id(if (true) run { p = null; n() } else run { n() }), 1, - run { p.length; 123 } // Bad (p = null possible) + run { p.length; 123 } // Bad (p = null possible) ) - p.length // Bad (p = null possible) + p.length // Bad (p = null possible) } } @@ -53,7 +53,7 @@ fun test5(x: String?, y: String?) { 1, run { "" } ) - x.length // Bad (x as String conditional) + x.length // Bad (x as String conditional) } fun test6(x: String?) { diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/inplaceLambdaInControlFlowExpressions.dot b/compiler/fir/analysis-tests/testData/resolve/cfg/inplaceLambdaInControlFlowExpressions.dot index 7c63fa016c3..7941a541602 100644 --- a/compiler/fir/analysis-tests/testData/resolve/cfg/inplaceLambdaInControlFlowExpressions.dot +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/inplaceLambdaInControlFlowExpressions.dot @@ -63,14 +63,14 @@ digraph inplaceLambdaInControlFlowExpressions_kt { 24 [label="Postponed enter to lambda"]; subgraph cluster_9 { color=blue - 33 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 34 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; subgraph cluster_10 { color=blue - 34 [label="Enter block"]; - 35 [label="Function call: R|/materialize|()"]; - 36 [label="Exit block"]; + 35 [label="Enter block"]; + 36 [label="Function call: R|/materialize|()"]; + 37 [label="Exit block"]; } - 37 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + 38 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } 25 [label="Postponed exit from lambda"]; 26 [label="Function call: R|kotlin/run|(...)"]; @@ -84,6 +84,7 @@ digraph inplaceLambdaInControlFlowExpressions_kt { } 32 [label="Exit function test_1" style="filled" fillcolor=red]; } + 33 [label="Merge postponed lambda exits"]; 9 -> {10}; 10 -> {11}; 11 -> {12}; @@ -99,89 +100,89 @@ digraph inplaceLambdaInControlFlowExpressions_kt { 21 -> {29}; 22 -> {23}; 23 -> {24}; - 24 -> {33}; + 24 -> {34}; 24 -> {25} [color=red]; - 24 -> {33} [style=dashed]; + 24 -> {34} [style=dashed]; 25 -> {26}; 26 -> {27}; 27 -> {28}; 28 -> {29}; - 29 -> {30}; + 29 -> {33 30}; 30 -> {31}; 31 -> {32}; - 33 -> {34}; 34 -> {35}; 35 -> {36}; 36 -> {37}; - 37 -> {25} [color=green]; + 37 -> {38}; + 38 -> {33} [color=red]; + 38 -> {25} [color=green]; subgraph cluster_11 { color=red - 38 [label="Enter function test_2" style="filled" fillcolor=red]; + 39 [label="Enter function test_2" style="filled" fillcolor=red]; subgraph cluster_12 { color=blue - 39 [label="Enter block"]; + 40 [label="Enter block"]; subgraph cluster_13 { color=blue - 40 [label="Try expression enter"]; + 41 [label="Try expression enter"]; subgraph cluster_14 { color=blue - 41 [label="Try main block enter"]; + 42 [label="Try main block enter"]; subgraph cluster_15 { color=blue - 42 [label="Enter block"]; - 43 [label="Postponed enter to lambda"]; + 43 [label="Enter block"]; + 44 [label="Postponed enter to lambda"]; subgraph cluster_16 { color=blue - 58 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 59 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; subgraph cluster_17 { color=blue - 59 [label="Enter block"]; - 60 [label="Function call: R|/materialize|()"]; - 61 [label="Exit block"]; + 60 [label="Enter block"]; + 61 [label="Function call: R|/materialize|()"]; + 62 [label="Exit block"]; } - 62 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + 63 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } - 44 [label="Postponed exit from lambda"]; - 45 [label="Function call: R|kotlin/run|(...)"]; - 46 [label="Exit block"]; + 45 [label="Postponed exit from lambda"]; + 46 [label="Function call: R|kotlin/run|(...)"]; + 47 [label="Exit block"]; } - 47 [label="Try main block exit"]; + 48 [label="Try main block exit"]; } subgraph cluster_18 { color=blue - 48 [label="Catch enter"]; + 49 [label="Catch enter"]; subgraph cluster_19 { color=blue - 49 [label="Enter block"]; - 50 [label="Const: String()"]; - 51 [label="Exit block"]; + 50 [label="Enter block"]; + 51 [label="Const: String()"]; + 52 [label="Exit block"]; } - 52 [label="Catch exit"]; + 53 [label="Catch exit"]; } - 53 [label="Try expression exit"]; + 54 [label="Try expression exit"]; } - 54 [label="Call arguments union" style="filled" fillcolor=yellow]; - 55 [label="Variable declaration: lval x: R|kotlin/String|"]; - 56 [label="Exit block"]; + 55 [label="Call arguments union" style="filled" fillcolor=yellow]; + 56 [label="Variable declaration: lval x: R|kotlin/String|"]; + 57 [label="Exit block"]; } - 57 [label="Exit function test_2" style="filled" fillcolor=red]; + 58 [label="Exit function test_2" style="filled" fillcolor=red]; } - 38 -> {39}; 39 -> {40}; - 40 -> {41 48}; - 41 -> {42}; + 40 -> {41}; + 41 -> {42 49}; 42 -> {43}; - 43 -> {58}; - 43 -> {44} [color=red]; - 43 -> {58} [style=dashed]; - 44 -> {45}; + 43 -> {44}; + 44 -> {59}; + 44 -> {45} [color=red]; + 44 -> {59} [style=dashed]; 45 -> {46}; 46 -> {47}; - 47 -> {53 48}; - 48 -> {49}; - 48 -> {57} [label=onUncaughtException]; + 47 -> {48}; + 48 -> {54 49}; 49 -> {50}; + 49 -> {58} [label=onUncaughtException]; 50 -> {51}; 51 -> {52}; 52 -> {53}; @@ -189,56 +190,57 @@ digraph inplaceLambdaInControlFlowExpressions_kt { 54 -> {55}; 55 -> {56}; 56 -> {57}; - 58 -> {59}; + 57 -> {58}; 59 -> {60}; 60 -> {61}; 61 -> {62}; - 62 -> {54} [color=red]; - 62 -> {44} [color=green]; + 62 -> {63}; + 63 -> {55} [color=red]; + 63 -> {45} [color=green]; subgraph cluster_20 { color=red - 63 [label="Enter function test_3" style="filled" fillcolor=red]; + 64 [label="Enter function test_3" style="filled" fillcolor=red]; subgraph cluster_21 { color=blue - 64 [label="Enter block"]; - 65 [label="Postponed enter to lambda"]; + 65 [label="Enter block"]; + 66 [label="Postponed enter to lambda"]; subgraph cluster_22 { color=blue - 73 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 74 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; subgraph cluster_23 { color=blue - 74 [label="Enter block"]; - 75 [label="Function call: R|/materialize|()"]; - 76 [label="Exit block"]; + 75 [label="Enter block"]; + 76 [label="Function call: R|/materialize|()"]; + 77 [label="Exit block"]; } - 77 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + 78 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } - 66 [label="Postponed exit from lambda"]; - 67 [label="Function call: R|kotlin/run|(...)"]; - 68 [label="Check not null: R|kotlin/run|(...)!!"]; - 69 [label="Call arguments union" style="filled" fillcolor=yellow]; - 70 [label="Variable declaration: lval x: R|kotlin/String|"]; - 71 [label="Exit block"]; + 67 [label="Postponed exit from lambda"]; + 68 [label="Function call: R|kotlin/run|(...)"]; + 69 [label="Check not null: R|kotlin/run|(...)!!"]; + 70 [label="Call arguments union" style="filled" fillcolor=yellow]; + 71 [label="Variable declaration: lval x: R|kotlin/String|"]; + 72 [label="Exit block"]; } - 72 [label="Exit function test_3" style="filled" fillcolor=red]; + 73 [label="Exit function test_3" style="filled" fillcolor=red]; } - 63 -> {64}; 64 -> {65}; - 65 -> {73}; - 65 -> {66} [color=red]; - 65 -> {73} [style=dashed]; - 66 -> {67}; + 65 -> {66}; + 66 -> {74}; + 66 -> {67} [color=red]; + 66 -> {74} [style=dashed]; 67 -> {68}; 68 -> {69}; 69 -> {70}; 70 -> {71}; 71 -> {72}; - 73 -> {74}; + 72 -> {73}; 74 -> {75}; 75 -> {76}; 76 -> {77}; - 77 -> {69} [color=red]; - 77 -> {66} [color=green]; + 77 -> {78}; + 78 -> {70} [color=red]; + 78 -> {67} [color=green]; } diff --git a/compiler/fir/analysis-tests/testData/resolve/smartcasts/incorrectSmartcastToNothing.dot b/compiler/fir/analysis-tests/testData/resolve/smartcasts/incorrectSmartcastToNothing.dot index a2da65ad285..6937870c277 100644 --- a/compiler/fir/analysis-tests/testData/resolve/smartcasts/incorrectSmartcastToNothing.dot +++ b/compiler/fir/analysis-tests/testData/resolve/smartcasts/incorrectSmartcastToNothing.dot @@ -69,16 +69,16 @@ digraph incorrectSmartcastToNothing_kt { 33 [label="Postponed enter to lambda"]; subgraph cluster_10 { color=blue - 43 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 45 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; subgraph cluster_11 { color=blue - 44 [label="Enter block"]; - 45 [label="Access variable R|/it|"]; - 46 [label="Const: String(main.kts.compiled.cache)"]; - 47 [label="Function call: R|java/io/File.File|(...)"]; - 48 [label="Exit block"]; + 46 [label="Enter block"]; + 47 [label="Access variable R|/it|"]; + 48 [label="Const: String(main.kts.compiled.cache)"]; + 49 [label="Function call: R|java/io/File.File|(...)"]; + 50 [label="Exit block"]; } - 49 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + 51 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } 34 [label="Postponed exit from lambda"]; 35 [label="Function call: $subj$.R|kotlin/let|(...)"]; @@ -93,6 +93,8 @@ digraph incorrectSmartcastToNothing_kt { } 42 [label="Exit function test" style="filled" fillcolor=red]; } + 43 [label="Merge postponed lambda exits"]; + 44 [label="Merge postponed lambda exits"]; 4 -> {5}; 5 -> {6}; 6 -> {7}; @@ -122,23 +124,25 @@ digraph incorrectSmartcastToNothing_kt { 30 -> {31}; 31 -> {32 36}; 32 -> {33}; - 33 -> {43}; + 33 -> {45}; 33 -> {34} [color=red]; - 33 -> {43} [style=dashed]; + 33 -> {45} [style=dashed]; 34 -> {35}; 35 -> {36}; - 36 -> {37}; + 36 -> {43 37}; 37 -> {38}; 38 -> {39}; - 39 -> {40}; + 39 -> {44 40}; 40 -> {41}; 41 -> {42}; - 43 -> {44}; - 44 -> {45}; + 43 -> {44} [color=red]; 45 -> {46}; 46 -> {47}; 47 -> {48}; 48 -> {49}; - 49 -> {34} [color=green]; + 49 -> {50}; + 50 -> {51}; + 51 -> {43} [color=red]; + 51 -> {34} [color=green]; } diff --git a/compiler/fir/analysis-tests/testData/resolve/smartcasts/lambdas/lambdaInWhenBranch.dot b/compiler/fir/analysis-tests/testData/resolve/smartcasts/lambdas/lambdaInWhenBranch.dot index 1caf339c387..7f24a13d4b5 100644 --- a/compiler/fir/analysis-tests/testData/resolve/smartcasts/lambdas/lambdaInWhenBranch.dot +++ b/compiler/fir/analysis-tests/testData/resolve/smartcasts/lambdas/lambdaInWhenBranch.dot @@ -135,14 +135,14 @@ digraph lambdaInWhenBranch_kt { 48 [label="Postponed enter to lambda"]; subgraph cluster_18 { color=blue - 81 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 82 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; subgraph cluster_19 { color=blue - 82 [label="Enter block"]; - 83 [label="Access variable R|/it|"]; - 84 [label="Exit block"]; + 83 [label="Enter block"]; + 84 [label="Access variable R|/it|"]; + 85 [label="Exit block"]; } - 85 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + 86 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } 49 [label="Postponed exit from lambda"]; 50 [label="Function call: String().R|kotlin/let|(...)"]; @@ -195,6 +195,7 @@ digraph lambdaInWhenBranch_kt { } 80 [label="Exit function foo" style="filled" fillcolor=red]; } + 81 [label="Merge postponed lambda exits"]; 28 -> {29}; 29 -> {30}; 30 -> {31}; @@ -215,14 +216,14 @@ digraph lambdaInWhenBranch_kt { 45 -> {46}; 46 -> {47}; 47 -> {48}; - 48 -> {81}; + 48 -> {82}; 48 -> {49} [color=red]; - 48 -> {81} [style=dashed]; + 48 -> {82} [style=dashed]; 49 -> {50}; 50 -> {51}; 51 -> {52}; 52 -> {53}; - 53 -> {54}; + 53 -> {81 54}; 54 -> {55}; 55 -> {56}; 56 -> {57}; @@ -249,10 +250,11 @@ digraph lambdaInWhenBranch_kt { 77 -> {78}; 78 -> {79}; 79 -> {80}; - 81 -> {82}; 82 -> {83}; 83 -> {84}; 84 -> {85}; - 85 -> {49} [color=green]; + 85 -> {86}; + 86 -> {81} [color=red]; + 86 -> {49} [color=green]; } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt index 816ed791253..7ac2edde0dc 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt @@ -826,7 +826,7 @@ abstract class FirDataFlowAnalyzer( } fun exitWhenExpression(whenExpression: FirWhenExpression) { - val (whenExitNode, syntheticElseNode) = graphBuilder.exitWhenExpression(whenExpression) + val (whenExitNode, syntheticElseNode, mergePostponedLambdaExitsNode) = graphBuilder.exitWhenExpression(whenExpression) if (syntheticElseNode != null) { val previousConditionExitNode = syntheticElseNode.firstPreviousNode as? WhenBranchConditionExitNode // previous node for syntheticElseNode can be not WhenBranchConditionExitNode in case of `when` without any branches @@ -844,6 +844,7 @@ abstract class FirDataFlowAnalyzer( } } whenExitNode.mergeIncomingFlow() + mergePostponedLambdaExitsNode?.mergeIncomingFlow() } fun exitWhenSubjectExpression(expression: FirWhenSubjectExpression) { @@ -1022,8 +1023,9 @@ abstract class FirDataFlowAnalyzer( } fun exitSafeCall(safeCall: FirSafeCallExpression) { - val node = graphBuilder.exitSafeCall().mergeIncomingFlow() - val flow = node.flow + val (node, mergePostponedLambdaExitsNode) = graphBuilder.exitSafeCall() + val flow = node.mergeIncomingFlow().flow + mergePostponedLambdaExitsNode?.mergeIncomingFlow() val variable = variableStorage.getOrCreateVariable(flow, safeCall) val receiverVariable = when (variable) { @@ -1462,7 +1464,9 @@ abstract class FirDataFlowAnalyzer( } fun exitElvis(elvisExpression: FirElvisExpression, isLhsNotNull: Boolean) { - val node = graphBuilder.exitElvis().mergeIncomingFlow() + val (node, mergePostponedLambdaExitsNode) = graphBuilder.exitElvis() + node.mergeIncomingFlow() + mergePostponedLambdaExitsNode?.mergeIncomingFlow() if (isLhsNotNull) { elvisExpression.lhs.propagateNotNullInfo(node) } 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 3a6053d542c..6fb92032da1 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 @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.util.ListMultimap import org.jetbrains.kotlin.fir.util.listMultimapOf import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor +import org.jetbrains.kotlin.utils.addToStdlib.popLast import org.jetbrains.kotlin.utils.addToStdlib.runIf import kotlin.random.Random @@ -85,7 +86,7 @@ class ControlFlowGraphBuilder { private val loopEnterNodes: NodeStorage> = NodeStorage() private val loopExitNodes: NodeStorage = NodeStorage() - private val exitsFromCompletedPostponedAnonymousFunctions: MutableList = mutableListOf() + private val exitsFromCompletedPostponedAnonymousFunctions: MutableList>> = mutableListOf() private val whenExitNodes: NodeStorage = NodeStorage() private val whenBranchIndices: Stack> = stackOf() @@ -341,7 +342,8 @@ class ControlFlowGraphBuilder { val graph = popGraph().also { graph -> assert(graph.declaration == anonymousFunction) assert(graph.exitNode == exitNode) - exitsFromCompletedPostponedAnonymousFunctions.removeAll { it.owner == graph } + // TODO: disregarding the edges is probably not correct, though this should never find any nodes anyway + exitsFromCompletedPostponedAnonymousFunctions.lastOrNull()?.removeAll { it.owner == graph } } val postponedEnterNode = entersToPostponedAnonymousFunctions.remove(symbol)!! @@ -361,7 +363,7 @@ class ControlFlowGraphBuilder { } if (invocationKind == EventOccurrencesRange.EXACTLY_ONCE && shouldPassFlowFromInplaceLambda.top()) { - exitsFromCompletedPostponedAnonymousFunctions += postponedExitNode + exitsFromCompletedPostponedAnonymousFunctions.lastOrNull()?.add(postponedExitNode) } val containingGraph = parentGraphForAnonymousFunctions.remove(symbol) ?: currentGraph @@ -666,6 +668,7 @@ class ControlFlowGraphBuilder { whenBranchIndices.push(whenExpression.branches.mapIndexed { index, branch -> branch to index }.toMap()) notCompletedFunctionCalls.push(mutableListOf()) levelCounter++ + splitDataFlowForPostponedLambdas() return node } @@ -696,7 +699,9 @@ class ControlFlowGraphBuilder { return node } - fun exitWhenExpression(whenExpression: FirWhenExpression): Pair { + fun exitWhenExpression( + whenExpression: FirWhenExpression + ): Triple { val whenExitNode = whenExitNodes.pop() // exit from last condition node still on stack // we should remove it @@ -710,10 +715,9 @@ class ControlFlowGraphBuilder { } else null whenExitNode.updateDeadStatus() lastNodes.push(whenExitNode) - dropPostponedLambdasForNonDeterministicCalls() levelCounter-- whenBranchIndices.pop() - return whenExitNode to syntheticElseBranchNode + return Triple(whenExitNode, syntheticElseBranchNode, joinDataFlowFromPostponedLambdasWith(whenExitNode)) } // ----------------------------------- While Loop ----------------------------------- @@ -920,6 +924,7 @@ class ControlFlowGraphBuilder { finallyExitNodes.push(createFinallyBlockExitNode(tryExpression)) } notCompletedFunctionCalls.push(mutableListOf()) + splitDataFlowForPostponedLambdas() return enterTryExpressionNode to enterTryNodeBlock } @@ -1074,6 +1079,7 @@ class ControlFlowGraphBuilder { fun enterCall() { levelCounter++ + splitDataFlowForPostponedLambdas() } fun exitIgnoredCall(functionCall: FirFunctionCall) { @@ -1151,53 +1157,92 @@ class ControlFlowGraphBuilder { return node to unionNode } - /* - * This is needed for some control flow constructions which are resolved as calls (when and elvis) - * For usual call we have invariant that all arguments will be called before function call, but for - * when and elvis only one of arguments will be actually called, so it's illegal to pass data flow info - * from lambda in one of branches - */ - private fun dropPostponedLambdasForNonDeterministicCalls() { - exitsFromCompletedPostponedAnonymousFunctions.clear() + // Arguments are evaluated left to right, and this is how data flows. + // foo(run { x as String; 1 }, { /* x smartcasted to String */ x.length }) + // + // However, as we need to fix type parameters before analyzing lambdas, this is not always the order of analysis; + // if that is possible, multiple lambdas should be considered to be concurrent. + // + // foo(run { x as String; genericFunction() }, run { /* x not smartcastable because this lambda may be resolved first */ 1 }) + // /* x is smartcastable after the call */ + // + // And if the lambda is conditional, then the data flow needs to be merged with other branches. + // + // foo(nullable?.let { x as String; genericFunction() }, run { 1 }) + // /* x is not smartcastable */ + // + // foo(nullable ?: run { x as String; genericFunction() }, run { 1 }) + // /* x is not smartcastable */ + // + // foo(if (condition) run { x as String; genericFunction() } else { genericFunction() }, run { 1 }) + // /* x is not smartcastable */ + // + // foo(if (condition) run { x as String; genericFunction() } else { x as String; genericFunction() }, run { 1 }) + // /* x is smartcastable */ + // + // `splitDataFlowForPostponedLambdas` in `enterX` should be matched with either `joinDataFlowFromPostponedLambdasWith` + // or `processUnionOfArguments` in `exitX`. The difference is that the latter creates an intersection of all the lambdas' + // type information (like after function calls - all casts from all lambdas are valid) while the former is a union + // (like after `if` - only the casts from one of the lambdas are valid, and we don't know which). + // + private fun splitDataFlowForPostponedLambdas() { + exitsFromCompletedPostponedAnonymousFunctions.add(mutableListOf()) + } + + private fun joinDataFlowFromPostponedLambdasWith(node: CFGNode<*>): MergePostponedLambdaExitsNode? { + val currentLevelExits = exitsFromCompletedPostponedAnonymousFunctions.popLast() + if (currentLevelExits.isEmpty()) { + return null + } + + val joinNode = createMergePostponedLambdaExitsNode(node.fir) + addEdge(node, joinNode) + currentLevelExits.joinDataFlowFromPostponedLambdasTo(joinNode) + exitsFromCompletedPostponedAnonymousFunctions.lastOrNull()?.add(joinNode) + return joinNode + } + + private fun MutableList>.joinDataFlowFromPostponedLambdasTo(node: CFGNode<*>) { + for (exitNode in this) { + // To avoid storing nodes from subgraphs in the list, we have PostponedLambdaExitNode instead of the real + // exit node of the lambda subgraph. The latter is the previous node of the former. Everything else is + // already a join/union node in this graph. + val functionExitOrMerge = if (exitNode is PostponedLambdaExitNode) exitNode.lastPreviousNode else exitNode + addEdge(functionExitOrMerge, node, preferredKind = EdgeKind.DfgForward) + } } private fun processUnionOfArguments( node: CFGNode<*>, callCompleted: Boolean ): Pair { - if (!shouldPassFlowFromInplaceLambda.top()) return EdgeKind.Forward to null - var kind = EdgeKind.Forward - if (!callCompleted || exitsFromCompletedPostponedAnonymousFunctions.isEmpty()) { + val currentLevelExits = exitsFromCompletedPostponedAnonymousFunctions.popLast() + if (currentLevelExits.isEmpty()) { return EdgeKind.Forward to null } - val unionNode by lazy { createUnionFunctionCallArgumentsNode(node.fir) } - var hasDirectPreviousNode = false - var hasPostponedLambdas = false - val iterator = exitsFromCompletedPostponedAnonymousFunctions.iterator() - val lastPostponedLambdaExitNode = lastNode - while (iterator.hasNext()) { - val exitNode = iterator.next() - if (node.level >= exitNode.level) continue - hasPostponedLambdas = true - if (exitNode == lastPostponedLambdaExitNode) { - popAndAddEdge(node, preferredKind = EdgeKind.CfgForward) - kind = EdgeKind.DfgForward - hasDirectPreviousNode = true + if (!callCompleted || !shouldPassFlowFromInplaceLambda.top()) { + currentLevelExits.singleOrNull()?.let { + exitsFromCompletedPostponedAnonymousFunctions.lastOrNull()?.add(it) + return EdgeKind.Forward to null } - addEdge(exitNode.lastPreviousNode, unionNode, preferredKind = EdgeKind.DfgForward) - iterator.remove() + + val unionNode = createUnionFunctionCallArgumentsNode(node.fir) + currentLevelExits.joinDataFlowFromPostponedLambdasTo(unionNode) + exitsFromCompletedPostponedAnonymousFunctions.lastOrNull()?.addAll(currentLevelExits) + return EdgeKind.Forward to unionNode } - if (hasPostponedLambdas) { - if (hasDirectPreviousNode) { - lastNodes.push(unionNode) - } else { - addNewSimpleNode(unionNode) - } - } else { - return EdgeKind.Forward to null + + val unionNode = createUnionFunctionCallArgumentsNode(node.fir) + currentLevelExits.joinDataFlowFromPostponedLambdasTo(unionNode) + + if (lastNode in currentLevelExits) { + popAndAddEdge(node, preferredKind = EdgeKind.CfgForward) + lastNodes.push(unionNode) + return EdgeKind.DfgForward to unionNode } - return Pair(kind, unionNode) + addNewSimpleNode(unionNode) + return EdgeKind.Forward to unionNode } fun exitWhenSubjectExpression(expression: FirWhenSubjectExpression): WhenSubjectExpressionExitNode { @@ -1290,19 +1335,21 @@ class ControlFlowGraphBuilder { } else { addEdge(lastNode, exitNode) } + splitDataFlowForPostponedLambdas() return enterNode } - fun exitSafeCall(): ExitSafeCallNode { + fun exitSafeCall(): Pair { // There will be two paths towards this exit safe call node: // one from the node prior to the enclosing safe call, and // the other from the selector part in the enclosing safe call. // Note that *neither* points to the safe call directly. // So, when it comes to the real exit of the enclosing block/function, // the safe call bound to this exit safe call node should be retrieved. - return exitSafeCallNodes.pop().also { + return exitSafeCallNodes.pop().let { addNewSimpleNode(it) it.updateDeadStatus() + it to joinDataFlowFromPostponedLambdasWith(it) } } @@ -1310,6 +1357,7 @@ class ControlFlowGraphBuilder { fun enterElvis(elvisExpression: FirElvisExpression) { elvisRhsEnterNodes.push(createElvisRhsEnterNode(elvisExpression)) + splitDataFlowForPostponedLambdas() } fun exitElvisLhs(elvisExpression: FirElvisExpression): Triple { @@ -1342,12 +1390,11 @@ class ControlFlowGraphBuilder { return Triple(lhsExitNode, lhsIsNotNullNode, rhsEnterNode) } - fun exitElvis(): ElvisExitNode { + fun exitElvis(): Pair { val exitNode = exitElvisExpressionNodes.pop() addNewSimpleNode(exitNode) exitNode.updateDeadStatus() - dropPostponedLambdasForNonDeterministicCalls() - return exitNode + return exitNode to joinDataFlowFromPostponedLambdasWith(exitNode) } // ----------------------------------- Contract description ----------------------------------- 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 1cc435beeee..edaaf6dbf8b 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 @@ -244,6 +244,9 @@ fun ControlFlowGraphBuilder.createAnonymousObjectExpressionExitNode(fir: FirAnon fun ControlFlowGraphBuilder.createUnionFunctionCallArgumentsNode(fir: FirElement): UnionFunctionCallArgumentsNode = UnionFunctionCallArgumentsNode(currentGraph, fir, levelCounter, createId()) +fun ControlFlowGraphBuilder.createMergePostponedLambdaExitsNode(fir: FirElement): MergePostponedLambdaExitsNode = + MergePostponedLambdaExitsNode(currentGraph, fir, levelCounter, createId()) + fun ControlFlowGraphBuilder.createClassEnterNode(fir: FirClass): ClassEnterNode = ClassEnterNode(currentGraph, fir, levelCounter, createId()) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt index 716dc7fc12e..590635635d6 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt @@ -789,6 +789,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform return checkNotNullCall } + dataFlowAnalyzer.enterCall() checkNotNullCall.argumentList.transformArguments(transformer, ResolutionMode.ContextDependent) checkNotNullCall.transformAnnotations(transformer, ResolutionMode.ContextIndependent) diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt index 3d883a70ebb..99462469393 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt @@ -228,17 +228,25 @@ class PostponedLambdaEnterNode(owner: ControlFlowGraph, override val fir: FirAno return visitor.visitPostponedLambdaEnterNode(this, data) } } + class PostponedLambdaExitNode(owner: ControlFlowGraph, override val fir: FirAnonymousFunctionExpression, level: Int, id: Int) : CFGNode(owner, level, id) { override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitPostponedLambdaExitNode(this, data) } } + class UnionFunctionCallArgumentsNode(owner: ControlFlowGraph, override val fir: FirElement, level: Int, id: Int) : CFGNode(owner, level, id) { override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitUnionFunctionCallArgumentsNode(this, data) } } +class MergePostponedLambdaExitsNode(owner: ControlFlowGraph, override val fir: FirElement, level: Int, id: Int) : CFGNode(owner, level, id) { + override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { + return visitor.visitMergePostponedLambdaExitsNode(this, data) + } +} + class AnonymousFunctionExpressionExitNode(owner: ControlFlowGraph, override val fir: FirAnonymousFunctionExpression, level: Int, id: Int) : CFGNode(owner, level, id) { override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitAnonymousFunctionExpressionExitNode(this, data) diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNodeRenderer.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNodeRenderer.kt index d2c23ac258b..7edaab2f05b 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNodeRenderer.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNodeRenderer.kt @@ -107,6 +107,7 @@ fun CFGNode<*>.render(): String = is AnonymousFunctionExpressionExitNode -> "Exit anonymous function expression" is UnionFunctionCallArgumentsNode -> "Call arguments union" + is MergePostponedLambdaExitsNode -> "Merge postponed lambda exits" is ClassEnterNode -> "Enter class ${owner.name}" is ClassExitNode -> "Exit class ${owner.name}" diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphVisitor.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphVisitor.kt index 6be5369ff50..511c784d8ab 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphVisitor.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphVisitor.kt @@ -46,6 +46,10 @@ abstract class ControlFlowGraphVisitor { return visitNode(node, data) } + open fun visitMergePostponedLambdaExitsNode(node: MergePostponedLambdaExitsNode, data: D): R { + return visitNode(node, data) + } + open fun visitAnonymousFunctionExpressionExitNode(node: AnonymousFunctionExpressionExitNode, data: D): R { return visitNode(node, data) } diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphVisitorVoid.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphVisitorVoid.kt index dde35e8fb6a..638af6f9c72 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphVisitorVoid.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphVisitorVoid.kt @@ -32,6 +32,10 @@ abstract class ControlFlowGraphVisitorVoid : ControlFlowGraphVisitor {142}; 142 -> {143}; - 143 -> {285} [color=green]; + 143 -> {287} [color=green]; subgraph cluster_47 { color=red @@ -595,20 +595,20 @@ digraph kt44814_kt { 191 [label="Postponed enter to lambda [8]"]; subgraph cluster_62 { color=blue - 237 [label="Enter function anonymousFunction [9]" style="filled" fillcolor=red]; + 239 [label="Enter function anonymousFunction [9]" style="filled" fillcolor=red]; subgraph cluster_63 { color=blue - 238 [label="Enter block [9]"]; - 239 [label="Access variable R|/it| [9]"]; - 240 [label="Enter safe call [9]"]; - 241 [label="Access variable R|/LighterASTNode.tokenType| [9]"]; - 242 [label="Exit safe call [9]"]; - 243 [label="Access qualifier /TokenType [9]"]; - 244 [label="Access variable R|/TokenType.Companion.MODIFIER_LIST| [9]"]; - 245 [label="Equality operator == [9]"]; - 246 [label="Exit block [9]"]; + 240 [label="Enter block [9]"]; + 241 [label="Access variable R|/it| [9]"]; + 242 [label="Enter safe call [9]"]; + 243 [label="Access variable R|/LighterASTNode.tokenType| [9]"]; + 244 [label="Exit safe call [9]"]; + 245 [label="Access qualifier /TokenType [9]"]; + 246 [label="Access variable R|/TokenType.Companion.MODIFIER_LIST| [9]"]; + 247 [label="Equality operator == [9]"]; + 248 [label="Exit block [9]"]; } - 247 [label="Exit function anonymousFunction [9]" style="filled" fillcolor=red]; + 249 [label="Exit function anonymousFunction [9]" style="filled" fillcolor=red]; } 192 [label="Postponed exit from lambda [8]"]; 193 [label="Function call: this@R|/FirModifierList.Companion.getModifierList|.R|/FirLightSourceElement.lighterASTNode|.R|/LighterASTNode.getChildren|(...).R|kotlin/collections/find|(...) [7]"]; @@ -639,15 +639,15 @@ digraph kt44814_kt { 215 [label="Postponed enter to lambda [7]"]; subgraph cluster_65 { color=blue - 231 [label="Enter function anonymousFunction [8]" style="filled" fillcolor=red]; + 233 [label="Enter function anonymousFunction [8]" style="filled" fillcolor=red]; subgraph cluster_66 { color=blue - 232 [label="Enter block [8]"]; - 233 [label="Access variable R|/it| [9]"]; - 234 [label="Function call: R|/FirModifierList.FirPsiModifierList.FirPsiModifierList|(...) [8]"]; - 235 [label="Exit block [8]"]; + 234 [label="Enter block [8]"]; + 235 [label="Access variable R|/it| [9]"]; + 236 [label="Function call: R|/FirModifierList.FirPsiModifierList.FirPsiModifierList|(...) [8]"]; + 237 [label="Exit block [8]"]; } - 236 [label="Exit function anonymousFunction [8]" style="filled" fillcolor=red]; + 238 [label="Exit function anonymousFunction [8]" style="filled" fillcolor=red]; } 216 [label="Postponed exit from lambda [7]"]; 217 [label="Function call: $subj$.R|kotlin/let|(...) [6]"]; @@ -655,17 +655,18 @@ digraph kt44814_kt { 219 [label="Exit block [6]"]; } 220 [label="Exit when branch result [5]"]; - 221 [label="Enter when branch result [5]"]; + 221 [label="Merge postponed lambda exits [6]"]; + 222 [label="Enter when branch result [5]"]; subgraph cluster_67 { color=blue - 222 [label="Enter block [5]"]; - 223 [label="Const: Null(null) [5]"]; - 224 [label="Exit block [5]"]; + 223 [label="Enter block [5]"]; + 224 [label="Const: Null(null) [5]"]; + 225 [label="Exit block [5]"]; } - 225 [label="Exit when branch result [4]"]; - 226 [label="Exit when [3]"]; + 226 [label="Exit when branch result [4]"]; + 227 [label="Exit when [3]"]; } - 227 [label="Jump: ^getModifierList when (this@R|/FirModifierList.Companion.getModifierList|) { + 228 [label="Jump: ^getModifierList when (this@R|/FirModifierList.Companion.getModifierList|) { ==($subj$, Null(null)) -> { Null(null) } @@ -684,11 +685,12 @@ digraph kt44814_kt { } } [3]"]; - 228 [label="Stub [3]" style="filled" fillcolor=gray]; - 229 [label="Exit block [3]" style="filled" fillcolor=gray]; + 229 [label="Stub [3]" style="filled" fillcolor=gray]; + 230 [label="Exit block [3]" style="filled" fillcolor=gray]; } - 230 [label="Exit function getModifierList [3]" style="filled" fillcolor=red]; + 231 [label="Exit function getModifierList [3]" style="filled" fillcolor=red]; } + 232 [label="Merge postponed lambda exits [3]"]; 169 -> {170}; 170 -> {171}; 171 -> {172}; @@ -697,7 +699,7 @@ digraph kt44814_kt { 174 -> {175}; 175 -> {176}; 176 -> {177}; - 177 -> {221 178}; + 177 -> {222 178}; 178 -> {179}; 179 -> {180}; 180 -> {181}; @@ -711,15 +713,15 @@ digraph kt44814_kt { 188 -> {189}; 189 -> {190}; 190 -> {191}; - 191 -> {237}; + 191 -> {239}; 191 -> {192} [color=red]; - 191 -> {237} [style=dashed]; + 191 -> {239} [style=dashed]; 192 -> {193}; 193 -> {194}; 194 -> {199 195}; 195 -> {196}; 196 -> {197}; - 197 -> {230}; + 197 -> {231}; 197 -> {198} [style=dotted]; 198 -> {200} [style=dotted]; 199 -> {200}; @@ -729,7 +731,7 @@ digraph kt44814_kt { 203 -> {204}; 204 -> {205}; 205 -> {206}; - 206 -> {226}; + 206 -> {227}; 207 -> {208}; 208 -> {209}; 209 -> {210}; @@ -738,92 +740,94 @@ digraph kt44814_kt { 212 -> {213}; 213 -> {214 218}; 214 -> {215}; - 215 -> {231}; + 215 -> {233}; 215 -> {216} [color=red]; - 215 -> {231} [style=dashed]; + 215 -> {233} [style=dashed]; 216 -> {217}; 217 -> {218}; - 218 -> {219}; + 218 -> {221 219}; 219 -> {220}; - 220 -> {226}; - 221 -> {222}; + 220 -> {227}; + 221 -> {232} [color=red]; 222 -> {223}; 223 -> {224}; 224 -> {225}; 225 -> {226}; 226 -> {227}; - 227 -> {230}; - 227 -> {228} [style=dotted]; + 227 -> {232 228}; + 228 -> {231}; 228 -> {229} [style=dotted]; 229 -> {230} [style=dotted]; - 231 -> {232}; - 232 -> {233}; + 230 -> {231} [style=dotted]; 233 -> {234}; 234 -> {235}; 235 -> {236}; - 236 -> {216} [color=green]; - 237 -> {247 238}; - 238 -> {239}; - 239 -> {240 242}; + 236 -> {237}; + 237 -> {238}; + 238 -> {221} [color=red]; + 238 -> {216} [color=green]; + 239 -> {249 240}; 240 -> {241}; - 241 -> {242}; + 241 -> {242 244}; 242 -> {243}; 243 -> {244}; 244 -> {245}; 245 -> {246}; 246 -> {247}; - 247 -> {192} [color=green]; - 247 -> {237} [color=green style=dashed]; + 247 -> {248}; + 248 -> {249}; + 249 -> {192} [color=green]; + 249 -> {239} [color=green style=dashed]; subgraph cluster_68 { color=red - 248 [label="Enter function boxImpl [3]" style="filled" fillcolor=red]; + 250 [label="Enter function boxImpl [3]" style="filled" fillcolor=red]; subgraph cluster_69 { color=blue - 249 [label="Enter block [3]"]; - 250 [label="Function call: R|/LighterASTNode.LighterASTNode|() [6]"]; - 251 [label="Function call: R|kotlin/collections/listOf|(...) [5]"]; - 252 [label="Function call: R|/LighterASTNode.LighterASTNode|(...) [4]"]; - 253 [label="Function call: R|/FlyweightCapableTreeStructure.FlyweightCapableTreeStructure|() [4]"]; - 254 [label="Function call: R|/FirLightSourceElement.FirLightSourceElement|(...) [3]"]; - 255 [label="Variable declaration: lval sourceElement: R|FirSourceElement?| [3]"]; - 256 [label="Access variable R|/sourceElement| [4]"]; - 257 [label="Function call: (this@R|/FirModifierList.Companion|, R|/sourceElement|).R|/FirModifierList.Companion.getModifierList|() [3]"]; - 258 [label="Variable declaration: lval result: R|FirModifierList?| [3]"]; + 251 [label="Enter block [3]"]; + 252 [label="Function call: R|/LighterASTNode.LighterASTNode|() [6]"]; + 253 [label="Function call: R|kotlin/collections/listOf|(...) [5]"]; + 254 [label="Function call: R|/LighterASTNode.LighterASTNode|(...) [4]"]; + 255 [label="Function call: R|/FlyweightCapableTreeStructure.FlyweightCapableTreeStructure|() [4]"]; + 256 [label="Function call: R|/FirLightSourceElement.FirLightSourceElement|(...) [3]"]; + 257 [label="Variable declaration: lval sourceElement: R|FirSourceElement?| [3]"]; + 258 [label="Access variable R|/sourceElement| [4]"]; + 259 [label="Function call: (this@R|/FirModifierList.Companion|, R|/sourceElement|).R|/FirModifierList.Companion.getModifierList|() [3]"]; + 260 [label="Variable declaration: lval result: R|FirModifierList?| [3]"]; subgraph cluster_70 { color=blue - 259 [label="Enter when [3]"]; + 261 [label="Enter when [3]"]; subgraph cluster_71 { color=blue - 260 [label="Enter when branch condition [4]"]; - 261 [label="Access variable R|/result| [5]"]; - 262 [label="Type operator: (R|/result| is R|FirModifierList.FirLightModifierList|) [5]"]; - 263 [label="Exit when branch condition [4]"]; + 262 [label="Enter when branch condition [4]"]; + 263 [label="Access variable R|/result| [5]"]; + 264 [label="Type operator: (R|/result| is R|FirModifierList.FirLightModifierList|) [5]"]; + 265 [label="Exit when branch condition [4]"]; } subgraph cluster_72 { color=blue - 264 [label="Enter when branch condition else [5]"]; - 265 [label="Exit when branch condition [5]"]; + 266 [label="Enter when branch condition else [5]"]; + 267 [label="Exit when branch condition [5]"]; } - 266 [label="Enter when branch result [6]"]; + 268 [label="Enter when branch result [6]"]; subgraph cluster_73 { color=blue - 267 [label="Enter block [6]"]; - 268 [label="Const: String(Fail) [6]"]; - 269 [label="Exit block [6]"]; + 269 [label="Enter block [6]"]; + 270 [label="Const: String(Fail) [6]"]; + 271 [label="Exit block [6]"]; } - 270 [label="Exit when branch result [5]"]; - 271 [label="Enter when branch result [5]"]; + 272 [label="Exit when branch result [5]"]; + 273 [label="Enter when branch result [5]"]; subgraph cluster_74 { color=blue - 272 [label="Enter block [5]"]; - 273 [label="Const: String(OK) [5]"]; - 274 [label="Exit block [5]"]; + 274 [label="Enter block [5]"]; + 275 [label="Const: String(OK) [5]"]; + 276 [label="Exit block [5]"]; } - 275 [label="Exit when branch result [4]"]; - 276 [label="Exit when [3]"]; + 277 [label="Exit when branch result [4]"]; + 278 [label="Exit when [3]"]; } - 277 [label="Jump: ^boxImpl when () { + 279 [label="Jump: ^boxImpl when () { (R|/result| is R|FirModifierList.FirLightModifierList|) -> { String(OK) } @@ -832,13 +836,11 @@ digraph kt44814_kt { } } [3]"]; - 278 [label="Stub [3]" style="filled" fillcolor=gray]; - 279 [label="Exit block [3]" style="filled" fillcolor=gray]; + 280 [label="Stub [3]" style="filled" fillcolor=gray]; + 281 [label="Exit block [3]" style="filled" fillcolor=gray]; } - 280 [label="Exit function boxImpl [3]" style="filled" fillcolor=red]; + 282 [label="Exit function boxImpl [3]" style="filled" fillcolor=red]; } - 248 -> {249}; - 249 -> {250}; 250 -> {251}; 251 -> {252}; 252 -> {253}; @@ -852,64 +854,66 @@ digraph kt44814_kt { 260 -> {261}; 261 -> {262}; 262 -> {263}; - 263 -> {271 264}; + 263 -> {264}; 264 -> {265}; - 265 -> {266}; + 265 -> {273 266}; 266 -> {267}; 267 -> {268}; 268 -> {269}; 269 -> {270}; - 270 -> {276}; + 270 -> {271}; 271 -> {272}; - 272 -> {273}; + 272 -> {278}; 273 -> {274}; 274 -> {275}; 275 -> {276}; 276 -> {277}; - 277 -> {280}; - 277 -> {278} [style=dotted]; - 278 -> {279} [style=dotted]; + 277 -> {278}; + 278 -> {279}; + 279 -> {282}; 279 -> {280} [style=dotted]; + 280 -> {281} [style=dotted]; + 281 -> {282} [style=dotted]; subgraph cluster_75 { color=red - 281 [label="Enter class Companion [2]" style="filled" fillcolor=red]; - 282 [label="Exit class Companion [2]" style="filled" fillcolor=red]; + 283 [label="Enter class Companion [2]" style="filled" fillcolor=red]; + 284 [label="Exit class Companion [2]" style="filled" fillcolor=red]; } - 281 -> {282} [color=green]; + 283 -> {284} [color=green]; subgraph cluster_76 { color=red - 283 [label="Enter class FirModifierList [1]" style="filled" fillcolor=red]; - 284 [label="Part of class initialization [1]"]; - 285 [label="Exit class FirModifierList [1]" style="filled" fillcolor=red]; + 285 [label="Enter class FirModifierList [1]" style="filled" fillcolor=red]; + 286 [label="Part of class initialization [1]"]; + 287 [label="Exit class FirModifierList [1]" style="filled" fillcolor=red]; } - 283 -> {284} [color=green]; - 284 -> {285} [style=dotted]; - 284 -> {141} [color=green]; - 284 -> {141} [style=dashed]; + 285 -> {286} [color=green]; + 286 -> {287} [style=dotted]; + 286 -> {141} [color=green]; + 286 -> {141} [style=dashed]; subgraph cluster_77 { color=red - 286 [label="Enter function box [1]" style="filled" fillcolor=red]; + 288 [label="Enter function box [1]" style="filled" fillcolor=red]; subgraph cluster_78 { color=blue - 287 [label="Enter block [1]"]; - 288 [label="Access qualifier /FirModifierList [2]"]; - 289 [label="Function call: Q|FirModifierList|.R|/FirModifierList.Companion.boxImpl|() [1]"]; - 290 [label="Jump: ^box Q|FirModifierList|.R|/FirModifierList.Companion.boxImpl|() [1]"]; - 291 [label="Stub [1]" style="filled" fillcolor=gray]; - 292 [label="Exit block [1]" style="filled" fillcolor=gray]; + 289 [label="Enter block [1]"]; + 290 [label="Access qualifier /FirModifierList [2]"]; + 291 [label="Function call: Q|FirModifierList|.R|/FirModifierList.Companion.boxImpl|() [1]"]; + 292 [label="Jump: ^box Q|FirModifierList|.R|/FirModifierList.Companion.boxImpl|() [1]"]; + 293 [label="Stub [1]" style="filled" fillcolor=gray]; + 294 [label="Exit block [1]" style="filled" fillcolor=gray]; } - 293 [label="Exit function box [1]" style="filled" fillcolor=red]; + 295 [label="Exit function box [1]" style="filled" fillcolor=red]; } - 286 -> {287}; - 287 -> {288}; 288 -> {289}; 289 -> {290}; - 290 -> {293}; - 290 -> {291} [style=dotted]; - 291 -> {292} [style=dotted]; + 290 -> {291}; + 291 -> {292}; + 292 -> {295}; 292 -> {293} [style=dotted]; + 293 -> {294} [style=dotted]; + 294 -> {295} [style=dotted]; } diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNullComplex.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNullComplex.fir.kt index f2e8cb736fe..76d58492ea1 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNullComplex.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/letChangesToNullComplex.fir.kt @@ -6,6 +6,6 @@ fun foo(y: String?) { var x: String? = "" if (x != null) { bar(y?.let { x = null; it }).length - x.length // Smart cast is not possible + x.length // Smart cast is not possible } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/withChangesToNull.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/withChangesToNull.fir.kt index 8de02b914d9..ddaab04f303 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/withChangesToNull.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/withChangesToNull.fir.kt @@ -3,8 +3,8 @@ fun foo(y: String?) { if (x != null) { with(y?.let { x = null; it }) { this.length - x.length + x.length } - x.length + x.length } }