[FIR] Maintain postponed lambdas as lazy Flow within CFGNode
^KT-60958 Fixed
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/ContextCollector.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/ContextCollector.kt
index d958b0e..ce5043e 100644
--- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/ContextCollector.kt
+++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/ContextCollector.kt
@@ -26,7 +26,6 @@
import org.jetbrains.kotlin.fir.resolve.dfa.RealVariable
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ClassExitNode
-import org.jetbrains.kotlin.fir.resolve.dfa.cfg.MergePostponedLambdaExitsNode
import org.jetbrains.kotlin.fir.resolve.dfa.controlFlowGraph
import org.jetbrains.kotlin.fir.resolve.dfa.smartCastedType
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculatorForFullBodyResolve
@@ -310,9 +309,6 @@
private fun isAcceptedControlFlowNode(node: CFGNode<*>): Boolean = when {
node is ClassExitNode -> false
- // TODO Remove as soon as KT-61728 is fixed
- node is MergePostponedLambdaExitsNode && !node.flowInitialized -> false
-
else -> true
}
diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda.dot b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda.dot
index cb0fa85..b450cea 100644
--- a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda.dot
+++ b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda.dot
@@ -329,8 +329,7 @@
94 -> {95};
95 -> {96};
96 -> {97};
- 97 -> {98} [color=green];
- 97 -> {115} [color=red];
+ 97 -> {98};
98 -> {99};
99 -> {100};
100 -> {101};
@@ -348,8 +347,7 @@
110 -> {111};
111 -> {112};
112 -> {113};
- 113 -> {114} [color=green];
- 113 -> {115} [color=red];
+ 113 -> {114};
114 -> {115};
115 -> {116};
116 -> {117};
@@ -431,9 +429,8 @@
133 -> {134};
134 -> {135};
135 -> {136};
+ 136 -> {137};
136 -> {127} [color=green style=dashed];
- 136 -> {137} [color=green];
- 136 -> {154} [color=red];
137 -> {138};
138 -> {139};
139 -> {140};
@@ -451,9 +448,8 @@
149 -> {150};
150 -> {151};
151 -> {152};
+ 152 -> {153};
152 -> {141} [color=green style=dashed];
- 152 -> {153} [color=green];
- 152 -> {154} [color=red];
153 -> {154};
154 -> {155};
155 -> {156};
@@ -533,8 +529,7 @@
172 -> {173};
173 -> {174};
174 -> {175};
- 175 -> {176} [color=green];
- 175 -> {193} [color=red];
+ 175 -> {176};
176 -> {177};
177 -> {178};
178 -> {179};
@@ -551,8 +546,7 @@
188 -> {189};
189 -> {190};
190 -> {191};
- 191 -> {192} [color=green];
- 191 -> {193} [color=red];
+ 191 -> {192};
192 -> {193};
193 -> {194};
194 -> {195};
@@ -631,9 +625,8 @@
210 -> {211};
211 -> {212};
212 -> {213};
+ 213 -> {214};
213 -> {204} [color=green style=dashed];
- 213 -> {214} [color=green];
- 213 -> {231} [color=red];
214 -> {215};
215 -> {216};
216 -> {217};
@@ -650,9 +643,8 @@
226 -> {227};
227 -> {228};
228 -> {229};
+ 229 -> {230};
229 -> {218} [color=green style=dashed];
- 229 -> {230} [color=green];
- 229 -> {231} [color=red];
230 -> {231};
231 -> {232};
232 -> {233};
@@ -730,8 +722,7 @@
247 -> {248};
248 -> {249};
249 -> {250};
- 251 -> {252} [color=green];
- 251 -> {269} [color=red];
+ 251 -> {252};
252 -> {253};
253 -> {254};
254 -> {255};
@@ -747,8 +738,7 @@
263 -> {264};
264 -> {265};
265 -> {266};
- 267 -> {268} [color=green];
- 267 -> {269} [color=red];
+ 267 -> {268};
268 -> {269};
269 -> {270};
270 -> {271};
@@ -837,8 +827,7 @@
290 -> {291};
291 -> {292};
292 -> {293};
- 293 -> {294} [color=green];
- 293 -> {311} [color=red];
+ 293 -> {294};
294 -> {295};
295 -> {296};
296 -> {297 310};
@@ -856,8 +845,7 @@
306 -> {307};
307 -> {308};
308 -> {309};
- 309 -> {310} [color=green];
- 309 -> {311} [color=red];
+ 309 -> {310};
310 -> {311};
311 -> {312};
312 -> {313};
@@ -947,9 +935,8 @@
333 -> {334};
334 -> {335};
335 -> {336};
+ 336 -> {337};
336 -> {323} [color=green style=dashed];
- 336 -> {337} [color=green];
- 336 -> {354} [color=red];
337 -> {338};
338 -> {339};
339 -> {340 353};
@@ -967,9 +954,8 @@
349 -> {350};
350 -> {351};
351 -> {352};
+ 352 -> {353};
352 -> {339} [color=green style=dashed];
- 352 -> {353} [color=green];
- 352 -> {354} [color=red];
353 -> {354};
354 -> {355};
355 -> {356};
@@ -1056,8 +1042,7 @@
376 -> {377};
377 -> {378};
378 -> {379};
- 379 -> {380} [color=green];
- 379 -> {397} [color=red];
+ 379 -> {380};
380 -> {381};
381 -> {382};
382 -> {383 395 396};
@@ -1074,8 +1059,7 @@
392 -> {393};
393 -> {394};
394 -> {395};
- 395 -> {396} [color=green];
- 395 -> {397} [color=red];
+ 395 -> {396};
396 -> {397};
397 -> {398};
398 -> {399};
@@ -1160,9 +1144,8 @@
417 -> {418};
418 -> {419};
419 -> {420};
+ 420 -> {421};
420 -> {407} [color=green style=dashed];
- 420 -> {421} [color=green];
- 420 -> {438} [color=red];
421 -> {422};
422 -> {423};
423 -> {424 436 437};
@@ -1179,9 +1162,8 @@
433 -> {434};
434 -> {435};
435 -> {436};
+ 436 -> {437};
436 -> {423} [color=green style=dashed];
- 436 -> {437} [color=green];
- 436 -> {438} [color=red];
437 -> {438};
438 -> {439};
439 -> {440};
@@ -1265,8 +1247,7 @@
457 -> {458};
458 -> {459};
459 -> {460};
- 461 -> {462} [color=green];
- 461 -> {479} [color=red];
+ 461 -> {462};
462 -> {463};
463 -> {464};
464 -> {465 477 478};
@@ -1282,8 +1263,7 @@
473 -> {474};
474 -> {475};
475 -> {476};
- 477 -> {478} [color=green];
- 477 -> {479} [color=red];
+ 477 -> {478};
478 -> {479};
479 -> {480};
480 -> {481};
@@ -1345,8 +1325,7 @@
492 -> {493};
493 -> {494};
494 -> {495};
- 495 -> {496} [color=green];
- 495 -> {505} [color=red];
+ 495 -> {496};
496 -> {497};
497 -> {498 504};
497 -> {503} [style=dotted];
@@ -1356,8 +1335,7 @@
500 -> {501};
501 -> {502};
502 -> {503};
- 503 -> {504} [color=green];
- 503 -> {505} [color=red];
+ 503 -> {504};
504 -> {505};
505 -> {506};
506 -> {507};
@@ -1420,12 +1398,10 @@
519 -> {520};
520 -> {521};
521 -> {522};
- 522 -> {523} [color=green];
- 522 -> {528} [color=red];
+ 522 -> {523};
523 -> {524};
524 -> {525};
- 526 -> {527} [color=green];
- 526 -> {528} [color=red];
+ 526 -> {527};
527 -> {528};
528 -> {529};
529 -> {530};
diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.dot b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.dot
index a459485..758fcbb 100644
--- a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.dot
+++ b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.dot
@@ -147,8 +147,7 @@
34 -> {35};
35 -> {36};
36 -> {37};
- 37 -> {38} [color=green];
- 37 -> {51} [color=red];
+ 37 -> {38};
38 -> {39};
39 -> {40};
40 -> {41};
@@ -162,8 +161,7 @@
46 -> {47};
47 -> {48};
48 -> {49};
- 49 -> {50} [color=green];
- 49 -> {51} [color=red];
+ 49 -> {50};
50 -> {51};
51 -> {52};
52 -> {53};
@@ -232,8 +230,7 @@
64 -> {65};
65 -> {66};
66 -> {67};
- 67 -> {68} [color=green];
- 67 -> {82} [color=red];
+ 67 -> {68};
68 -> {69};
69 -> {70};
70 -> {71};
@@ -248,8 +245,7 @@
77 -> {78};
78 -> {79};
79 -> {80};
- 80 -> {81} [color=green];
- 80 -> {82} [color=red];
+ 80 -> {81};
81 -> {82};
82 -> {83};
83 -> {84};
@@ -348,8 +344,7 @@
95 -> {96};
96 -> {97};
97 -> {98};
- 98 -> {99} [color=green];
- 98 -> {128} [color=red];
+ 98 -> {99};
99 -> {100};
100 -> {101};
101 -> {102};
@@ -379,8 +374,7 @@
123 -> {124};
124 -> {125};
125 -> {126};
- 126 -> {127} [color=green];
- 126 -> {128} [color=red];
+ 126 -> {127};
127 -> {128};
128 -> {129};
129 -> {130};
@@ -491,18 +485,17 @@
}
191 [label="Postponed exit from lambda"];
192 [label="Function call: R|kotlin/run|<R|kotlin/Int|>(...)" style="filled" fillcolor=yellow];
- 193 [label="Merge postponed lambda exits"];
- 194 [label="Function call: R|/foo|<R|kotlin/Int|>(...)" style="filled" fillcolor=yellow];
- 195 [label="Access variable R|<local>/p|"];
- 196 [label="Access variable R|kotlin/String.length<Inapplicable(UNSAFE_CALL): kotlin/String.length>#|"];
- 197 [label="Exit block"];
+ 193 [label="Function call: R|/foo|<R|kotlin/Int|>(...)" style="filled" fillcolor=yellow];
+ 194 [label="Access variable R|<local>/p|"];
+ 195 [label="Access variable R|kotlin/String.length<Inapplicable(UNSAFE_CALL): kotlin/String.length>#|"];
+ 196 [label="Exit block"];
}
- 198 [label="Exit when branch result"];
- 199 [label="Exit when"];
+ 197 [label="Exit when branch result"];
+ 198 [label="Exit when"];
}
- 200 [label="Exit block"];
+ 199 [label="Exit block"];
}
- 201 [label="Exit function test4" style="filled" fillcolor=red];
+ 200 [label="Exit function test4" style="filled" fillcolor=red];
}
134 -> {135};
135 -> {136};
@@ -514,7 +507,7 @@
141 -> {142};
142 -> {143};
143 -> {144 145};
- 144 -> {199};
+ 144 -> {198};
145 -> {146};
146 -> {147};
147 -> {148};
@@ -533,8 +526,7 @@
158 -> {159};
159 -> {160};
160 -> {161};
- 161 -> {162} [color=green];
- 161 -> {193} [color=red];
+ 161 -> {162};
162 -> {163};
163 -> {164};
164 -> {179};
@@ -550,12 +542,11 @@
172 -> {173};
173 -> {174};
174 -> {175};
- 175 -> {176} [color=green];
- 175 -> {193} [color=red];
+ 175 -> {176};
176 -> {177};
177 -> {178};
178 -> {179};
- 179 -> {180 193};
+ 179 -> {180};
180 -> {181};
181 -> {182};
182 -> {183 192};
@@ -569,73 +560,71 @@
188 -> {189};
189 -> {190};
190 -> {191};
- 191 -> {192} [color=green];
- 191 -> {194} [color=red];
- 192 -> {194};
- 193 -> {194} [color=red];
+ 191 -> {192};
+ 192 -> {193};
+ 193 -> {194};
194 -> {195};
195 -> {196};
196 -> {197};
197 -> {198};
198 -> {199};
199 -> {200};
- 200 -> {201};
subgraph cluster_48 {
color=red
- 202 [label="Enter function test5" style="filled" fillcolor=red];
+ 201 [label="Enter function test5" style="filled" fillcolor=red];
subgraph cluster_49 {
color=blue
- 203 [label="Enter block"];
- 204 [label="Access variable R|<local>/y|"];
- 205 [label="Enter safe call"];
- 206 [label="Postponed enter to lambda"];
+ 202 [label="Enter block"];
+ 203 [label="Access variable R|<local>/y|"];
+ 204 [label="Enter safe call"];
+ 205 [label="Postponed enter to lambda"];
subgraph cluster_50 {
color=blue
- 207 [label="Enter function <anonymous>" style="filled" fillcolor=red];
+ 206 [label="Enter function <anonymous>" style="filled" fillcolor=red];
subgraph cluster_51 {
color=blue
- 208 [label="Enter block"];
- 209 [label="Access variable R|<local>/x|"];
- 210 [label="Type operator: (R|<local>/x| as R|kotlin/String|)"];
- 211 [label="Function call: R|/n|<R|kotlin/String?|>()" style="filled" fillcolor=yellow];
- 212 [label="Exit block"];
+ 207 [label="Enter block"];
+ 208 [label="Access variable R|<local>/x|"];
+ 209 [label="Type operator: (R|<local>/x| as R|kotlin/String|)"];
+ 210 [label="Function call: R|/n|<R|kotlin/String?|>()" style="filled" fillcolor=yellow];
+ 211 [label="Exit block"];
}
- 213 [label="Exit function <anonymous>" style="filled" fillcolor=red];
+ 212 [label="Exit function <anonymous>" style="filled" fillcolor=red];
}
- 214 [label="Postponed exit from lambda"];
- 215 [label="Function call: $subj$.R|kotlin/let|<R|kotlin/String|, R|kotlin/String?|>(...)" style="filled" fillcolor=yellow];
- 216 [label="Exit safe call"];
- 217 [label="Const: Int(1)"];
- 218 [label="Postponed enter to lambda"];
+ 213 [label="Postponed exit from lambda"];
+ 214 [label="Function call: $subj$.R|kotlin/let|<R|kotlin/String|, R|kotlin/String?|>(...)" style="filled" fillcolor=yellow];
+ 215 [label="Exit safe call"];
+ 216 [label="Const: Int(1)"];
+ 217 [label="Postponed enter to lambda"];
subgraph cluster_52 {
color=blue
- 219 [label="Enter function <anonymous>" style="filled" fillcolor=red];
+ 218 [label="Enter function <anonymous>" style="filled" fillcolor=red];
subgraph cluster_53 {
color=blue
- 220 [label="Enter block"];
- 221 [label="Const: String()"];
- 222 [label="Exit block"];
+ 219 [label="Enter block"];
+ 220 [label="Const: String()"];
+ 221 [label="Exit block"];
}
- 223 [label="Exit function <anonymous>" style="filled" fillcolor=red];
+ 222 [label="Exit function <anonymous>" style="filled" fillcolor=red];
}
- 224 [label="Postponed exit from lambda"];
- 225 [label="Function call: R|kotlin/run|<R|kotlin/String|>(...)" style="filled" fillcolor=yellow];
- 226 [label="Merge postponed lambda exits"];
- 227 [label="Function call: R|/foo|<R|kotlin/String|>(...)" style="filled" fillcolor=yellow];
- 228 [label="Access variable R|<local>/x|"];
- 229 [label="Access variable R|kotlin/String.length<Inapplicable(UNSAFE_CALL): kotlin/String.length>#|"];
- 230 [label="Exit block"];
+ 223 [label="Postponed exit from lambda"];
+ 224 [label="Function call: R|kotlin/run|<R|kotlin/String|>(...)" style="filled" fillcolor=yellow];
+ 225 [label="Function call: R|/foo|<R|kotlin/String|>(...)" style="filled" fillcolor=yellow];
+ 226 [label="Access variable R|<local>/x|"];
+ 227 [label="Access variable R|kotlin/String.length<Inapplicable(UNSAFE_CALL): kotlin/String.length>#|"];
+ 228 [label="Exit block"];
}
- 231 [label="Exit function test5" style="filled" fillcolor=red];
+ 229 [label="Exit function test5" style="filled" fillcolor=red];
}
+ 201 -> {202};
202 -> {203};
- 203 -> {204};
- 204 -> {205 216};
- 205 -> {206};
- 206 -> {207 215};
- 206 -> {214} [style=dotted];
- 206 -> {207} [style=dashed];
+ 203 -> {204 215};
+ 204 -> {205};
+ 205 -> {206 214};
+ 205 -> {213} [style=dotted];
+ 205 -> {206} [style=dashed];
+ 206 -> {207};
207 -> {208};
208 -> {209};
209 -> {210};
@@ -643,135 +632,133 @@
211 -> {212};
212 -> {213};
213 -> {214};
- 214 -> {215} [color=green];
- 214 -> {226} [color=red];
+ 214 -> {215};
215 -> {216};
- 216 -> {217 226};
- 217 -> {218};
- 218 -> {219 225};
- 218 -> {224} [style=dotted];
- 218 -> {219} [style=dashed];
+ 216 -> {217};
+ 217 -> {218 224};
+ 217 -> {223} [style=dotted];
+ 217 -> {218} [style=dashed];
+ 218 -> {219};
219 -> {220};
220 -> {221};
221 -> {222};
222 -> {223};
223 -> {224};
- 224 -> {225} [color=green];
- 224 -> {227} [color=red];
- 225 -> {227};
- 226 -> {227} [color=red];
+ 224 -> {225};
+ 225 -> {226};
+ 226 -> {227};
227 -> {228};
228 -> {229};
- 229 -> {230};
- 230 -> {231};
subgraph cluster_54 {
color=red
- 232 [label="Enter function test6" style="filled" fillcolor=red];
+ 230 [label="Enter function test6" style="filled" fillcolor=red];
subgraph cluster_55 {
color=blue
- 233 [label="Enter block"];
+ 231 [label="Enter block"];
subgraph cluster_56 {
color=blue
- 234 [label="Enter when"];
+ 232 [label="Enter when"];
subgraph cluster_57 {
color=blue
- 235 [label="Enter when branch condition "];
- 236 [label="Const: Boolean(true)"];
- 237 [label="Exit when branch condition"];
+ 233 [label="Enter when branch condition "];
+ 234 [label="Const: Boolean(true)"];
+ 235 [label="Exit when branch condition"];
}
subgraph cluster_58 {
color=blue
- 238 [label="Enter when branch condition else"];
- 239 [label="Exit when branch condition"];
+ 236 [label="Enter when branch condition else"];
+ 237 [label="Exit when branch condition"];
}
- 240 [label="Enter when branch result"];
+ 238 [label="Enter when branch result"];
subgraph cluster_59 {
color=blue
- 241 [label="Enter block"];
- 242 [label="Postponed enter to lambda"];
+ 239 [label="Enter block"];
+ 240 [label="Postponed enter to lambda"];
subgraph cluster_60 {
color=blue
- 243 [label="Enter function <anonymous>" style="filled" fillcolor=red];
+ 241 [label="Enter function <anonymous>" style="filled" fillcolor=red];
subgraph cluster_61 {
color=blue
- 244 [label="Enter block"];
- 245 [label="Access variable R|<local>/x|"];
- 246 [label="Type operator: (R|<local>/x| as R|kotlin/String|)"];
- 247 [label="Function call: R|/n|<R|kotlin/Int?|>()" style="filled" fillcolor=yellow];
- 248 [label="Exit block"];
+ 242 [label="Enter block"];
+ 243 [label="Access variable R|<local>/x|"];
+ 244 [label="Type operator: (R|<local>/x| as R|kotlin/String|)"];
+ 245 [label="Function call: R|/n|<R|kotlin/Int?|>()" style="filled" fillcolor=yellow];
+ 246 [label="Exit block"];
}
- 249 [label="Exit function <anonymous>" style="filled" fillcolor=red];
+ 247 [label="Exit function <anonymous>" style="filled" fillcolor=red];
}
- 250 [label="Postponed exit from lambda"];
- 251 [label="Function call: R|kotlin/run|<R|kotlin/Int?|>(...)" style="filled" fillcolor=yellow];
- 252 [label="Exit block"];
+ 248 [label="Postponed exit from lambda"];
+ 249 [label="Function call: R|kotlin/run|<R|kotlin/Int?|>(...)" style="filled" fillcolor=yellow];
+ 250 [label="Exit block"];
}
- 253 [label="Exit when branch result"];
- 254 [label="Enter when branch result"];
+ 251 [label="Exit when branch result"];
+ 252 [label="Enter when branch result"];
subgraph cluster_62 {
color=blue
- 255 [label="Enter block"];
- 256 [label="Postponed enter to lambda"];
+ 253 [label="Enter block"];
+ 254 [label="Postponed enter to lambda"];
subgraph cluster_63 {
color=blue
- 257 [label="Enter function <anonymous>" style="filled" fillcolor=red];
+ 255 [label="Enter function <anonymous>" style="filled" fillcolor=red];
subgraph cluster_64 {
color=blue
- 258 [label="Enter block"];
- 259 [label="Access variable R|<local>/x|"];
- 260 [label="Type operator: (R|<local>/x| as R|kotlin/String|)"];
- 261 [label="Function call: R|/n|<R|kotlin/Int?|>()" style="filled" fillcolor=yellow];
- 262 [label="Exit block"];
+ 256 [label="Enter block"];
+ 257 [label="Access variable R|<local>/x|"];
+ 258 [label="Type operator: (R|<local>/x| as R|kotlin/String|)"];
+ 259 [label="Function call: R|/n|<R|kotlin/Int?|>()" style="filled" fillcolor=yellow];
+ 260 [label="Exit block"];
}
- 263 [label="Exit function <anonymous>" style="filled" fillcolor=red];
+ 261 [label="Exit function <anonymous>" style="filled" fillcolor=red];
}
- 264 [label="Postponed exit from lambda"];
- 265 [label="Function call: R|kotlin/run|<R|kotlin/Int?|>(...)" style="filled" fillcolor=yellow];
- 266 [label="Exit block"];
+ 262 [label="Postponed exit from lambda"];
+ 263 [label="Function call: R|kotlin/run|<R|kotlin/Int?|>(...)" style="filled" fillcolor=yellow];
+ 264 [label="Exit block"];
}
- 267 [label="Exit when branch result"];
- 268 [label="Exit when"];
+ 265 [label="Exit when branch result"];
+ 266 [label="Exit when"];
}
- 269 [label="Function call: R|/id|<R|kotlin/Int?|>(...)" style="filled" fillcolor=yellow];
- 270 [label="Const: Int(1)"];
- 271 [label="Postponed enter to lambda"];
+ 267 [label="Function call: R|/id|<R|kotlin/Int?|>(...)" style="filled" fillcolor=yellow];
+ 268 [label="Const: Int(1)"];
+ 269 [label="Postponed enter to lambda"];
subgraph cluster_65 {
color=blue
- 272 [label="Enter function <anonymous>" style="filled" fillcolor=red];
+ 270 [label="Enter function <anonymous>" style="filled" fillcolor=red];
subgraph cluster_66 {
color=blue
- 273 [label="Enter block"];
- 274 [label="Access variable R|<local>/x|"];
- 275 [label="Access variable R|kotlin/String.length<Inapplicable(UNSAFE_CALL): kotlin/String.length>#|"];
- 276 [label="Const: Int(123)"];
- 277 [label="Exit block"];
+ 271 [label="Enter block"];
+ 272 [label="Access variable R|<local>/x|"];
+ 273 [label="Access variable R|kotlin/String.length<Inapplicable(UNSAFE_CALL): kotlin/String.length>#|"];
+ 274 [label="Const: Int(123)"];
+ 275 [label="Exit block"];
}
- 278 [label="Exit function <anonymous>" style="filled" fillcolor=red];
+ 276 [label="Exit function <anonymous>" style="filled" fillcolor=red];
}
- 279 [label="Postponed exit from lambda"];
- 280 [label="Function call: R|kotlin/run|<R|kotlin/Int|>(...)" style="filled" fillcolor=yellow];
- 281 [label="Merge postponed lambda exits"];
- 282 [label="Function call: R|/foo|<R|kotlin/Int|>(...)" style="filled" fillcolor=yellow];
- 283 [label="Access variable R|<local>/x|"];
- 284 [label="Access variable R|kotlin/String.length<Inapplicable(UNSAFE_CALL): kotlin/String.length>#|"];
- 285 [label="Exit block"];
+ 277 [label="Postponed exit from lambda"];
+ 278 [label="Function call: R|kotlin/run|<R|kotlin/Int|>(...)" style="filled" fillcolor=yellow];
+ 279 [label="Function call: R|/foo|<R|kotlin/Int|>(...)" style="filled" fillcolor=yellow];
+ 280 [label="Access variable R|<local>/x|"];
+ 281 [label="Smart cast: R|<local>/x|"];
+ 282 [label="Access variable R|kotlin/String.length|"];
+ 283 [label="Exit block"];
}
- 286 [label="Exit function test6" style="filled" fillcolor=red];
+ 284 [label="Exit function test6" style="filled" fillcolor=red];
}
+ 230 -> {231};
+ 231 -> {232};
232 -> {233};
233 -> {234};
234 -> {235};
- 235 -> {236};
+ 235 -> {236 252};
236 -> {237};
- 237 -> {238 254};
+ 237 -> {238};
238 -> {239};
239 -> {240};
- 240 -> {241};
+ 240 -> {241 249};
+ 240 -> {248} [style=dotted];
+ 240 -> {241} [style=dashed];
241 -> {242};
- 242 -> {243 251};
- 242 -> {250} [style=dotted];
- 242 -> {243} [style=dashed];
+ 242 -> {243};
243 -> {244};
244 -> {245};
245 -> {246};
@@ -779,16 +766,15 @@
247 -> {248};
248 -> {249};
249 -> {250};
- 250 -> {251} [color=green];
- 250 -> {281} [color=red];
- 251 -> {252};
+ 250 -> {251};
+ 251 -> {266};
252 -> {253};
- 253 -> {268};
- 254 -> {255};
+ 253 -> {254};
+ 254 -> {255 263};
+ 254 -> {262} [style=dotted];
+ 254 -> {255} [style=dashed];
255 -> {256};
- 256 -> {257 265};
- 256 -> {264} [style=dotted];
- 256 -> {257} [style=dashed];
+ 256 -> {257};
257 -> {258};
258 -> {259};
259 -> {260};
@@ -796,17 +782,16 @@
261 -> {262};
262 -> {263};
263 -> {264};
- 264 -> {265} [color=green];
- 264 -> {281} [color=red];
+ 264 -> {265};
265 -> {266};
266 -> {267};
267 -> {268};
- 268 -> {269 281};
- 269 -> {270};
+ 268 -> {269};
+ 269 -> {270 278};
+ 269 -> {277} [style=dotted];
+ 269 -> {270} [style=dashed];
270 -> {271};
- 271 -> {272 280};
- 271 -> {279} [style=dotted];
- 271 -> {272} [style=dashed];
+ 271 -> {272};
272 -> {273};
273 -> {274};
274 -> {275};
@@ -814,87 +799,86 @@
276 -> {277};
277 -> {278};
278 -> {279};
- 279 -> {280} [color=green];
- 279 -> {282} [color=red];
- 280 -> {282};
- 281 -> {282} [color=red];
+ 279 -> {280};
+ 280 -> {281};
+ 281 -> {282};
282 -> {283};
283 -> {284};
- 284 -> {285};
- 285 -> {286};
subgraph cluster_67 {
color=red
- 287 [label="Enter function test7" style="filled" fillcolor=red];
+ 285 [label="Enter function test7" style="filled" fillcolor=red];
subgraph cluster_68 {
color=blue
- 288 [label="Enter block"];
- 289 [label="Access variable R|<local>/x|"];
- 290 [label="Variable declaration: lvar p: R|kotlin/String?|"];
+ 286 [label="Enter block"];
+ 287 [label="Access variable R|<local>/x|"];
+ 288 [label="Variable declaration: lvar p: R|kotlin/String?|"];
subgraph cluster_69 {
color=blue
- 291 [label="Enter when"];
+ 289 [label="Enter when"];
subgraph cluster_70 {
color=blue
- 292 [label="Enter when branch condition "];
- 293 [label="Access variable R|<local>/p|"];
- 294 [label="Const: Null(null)"];
- 295 [label="Equality operator !="];
- 296 [label="Exit when branch condition"];
+ 290 [label="Enter when branch condition "];
+ 291 [label="Access variable R|<local>/p|"];
+ 292 [label="Const: Null(null)"];
+ 293 [label="Equality operator !="];
+ 294 [label="Exit when branch condition"];
}
- 297 [label="Synthetic else branch"];
- 298 [label="Enter when branch result"];
+ 295 [label="Synthetic else branch"];
+ 296 [label="Enter when branch result"];
subgraph cluster_71 {
color=blue
- 299 [label="Enter block"];
- 300 [label="Postponed enter to lambda"];
+ 297 [label="Enter block"];
+ 298 [label="Postponed enter to lambda"];
subgraph cluster_72 {
color=blue
- 301 [label="Enter function <anonymous>" style="filled" fillcolor=red];
+ 299 [label="Enter function <anonymous>" style="filled" fillcolor=red];
subgraph cluster_73 {
color=blue
- 302 [label="Enter block"];
- 303 [label="Const: Null(null)"];
- 304 [label="Assignment: R|<local>/p|"];
- 305 [label="Function call: R|/n|<R|kotlin/Int?|>()" style="filled" fillcolor=yellow];
- 306 [label="Exit block"];
+ 300 [label="Enter block"];
+ 301 [label="Const: Null(null)"];
+ 302 [label="Assignment: R|<local>/p|"];
+ 303 [label="Function call: R|/n|<R|kotlin/Int?|>()" style="filled" fillcolor=yellow];
+ 304 [label="Exit block"];
}
- 307 [label="Exit function <anonymous>" style="filled" fillcolor=red];
+ 305 [label="Exit function <anonymous>" style="filled" fillcolor=red];
}
- 308 [label="Postponed exit from lambda"];
- 309 [label="Function call: R|kotlin/run|<R|kotlin/Int?|>(...)" style="filled" fillcolor=yellow];
- 310 [label="Function call: R|/id|<R|kotlin/Int?|>(...)" style="filled" fillcolor=yellow];
- 311 [label="Const: Int(1)"];
- 312 [label="Postponed enter to lambda"];
+ 306 [label="Postponed exit from lambda"];
+ 307 [label="Function call: R|kotlin/run|<R|kotlin/Int?|>(...)" style="filled" fillcolor=yellow];
+ 308 [label="Function call: R|/id|<R|kotlin/Int?|>(...)" style="filled" fillcolor=yellow];
+ 309 [label="Const: Int(1)"];
+ 310 [label="Postponed enter to lambda"];
subgraph cluster_74 {
color=blue
- 313 [label="Enter function <anonymous>" style="filled" fillcolor=red];
+ 311 [label="Enter function <anonymous>" style="filled" fillcolor=red];
subgraph cluster_75 {
color=blue
- 314 [label="Enter block"];
- 315 [label="Access variable R|<local>/p|"];
- 316 [label="Smart cast: R|<local>/p|"];
- 317 [label="Access variable R|kotlin/String.length<Inapplicable(UNSTABLE_SMARTCAST): kotlin/String.length>#|"];
- 318 [label="Const: Int(123)"];
- 319 [label="Exit block"];
+ 312 [label="Enter block"];
+ 313 [label="Access variable R|<local>/p|"];
+ 314 [label="Smart cast: R|<local>/p|"];
+ 315 [label="Access variable R|kotlin/String.length<Inapplicable(UNSTABLE_SMARTCAST): kotlin/String.length>#|"];
+ 316 [label="Const: Int(123)"];
+ 317 [label="Exit block"];
}
- 320 [label="Exit function <anonymous>" style="filled" fillcolor=red];
+ 318 [label="Exit function <anonymous>" style="filled" fillcolor=red];
}
- 321 [label="Postponed exit from lambda"];
- 322 [label="Function call: R|kotlin/run|<R|kotlin/Int|>(...)" style="filled" fillcolor=yellow];
- 323 [label="Function call: R|/foo|<R|kotlin/Int|>(...)" style="filled" fillcolor=yellow];
- 324 [label="Access variable R|<local>/p|"];
- 325 [label="Smart cast: R|<local>/p|"];
- 326 [label="Access variable R|kotlin/String.length<Inapplicable(UNSAFE_CALL): kotlin/String.length>#|"];
- 327 [label="Exit block"];
+ 319 [label="Postponed exit from lambda"];
+ 320 [label="Function call: R|kotlin/run|<R|kotlin/Int|>(...)" style="filled" fillcolor=yellow];
+ 321 [label="Function call: R|/foo|<R|kotlin/Int|>(...)" style="filled" fillcolor=yellow];
+ 322 [label="Access variable R|<local>/p|"];
+ 323 [label="Smart cast: R|<local>/p|"];
+ 324 [label="Access variable R|kotlin/String.length<Inapplicable(UNSAFE_CALL): kotlin/String.length>#|"];
+ 325 [label="Exit block"];
}
- 328 [label="Exit when branch result"];
- 329 [label="Exit when"];
+ 326 [label="Exit when branch result"];
+ 327 [label="Exit when"];
}
- 330 [label="Exit block"];
+ 328 [label="Exit block"];
}
- 331 [label="Exit function test7" style="filled" fillcolor=red];
+ 329 [label="Exit function test7" style="filled" fillcolor=red];
}
+ 285 -> {286};
+ 286 -> {287};
287 -> {288};
288 -> {289};
289 -> {290};
@@ -902,15 +886,15 @@
291 -> {292};
292 -> {293};
293 -> {294};
- 294 -> {295};
- 295 -> {296};
- 296 -> {297 298};
- 297 -> {329};
- 298 -> {299};
+ 294 -> {295 296};
+ 295 -> {327};
+ 296 -> {297};
+ 297 -> {298};
+ 298 -> {299 307};
+ 298 -> {306} [style=dotted];
+ 298 -> {299} [style=dashed];
299 -> {300};
- 300 -> {301 309};
- 300 -> {308} [style=dotted];
- 300 -> {301} [style=dashed];
+ 300 -> {301};
301 -> {302};
302 -> {303};
303 -> {304};
@@ -918,14 +902,13 @@
305 -> {306};
306 -> {307};
307 -> {308};
- 308 -> {309} [color=green];
- 308 -> {323} [color=red];
+ 308 -> {309};
309 -> {310};
- 310 -> {311};
+ 310 -> {311 320};
+ 310 -> {319} [style=dotted];
+ 310 -> {311} [style=dashed];
311 -> {312};
- 312 -> {313 322};
- 312 -> {321} [style=dotted];
- 312 -> {313} [style=dashed];
+ 312 -> {313};
313 -> {314};
314 -> {315};
315 -> {316};
@@ -934,8 +917,7 @@
318 -> {319};
319 -> {320};
320 -> {321};
- 321 -> {322} [color=green];
- 321 -> {323} [color=red];
+ 321 -> {322};
322 -> {323};
323 -> {324};
324 -> {325};
@@ -943,7 +925,5 @@
326 -> {327};
327 -> {328};
328 -> {329};
- 329 -> {330};
- 330 -> {331};
}
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 65d823c..aa61eba 100644
--- a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.fir.txt
+++ b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.fir.txt
@@ -116,7 +116,7 @@
^ Int(123)
}
))
- R|<local>/x|.R|kotlin/String.length<Inapplicable(UNSAFE_CALL): kotlin/String.length>#|
+ R|<local>/x|.R|kotlin/String.length|
}
public final fun test7(x: R|kotlin/String?|): R|kotlin/Unit| {
lvar p: R|kotlin/String?| = R|<local>/x|
diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.kt b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.kt
index 62c3af3..976a893 100644
--- a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.kt
+++ b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.kt
@@ -62,7 +62,7 @@
1,
run { x<!UNSAFE_CALL!>.<!>length; 123 } // Bad (resolution order undefined)
)
- x<!UNSAFE_CALL!>.<!>length // OK (x as String in both branches)
+ x.length // OK (x as String in both branches)
}
fun test7(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 9782f5f..ccaf1d9 100644
--- a/compiler/fir/analysis-tests/testData/resolve/cfg/inplaceLambdaInControlFlowExpressions.dot
+++ b/compiler/fir/analysis-tests/testData/resolve/cfg/inplaceLambdaInControlFlowExpressions.dot
@@ -113,8 +113,7 @@
29 -> {30};
30 -> {31};
31 -> {32};
- 32 -> {33} [color=green];
- 32 -> {36} [color=red];
+ 32 -> {33};
33 -> {34};
34 -> {35};
35 -> {36};
@@ -187,8 +186,7 @@
48 -> {49};
49 -> {50};
50 -> {51};
- 51 -> {52} [color=green];
- 51 -> {61} [color=red];
+ 51 -> {52};
52 -> {53};
53 -> {54};
54 -> {55 61};
@@ -238,8 +236,7 @@
70 -> {71};
71 -> {72};
72 -> {73};
- 73 -> {74} [color=green];
- 73 -> {75} [color=red];
+ 73 -> {74};
74 -> {75};
75 -> {76};
76 -> {77};
diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/lambdaReturningObject.dot b/compiler/fir/analysis-tests/testData/resolve/cfg/lambdaReturningObject.dot
index 0402dc7..aecec5c 100644
--- a/compiler/fir/analysis-tests/testData/resolve/cfg/lambdaReturningObject.dot
+++ b/compiler/fir/analysis-tests/testData/resolve/cfg/lambdaReturningObject.dot
@@ -111,8 +111,7 @@
27 -> {28};
28 -> {29};
29 -> {30};
- 31 -> {32} [color=green];
- 31 -> {33} [color=red];
+ 31 -> {32};
32 -> {33};
33 -> {34};
34 -> {35};
diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInConstructor.dot b/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInConstructor.dot
index 677ee08..136b58d 100644
--- a/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInConstructor.dot
+++ b/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInConstructor.dot
@@ -92,8 +92,7 @@
20 -> {21};
22 -> {23};
23 -> {24};
- 24 -> {25} [color=green];
- 24 -> {26} [color=red];
+ 24 -> {25};
25 -> {26};
26 -> {27};
27 -> {28} [color=green];
diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.dot b/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.dot
index 7894832..38b1b18 100644
--- a/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.dot
+++ b/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.dot
@@ -193,7 +193,7 @@
50 -> {51};
51 -> {52};
52 -> {53};
- 53 -> {54} [color=green];
+ 53 -> {54};
54 -> {55};
55 -> {61};
55 -> {56} [style=dotted];
@@ -332,7 +332,7 @@
87 -> {88};
88 -> {89};
89 -> {90};
- 90 -> {91} [color=green];
+ 90 -> {91};
91 -> {92};
92 -> {109};
92 -> {93} [style=dotted];
@@ -441,11 +441,10 @@
160 [label="Function call: R|kotlin/run|<R|kotlin/String|>(...)" style="filled" fillcolor=yellow];
161 [label="Variable declaration: lval x: R|kotlin/String|"];
162 [label="Access variable R|<local>/y|"];
- 163 [label="Smart cast: R|<local>/y|"];
- 164 [label="Access variable R|kotlin/String.length|"];
- 165 [label="Exit block"];
+ 163 [label="Access variable R|kotlin/String.length<Inapplicable(UNSAFE_CALL): kotlin/String.length>#|"];
+ 164 [label="Exit block"];
}
- 166 [label="Exit function test3" style="filled" fillcolor=red];
+ 165 [label="Exit function test3" style="filled" fillcolor=red];
}
117 -> {118};
118 -> {119};
@@ -485,7 +484,7 @@
147 -> {148};
148 -> {149};
149 -> {150};
- 150 -> {151} [color=green];
+ 150 -> {151};
151 -> {152};
152 -> {158};
152 -> {153} [style=dotted];
@@ -501,6 +500,5 @@
162 -> {163};
163 -> {164};
164 -> {165};
- 165 -> {166};
}
diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.fir.txt b/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.fir.txt
index 80e1183..cc0e365 100644
--- a/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.fir.txt
+++ b/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.fir.txt
@@ -84,5 +84,5 @@
}
)
- R|<local>/y|.R|kotlin/String.length|
+ R|<local>/y|.R|kotlin/String.length<Inapplicable(UNSAFE_CALL): kotlin/String.length>#|
}
diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.kt b/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.kt
index 8559de2..d80c0d1 100644
--- a/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.kt
+++ b/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.kt
@@ -46,5 +46,5 @@
else
return@run ""
}
- y.length // bad
+ y<!UNSAFE_CALL!>.<!>length // bad
}
diff --git a/compiler/fir/analysis-tests/testData/resolve/smartcasts/incorrectSmartcastToNothing.dot b/compiler/fir/analysis-tests/testData/resolve/smartcasts/incorrectSmartcastToNothing.dot
index 31412cf..44a8ab3 100644
--- a/compiler/fir/analysis-tests/testData/resolve/smartcasts/incorrectSmartcastToNothing.dot
+++ b/compiler/fir/analysis-tests/testData/resolve/smartcasts/incorrectSmartcastToNothing.dot
@@ -97,13 +97,12 @@
48 [label="Exit block"];
}
49 [label="Exit when branch result"];
- 50 [label="Merge postponed lambda exits"];
- 51 [label="Exit when"];
+ 50 [label="Exit when"];
}
- 52 [label="Variable declaration: lval cacheBaseDir: R|java/io/File?|"];
- 53 [label="Exit block"];
+ 51 [label="Variable declaration: lval cacheBaseDir: R|java/io/File?|"];
+ 52 [label="Exit block"];
}
- 54 [label="Exit function test" style="filled" fillcolor=red];
+ 53 [label="Exit function test" style="filled" fillcolor=red];
}
6 -> {7};
7 -> {8};
@@ -126,12 +125,12 @@
24 -> {25};
25 -> {26};
26 -> {27};
- 27 -> {51};
+ 27 -> {50};
28 -> {29};
29 -> {30};
30 -> {31};
31 -> {32};
- 32 -> {51};
+ 32 -> {50};
33 -> {34};
34 -> {35};
35 -> {36 47};
@@ -146,15 +145,13 @@
42 -> {43};
43 -> {44};
44 -> {45};
- 45 -> {46} [color=green];
- 45 -> {50} [color=red];
+ 45 -> {46};
46 -> {47};
- 47 -> {48 50};
+ 47 -> {48};
48 -> {49};
- 49 -> {51};
- 50 -> {51} [color=red];
+ 49 -> {50};
+ 50 -> {51};
51 -> {52};
52 -> {53};
- 53 -> {54};
}
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 58f5158..ac6d763 100644
--- a/compiler/fir/analysis-tests/testData/resolve/smartcasts/lambdas/lambdaInWhenBranch.dot
+++ b/compiler/fir/analysis-tests/testData/resolve/smartcasts/lambdas/lambdaInWhenBranch.dot
@@ -237,8 +237,7 @@
54 -> {55};
55 -> {56};
56 -> {57};
- 57 -> {58} [color=green];
- 57 -> {61} [color=red];
+ 57 -> {58};
58 -> {59};
59 -> {60};
60 -> {61};
diff --git a/compiler/fir/analysis-tests/testData/resolve/smartcasts/receivers/implicitReceivers.dot b/compiler/fir/analysis-tests/testData/resolve/smartcasts/receivers/implicitReceivers.dot
index 76722b6..f77c9cd 100644
--- a/compiler/fir/analysis-tests/testData/resolve/smartcasts/receivers/implicitReceivers.dot
+++ b/compiler/fir/analysis-tests/testData/resolve/smartcasts/receivers/implicitReceivers.dot
@@ -348,8 +348,7 @@
111 -> {112};
112 -> {113};
113 -> {114};
- 114 -> {115} [color=green];
- 114 -> {119} [color=red];
+ 114 -> {115};
115 -> {116};
116 -> {117};
117 -> {118};
diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/complexPostponedCfg.dot b/compiler/fir/analysis-tests/testData/resolveWithStdlib/complexPostponedCfg.dot
index 7f14d09..1a805ef 100644
--- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/complexPostponedCfg.dot
+++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/complexPostponedCfg.dot
@@ -126,8 +126,7 @@
33 -> {34};
34 -> {35};
35 -> {36};
- 36 -> {37} [color=green];
- 36 -> {41} [color=red];
+ 36 -> {37};
37 -> {38};
38 -> {39};
39 -> {40};
diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/delegates/delegateWithAnonymousObject.dot b/compiler/fir/analysis-tests/testData/resolveWithStdlib/delegates/delegateWithAnonymousObject.dot
index f1f7f22..7ccfee5 100644
--- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/delegates/delegateWithAnonymousObject.dot
+++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/delegates/delegateWithAnonymousObject.dot
@@ -195,8 +195,7 @@
51 -> {52} [style=dotted];
52 -> {53} [style=dotted];
53 -> {54} [style=dotted];
- 55 -> {56} [color=green];
- 55 -> {58} [color=red];
+ 55 -> {56};
56 -> {57};
57 -> {58};
58 -> {59};
diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/smartcasts/tryWithLambdaInside.dot b/compiler/fir/analysis-tests/testData/resolveWithStdlib/smartcasts/tryWithLambdaInside.dot
index 75de9b9..27d5b6c 100644
--- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/smartcasts/tryWithLambdaInside.dot
+++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/smartcasts/tryWithLambdaInside.dot
@@ -119,9 +119,8 @@
22 -> {23};
23 -> {24};
24 -> {25};
+ 25 -> {26};
25 -> {19} [color=green style=dashed];
- 25 -> {26} [color=green];
- 25 -> {33} [color=red];
26 -> {27};
27 -> {28};
28 -> {29};
@@ -208,8 +207,7 @@
46 -> {47};
47 -> {48};
48 -> {49};
- 50 -> {51} [color=green];
- 50 -> {58} [color=red];
+ 50 -> {51};
51 -> {52};
52 -> {53};
53 -> {54};
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 703e74f..b6cfcd5 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
@@ -173,7 +173,7 @@
if (function is FirAnonymousFunction) {
val (functionExitNode, postponedLambdaExitNode, graph) = graphBuilder.exitAnonymousFunction(function)
functionExitNode.mergeIncomingFlow()
- postponedLambdaExitNode?.mergeIncomingFlow()
+ if (postponedLambdaExitNode?.flowInitialized == false) postponedLambdaExitNode.mergeIncomingFlow()
resetReceivers() // roll back to state before function
return FirControlFlowGraphReferenceImpl(graph)
}
@@ -594,10 +594,6 @@
// ----------------------------------- Jump -----------------------------------
- fun enterJump(jump: FirJump<*>) {
- graphBuilder.enterJump(jump)
- }
-
fun exitJump(jump: FirJump<*>) {
graphBuilder.exitJump(jump).mergeIncomingFlow()
}
@@ -609,7 +605,7 @@
}
fun exitCheckNotNullCall(checkNotNullCall: FirCheckNotNullCall, callCompleted: Boolean) {
- graphBuilder.exitCheckNotNullCall(checkNotNullCall, callCompleted).mergeIncomingFlow { _, flow ->
+ graphBuilder.exitCheckNotNullCall(checkNotNullCall).mergeIncomingFlow(callCompleted) { _, flow ->
val argumentVariable = variableStorage.getOrCreateIfReal(flow, checkNotNullCall.argument) ?: return@mergeIncomingFlow
flow.commitOperationStatement(argumentVariable notEq null)
}
@@ -650,9 +646,9 @@
}
fun exitWhenExpression(whenExpression: FirWhenExpression, callCompleted: Boolean) {
- val (whenExitNode, syntheticElseNode) = graphBuilder.exitWhenExpression(whenExpression, callCompleted)
+ val (whenExitNode, syntheticElseNode) = graphBuilder.exitWhenExpression(whenExpression)
syntheticElseNode?.mergeWhenBranchEntryFlow()
- whenExitNode.mergeIncomingFlow()
+ whenExitNode.mergeIncomingFlow(callCompleted)
}
fun exitWhenSubjectExpression(expression: FirWhenSubjectExpression) {
@@ -797,7 +793,7 @@
}
fun exitTryExpression(callCompleted: Boolean) {
- graphBuilder.exitTryExpression(callCompleted).mergeIncomingFlow()
+ graphBuilder.exitTryExpression().mergeIncomingFlow(callCompleted)
}
// ----------------------------------- Resolvable call -----------------------------------
@@ -883,14 +879,15 @@
fun exitFunctionCall(functionCall: FirFunctionCall, callCompleted: Boolean) {
context.variableAssignmentAnalyzer.exitFunctionCall(callCompleted)
- graphBuilder.exitFunctionCall(functionCall, callCompleted).mergeIncomingFlow { _, flow ->
+ val node = graphBuilder.exitFunctionCall(functionCall, callCompleted)
+ node.mergeIncomingFlow(callCompleted) { _, flow ->
processConditionalContract(flow, functionCall)
}
}
fun exitDelegatedConstructorCall(call: FirDelegatedConstructorCall, callCompleted: Boolean) {
context.variableAssignmentAnalyzer.exitFunctionCall(callCompleted)
- graphBuilder.exitDelegatedConstructorCall(call, callCompleted).mergeIncomingFlow()
+ graphBuilder.exitDelegatedConstructorCall(call).mergeIncomingFlow(callCompleted)
}
fun enterStringConcatenationCall() {
@@ -1200,8 +1197,8 @@
}
fun exitElvis(elvisExpression: FirElvisExpression, isLhsNotNull: Boolean, callCompleted: Boolean) {
- val node = graphBuilder.exitElvis(isLhsNotNull, callCompleted)
- node.mergeIncomingFlow { path, flow ->
+ val node = graphBuilder.exitElvis(isLhsNotNull)
+ node.mergeIncomingFlow(callCompleted) { path, flow ->
// If LHS is never null, then the edge from RHS is dead and this node's flow already contains
// all statements from LHS unconditionally.
if (isLhsNotNull) return@mergeIncomingFlow
@@ -1255,15 +1252,12 @@
private var currentReceiverState: Flow? = null
private fun CFGNode<*>.buildDefaultFlow(
+ mergePostponed: Boolean,
builder: (FlowPath, MutableFlow) -> Unit,
): MutableFlow {
val previousFlows = previousNodes.mapNotNull { node ->
val edge = edgeFrom(node)
- if (!usedInDfa(edge)) return@mapNotNull null
-
- // `MergePostponedLambdaExitsNode` nodes form a parallel data flow graph. We never compute
- // data flow for any of them until reaching a completed call.
- if (node is MergePostponedLambdaExitsNode && !node.flowInitialized) node.mergeIncomingFlow()
+ if ((edge.kind.isPostponed && !mergePostponed) || !usedInDfa(edge)) return@mapNotNull null
// For CFGNodes that are the end of alternate flows, use the alternate flow associated with the edge label.
if (node is FinallyBlockExitNode) {
@@ -1273,7 +1267,18 @@
node.flow
}
}
- val result = logicSystem.joinFlow(previousFlows, isUnion)
+
+ val result = if (mergePostponed) {
+ val postponedFlows = previousNodes.mapNotNull { node ->
+ val edge = edgeFrom(node)
+ if (node.postponedFlowInitialized && usedInDfa(edge)) node.postponedFlow()
+ else null
+ }
+ logicSystem.joinFlow(previousFlows + postponedFlows, isUnion)
+ } else {
+ logicSystem.joinFlow(previousFlows, isUnion)
+ }
+
if (graphBuilder.lastNodeOrNull == this) {
// Here it is, the new `lastNode`. If the previous state is the only predecessor, then there is actually
// nothing to update; `addTypeStatement` has already ensured we have the correct information.
@@ -1315,20 +1320,59 @@
// In that case `mergeIncomingFlow` will automatically ensure consistency once called on that node.
@OptIn(CfgInternals::class)
private fun CFGNode<*>.mergeIncomingFlow(
+ mergePostponed: Boolean = false,
builder: (FlowPath, MutableFlow) -> Unit = { _, _ -> },
) {
// Always build the default flow path for all nodes.
- val mutableDefaultFlow = buildDefaultFlow(builder)
+ val mutableDefaultFlow = buildDefaultFlow(mergePostponed, builder)
val defaultFlow = mutableDefaultFlow.freeze().also { this.flow = it }
if (currentReceiverState === mutableDefaultFlow) {
currentReceiverState = defaultFlow
}
+ if (!mergePostponed) {
+ // Build flow for postponed lambdas.
+ propagatePostponedFlow(builder)
+ }
+
// Propagate alternate flows from previous nodes.
propagateAlternateFlows(builder)
}
@OptIn(CfgInternals::class)
+ private fun CFGNode<*>.propagatePostponedFlow(
+ builder: (FlowPath, MutableFlow) -> Unit,
+ ) {
+ var propagate = false
+ val previousFlows = previousNodes.mapNotNull { node ->
+ val edge = edgeFrom(node)
+ if (!usedInDfa(edge)) return@mapNotNull null
+
+ // Only propagate if at least one previous node is from a postponed edge or has postponed flow.
+ propagate = propagate || edge.kind.isPostponed || node.postponedFlowInitialized
+
+ if (node.postponedFlowInitialized) {
+ return@mapNotNull node.postponedFlow
+ } else {
+ return@mapNotNull {
+ // `PostponedLambdaExitNode` nodes form a parallel data flow graph. We never compute
+ // data flow for any of them until reaching a completed call.
+ if (node is PostponedLambdaExitNode && !node.flowInitialized) node.mergeIncomingFlow()
+ node.flow
+ }
+ }
+ }
+
+ if (propagate) {
+ postponedFlow = {
+ val flows = previousFlows.map { it() }
+ val result = logicSystem.joinFlow(flows, isUnion)
+ result.also { builder(FlowPath.Postponed, it) }.freeze()
+ }
+ }
+ }
+
+ @OptIn(CfgInternals::class)
private fun CFGNode<*>.propagateAlternateFlows(
builder: (FlowPath, MutableFlow) -> Unit,
) {
@@ -1338,7 +1382,7 @@
val edge = edgeFrom(node)
// Only propagate alternate flows which originate along a normal path edge and are used in DFA.
- if (edge.label != NormalPath || !usedInDfa(edge)) continue
+ if (edge.label != NormalPath || edge.kind.isPostponed || !usedInDfa(edge)) continue
for (path in node.alternateFlowPaths) {
// If the source node is the end of alternate flows, do not propagate the alternate flows which have ended.
@@ -1370,6 +1414,7 @@
private fun CFGNode<*>.getFlow(path: FlowPath): PersistentFlow {
return when (path) {
FlowPath.Default -> flow
+ FlowPath.Postponed -> if (postponedFlowInitialized) postponedFlow() else flow
else -> getAlternateFlow(path) ?: error("no alternate flow for $path")
}
}
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 0f26cc4..5603d83 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
@@ -63,7 +63,7 @@
private val argumentListSplitNodes: Stack<SplitPostponedLambdasNode?> = stackOf()
private val postponedAnonymousFunctionNodes =
mutableMapOf<FirFunctionSymbol<*>, Pair<CFGNode<*>, PostponedLambdaExitNode?>>()
- private val postponedLambdaExits: Stack<MutableList<Pair<CFGNode<*>, EdgeKind>>> = stackOf()
+ private val postponedLambdaExits: Stack<MutableList<PostponedLambdaExitNode>> = stackOf()
private val loopConditionEnterNodes: MutableMap<FirLoop, LoopConditionEnterNode> = mutableMapOf()
private val loopExitNodes: MutableMap<FirLoop, LoopExitNode> = mutableMapOf()
@@ -263,7 +263,7 @@
// So we need an edge right now to enforce ordering, and mark it as dead later if needed.
addEdge(enterNode, exitNode)
postponedAnonymousFunctionNodes[symbol] = enterNode to exitNode
- postponedLambdaExits.top().add(exitNode to EdgeKind.Forward)
+ postponedLambdaExits.top().add(exitNode)
return null
}
@@ -311,75 +311,11 @@
postponedLambdaExits.push(mutableListOf())
}
- private fun unifyDataFlowFromPostponedLambdas(node: CFGNode<*>, callCompleted: Boolean) {
- val currentLevelExits = postponedLambdaExits.pop()
- if (currentLevelExits.isEmpty()) return
-
- val nextLevelExits = postponedLambdaExits.topOrNull().takeIf { !callCompleted }
- if (nextLevelExits != null) {
- // Call is incomplete, don't pass data flow from lambdas inside it to lambdas in the outer call.
- for ((exit, kind) in currentLevelExits) {
- if (kind.usedInCfa) {
- addEdge(exit, node, preferredKind = EdgeKind.CfgForward)
- }
- nextLevelExits.add(exit to EdgeKind.DfgForward)
- }
- } else {
- for ((exit, kind) in currentLevelExits) {
- // Do not add data flow edges from non-terminating lambdas; there is no "dead data flow only"
- if (kind.usedInCfa || !exit.isDead) {
- // Since `node` is a union node, it is dead iff any input is dead. For once, `propagateDeadness`
- // semantics are correct without an `updateDeadStatus`.
- addEdge(exit, node, preferredKind = kind)
- }
- }
- }
- }
-
- // There may be branching expressions on the way from a called-in-place lambda
- // to the next completed call:
- //
- // f(if (p) { x; run { a } else { y; run { b } }, c)
- //
- // which result in a hole-y control flow graph at the time when we need to resolve `c`:
- //
- // p -+--> x -> ?? -> run#1 --+-> c -> f
- // \-> y -> ?? -> run#2 -/
- //
- // Ideally, we want to pretend that the lambdas are not called-in-place until we get
- // to `f`, at which point the lambdas are guaranteed to be resolved, and we should be
- // able to reconstruct the entire data flow. The problem is that the call/when/etc.
- // exit nodes on the way from the lambda to the function call exit node can have
- // statements attached to them, so unless we want to re-do all the work, it's too late
- // by the time we get there. And we can't just forever ignore the lambdas either, as
- // they may reassign variables and so the data we've gathered about them should be
- // invalidated. So what we do here is merge the data from the lambdas with the data
- // obtained without them: this can only erase statements that are not provably correct.
- //
- // TODO: an alternative is to delay computing incoming flow for "branch result exit" nodes
- // until the entire "when" is resolved; then either unify each branch's lambdas into its
- // exit node, or create N union nodes (1/branch) and point them into the merge node. KT-59730
- private fun mergeDataFlowFromPostponedLambdas(node: CFGNode<*>, callCompleted: Boolean) {
- val currentLevelExits = postponedLambdaExits.pop()
- if (currentLevelExits.isEmpty()) return
-
- val nextLevelExits = postponedLambdaExits.topOrNull().takeIf { !callCompleted }
- if (nextLevelExits != null) {
- node.updateDeadStatus()
- nextLevelExits += createMergePostponedLambdaExitsNode(node.fir).also {
- addEdge(node, it) // copy liveness (deadness?) from `node`
- for ((exit, kind) in currentLevelExits) {
- if (kind.usedInCfa) {
- addEdge(exit, node, preferredKind = EdgeKind.CfgForward, propagateDeadness = false)
- }
- addEdge(exit, it, preferredKind = EdgeKind.DfgForward, propagateDeadness = false)
- }
- } to EdgeKind.DfgForward
- } else {
- for ((exit, kind) in currentLevelExits) {
- // `node` is a merge node for many inputs anyhow so someone will call `updateDeadStatus` on it.
- addEdge(exit, node, preferredKind = kind, propagateDeadness = false)
- }
+ private fun unifyDataFlowFromPostponedLambdas(node: CFGNode<*>) {
+ for (exit in postponedLambdaExits.pop()) {
+ // Since `node` is a union node, it is dead iff any input is dead. For once, `propagateDeadness`
+ // semantics are correct without an `updateDeadStatus`.
+ addEdge(exit, node, preferredKind = EdgeKind.PostponedForward)
}
}
@@ -721,7 +657,7 @@
// where `fun <T> T.id(): T`...except `id` doesn't exist, and what that means is that `y` is resolved in
// context-dependent mode, and we don't necessarily get an enclosing completed call to unify data flow in.
// This node serves as a substitute.
- unifyDataFlowFromPostponedLambdas(it, callCompleted = true)
+ unifyDataFlowFromPostponedLambdas(it)
addNewSimpleNode(it)
}
}
@@ -753,26 +689,10 @@
// ----------------------------------- Jump -----------------------------------
- fun enterJump(jump: FirJump<*>) {
- // Data flow from anonymous functions in return values does not merge with any enclosing calls.
- // For named functions, the return value has to be a completed call anyway, so there should
- // be no postponed lambdas in it.
- if (jump is FirReturnExpression && jump.target.labeledElement is FirAnonymousFunction) {
- splitDataFlowForPostponedLambdas()
- }
- }
-
fun exitJump(jump: FirJump<*>): JumpNode {
val node = createJumpNode(jump)
addNonSuccessfullyTerminatingNode(node)
- if (jump is FirReturnExpression && jump.target.labeledElement is FirAnonymousFunction) {
- // TODO: these should be DFA-only edges; they should be pointed into the postponed function exit node?
- // With builder inference, lambdas are not necessarily resolved starting from the innermost one...
- // See analysis test cfg/postponedLambdaInReturn.kt. KT-59729
- postponedLambdaExits.pop()
- }
-
val nextNode = when (jump) {
is FirReturnExpression -> exitTargetsForReturn[jump.target.labeledElement.symbol]
is FirContinueExpression -> loopConditionEnterNodes[jump.target.labeledElement]
@@ -807,7 +727,6 @@
addNewSimpleNode(node)
whenExitNodes.push(createWhenExitNode(whenExpression))
notCompletedFunctionCalls.push(mutableListOf())
- splitDataFlowForPostponedLambdas()
return node
}
@@ -833,10 +752,7 @@
return node
}
- fun exitWhenExpression(
- whenExpression: FirWhenExpression,
- callCompleted: Boolean
- ): Pair<WhenExitNode, WhenSyntheticElseBranchNode?> {
+ fun exitWhenExpression(whenExpression: FirWhenExpression): Pair<WhenExitNode, WhenSyntheticElseBranchNode?> {
val whenExitNode = whenExitNodes.pop()
// exit from last condition node still on stack
// we should remove it
@@ -848,7 +764,6 @@
addEdge(this, whenExitNode)
}
} else null
- mergeDataFlowFromPostponedLambdas(whenExitNode, callCompleted)
whenExitNode.updateDeadStatus()
lastNodes.push(whenExitNode)
return whenExitNode to syntheticElseBranchNode
@@ -986,7 +901,6 @@
}
notCompletedFunctionCalls.push(mutableListOf())
- splitDataFlowForPostponedLambdas()
return enterTryExpressionNode to enterTryMainBlockNode
}
@@ -1093,7 +1007,7 @@
}
}
- fun exitTryExpression(callCompleted: Boolean): TryExpressionExitNode {
+ fun exitTryExpression(): TryExpressionExitNode {
var haveNothingReturnCall = false
notCompletedFunctionCalls.pop().forEach { haveNothingReturnCall = completeFunctionCall(it) || haveNothingReturnCall }
val node = tryExitNodes.pop()
@@ -1113,7 +1027,6 @@
addEdge(exitFinallyNode, node, isDead = true)
}
}
- mergeDataFlowFromPostponedLambdas(node, callCompleted)
node.updateDeadStatus()
lastNodes.push(node)
return node
@@ -1189,7 +1102,7 @@
fun exitFunctionCall(functionCall: FirFunctionCall, callCompleted: Boolean): FunctionCallNode {
val returnsNothing = functionCall.hasNothingType
val node = createFunctionCallNode(functionCall)
- unifyDataFlowFromPostponedLambdas(node, callCompleted)
+ unifyDataFlowFromPostponedLambdas(node)
if (returnsNothing) {
addNonSuccessfullyTerminatingNode(node)
} else {
@@ -1201,16 +1114,16 @@
return node
}
- fun exitDelegatedConstructorCall(call: FirDelegatedConstructorCall, callCompleted: Boolean): DelegatedConstructorCallNode {
+ fun exitDelegatedConstructorCall(call: FirDelegatedConstructorCall): DelegatedConstructorCallNode {
val node = createDelegatedConstructorCallNode(call)
- unifyDataFlowFromPostponedLambdas(node, callCompleted)
+ unifyDataFlowFromPostponedLambdas(node)
addNewSimpleNode(node)
return node
}
fun exitStringConcatenationCall(call: FirStringConcatenationCall): StringConcatenationCallNode {
val node = createStringConcatenationCallNode(call)
- unifyDataFlowFromPostponedLambdas(node, callCompleted = true)
+ unifyDataFlowFromPostponedLambdas(node)
addNewSimpleNode(node)
return node
}
@@ -1249,9 +1162,9 @@
return createThrowExceptionNode(throwExpression).also { addNonSuccessfullyTerminatingNode(it) }
}
- fun exitCheckNotNullCall(checkNotNullCall: FirCheckNotNullCall, callCompleted: Boolean): CheckNotNullCallNode {
+ fun exitCheckNotNullCall(checkNotNullCall: FirCheckNotNullCall): CheckNotNullCallNode {
val node = createCheckNotNullCallNode(checkNotNullCall)
- unifyDataFlowFromPostponedLambdas(node, callCompleted)
+ unifyDataFlowFromPostponedLambdas(node)
if (checkNotNullCall.hasNothingType) {
addNonSuccessfullyTerminatingNode(node)
} else {
@@ -1323,7 +1236,6 @@
addEdge(lastNode, exitNode)
}
lastNodes.push(enterNode)
- splitDataFlowForPostponedLambdas()
return enterNode
}
@@ -1336,8 +1248,6 @@
// the safe call bound to this exit safe call node should be retrieved.
return exitSafeCallNodes.pop().also {
addNewSimpleNode(it)
- // Safe calls only have one user-specified branch, so if any lambdas were postponed, they still are.
- mergeDataFlowFromPostponedLambdas(it, callCompleted = false)
it.updateDeadStatus()
}
}
@@ -1346,7 +1256,6 @@
fun enterElvis(elvisExpression: FirElvisExpression) {
elvisRhsEnterNodes.push(createElvisRhsEnterNode(elvisExpression))
- splitDataFlowForPostponedLambdas()
}
fun exitElvisLhs(elvisExpression: FirElvisExpression): Triple<ElvisLhsExitNode, ElvisLhsIsNotNullNode, ElvisRhsEnterNode> {
@@ -1375,10 +1284,9 @@
return Triple(lhsExitNode, lhsIsNotNullNode, rhsEnterNode)
}
- fun exitElvis(lhsIsNotNull: Boolean, callCompleted: Boolean): ElvisExitNode {
+ fun exitElvis(lhsIsNotNull: Boolean): ElvisExitNode {
val exitNode = exitElvisExpressionNodes.pop()
addNewSimpleNode(exitNode, isDead = lhsIsNotNull)
- mergeDataFlowFromPostponedLambdas(exitNode, callCompleted)
exitNode.updateDeadStatus()
return exitNode
}
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 350a7ea..991c1a8 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
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.fir.resolve.dfa.cfg
-import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.*
@@ -205,9 +204,6 @@
fun ControlFlowGraphBuilder.createSplitPostponedLambdasNode(fir: FirStatement, lambdas: List<FirAnonymousFunction>): SplitPostponedLambdasNode =
SplitPostponedLambdasNode(currentGraph, fir, lambdas, levelCounter)
-fun ControlFlowGraphBuilder.createMergePostponedLambdaExitsNode(fir: FirElement): MergePostponedLambdaExitsNode =
- MergePostponedLambdaExitsNode(currentGraph, fir, levelCounter)
-
fun ControlFlowGraphBuilder.createAnonymousFunctionExpressionNode(fir: FirAnonymousFunctionExpression): AnonymousFunctionExpressionNode =
AnonymousFunctionExpressionNode(currentGraph, fir, levelCounter)
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphRenderer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphRenderer.kt
index 04b4561..476359e 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphRenderer.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphRenderer.kt
@@ -74,7 +74,17 @@
append("</B></TD></TR>")
if (node.flowInitialized) {
append("<TR><TD ALIGN=\"LEFT\" BALIGN=\"LEFT\">")
- append(node.renderFlowHtmlLike())
+ append(node.flow.renderFlowHtmlLike())
+ append("</TD></TR>")
+ }
+ if (node.postponedFlowInitialized) {
+ append("<TR><TD><B>Postponed</B></TD></TR>")
+ append("<TR><TD ALIGN=\"LEFT\" BALIGN=\"LEFT\">")
+ try {
+ append(node.postponedFlow().renderFlowHtmlLike())
+ } catch (e: Throwable) {
+ append("ERROR")
+ }
append("</TD></TR>")
}
append("</TABLE>")
@@ -166,8 +176,8 @@
println("}")
}
- private fun CFGNode<*>.renderFlowHtmlLike(): String {
- val flow = flow
+ private fun PersistentFlow.renderFlowHtmlLike(): String {
+ val flow = this
val variables = flow.knownVariables + flow.implications.keys +
flow.implications.flatMap { it.value }.map { it.condition.variable } +
flow.implications.flatMap { it.value }.map { it.effect.variable }
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt
index 0b243ed..355d509 100644
--- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt
+++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt
@@ -182,7 +182,6 @@
// ------------------------------- Jumps -------------------------------
override fun <E : FirTargetElement> transformJump(jump: FirJump<E>, data: ResolutionMode): FirStatement {
- dataFlowAnalyzer.enterJump(jump)
val result = transformer.transformExpression(jump, data)
dataFlowAnalyzer.exitJump(jump)
return result
diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/FlowPath.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/FlowPath.kt
index fcf0360..21e6972 100644
--- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/FlowPath.kt
+++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/FlowPath.kt
@@ -11,8 +11,12 @@
/**
* Sealed class representing a type of path through a [Control Flow Graph (CFG) Node][org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode] used
* for data flow analysis. Most CFG nodes only have a single data flow path through them, but there are times when a node may require
- * multiple paths to be calculated. The most common use case is for `finally` code blocks, where multiple code paths may enter, but these
- * paths diverge after exiting the code block. Consider the following (very) contrived example:
+ * multiple paths to be calculated.
+ *
+ * ## Finally Blocks
+ *
+ * A common use case is for `finally` code blocks, where multiple code paths may enter, but these paths diverge after exiting the code
+ * block. Consider the following (very) contrived example:
*
* ```kotlin
* fun test() {
@@ -55,6 +59,31 @@
* the data flow leading into the `finally` block from the `break` statement.
* 3. A flow which will be used when the entire `try` expression exits without exception or jumping. This data flow is a continuation of the
* data flow leading into the `finally` block from the main `try` block.
+ *
+ * ## Lambda Expressions
+ *
+ * Another common case for separate flow path is when dealing with postponed lambda expression analysis. When a call is being resolved,
+ * lambda expression resolution order is undefined, especially when generics are involved. Consider the following contrived example:
+ *
+ * ```kotlin
+ * fun <T> foo(x: T?, y: T) {}
+ * fun <T> id(x: T): T = x
+ * fun <T> n(): T? = null
+ *
+ * fun test(x: String?) {
+ * foo(
+ * id(if (true) run { x as String; n() } else run { x as String; n() }),
+ * run { x.length; 123 } // (1)
+ * )
+ * x.length // (2)
+ * }
+ * ```
+ *
+ * Just based on the first parameter (`id(...)`) of the call to `foo(...)`, Kotlin is unable to determine what the time of `T` should be. It
+ * must also resolve the section parameter (`run { ... }`). So while the flow through this code would result in `x` being a `String` at (1),
+ * call resolution is unable to determine that. This means the implication that `x` is a `String` must be postponed until after the
+ * `foo(...)` call is fully resolved. Once fully resolved, the postponed flow can be integrated back into the default flow and used to
+ * determine `x` is a `String` at (2).
*/
sealed class FlowPath {
/**
@@ -63,6 +92,11 @@
data object Default : FlowPath()
/**
+ * The [FlowPath] which represent lambda expression analysis when call resolution is incomplete.
+ */
+ data object Postponed : FlowPath()
+
+ /**
* The [FlowPath] which represents the combination of all flows leading into a CFG Node that follow an edge with the specified
* [edge label][EdgeLabel]. The edge label is also combined with an [FIR element][FirElement] as the same edge label can be used for
* multiple flows. For example, a `finally` block within another `finally` block will require multiple
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 59be999..054fb28 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
@@ -117,7 +117,7 @@
private var _flow: PersistentFlow? = null
open val flowInitialized: Boolean get() = _flow != null
open var flow: PersistentFlow
- get() = _flow ?: throw IllegalStateException("flow for $this not initialized - traversing nodes in wrong order?")
+ get() = _flow ?: error("flow for $this not initialized - traversing nodes in wrong order?")
@CfgInternals
set(value) {
assert(_flow == null) { "reassigning flow for $this" }
@@ -125,6 +125,21 @@
}
/**
+ * Lazily calculated [Flow][org.jetbrains.kotlin.fir.resolve.dfa.Flow] representing the [postponed path][FlowPath.Postponed] for this
+ * node. This flow is used to represent lambda expression analysis when call resolution is incomplete.
+ */
+ private var _postponedFlow: (() -> PersistentFlow)? = null
+ open val postponedFlowInitialized: Boolean get() = _postponedFlow != null
+ open var postponedFlow: () -> PersistentFlow
+ get() = _postponedFlow ?: error("postponed flow for $this not initialized - traversing nodes in wrong order?")
+ @CfgInternals
+ set(value) {
+ check(_postponedFlow == null) { "reassigning postponed flow for $this" }
+ var memorized: PersistentFlow? = null // Do not recalculate flow when accessed multiple times.
+ _postponedFlow = { memorized ?: value().also { memorized = it } }
+ }
+
+ /**
* All other [flows][org.jetbrains.kotlin.fir.resolve.dfa.Flow] through this node which are not the [default][FlowPath.Default]. These
* flows should only be used by following nodes when the path through this node diverges (ex., following a `finally` code block).
*/
@@ -139,6 +154,7 @@
@CfgInternals
open fun addAlternateFlow(path: FlowPath, flow: PersistentFlow) {
assert(path !== FlowPath.Default) { "cannot add default flow path as alternate for $this" }
+ assert(path !== FlowPath.Postponed) { "cannot add postponed flow path as alternate for $this" }
assert(_alternateFlows?.get(path) == null) { "reassigning $path flow for $this" }
var alternateFlows = _alternateFlows
@@ -262,12 +278,6 @@
}
}
-class MergePostponedLambdaExitsNode(owner: ControlFlowGraph, override val fir: FirElement, level: Int) : CFGNode<FirElement>(owner, level) {
- override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
- return visitor.visitMergePostponedLambdaExitsNode(this, data)
- }
-}
-
class AnonymousFunctionExpressionNode(owner: ControlFlowGraph, override val fir: FirAnonymousFunctionExpression, level: Int) : CFGNodeWithSubgraphs<FirAnonymousFunctionExpression>(owner, level) {
override val subGraphs: List<ControlFlowGraph>
get() = listOfNotNull(fir.anonymousFunction.controlFlowGraphReference?.controlFlowGraph)
@@ -774,6 +784,12 @@
@CfgInternals
set(_) = throw IllegalStateException("can't set flow for stub node")
+ override val postponedFlowInitialized: Boolean get() = firstPreviousNode.postponedFlowInitialized
+ override var postponedFlow: () -> PersistentFlow
+ get() = firstPreviousNode.postponedFlow
+ @CfgInternals
+ set(_) = throw IllegalStateException("can't set postponedFlow for stub node")
+
override val alternateFlowPaths: Set<FlowPath>
get() = firstPreviousNode.alternateFlowPaths
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 2f1ed70b..c3b8314 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
@@ -95,7 +95,6 @@
is SplitPostponedLambdasNode -> "Postponed enter to lambda"
is PostponedLambdaExitNode -> "Postponed exit from lambda"
- is MergePostponedLambdaExitsNode -> "Merge postponed lambda exits"
is AnonymousFunctionExpressionNode -> "Exit anonymous function expression"
is FileEnterNode -> "Enter file ${fir.name}"
diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt
index 91f4a38..b09e5bd 100644
--- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt
+++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt
@@ -68,6 +68,7 @@
) {
companion object {
val Normal_Forward = Edge(NormalPath, EdgeKind.Forward)
+ private val Normal_PostponedForward = Edge(NormalPath, EdgeKind.PostponedForward)
private val Normal_DeadForward = Edge(NormalPath, EdgeKind.DeadForward)
private val Normal_DfgForward = Edge(NormalPath, EdgeKind.DfgForward)
private val Normal_CfgForward = Edge(NormalPath, EdgeKind.CfgForward)
@@ -79,6 +80,7 @@
NormalPath -> {
when (kind) {
EdgeKind.Forward -> Normal_Forward
+ EdgeKind.PostponedForward -> Normal_PostponedForward
EdgeKind.DeadForward -> Normal_DeadForward
EdgeKind.DfgForward -> Normal_DfgForward
EdgeKind.CfgForward -> Normal_CfgForward
@@ -110,14 +112,16 @@
val usedInDeadDfa: Boolean, // propagate flow to dead nodes
val usedInCfa: Boolean,
val isBack: Boolean,
- val isDead: Boolean
+ val isDead: Boolean,
+ val isPostponed: Boolean,
) {
- Forward(usedInDfa = true, usedInDeadDfa = true, usedInCfa = true, isBack = false, isDead = false),
- DeadForward(usedInDfa = false, usedInDeadDfa = true, usedInCfa = true, isBack = false, isDead = true),
- DfgForward(usedInDfa = true, usedInDeadDfa = true, usedInCfa = false, isBack = false, isDead = false),
- CfgForward(usedInDfa = false, usedInDeadDfa = false, usedInCfa = true, isBack = false, isDead = false),
- CfgBackward(usedInDfa = false, usedInDeadDfa = false, usedInCfa = true, isBack = true, isDead = false),
- DeadBackward(usedInDfa = false, usedInDeadDfa = false, usedInCfa = true, isBack = true, isDead = true)
+ Forward(usedInDfa = true, usedInDeadDfa = true, usedInCfa = true, isBack = false, isDead = false, isPostponed = false),
+ PostponedForward(usedInDfa = true, usedInDeadDfa = true, usedInCfa = true, isBack = false, isDead = false, isPostponed = true),
+ DeadForward(usedInDfa = false, usedInDeadDfa = true, usedInCfa = true, isBack = false, isDead = true, isPostponed = false),
+ DfgForward(usedInDfa = true, usedInDeadDfa = true, usedInCfa = false, isBack = false, isDead = false, isPostponed = false),
+ CfgForward(usedInDfa = false, usedInDeadDfa = false, usedInCfa = true, isBack = false, isDead = false, isPostponed = false),
+ CfgBackward(usedInDfa = false, usedInDeadDfa = false, usedInCfa = true, isBack = true, isDead = false, isPostponed = false),
+ DeadBackward(usedInDfa = false, usedInDeadDfa = false, usedInCfa = true, isBack = true, isDead = true, isPostponed = false),
}
private val CFGNode<*>.previousNodeCount
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 182f5d7..d382fa0 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
@@ -50,10 +50,6 @@
return visitNode(node, data)
}
- open fun visitMergePostponedLambdaExitsNode(node: MergePostponedLambdaExitsNode, data: D): R {
- return visitNode(node, data)
- }
-
open fun visitAnonymousFunctionExpressionNode(node: AnonymousFunctionExpressionNode, 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 f8c23f3..c08ecaf 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,10 +32,6 @@
visitNode(node)
}
- open fun visitMergePostponedLambdaExitsNode(node: MergePostponedLambdaExitsNode) {
- visitNode(node)
- }
-
open fun visitAnonymousFunctionExpressionNode(node: AnonymousFunctionExpressionNode) {
visitNode(node)
}
@@ -305,10 +301,6 @@
visitPostponedLambdaExitNode(node)
}
- final override fun visitMergePostponedLambdaExitsNode(node: MergePostponedLambdaExitsNode, data: Nothing?) {
- visitMergePostponedLambdaExitsNode(node)
- }
-
final override fun visitAnonymousFunctionExpressionNode(node: AnonymousFunctionExpressionNode, data: Nothing?) {
visitAnonymousFunctionExpressionNode(node)
}
diff --git a/compiler/testData/codegen/box/smartCasts/kt44814.dot b/compiler/testData/codegen/box/smartCasts/kt44814.dot
index 81e5d96..a33dae9 100644
--- a/compiler/testData/codegen/box/smartCasts/kt44814.dot
+++ b/compiler/testData/codegen/box/smartCasts/kt44814.dot
@@ -643,18 +643,17 @@
229 [label="Exit block [4]"];
}
230 [label="Exit when branch result [4]"];
- 231 [label="Merge postponed lambda exits [4]"];
- 232 [label="Enter when branch result [4]"];
+ 231 [label="Enter when branch result [4]"];
subgraph cluster_70 {
color=blue
- 233 [label="Enter block [4]"];
- 234 [label="Const: Null(null) [4]"];
- 235 [label="Exit block [4]"];
+ 232 [label="Enter block [4]"];
+ 233 [label="Const: Null(null) [4]"];
+ 234 [label="Exit block [4]"];
}
- 236 [label="Exit when branch result [4]"];
- 237 [label="Exit when [4]"];
+ 235 [label="Exit when branch result [4]"];
+ 236 [label="Exit when [4]"];
}
- 238 [label="Jump: ^getModifierList when (this@R|/FirModifierList.Companion.getModifierList|) {
+ 237 [label="Jump: ^getModifierList when (this@R|/FirModifierList.Companion.getModifierList|) {
==($subj$, Null(null)) -> {
Null(null)
}
@@ -673,10 +672,10 @@
}
}
[4]"];
- 239 [label="Stub [4]" style="filled" fillcolor=gray];
- 240 [label="Exit block [4]" style="filled" fillcolor=gray];
+ 238 [label="Stub [4]" style="filled" fillcolor=gray];
+ 239 [label="Exit block [4]" style="filled" fillcolor=gray];
}
- 241 [label="Exit function getModifierList [4]" style="filled" fillcolor=red];
+ 240 [label="Exit function getModifierList [4]" style="filled" fillcolor=red];
}
162 -> {163};
163 -> {164};
@@ -686,7 +685,7 @@
167 -> {168};
168 -> {169};
169 -> {170};
- 170 -> {171 232};
+ 170 -> {171 231};
171 -> {172};
172 -> {173};
173 -> {174};
@@ -713,14 +712,13 @@
193 -> {194};
194 -> {195};
195 -> {196};
+ 196 -> {197};
196 -> {184} [color=green style=dashed];
- 196 -> {197} [color=green];
- 196 -> {204} [style=dotted];
197 -> {198};
198 -> {199 203};
199 -> {200};
200 -> {201};
- 201 -> {241};
+ 201 -> {240};
201 -> {202} [style=dotted];
202 -> {204} [style=dotted];
203 -> {204};
@@ -730,7 +728,7 @@
207 -> {208};
208 -> {209};
209 -> {210};
- 210 -> {237};
+ 210 -> {236};
211 -> {212};
212 -> {213};
213 -> {214};
@@ -747,74 +745,72 @@
222 -> {223};
223 -> {224};
224 -> {225};
- 225 -> {226} [color=green];
- 225 -> {231} [color=red];
+ 225 -> {226};
226 -> {228};
227 -> {228};
- 228 -> {229 231};
+ 228 -> {229};
229 -> {230};
- 230 -> {237};
- 231 -> {237} [color=red];
+ 230 -> {236};
+ 231 -> {232};
232 -> {233};
233 -> {234};
234 -> {235};
235 -> {236};
236 -> {237};
- 237 -> {238};
- 238 -> {241};
+ 237 -> {240};
+ 237 -> {238} [style=dotted];
238 -> {239} [style=dotted];
239 -> {240} [style=dotted];
- 240 -> {241} [style=dotted];
subgraph cluster_71 {
color=red
- 242 [label="Enter function boxImpl [4]" style="filled" fillcolor=red];
+ 241 [label="Enter function boxImpl [4]" style="filled" fillcolor=red];
subgraph cluster_72 {
color=blue
- 243 [label="Enter block [4]"];
- 244 [label="Function call: R|/LighterASTNode.LighterASTNode|() [4]" style="filled" fillcolor=yellow];
- 245 [label="Function call: R|kotlin/collections/listOf|<R|LighterASTNode|>(...) [4]" style="filled" fillcolor=yellow];
- 246 [label="Function call: R|/LighterASTNode.LighterASTNode|(...) [4]" style="filled" fillcolor=yellow];
- 247 [label="Function call: R|/FlyweightCapableTreeStructure.FlyweightCapableTreeStructure|() [4]" style="filled" fillcolor=yellow];
- 248 [label="Function call: R|/FirLightSourceElement.FirLightSourceElement|(...) [4]" style="filled" fillcolor=yellow];
- 249 [label="Variable declaration: lval sourceElement: R|FirSourceElement?| [4]"];
- 250 [label="Access variable R|<local>/sourceElement| [4]"];
- 251 [label="Function call: (this@R|/FirModifierList.Companion|, R|<local>/sourceElement|).R|/FirModifierList.Companion.getModifierList|() [4]" style="filled" fillcolor=yellow];
- 252 [label="Variable declaration: lval result: R|FirModifierList?| [4]"];
+ 242 [label="Enter block [4]"];
+ 243 [label="Function call: R|/LighterASTNode.LighterASTNode|() [4]" style="filled" fillcolor=yellow];
+ 244 [label="Function call: R|kotlin/collections/listOf|<R|LighterASTNode|>(...) [4]" style="filled" fillcolor=yellow];
+ 245 [label="Function call: R|/LighterASTNode.LighterASTNode|(...) [4]" style="filled" fillcolor=yellow];
+ 246 [label="Function call: R|/FlyweightCapableTreeStructure.FlyweightCapableTreeStructure|() [4]" style="filled" fillcolor=yellow];
+ 247 [label="Function call: R|/FirLightSourceElement.FirLightSourceElement|(...) [4]" style="filled" fillcolor=yellow];
+ 248 [label="Variable declaration: lval sourceElement: R|FirSourceElement?| [4]"];
+ 249 [label="Access variable R|<local>/sourceElement| [4]"];
+ 250 [label="Function call: (this@R|/FirModifierList.Companion|, R|<local>/sourceElement|).R|/FirModifierList.Companion.getModifierList|() [4]" style="filled" fillcolor=yellow];
+ 251 [label="Variable declaration: lval result: R|FirModifierList?| [4]"];
subgraph cluster_73 {
color=blue
- 253 [label="Enter when [4]"];
+ 252 [label="Enter when [4]"];
subgraph cluster_74 {
color=blue
- 254 [label="Enter when branch condition [4]"];
- 255 [label="Access variable R|<local>/result| [4]"];
- 256 [label="Type operator: (R|<local>/result| is R|FirModifierList.FirLightModifierList|) [4]"];
- 257 [label="Exit when branch condition [4]"];
+ 253 [label="Enter when branch condition [4]"];
+ 254 [label="Access variable R|<local>/result| [4]"];
+ 255 [label="Type operator: (R|<local>/result| is R|FirModifierList.FirLightModifierList|) [4]"];
+ 256 [label="Exit when branch condition [4]"];
}
subgraph cluster_75 {
color=blue
- 258 [label="Enter when branch condition else [4]"];
- 259 [label="Exit when branch condition [4]"];
+ 257 [label="Enter when branch condition else [4]"];
+ 258 [label="Exit when branch condition [4]"];
}
- 260 [label="Enter when branch result [4]"];
+ 259 [label="Enter when branch result [4]"];
subgraph cluster_76 {
color=blue
- 261 [label="Enter block [4]"];
- 262 [label="Const: String(Fail) [4]"];
- 263 [label="Exit block [4]"];
+ 260 [label="Enter block [4]"];
+ 261 [label="Const: String(Fail) [4]"];
+ 262 [label="Exit block [4]"];
}
- 264 [label="Exit when branch result [4]"];
- 265 [label="Enter when branch result [4]"];
+ 263 [label="Exit when branch result [4]"];
+ 264 [label="Enter when branch result [4]"];
subgraph cluster_77 {
color=blue
- 266 [label="Enter block [4]"];
- 267 [label="Const: String(OK) [4]"];
- 268 [label="Exit block [4]"];
+ 265 [label="Enter block [4]"];
+ 266 [label="Const: String(OK) [4]"];
+ 267 [label="Exit block [4]"];
}
- 269 [label="Exit when branch result [4]"];
- 270 [label="Exit when [4]"];
+ 268 [label="Exit when branch result [4]"];
+ 269 [label="Exit when [4]"];
}
- 271 [label="Jump: ^boxImpl when () {
+ 270 [label="Jump: ^boxImpl when () {
(R|<local>/result| is R|FirModifierList.FirLightModifierList|) -> {
String(OK)
}
@@ -823,11 +819,12 @@
}
}
[4]"];
- 272 [label="Stub [4]" style="filled" fillcolor=gray];
- 273 [label="Exit block [4]" style="filled" fillcolor=gray];
+ 271 [label="Stub [4]" style="filled" fillcolor=gray];
+ 272 [label="Exit block [4]" style="filled" fillcolor=gray];
}
- 274 [label="Exit function boxImpl [4]" style="filled" fillcolor=red];
+ 273 [label="Exit function boxImpl [4]" style="filled" fillcolor=red];
}
+ 241 -> {242};
242 -> {243};
243 -> {244};
244 -> {245};
@@ -842,47 +839,46 @@
253 -> {254};
254 -> {255};
255 -> {256};
- 256 -> {257};
- 257 -> {258 265};
+ 256 -> {257 264};
+ 257 -> {258};
258 -> {259};
259 -> {260};
260 -> {261};
261 -> {262};
262 -> {263};
- 263 -> {264};
- 264 -> {270};
+ 263 -> {269};
+ 264 -> {265};
265 -> {266};
266 -> {267};
267 -> {268};
268 -> {269};
269 -> {270};
- 270 -> {271};
- 271 -> {274};
+ 270 -> {273};
+ 270 -> {271} [style=dotted];
271 -> {272} [style=dotted];
272 -> {273} [style=dotted];
- 273 -> {274} [style=dotted];
subgraph cluster_78 {
color=red
- 275 [label="Enter function box [2]" style="filled" fillcolor=red];
+ 274 [label="Enter function box [2]" style="filled" fillcolor=red];
subgraph cluster_79 {
color=blue
- 276 [label="Enter block [2]"];
- 277 [label="Access qualifier /FirModifierList [2]"];
- 278 [label="Function call: Q|FirModifierList|.R|/FirModifierList.Companion.boxImpl|() [2]" style="filled" fillcolor=yellow];
- 279 [label="Jump: ^box Q|FirModifierList|.R|/FirModifierList.Companion.boxImpl|() [2]"];
- 280 [label="Stub [2]" style="filled" fillcolor=gray];
- 281 [label="Exit block [2]" style="filled" fillcolor=gray];
+ 275 [label="Enter block [2]"];
+ 276 [label="Access qualifier /FirModifierList [2]"];
+ 277 [label="Function call: Q|FirModifierList|.R|/FirModifierList.Companion.boxImpl|() [2]" style="filled" fillcolor=yellow];
+ 278 [label="Jump: ^box Q|FirModifierList|.R|/FirModifierList.Companion.boxImpl|() [2]"];
+ 279 [label="Stub [2]" style="filled" fillcolor=gray];
+ 280 [label="Exit block [2]" style="filled" fillcolor=gray];
}
- 282 [label="Exit function box [2]" style="filled" fillcolor=red];
+ 281 [label="Exit function box [2]" style="filled" fillcolor=red];
}
+ 274 -> {275};
275 -> {276};
276 -> {277};
277 -> {278};
- 278 -> {279};
- 279 -> {282};
+ 278 -> {281};
+ 278 -> {279} [style=dotted];
279 -> {280} [style=dotted];
280 -> {281} [style=dotted];
- 281 -> {282} [style=dotted];
}
diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/singleReturnFromTry.dot b/compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/singleReturnFromTry.dot
index e33b43b..9b48306 100644
--- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/singleReturnFromTry.dot
+++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/singleReturnFromTry.dot
@@ -202,9 +202,8 @@
51 -> {52} [style=dotted];
52 -> {53} [style=dotted];
53 -> {54} [style=dotted];
+ 54 -> {55};
54 -> {46} [color=green style=dashed];
- 54 -> {55} [color=green];
- 54 -> {63} [style=dotted];
55 -> {56} [style=dotted];
56 -> {57} [style=dotted];
57 -> {58} [style=dotted];
@@ -379,9 +378,8 @@
101 -> {102} [style=dotted];
102 -> {103} [style=dotted];
103 -> {104} [style=dotted];
+ 104 -> {105};
104 -> {96} [color=green style=dashed];
- 104 -> {105} [color=green];
- 104 -> {119} [color=red];
105 -> {106} [style=dotted];
106 -> {107} [style=dotted];
107 -> {108} [style=dotted];
diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/delegatedConstructorArguments.dot b/compiler/testData/diagnostics/tests/controlFlowAnalysis/delegatedConstructorArguments.dot
index f3a720d..e7443f15 100644
--- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/delegatedConstructorArguments.dot
+++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/delegatedConstructorArguments.dot
@@ -121,9 +121,8 @@
8 -> {9};
9 -> {10};
10 -> {11};
+ 11 -> {12};
11 -> {5} [color=green style=dashed];
- 11 -> {12} [color=green];
- 11 -> {13} [color=red];
12 -> {13};
13 -> {26} [color=green label="return@/Test.Test"];
13 -> {41} [color=red];
@@ -137,9 +136,8 @@
20 -> {21};
21 -> {22};
22 -> {23};
+ 23 -> {24};
23 -> {17} [color=green style=dashed];
- 23 -> {24} [color=green];
- 23 -> {25} [color=red];
24 -> {25};
25 -> {26} [color=green label="return@/Test.Test"];
25 -> {51} [color=red];
diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/smartCastInCatch.dot b/compiler/testData/diagnostics/tests/controlFlowAnalysis/smartCastInCatch.dot
index 36aaf7e..4ae5a4b 100644
--- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/smartCastInCatch.dot
+++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/smartCastInCatch.dot
@@ -398,8 +398,7 @@
129 -> {130 136};
130 -> {131};
131 -> {132};
- 132 -> {133} [color=green];
- 132 -> {143} [color=red];
+ 132 -> {133};
133 -> {134};
134 -> {135};
135 -> {136 143};
diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/unsoundSmartcast.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/unsoundSmartcast.fir.kt
index e8313dd..a0c8e38 100644
--- a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/unsoundSmartcast.fir.kt
+++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/unsoundSmartcast.fir.kt
@@ -39,3 +39,14 @@
<!UNSAFE_IMPLICIT_INVOKE_CALL!>x<!>(if (true) { x = null; 0 } else 0, <!ARGUMENT_TYPE_MISMATCH!>x<!>)
x<!UNSAFE_CALL!>.<!>inv()
}
+
+fun test6(x: Any, b: Boolean) {
+ if (x !is String) {
+ if (b) {
+ require(x is String) { "" }
+ } else {
+ return
+ }
+ }
+ x.length
+}
diff --git a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/unsoundSmartcast.kt b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/unsoundSmartcast.kt
index b0e4499..40198b7 100644
--- a/compiler/testData/diagnostics/testsWithStdLib/smartcasts/unsoundSmartcast.kt
+++ b/compiler/testData/diagnostics/testsWithStdLib/smartcasts/unsoundSmartcast.kt
@@ -39,3 +39,14 @@
<!DEBUG_INFO_SMARTCAST!>x<!>(if (true) { x = null; 0 } else 0, <!DEBUG_INFO_SMARTCAST!>x<!>)
<!DEBUG_INFO_SMARTCAST!>x<!>.inv()
}
+
+fun test6(x: Any, b: Boolean) {
+ if (x !is String) {
+ if (b) {
+ require(x is String) { "" }
+ } else {
+ return
+ }
+ }
+ <!DEBUG_INFO_SMARTCAST!>x<!>.length
+}