FIR DFA: fork flow everywhere
In theory, forking persistent flows should be cheap because of object reuse, so the proposal here is to start from scratch and prove redundancy of forks on a case-by-case basis. Something something better safe than sorry. ^KT-28333 tag fixed-in-k2 ^KT-28489 tag fixed-in-k2
This commit is contained in:
+212
-210
@@ -481,11 +481,12 @@ digraph flowFromInplaceLambda2_kt {
|
||||
color=blue
|
||||
197 [label="Enter block"];
|
||||
198 [label="Access variable R|<local>/p|"];
|
||||
199 [label="Access variable <Inapplicable(UNSAFE_CALL): kotlin/String.length>#"];
|
||||
200 [label="Const: Int(123)"];
|
||||
201 [label="Exit block"];
|
||||
199 [label="Smart cast: R|<local>/p|"];
|
||||
200 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): kotlin/String.length>#"];
|
||||
201 [label="Const: Int(123)"];
|
||||
202 [label="Exit block"];
|
||||
}
|
||||
202 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
203 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
}
|
||||
172 [label="Postponed exit from lambda"];
|
||||
173 [label="Function call: R|kotlin/run|<R|kotlin/Int|>(...)"];
|
||||
@@ -577,329 +578,329 @@ digraph flowFromInplaceLambda2_kt {
|
||||
199 -> {200};
|
||||
200 -> {201};
|
||||
201 -> {202};
|
||||
202 -> {175} [color=red];
|
||||
202 -> {172} [color=green];
|
||||
202 -> {203};
|
||||
203 -> {175} [color=red];
|
||||
203 -> {172} [color=green];
|
||||
|
||||
subgraph cluster_47 {
|
||||
color=red
|
||||
203 [label="Enter function test5" style="filled" fillcolor=red];
|
||||
204 [label="Enter function test5" style="filled" fillcolor=red];
|
||||
subgraph cluster_48 {
|
||||
color=blue
|
||||
204 [label="Enter block"];
|
||||
205 [label="Access variable R|<local>/y|"];
|
||||
206 [label="Enter safe call"];
|
||||
207 [label="Postponed enter to lambda"];
|
||||
205 [label="Enter block"];
|
||||
206 [label="Access variable R|<local>/y|"];
|
||||
207 [label="Enter safe call"];
|
||||
208 [label="Postponed enter to lambda"];
|
||||
subgraph cluster_49 {
|
||||
color=blue
|
||||
222 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
223 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
subgraph cluster_50 {
|
||||
color=blue
|
||||
223 [label="Enter block"];
|
||||
224 [label="Access variable R|<local>/x|"];
|
||||
225 [label="Type operator: (R|<local>/x| as R|kotlin/String|)"];
|
||||
226 [label="Function call: R|/n|<R|kotlin/Nothing?|>()"];
|
||||
227 [label="Exit block"];
|
||||
224 [label="Enter block"];
|
||||
225 [label="Access variable R|<local>/x|"];
|
||||
226 [label="Type operator: (R|<local>/x| as R|kotlin/String|)"];
|
||||
227 [label="Function call: R|/n|<R|kotlin/Nothing?|>()"];
|
||||
228 [label="Exit block"];
|
||||
}
|
||||
228 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
229 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
}
|
||||
208 [label="Postponed exit from lambda"];
|
||||
209 [label="Function call: $subj$.R|kotlin/let|<R|kotlin/String|, R|kotlin/Nothing?|>(...)"];
|
||||
210 [label="Exit safe call"];
|
||||
211 [label="Const: Int(1)"];
|
||||
212 [label="Postponed enter to lambda"];
|
||||
209 [label="Postponed exit from lambda"];
|
||||
210 [label="Function call: $subj$.R|kotlin/let|<R|kotlin/String|, R|kotlin/Nothing?|>(...)"];
|
||||
211 [label="Exit safe call"];
|
||||
212 [label="Const: Int(1)"];
|
||||
213 [label="Postponed enter to lambda"];
|
||||
subgraph cluster_51 {
|
||||
color=blue
|
||||
229 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
230 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
subgraph cluster_52 {
|
||||
color=blue
|
||||
230 [label="Enter block"];
|
||||
231 [label="Const: String()"];
|
||||
232 [label="Exit block"];
|
||||
231 [label="Enter block"];
|
||||
232 [label="Const: String()"];
|
||||
233 [label="Exit block"];
|
||||
}
|
||||
233 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
234 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
}
|
||||
213 [label="Postponed exit from lambda"];
|
||||
214 [label="Function call: R|kotlin/run|<R|kotlin/String|>(...)"];
|
||||
215 [label="Merge postponed lambda exits"];
|
||||
216 [label="Call arguments union" style="filled" fillcolor=yellow];
|
||||
217 [label="Function call: R|/foo|<R|kotlin/String|>(...)"];
|
||||
218 [label="Access variable R|<local>/x|"];
|
||||
219 [label="Access variable <Inapplicable(UNSAFE_CALL): kotlin/String.length>#"];
|
||||
220 [label="Exit block"];
|
||||
214 [label="Postponed exit from lambda"];
|
||||
215 [label="Function call: R|kotlin/run|<R|kotlin/String|>(...)"];
|
||||
216 [label="Merge postponed lambda exits"];
|
||||
217 [label="Call arguments union" style="filled" fillcolor=yellow];
|
||||
218 [label="Function call: R|/foo|<R|kotlin/String|>(...)"];
|
||||
219 [label="Access variable R|<local>/x|"];
|
||||
220 [label="Access variable <Inapplicable(UNSAFE_CALL): kotlin/String.length>#"];
|
||||
221 [label="Exit block"];
|
||||
}
|
||||
221 [label="Exit function test5" style="filled" fillcolor=red];
|
||||
222 [label="Exit function test5" style="filled" fillcolor=red];
|
||||
}
|
||||
203 -> {204};
|
||||
204 -> {205};
|
||||
205 -> {206 210};
|
||||
206 -> {207};
|
||||
207 -> {222};
|
||||
207 -> {208} [color=red];
|
||||
207 -> {222} [style=dashed];
|
||||
208 -> {209};
|
||||
205 -> {206};
|
||||
206 -> {207 211};
|
||||
207 -> {208};
|
||||
208 -> {223};
|
||||
208 -> {209} [color=red];
|
||||
208 -> {223} [style=dashed];
|
||||
209 -> {210};
|
||||
210 -> {215 211};
|
||||
211 -> {212};
|
||||
212 -> {229};
|
||||
212 -> {213} [color=red];
|
||||
212 -> {229} [style=dashed];
|
||||
213 -> {214};
|
||||
214 -> {216};
|
||||
215 -> {216} [color=red];
|
||||
216 -> {217};
|
||||
210 -> {211};
|
||||
211 -> {216 212};
|
||||
212 -> {213};
|
||||
213 -> {230};
|
||||
213 -> {214} [color=red];
|
||||
213 -> {230} [style=dashed];
|
||||
214 -> {215};
|
||||
215 -> {217};
|
||||
216 -> {217} [color=red];
|
||||
217 -> {218};
|
||||
218 -> {219};
|
||||
219 -> {220};
|
||||
220 -> {221};
|
||||
222 -> {223};
|
||||
221 -> {222};
|
||||
223 -> {224};
|
||||
224 -> {225};
|
||||
225 -> {226};
|
||||
226 -> {227};
|
||||
227 -> {228};
|
||||
228 -> {215} [color=red];
|
||||
228 -> {208} [color=green];
|
||||
229 -> {230};
|
||||
228 -> {229};
|
||||
229 -> {216} [color=red];
|
||||
229 -> {209} [color=green];
|
||||
230 -> {231};
|
||||
231 -> {232};
|
||||
232 -> {233};
|
||||
233 -> {216} [color=red];
|
||||
233 -> {213} [color=green];
|
||||
233 -> {234};
|
||||
234 -> {217} [color=red];
|
||||
234 -> {214} [color=green];
|
||||
|
||||
subgraph cluster_53 {
|
||||
color=red
|
||||
234 [label="Enter function test6" style="filled" fillcolor=red];
|
||||
235 [label="Enter function test6" style="filled" fillcolor=red];
|
||||
subgraph cluster_54 {
|
||||
color=blue
|
||||
235 [label="Enter block"];
|
||||
236 [label="Enter block"];
|
||||
subgraph cluster_55 {
|
||||
color=blue
|
||||
236 [label="Enter when"];
|
||||
237 [label="Enter when"];
|
||||
subgraph cluster_56 {
|
||||
color=blue
|
||||
237 [label="Enter when branch condition "];
|
||||
238 [label="Const: Boolean(true)"];
|
||||
239 [label="Exit when branch condition"];
|
||||
238 [label="Enter when branch condition "];
|
||||
239 [label="Const: Boolean(true)"];
|
||||
240 [label="Exit when branch condition"];
|
||||
}
|
||||
subgraph cluster_57 {
|
||||
color=blue
|
||||
240 [label="Enter when branch condition else"];
|
||||
241 [label="Exit when branch condition"];
|
||||
241 [label="Enter when branch condition else"];
|
||||
242 [label="Exit when branch condition"];
|
||||
}
|
||||
242 [label="Enter when branch result"];
|
||||
243 [label="Enter when branch result"];
|
||||
subgraph cluster_58 {
|
||||
color=blue
|
||||
243 [label="Enter block"];
|
||||
244 [label="Postponed enter to lambda"];
|
||||
244 [label="Enter block"];
|
||||
245 [label="Postponed enter to lambda"];
|
||||
subgraph cluster_59 {
|
||||
color=blue
|
||||
276 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
277 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
subgraph cluster_60 {
|
||||
color=blue
|
||||
277 [label="Enter block"];
|
||||
278 [label="Access variable R|<local>/x|"];
|
||||
279 [label="Type operator: (R|<local>/x| as R|kotlin/String|)"];
|
||||
280 [label="Function call: R|/n|<R|kotlin/Nothing?|>()"];
|
||||
281 [label="Exit block"];
|
||||
278 [label="Enter block"];
|
||||
279 [label="Access variable R|<local>/x|"];
|
||||
280 [label="Type operator: (R|<local>/x| as R|kotlin/String|)"];
|
||||
281 [label="Function call: R|/n|<R|kotlin/Nothing?|>()"];
|
||||
282 [label="Exit block"];
|
||||
}
|
||||
282 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
283 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
}
|
||||
245 [label="Postponed exit from lambda"];
|
||||
246 [label="Function call: R|kotlin/run|<R|kotlin/Nothing?|>(...)"];
|
||||
247 [label="Exit block"];
|
||||
246 [label="Postponed exit from lambda"];
|
||||
247 [label="Function call: R|kotlin/run|<R|kotlin/Nothing?|>(...)"];
|
||||
248 [label="Exit block"];
|
||||
}
|
||||
248 [label="Exit when branch result"];
|
||||
249 [label="Enter when branch result"];
|
||||
249 [label="Exit when branch result"];
|
||||
250 [label="Enter when branch result"];
|
||||
subgraph cluster_61 {
|
||||
color=blue
|
||||
250 [label="Enter block"];
|
||||
251 [label="Postponed enter to lambda"];
|
||||
251 [label="Enter block"];
|
||||
252 [label="Postponed enter to lambda"];
|
||||
subgraph cluster_62 {
|
||||
color=blue
|
||||
269 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
270 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
subgraph cluster_63 {
|
||||
color=blue
|
||||
270 [label="Enter block"];
|
||||
271 [label="Access variable R|<local>/x|"];
|
||||
272 [label="Type operator: (R|<local>/x| as R|kotlin/String|)"];
|
||||
273 [label="Function call: R|/n|<R|kotlin/Nothing?|>()"];
|
||||
274 [label="Exit block"];
|
||||
271 [label="Enter block"];
|
||||
272 [label="Access variable R|<local>/x|"];
|
||||
273 [label="Type operator: (R|<local>/x| as R|kotlin/String|)"];
|
||||
274 [label="Function call: R|/n|<R|kotlin/Nothing?|>()"];
|
||||
275 [label="Exit block"];
|
||||
}
|
||||
275 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
276 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
}
|
||||
252 [label="Postponed exit from lambda"];
|
||||
253 [label="Function call: R|kotlin/run|<R|kotlin/Nothing?|>(...)"];
|
||||
254 [label="Exit block"];
|
||||
253 [label="Postponed exit from lambda"];
|
||||
254 [label="Function call: R|kotlin/run|<R|kotlin/Nothing?|>(...)"];
|
||||
255 [label="Exit block"];
|
||||
}
|
||||
255 [label="Exit when branch result"];
|
||||
256 [label="Exit when"];
|
||||
256 [label="Exit when branch result"];
|
||||
257 [label="Exit when"];
|
||||
}
|
||||
257 [label="Function call: R|/id|<R|kotlin/Nothing?|>(...)"];
|
||||
258 [label="Const: Int(1)"];
|
||||
259 [label="Postponed enter to lambda"];
|
||||
258 [label="Function call: R|/id|<R|kotlin/Nothing?|>(...)"];
|
||||
259 [label="Const: Int(1)"];
|
||||
260 [label="Postponed enter to lambda"];
|
||||
subgraph cluster_64 {
|
||||
color=blue
|
||||
283 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
284 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
subgraph cluster_65 {
|
||||
color=blue
|
||||
284 [label="Enter block"];
|
||||
285 [label="Access variable R|<local>/x|"];
|
||||
286 [label="Access variable <Inapplicable(UNSAFE_CALL): kotlin/String.length>#"];
|
||||
287 [label="Const: Int(123)"];
|
||||
288 [label="Exit block"];
|
||||
285 [label="Enter block"];
|
||||
286 [label="Access variable R|<local>/x|"];
|
||||
287 [label="Access variable <Inapplicable(UNSAFE_CALL): kotlin/String.length>#"];
|
||||
288 [label="Const: Int(123)"];
|
||||
289 [label="Exit block"];
|
||||
}
|
||||
289 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
290 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
}
|
||||
260 [label="Postponed exit from lambda"];
|
||||
261 [label="Function call: R|kotlin/run|<R|kotlin/Int|>(...)"];
|
||||
262 [label="Merge postponed lambda exits"];
|
||||
263 [label="Call arguments union" style="filled" fillcolor=yellow];
|
||||
264 [label="Function call: R|/foo|<R|kotlin/Int|>(...)"];
|
||||
265 [label="Access variable R|<local>/x|"];
|
||||
266 [label="Access variable <Inapplicable(UNSAFE_CALL): kotlin/String.length>#"];
|
||||
267 [label="Exit block"];
|
||||
261 [label="Postponed exit from lambda"];
|
||||
262 [label="Function call: R|kotlin/run|<R|kotlin/Int|>(...)"];
|
||||
263 [label="Merge postponed lambda exits"];
|
||||
264 [label="Call arguments union" style="filled" fillcolor=yellow];
|
||||
265 [label="Function call: R|/foo|<R|kotlin/Int|>(...)"];
|
||||
266 [label="Access variable R|<local>/x|"];
|
||||
267 [label="Access variable <Inapplicable(UNSAFE_CALL): kotlin/String.length>#"];
|
||||
268 [label="Exit block"];
|
||||
}
|
||||
268 [label="Exit function test6" style="filled" fillcolor=red];
|
||||
269 [label="Exit function test6" style="filled" fillcolor=red];
|
||||
}
|
||||
234 -> {235};
|
||||
235 -> {236};
|
||||
236 -> {237};
|
||||
237 -> {238};
|
||||
238 -> {239};
|
||||
239 -> {249 240};
|
||||
240 -> {241};
|
||||
239 -> {240};
|
||||
240 -> {250 241};
|
||||
241 -> {242};
|
||||
242 -> {243};
|
||||
243 -> {244};
|
||||
244 -> {276};
|
||||
244 -> {245} [color=red];
|
||||
244 -> {276} [style=dashed];
|
||||
245 -> {246};
|
||||
244 -> {245};
|
||||
245 -> {277};
|
||||
245 -> {246} [color=red];
|
||||
245 -> {277} [style=dashed];
|
||||
246 -> {247};
|
||||
247 -> {248};
|
||||
248 -> {256};
|
||||
249 -> {250};
|
||||
248 -> {249};
|
||||
249 -> {257};
|
||||
250 -> {251};
|
||||
251 -> {269};
|
||||
251 -> {252} [color=red];
|
||||
251 -> {269} [style=dashed];
|
||||
252 -> {253};
|
||||
251 -> {252};
|
||||
252 -> {270};
|
||||
252 -> {253} [color=red];
|
||||
252 -> {270} [style=dashed];
|
||||
253 -> {254};
|
||||
254 -> {255};
|
||||
255 -> {256};
|
||||
256 -> {262 257};
|
||||
257 -> {258};
|
||||
256 -> {257};
|
||||
257 -> {263 258};
|
||||
258 -> {259};
|
||||
259 -> {283};
|
||||
259 -> {260} [color=red];
|
||||
259 -> {283} [style=dashed];
|
||||
260 -> {261};
|
||||
261 -> {263};
|
||||
262 -> {263} [color=red];
|
||||
263 -> {264};
|
||||
259 -> {260};
|
||||
260 -> {284};
|
||||
260 -> {261} [color=red];
|
||||
260 -> {284} [style=dashed];
|
||||
261 -> {262};
|
||||
262 -> {264};
|
||||
263 -> {264} [color=red];
|
||||
264 -> {265};
|
||||
265 -> {266};
|
||||
266 -> {267};
|
||||
267 -> {268};
|
||||
269 -> {270};
|
||||
268 -> {269};
|
||||
270 -> {271};
|
||||
271 -> {272};
|
||||
272 -> {273};
|
||||
273 -> {274};
|
||||
274 -> {275};
|
||||
275 -> {262} [color=red];
|
||||
275 -> {252} [color=green];
|
||||
276 -> {277};
|
||||
275 -> {276};
|
||||
276 -> {263} [color=red];
|
||||
276 -> {253} [color=green];
|
||||
277 -> {278};
|
||||
278 -> {279};
|
||||
279 -> {280};
|
||||
280 -> {281};
|
||||
281 -> {282};
|
||||
282 -> {262} [color=red];
|
||||
282 -> {245} [color=green];
|
||||
283 -> {284};
|
||||
282 -> {283};
|
||||
283 -> {263} [color=red];
|
||||
283 -> {246} [color=green];
|
||||
284 -> {285};
|
||||
285 -> {286};
|
||||
286 -> {287};
|
||||
287 -> {288};
|
||||
288 -> {289};
|
||||
289 -> {263} [color=red];
|
||||
289 -> {260} [color=green];
|
||||
289 -> {290};
|
||||
290 -> {264} [color=red];
|
||||
290 -> {261} [color=green];
|
||||
|
||||
subgraph cluster_66 {
|
||||
color=red
|
||||
290 [label="Enter function test7" style="filled" fillcolor=red];
|
||||
291 [label="Enter function test7" style="filled" fillcolor=red];
|
||||
subgraph cluster_67 {
|
||||
color=blue
|
||||
291 [label="Enter block"];
|
||||
292 [label="Access variable R|<local>/x|"];
|
||||
293 [label="Variable declaration: lvar p: R|kotlin/String?|"];
|
||||
292 [label="Enter block"];
|
||||
293 [label="Access variable R|<local>/x|"];
|
||||
294 [label="Variable declaration: lvar p: R|kotlin/String?|"];
|
||||
subgraph cluster_68 {
|
||||
color=blue
|
||||
294 [label="Enter when"];
|
||||
295 [label="Enter when"];
|
||||
subgraph cluster_69 {
|
||||
color=blue
|
||||
295 [label="Enter when branch condition "];
|
||||
296 [label="Access variable R|<local>/p|"];
|
||||
297 [label="Const: Null(null)"];
|
||||
298 [label="Equality operator !="];
|
||||
299 [label="Exit when branch condition"];
|
||||
296 [label="Enter when branch condition "];
|
||||
297 [label="Access variable R|<local>/p|"];
|
||||
298 [label="Const: Null(null)"];
|
||||
299 [label="Equality operator !="];
|
||||
300 [label="Exit when branch condition"];
|
||||
}
|
||||
300 [label="Synthetic else branch"];
|
||||
301 [label="Enter when branch result"];
|
||||
301 [label="Synthetic else branch"];
|
||||
302 [label="Enter when branch result"];
|
||||
subgraph cluster_70 {
|
||||
color=blue
|
||||
302 [label="Enter block"];
|
||||
303 [label="Postponed enter to lambda"];
|
||||
303 [label="Enter block"];
|
||||
304 [label="Postponed enter to lambda"];
|
||||
subgraph cluster_71 {
|
||||
color=blue
|
||||
321 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
322 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
subgraph cluster_72 {
|
||||
color=blue
|
||||
322 [label="Enter block"];
|
||||
323 [label="Const: Null(null)"];
|
||||
324 [label="Assignment: R|<local>/p|"];
|
||||
325 [label="Function call: R|/n|<R|kotlin/Nothing?|>()"];
|
||||
326 [label="Exit block"];
|
||||
323 [label="Enter block"];
|
||||
324 [label="Const: Null(null)"];
|
||||
325 [label="Assignment: R|<local>/p|"];
|
||||
326 [label="Function call: R|/n|<R|kotlin/Nothing?|>()"];
|
||||
327 [label="Exit block"];
|
||||
}
|
||||
327 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
328 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
}
|
||||
304 [label="Postponed exit from lambda"];
|
||||
305 [label="Function call: R|kotlin/run|<R|kotlin/Nothing?|>(...)"];
|
||||
306 [label="Function call: R|/id|<R|kotlin/Nothing?|>(...)"];
|
||||
307 [label="Const: Int(1)"];
|
||||
308 [label="Postponed enter to lambda"];
|
||||
305 [label="Postponed exit from lambda"];
|
||||
306 [label="Function call: R|kotlin/run|<R|kotlin/Nothing?|>(...)"];
|
||||
307 [label="Function call: R|/id|<R|kotlin/Nothing?|>(...)"];
|
||||
308 [label="Const: Int(1)"];
|
||||
309 [label="Postponed enter to lambda"];
|
||||
subgraph cluster_73 {
|
||||
color=blue
|
||||
328 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
329 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
subgraph cluster_74 {
|
||||
color=blue
|
||||
329 [label="Enter block"];
|
||||
330 [label="Access variable R|<local>/p|"];
|
||||
331 [label="Smart cast: R|<local>/p|"];
|
||||
332 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): kotlin/String.length>#"];
|
||||
333 [label="Const: Int(123)"];
|
||||
334 [label="Exit block"];
|
||||
330 [label="Enter block"];
|
||||
331 [label="Access variable R|<local>/p|"];
|
||||
332 [label="Smart cast: R|<local>/p|"];
|
||||
333 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): kotlin/String.length>#"];
|
||||
334 [label="Const: Int(123)"];
|
||||
335 [label="Exit block"];
|
||||
}
|
||||
335 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
336 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
}
|
||||
309 [label="Postponed exit from lambda"];
|
||||
310 [label="Function call: R|kotlin/run|<R|kotlin/Int|>(...)"];
|
||||
311 [label="Call arguments union" style="filled" fillcolor=yellow];
|
||||
312 [label="Function call: R|/foo|<R|kotlin/Int|>(...)"];
|
||||
313 [label="Access variable R|<local>/p|"];
|
||||
314 [label="Smart cast: R|<local>/p|"];
|
||||
315 [label="Access variable <Inapplicable(UNSAFE_CALL): kotlin/String.length>#"];
|
||||
316 [label="Exit block"];
|
||||
310 [label="Postponed exit from lambda"];
|
||||
311 [label="Function call: R|kotlin/run|<R|kotlin/Int|>(...)"];
|
||||
312 [label="Call arguments union" style="filled" fillcolor=yellow];
|
||||
313 [label="Function call: R|/foo|<R|kotlin/Int|>(...)"];
|
||||
314 [label="Access variable R|<local>/p|"];
|
||||
315 [label="Smart cast: R|<local>/p|"];
|
||||
316 [label="Access variable <Inapplicable(UNSAFE_CALL): kotlin/String.length>#"];
|
||||
317 [label="Exit block"];
|
||||
}
|
||||
317 [label="Exit when branch result"];
|
||||
318 [label="Exit when"];
|
||||
318 [label="Exit when branch result"];
|
||||
319 [label="Exit when"];
|
||||
}
|
||||
319 [label="Exit block"];
|
||||
320 [label="Exit block"];
|
||||
}
|
||||
320 [label="Exit function test7" style="filled" fillcolor=red];
|
||||
321 [label="Exit function test7" style="filled" fillcolor=red];
|
||||
}
|
||||
290 -> {291};
|
||||
291 -> {292};
|
||||
292 -> {293};
|
||||
293 -> {294};
|
||||
@@ -908,21 +909,21 @@ digraph flowFromInplaceLambda2_kt {
|
||||
296 -> {297};
|
||||
297 -> {298};
|
||||
298 -> {299};
|
||||
299 -> {301 300};
|
||||
300 -> {318};
|
||||
301 -> {302};
|
||||
299 -> {300};
|
||||
300 -> {302 301};
|
||||
301 -> {319};
|
||||
302 -> {303};
|
||||
303 -> {321};
|
||||
303 -> {304} [color=red];
|
||||
303 -> {321} [style=dashed];
|
||||
304 -> {305};
|
||||
303 -> {304};
|
||||
304 -> {322};
|
||||
304 -> {305} [color=red];
|
||||
304 -> {322} [style=dashed];
|
||||
305 -> {306};
|
||||
306 -> {307};
|
||||
307 -> {308};
|
||||
308 -> {328};
|
||||
308 -> {309} [color=red];
|
||||
308 -> {328} [style=dashed];
|
||||
309 -> {310};
|
||||
308 -> {309};
|
||||
309 -> {329};
|
||||
309 -> {310} [color=red];
|
||||
309 -> {329} [style=dashed];
|
||||
310 -> {311};
|
||||
311 -> {312};
|
||||
312 -> {313};
|
||||
@@ -933,22 +934,23 @@ digraph flowFromInplaceLambda2_kt {
|
||||
317 -> {318};
|
||||
318 -> {319};
|
||||
319 -> {320};
|
||||
321 -> {322};
|
||||
320 -> {321};
|
||||
322 -> {323};
|
||||
323 -> {324};
|
||||
324 -> {325};
|
||||
325 -> {326};
|
||||
326 -> {327};
|
||||
327 -> {311} [color=red];
|
||||
327 -> {304} [color=green];
|
||||
328 -> {329};
|
||||
327 -> {328};
|
||||
328 -> {312} [color=red];
|
||||
328 -> {305} [color=green];
|
||||
329 -> {330};
|
||||
330 -> {331};
|
||||
331 -> {332};
|
||||
332 -> {333};
|
||||
333 -> {334};
|
||||
334 -> {335};
|
||||
335 -> {311} [color=red];
|
||||
335 -> {309} [color=green];
|
||||
335 -> {336};
|
||||
336 -> {312} [color=red];
|
||||
336 -> {310} [color=green];
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ FILE: flowFromInplaceLambda2.kt
|
||||
}
|
||||
}
|
||||
), Int(1), R|kotlin/run|<R|kotlin/Int|>(<L> = run@fun <anonymous>(): R|kotlin/Int| <inline=Inline, kind=EXACTLY_ONCE> {
|
||||
R|<local>/p|.<Inapplicable(UNSAFE_CALL): kotlin/String.length>#
|
||||
R|<local>/p|.<Inapplicable(UNSTABLE_SMARTCAST): kotlin/String.length>#
|
||||
^ Int(123)
|
||||
}
|
||||
))
|
||||
|
||||
@@ -41,7 +41,7 @@ fun test4(x: String?) {
|
||||
foo(
|
||||
id(if (true) run { p = null; n() } else run { n() }),
|
||||
1,
|
||||
run { p<!UNSAFE_CALL!>.<!>length; 123 } // Bad (p = null possible)
|
||||
run { <!SMARTCAST_IMPOSSIBLE!>p<!>.length; 123 } // Bad (p = null possible)
|
||||
)
|
||||
p<!UNSAFE_CALL!>.<!>length // Bad (p = null possible)
|
||||
}
|
||||
|
||||
+130
-134
@@ -80,27 +80,25 @@ digraph flowFromInplaceLambda3_kt {
|
||||
27 [label="Postponed enter to lambda"];
|
||||
subgraph cluster_10 {
|
||||
color=blue
|
||||
38 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
36 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
subgraph cluster_11 {
|
||||
color=blue
|
||||
39 [label="Enter block"];
|
||||
40 [label="Const: Int(1)"];
|
||||
41 [label="Assignment: R|<local>/x|"];
|
||||
42 [label="Exit block"];
|
||||
37 [label="Enter block"];
|
||||
38 [label="Const: Int(1)"];
|
||||
39 [label="Assignment: R|<local>/x|"];
|
||||
40 [label="Exit block"];
|
||||
}
|
||||
43 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
41 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
}
|
||||
28 [label="Postponed exit from lambda"];
|
||||
29 [label="Function call: R|/unknown|(...)"];
|
||||
30 [label="Access variable R|<local>/x|"];
|
||||
31 [label="Smart cast: R|<local>/x|"];
|
||||
32 [label="Access variable <Unresolved name: length>#"];
|
||||
33 [label="Access variable R|<local>/x|"];
|
||||
34 [label="Smart cast: R|<local>/x|"];
|
||||
35 [label="Function call: R|<local>/x|.<Unresolved name: inc>#()"];
|
||||
36 [label="Exit block"];
|
||||
31 [label="Access variable <Unresolved name: length>#"];
|
||||
32 [label="Access variable R|<local>/x|"];
|
||||
33 [label="Function call: R|<local>/x|.<Unresolved name: inc>#()"];
|
||||
34 [label="Exit block"];
|
||||
}
|
||||
37 [label="Exit function test1" style="filled" fillcolor=red];
|
||||
35 [label="Exit function test1" style="filled" fillcolor=red];
|
||||
}
|
||||
19 -> {20};
|
||||
20 -> {21};
|
||||
@@ -110,8 +108,8 @@ digraph flowFromInplaceLambda3_kt {
|
||||
24 -> {25};
|
||||
25 -> {26};
|
||||
26 -> {27};
|
||||
27 -> {28 38};
|
||||
27 -> {38} [style=dashed];
|
||||
27 -> {28 36};
|
||||
27 -> {36} [style=dashed];
|
||||
28 -> {29};
|
||||
29 -> {30};
|
||||
30 -> {31};
|
||||
@@ -119,199 +117,199 @@ digraph flowFromInplaceLambda3_kt {
|
||||
32 -> {33};
|
||||
33 -> {34};
|
||||
34 -> {35};
|
||||
35 -> {36};
|
||||
36 -> {37};
|
||||
38 -> {43 39};
|
||||
36 -> {41 37};
|
||||
37 -> {38};
|
||||
38 -> {39};
|
||||
39 -> {40};
|
||||
40 -> {41};
|
||||
41 -> {42};
|
||||
42 -> {43};
|
||||
43 -> {28};
|
||||
43 -> {38} [color=green style=dashed];
|
||||
41 -> {28};
|
||||
41 -> {36} [color=green style=dashed];
|
||||
|
||||
subgraph cluster_12 {
|
||||
color=red
|
||||
44 [label="Enter function test2" style="filled" fillcolor=red];
|
||||
42 [label="Enter function test2" style="filled" fillcolor=red];
|
||||
subgraph cluster_13 {
|
||||
color=blue
|
||||
45 [label="Enter block"];
|
||||
46 [label="Variable declaration: lvar x: R|kotlin/Any?|"];
|
||||
47 [label="Const: String()"];
|
||||
48 [label="Assignment: R|<local>/x|"];
|
||||
49 [label="Access variable R|<local>/x|"];
|
||||
50 [label="Smart cast: R|<local>/x|"];
|
||||
51 [label="Access variable R|kotlin/String.length|"];
|
||||
52 [label="Postponed enter to lambda"];
|
||||
43 [label="Enter block"];
|
||||
44 [label="Variable declaration: lvar x: R|kotlin/Any?|"];
|
||||
45 [label="Const: String()"];
|
||||
46 [label="Assignment: R|<local>/x|"];
|
||||
47 [label="Access variable R|<local>/x|"];
|
||||
48 [label="Smart cast: R|<local>/x|"];
|
||||
49 [label="Access variable R|kotlin/String.length|"];
|
||||
50 [label="Postponed enter to lambda"];
|
||||
subgraph cluster_14 {
|
||||
color=blue
|
||||
64 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
62 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
subgraph cluster_15 {
|
||||
color=blue
|
||||
65 [label="Enter block"];
|
||||
66 [label="Const: Int(1)"];
|
||||
67 [label="Assignment: R|<local>/x|"];
|
||||
68 [label="Exit block"];
|
||||
63 [label="Enter block"];
|
||||
64 [label="Const: Int(1)"];
|
||||
65 [label="Assignment: R|<local>/x|"];
|
||||
66 [label="Exit block"];
|
||||
}
|
||||
69 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
67 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
}
|
||||
53 [label="Call arguments union" style="filled" fillcolor=yellow];
|
||||
54 [label="Postponed exit from lambda"];
|
||||
55 [label="Function call: R|/atLeastOnce|(...)"];
|
||||
56 [label="Access variable R|<local>/x|"];
|
||||
57 [label="Smart cast: R|<local>/x|"];
|
||||
58 [label="Access variable <Unresolved name: length>#"];
|
||||
59 [label="Access variable R|<local>/x|"];
|
||||
60 [label="Smart cast: R|<local>/x|"];
|
||||
61 [label="Function call: R|<local>/x|.R|kotlin/Int.inc|()"];
|
||||
62 [label="Exit block"];
|
||||
51 [label="Call arguments union" style="filled" fillcolor=yellow];
|
||||
52 [label="Postponed exit from lambda"];
|
||||
53 [label="Function call: R|/atLeastOnce|(...)"];
|
||||
54 [label="Access variable R|<local>/x|"];
|
||||
55 [label="Smart cast: R|<local>/x|"];
|
||||
56 [label="Access variable <Unresolved name: length>#"];
|
||||
57 [label="Access variable R|<local>/x|"];
|
||||
58 [label="Smart cast: R|<local>/x|"];
|
||||
59 [label="Function call: R|<local>/x|.R|kotlin/Int.inc|()"];
|
||||
60 [label="Exit block"];
|
||||
}
|
||||
63 [label="Exit function test2" style="filled" fillcolor=red];
|
||||
61 [label="Exit function test2" style="filled" fillcolor=red];
|
||||
}
|
||||
42 -> {43};
|
||||
43 -> {44};
|
||||
44 -> {45};
|
||||
45 -> {46};
|
||||
46 -> {47};
|
||||
47 -> {48};
|
||||
48 -> {49};
|
||||
49 -> {50};
|
||||
50 -> {51};
|
||||
51 -> {52};
|
||||
52 -> {64};
|
||||
52 -> {54} [color=red];
|
||||
52 -> {64} [style=dashed];
|
||||
53 -> {55} [color=red];
|
||||
54 -> {55} [color=green];
|
||||
50 -> {62};
|
||||
50 -> {52} [color=red];
|
||||
50 -> {62} [style=dashed];
|
||||
51 -> {53} [color=red];
|
||||
52 -> {53} [color=green];
|
||||
53 -> {54};
|
||||
54 -> {55};
|
||||
55 -> {56};
|
||||
56 -> {57};
|
||||
57 -> {58};
|
||||
58 -> {59};
|
||||
59 -> {60};
|
||||
60 -> {61};
|
||||
61 -> {62};
|
||||
62 -> {63};
|
||||
63 -> {64};
|
||||
64 -> {65};
|
||||
65 -> {66};
|
||||
66 -> {67};
|
||||
67 -> {68};
|
||||
68 -> {69};
|
||||
69 -> {53} [color=red];
|
||||
69 -> {54} [color=green];
|
||||
69 -> {64} [color=green style=dashed];
|
||||
67 -> {51} [color=red];
|
||||
67 -> {52} [color=green];
|
||||
67 -> {62} [color=green style=dashed];
|
||||
|
||||
subgraph cluster_16 {
|
||||
color=red
|
||||
70 [label="Enter function test3" style="filled" fillcolor=red];
|
||||
68 [label="Enter function test3" style="filled" fillcolor=red];
|
||||
subgraph cluster_17 {
|
||||
color=blue
|
||||
71 [label="Enter block"];
|
||||
72 [label="Variable declaration: lvar x: R|kotlin/Any?|"];
|
||||
73 [label="Const: String()"];
|
||||
74 [label="Assignment: R|<local>/x|"];
|
||||
75 [label="Access variable R|<local>/x|"];
|
||||
76 [label="Smart cast: R|<local>/x|"];
|
||||
77 [label="Access variable R|kotlin/String.length|"];
|
||||
78 [label="Postponed enter to lambda"];
|
||||
69 [label="Enter block"];
|
||||
70 [label="Variable declaration: lvar x: R|kotlin/Any?|"];
|
||||
71 [label="Const: String()"];
|
||||
72 [label="Assignment: R|<local>/x|"];
|
||||
73 [label="Access variable R|<local>/x|"];
|
||||
74 [label="Smart cast: R|<local>/x|"];
|
||||
75 [label="Access variable R|kotlin/String.length|"];
|
||||
76 [label="Postponed enter to lambda"];
|
||||
subgraph cluster_18 {
|
||||
color=blue
|
||||
90 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
88 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
subgraph cluster_19 {
|
||||
color=blue
|
||||
91 [label="Enter block"];
|
||||
92 [label="Const: Int(1)"];
|
||||
93 [label="Assignment: R|<local>/x|"];
|
||||
94 [label="Exit block"];
|
||||
89 [label="Enter block"];
|
||||
90 [label="Const: Int(1)"];
|
||||
91 [label="Assignment: R|<local>/x|"];
|
||||
92 [label="Exit block"];
|
||||
}
|
||||
95 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
93 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
}
|
||||
79 [label="Call arguments union" style="filled" fillcolor=yellow];
|
||||
80 [label="Postponed exit from lambda"];
|
||||
81 [label="Function call: R|/exactlyOnce|(...)"];
|
||||
82 [label="Access variable R|<local>/x|"];
|
||||
83 [label="Smart cast: R|<local>/x|"];
|
||||
84 [label="Access variable <Unresolved name: length>#"];
|
||||
85 [label="Access variable R|<local>/x|"];
|
||||
86 [label="Smart cast: R|<local>/x|"];
|
||||
87 [label="Function call: R|<local>/x|.R|kotlin/Int.inc|()"];
|
||||
88 [label="Exit block"];
|
||||
77 [label="Call arguments union" style="filled" fillcolor=yellow];
|
||||
78 [label="Postponed exit from lambda"];
|
||||
79 [label="Function call: R|/exactlyOnce|(...)"];
|
||||
80 [label="Access variable R|<local>/x|"];
|
||||
81 [label="Smart cast: R|<local>/x|"];
|
||||
82 [label="Access variable <Unresolved name: length>#"];
|
||||
83 [label="Access variable R|<local>/x|"];
|
||||
84 [label="Smart cast: R|<local>/x|"];
|
||||
85 [label="Function call: R|<local>/x|.R|kotlin/Int.inc|()"];
|
||||
86 [label="Exit block"];
|
||||
}
|
||||
89 [label="Exit function test3" style="filled" fillcolor=red];
|
||||
87 [label="Exit function test3" style="filled" fillcolor=red];
|
||||
}
|
||||
68 -> {69};
|
||||
69 -> {70};
|
||||
70 -> {71};
|
||||
71 -> {72};
|
||||
72 -> {73};
|
||||
73 -> {74};
|
||||
74 -> {75};
|
||||
75 -> {76};
|
||||
76 -> {77};
|
||||
77 -> {78};
|
||||
78 -> {90};
|
||||
78 -> {80} [color=red];
|
||||
78 -> {90} [style=dashed];
|
||||
79 -> {81} [color=red];
|
||||
80 -> {81} [color=green];
|
||||
76 -> {88};
|
||||
76 -> {78} [color=red];
|
||||
76 -> {88} [style=dashed];
|
||||
77 -> {79} [color=red];
|
||||
78 -> {79} [color=green];
|
||||
79 -> {80};
|
||||
80 -> {81};
|
||||
81 -> {82};
|
||||
82 -> {83};
|
||||
83 -> {84};
|
||||
84 -> {85};
|
||||
85 -> {86};
|
||||
86 -> {87};
|
||||
87 -> {88};
|
||||
88 -> {89};
|
||||
89 -> {90};
|
||||
90 -> {91};
|
||||
91 -> {92};
|
||||
92 -> {93};
|
||||
93 -> {94};
|
||||
94 -> {95};
|
||||
95 -> {79} [color=red];
|
||||
95 -> {80} [color=green];
|
||||
95 -> {90} [color=green style=dashed];
|
||||
93 -> {77} [color=red];
|
||||
93 -> {78} [color=green];
|
||||
93 -> {88} [color=green style=dashed];
|
||||
|
||||
subgraph cluster_20 {
|
||||
color=red
|
||||
96 [label="Enter function test4" style="filled" fillcolor=red];
|
||||
94 [label="Enter function test4" style="filled" fillcolor=red];
|
||||
subgraph cluster_21 {
|
||||
color=blue
|
||||
97 [label="Enter block"];
|
||||
98 [label="Variable declaration: lvar x: R|kotlin/Any?|"];
|
||||
99 [label="Const: String()"];
|
||||
100 [label="Assignment: R|<local>/x|"];
|
||||
101 [label="Access variable R|<local>/x|"];
|
||||
102 [label="Smart cast: R|<local>/x|"];
|
||||
103 [label="Access variable R|kotlin/String.length|"];
|
||||
104 [label="Postponed enter to lambda"];
|
||||
95 [label="Enter block"];
|
||||
96 [label="Variable declaration: lvar x: R|kotlin/Any?|"];
|
||||
97 [label="Const: String()"];
|
||||
98 [label="Assignment: R|<local>/x|"];
|
||||
99 [label="Access variable R|<local>/x|"];
|
||||
100 [label="Smart cast: R|<local>/x|"];
|
||||
101 [label="Access variable R|kotlin/String.length|"];
|
||||
102 [label="Postponed enter to lambda"];
|
||||
subgraph cluster_22 {
|
||||
color=blue
|
||||
115 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
113 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
subgraph cluster_23 {
|
||||
color=blue
|
||||
116 [label="Enter block"];
|
||||
117 [label="Const: Int(1)"];
|
||||
118 [label="Assignment: R|<local>/x|"];
|
||||
119 [label="Exit block"];
|
||||
114 [label="Enter block"];
|
||||
115 [label="Const: Int(1)"];
|
||||
116 [label="Assignment: R|<local>/x|"];
|
||||
117 [label="Exit block"];
|
||||
}
|
||||
120 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
118 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
}
|
||||
105 [label="Postponed exit from lambda"];
|
||||
106 [label="Function call: R|/atMostOnce|(...)"];
|
||||
107 [label="Access variable R|<local>/x|"];
|
||||
108 [label="Smart cast: R|<local>/x|"];
|
||||
109 [label="Access variable <Unresolved name: length>#"];
|
||||
110 [label="Access variable R|<local>/x|"];
|
||||
111 [label="Smart cast: R|<local>/x|"];
|
||||
112 [label="Function call: R|<local>/x|.<Unresolved name: inc>#()"];
|
||||
113 [label="Exit block"];
|
||||
103 [label="Postponed exit from lambda"];
|
||||
104 [label="Function call: R|/atMostOnce|(...)"];
|
||||
105 [label="Access variable R|<local>/x|"];
|
||||
106 [label="Smart cast: R|<local>/x|"];
|
||||
107 [label="Access variable <Unresolved name: length>#"];
|
||||
108 [label="Access variable R|<local>/x|"];
|
||||
109 [label="Smart cast: R|<local>/x|"];
|
||||
110 [label="Function call: R|<local>/x|.<Unresolved name: inc>#()"];
|
||||
111 [label="Exit block"];
|
||||
}
|
||||
114 [label="Exit function test4" style="filled" fillcolor=red];
|
||||
112 [label="Exit function test4" style="filled" fillcolor=red];
|
||||
}
|
||||
94 -> {95};
|
||||
95 -> {96};
|
||||
96 -> {97};
|
||||
97 -> {98};
|
||||
98 -> {99};
|
||||
99 -> {100};
|
||||
100 -> {101};
|
||||
101 -> {102};
|
||||
102 -> {103};
|
||||
102 -> {103 113};
|
||||
102 -> {113} [style=dashed];
|
||||
103 -> {104};
|
||||
104 -> {105 115};
|
||||
104 -> {115} [style=dashed];
|
||||
104 -> {105};
|
||||
105 -> {106};
|
||||
106 -> {107};
|
||||
107 -> {108};
|
||||
@@ -319,13 +317,11 @@ digraph flowFromInplaceLambda3_kt {
|
||||
109 -> {110};
|
||||
110 -> {111};
|
||||
111 -> {112};
|
||||
112 -> {113};
|
||||
113 -> {114};
|
||||
115 -> {120 116};
|
||||
113 -> {118 114};
|
||||
114 -> {115};
|
||||
115 -> {116};
|
||||
116 -> {117};
|
||||
117 -> {118};
|
||||
118 -> {119};
|
||||
119 -> {120};
|
||||
120 -> {105};
|
||||
118 -> {103};
|
||||
|
||||
}
|
||||
|
||||
+96
-178
@@ -237,9 +237,9 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
// All non-lambda function are treated as concurrent since we do not make any assumption about when and how it's invoked.
|
||||
getOrCreateLocalVariableAssignmentAnalyzer(function)?.enterLocalFunction(function)
|
||||
|
||||
val (functionEnterNode, localFunctionNode, previousNode) = graphBuilder.enterFunction(function)
|
||||
val (functionEnterNode, localFunctionNode) = graphBuilder.enterFunction(function)
|
||||
localFunctionNode?.mergeIncomingFlow()
|
||||
functionEnterNode.mergeIncomingFlow(shouldForkFlow = previousNode != null)
|
||||
functionEnterNode.mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun exitFunction(function: FirFunction): FirControlFlowGraphReference? {
|
||||
@@ -262,8 +262,9 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
|
||||
if (graphBuilder.isTopLevel()) {
|
||||
context.reset()
|
||||
} else {
|
||||
logicSystem.updateAllReceivers(graph.enterNode.flow)
|
||||
}
|
||||
logicSystem.updateAllReceivers(graph.enterNode.computeIncomingFlow().first)
|
||||
return FirControlFlowGraphReferenceImpl(graph, DataFlowInfo(variableStorage, flowOnNodes))
|
||||
}
|
||||
|
||||
@@ -275,9 +276,8 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
enterLocalFunction(anonymousFunction)
|
||||
}
|
||||
val (postponedLambdaEnterNode, functionEnterNode) = graphBuilder.enterAnonymousFunction(anonymousFunction)
|
||||
// TODO: questionable
|
||||
postponedLambdaEnterNode?.mergeIncomingFlow()
|
||||
functionEnterNode.mergeIncomingFlow(shouldForkFlow = true)
|
||||
val flowOnEntry = functionEnterNode.mergeIncomingFlow()
|
||||
when (anonymousFunction.invocationKind) {
|
||||
EventOccurrencesRange.AT_LEAST_ONCE,
|
||||
EventOccurrencesRange.MORE_THAN_ONCE,
|
||||
@@ -285,7 +285,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
enterCapturingStatement(functionEnterNode, anonymousFunction)
|
||||
else -> {}
|
||||
}
|
||||
logicSystem.updateAllReceivers(functionEnterNode.flow)
|
||||
logicSystem.updateAllReceivers(flowOnEntry)
|
||||
}
|
||||
|
||||
private fun exitAnonymousFunction(anonymousFunction: FirAnonymousFunction): FirControlFlowGraphReference {
|
||||
@@ -300,10 +300,9 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
exitCapturingStatement(anonymousFunction)
|
||||
else -> {}
|
||||
}
|
||||
// TODO: questionable
|
||||
functionExitNode.mergeIncomingFlow()
|
||||
postponedLambdaExitNode?.mergeIncomingFlow()
|
||||
logicSystem.updateAllReceivers(graph.enterNode.computeIncomingFlow().first)
|
||||
logicSystem.updateAllReceivers(graph.enterNode.flow)
|
||||
return FirControlFlowGraphReferenceImpl(graph)
|
||||
}
|
||||
|
||||
@@ -313,7 +312,6 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
val (enterNode, exitNode) = graphBuilder.visitPostponedAnonymousFunction(anonymousFunctionExpression)
|
||||
enterNode.mergeIncomingFlow()
|
||||
exitNode.mergeIncomingFlow()
|
||||
enterNode.flow = enterNode.flow.fork()
|
||||
}
|
||||
|
||||
fun exitAnonymousFunctionExpression(anonymousFunctionExpression: FirAnonymousFunctionExpression) {
|
||||
@@ -359,7 +357,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
// ----------------------------------- Value parameters (and it's defaults) -----------------------------------
|
||||
|
||||
fun enterValueParameter(valueParameter: FirValueParameter) {
|
||||
graphBuilder.enterValueParameter(valueParameter)?.mergeIncomingFlow(shouldForkFlow = true)
|
||||
graphBuilder.enterValueParameter(valueParameter)?.mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun exitValueParameter(valueParameter: FirValueParameter): ControlFlowGraph? {
|
||||
@@ -415,11 +413,11 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
// ----------------------------------- Operator call -----------------------------------
|
||||
|
||||
fun exitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall) {
|
||||
val node = graphBuilder.exitTypeOperatorCall(typeOperatorCall).mergeIncomingFlow()
|
||||
val node = graphBuilder.exitTypeOperatorCall(typeOperatorCall)
|
||||
val flow = node.mergeIncomingFlow()
|
||||
if (typeOperatorCall.operation !in FirOperation.TYPES) return
|
||||
val type = typeOperatorCall.conversionTypeRef.coneType
|
||||
val operandVariable = variableStorage.getOrCreateVariable(node.previousFlow, typeOperatorCall.argument)
|
||||
val flow = node.flow
|
||||
|
||||
when (val operation = typeOperatorCall.operation) {
|
||||
FirOperation.IS, FirOperation.NOT_IS -> {
|
||||
@@ -446,7 +444,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
flow.addTypeStatement(operandVariable typeEq type)
|
||||
}
|
||||
if (!type.canBeNull) {
|
||||
flow.assumeNotNull(operandVariable, shouldForkFlow = false, shouldRemoveSynthetics = true)
|
||||
flow.assumeNotNull(operandVariable, shouldRemoveSynthetics = true)
|
||||
} else {
|
||||
val expressionVariable = variableStorage.createSyntheticVariable(typeOperatorCall)
|
||||
flow.addImplication((expressionVariable notEq null) implies (operandVariable notEq null))
|
||||
@@ -471,7 +469,8 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
}
|
||||
|
||||
fun exitEqualityOperatorCall(equalityOperatorCall: FirEqualityOperatorCall) {
|
||||
val node = graphBuilder.exitEqualityOperatorCall(equalityOperatorCall).mergeIncomingFlow()
|
||||
val node = graphBuilder.exitEqualityOperatorCall(equalityOperatorCall)
|
||||
node.mergeIncomingFlow()
|
||||
val operation = equalityOperatorCall.operation
|
||||
val leftOperand = equalityOperatorCall.arguments[0]
|
||||
val rightOperand = equalityOperatorCall.arguments[1]
|
||||
@@ -669,20 +668,17 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
fun exitCheckNotNullCall(checkNotNullCall: FirCheckNotNullCall, callCompleted: Boolean) {
|
||||
// Add `Any` to the set of possible types; the intersection type `T? & Any` will be reduced to `T` after smartcast.
|
||||
val (node, unionNode) = graphBuilder.exitCheckNotNullCall(checkNotNullCall, callCompleted)
|
||||
node.mergeIncomingFlow()
|
||||
|
||||
val argumentVariable = variableStorage.getOrCreateVariable(node.previousFlow, checkNotNullCall.argument)
|
||||
node.flow.assumeNotNull(argumentVariable, shouldForkFlow = false, shouldRemoveSynthetics = false)
|
||||
|
||||
unionNode?.let { unionFlowFromArguments(it) }
|
||||
node.mergeIncomingFlow().assumeNotNull(argumentVariable, shouldRemoveSynthetics = false)
|
||||
unionNode?.unionFlowFromArguments()
|
||||
}
|
||||
|
||||
private fun FLOW.assumeNotNull(variable: DataFlowVariable, shouldForkFlow: Boolean, shouldRemoveSynthetics: Boolean): FLOW =
|
||||
logicSystem.approveStatementsInsideFlow(this, variable notEq null, shouldForkFlow, shouldRemoveSynthetics).also {
|
||||
if (variable is RealVariable) {
|
||||
it.addTypeStatement(variable typeEq any)
|
||||
}
|
||||
private fun FLOW.assumeNotNull(variable: DataFlowVariable, shouldRemoveSynthetics: Boolean) {
|
||||
logicSystem.commitOperationStatement(this, variable notEq null, shouldRemoveSynthetics)
|
||||
if (variable is RealVariable) {
|
||||
addTypeStatement(variable typeEq any)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- When -----------------------------------
|
||||
|
||||
@@ -691,32 +687,23 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
}
|
||||
|
||||
fun enterWhenBranchCondition(whenBranch: FirWhenBranch) {
|
||||
val node = graphBuilder.enterWhenBranchCondition(whenBranch).mergeIncomingFlow(updateReceivers = true)
|
||||
val node = graphBuilder.enterWhenBranchCondition(whenBranch)
|
||||
val flow = node.mergeIncomingFlow(updateReceivers = true)
|
||||
val previousNode = node.previousNodes.single()
|
||||
if (previousNode is WhenBranchConditionExitNode) {
|
||||
val conditionVariable = context.variablesForWhenConditions.remove(previousNode)!!
|
||||
node.flow = logicSystem.approveStatementsInsideFlow(
|
||||
node.flow,
|
||||
conditionVariable eq false,
|
||||
shouldForkFlow = true,
|
||||
shouldRemoveSynthetics = true
|
||||
)
|
||||
logicSystem.commitOperationStatement(flow, conditionVariable eq false, shouldRemoveSynthetics = true)
|
||||
}
|
||||
}
|
||||
|
||||
fun exitWhenBranchCondition(whenBranch: FirWhenBranch) {
|
||||
val (conditionExitNode, branchEnterNode) = graphBuilder.exitWhenBranchCondition(whenBranch)
|
||||
conditionExitNode.mergeIncomingFlow()
|
||||
|
||||
val conditionExitFlow = conditionExitNode.flow
|
||||
val conditionExitFlow = conditionExitNode.mergeIncomingFlow()
|
||||
val conditionVariable = variableStorage.getOrCreateVariable(conditionExitFlow, whenBranch.condition)
|
||||
context.variablesForWhenConditions[conditionExitNode] = conditionVariable
|
||||
branchEnterNode.flow = logicSystem.approveStatementsInsideFlow(
|
||||
conditionExitFlow,
|
||||
conditionVariable eq true,
|
||||
shouldForkFlow = true,
|
||||
shouldRemoveSynthetics = false
|
||||
)
|
||||
branchEnterNode.flow = conditionExitFlow.fork().also {
|
||||
logicSystem.commitOperationStatement(it, conditionVariable eq true, shouldRemoveSynthetics = false)
|
||||
}
|
||||
}
|
||||
|
||||
fun exitWhenBranchResult(whenBranch: FirWhenBranch) {
|
||||
@@ -731,12 +718,9 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
// in that case there will be when enter or subject access node
|
||||
if (previousConditionExitNode != null) {
|
||||
val conditionVariable = context.variablesForWhenConditions.remove(previousConditionExitNode)!!
|
||||
syntheticElseNode.flow = logicSystem.approveStatementsInsideFlow(
|
||||
previousConditionExitNode.flow,
|
||||
conditionVariable eq false,
|
||||
shouldForkFlow = true,
|
||||
shouldRemoveSynthetics = true
|
||||
)
|
||||
syntheticElseNode.flow = previousConditionExitNode.flow.fork().also {
|
||||
logicSystem.commitOperationStatement(it, conditionVariable eq false, shouldRemoveSynthetics = true)
|
||||
}
|
||||
} else {
|
||||
syntheticElseNode.mergeIncomingFlow()
|
||||
}
|
||||
@@ -755,12 +739,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
val singlePreviousNode = exitNode.previousNodes.singleOrNull { !it.isDead }
|
||||
if (singlePreviousNode is LoopConditionExitNode) {
|
||||
val variable = variableStorage.getOrCreateVariable(exitNode.previousFlow, singlePreviousNode.fir)
|
||||
exitNode.flow = logicSystem.approveStatementsInsideFlow(
|
||||
exitNode.flow,
|
||||
variable eq false,
|
||||
shouldForkFlow = false,
|
||||
shouldRemoveSynthetics = true
|
||||
)
|
||||
logicSystem.commitOperationStatement(exitNode.flow, variable eq false, shouldRemoveSynthetics = true)
|
||||
}
|
||||
exitCapturingStatement(exitNode.fir)
|
||||
}
|
||||
@@ -774,16 +753,13 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
|
||||
fun exitWhileLoopCondition(loop: FirLoop) {
|
||||
val (loopConditionExitNode, loopBlockEnterNode) = graphBuilder.exitWhileLoopCondition(loop)
|
||||
loopConditionExitNode.mergeIncomingFlow()
|
||||
val conditionExitFlow = loopConditionExitNode.flow
|
||||
loopBlockEnterNode.flow = variableStorage.getVariable(conditionExitFlow, loop.condition)?.let { conditionVariable ->
|
||||
logicSystem.approveStatementsInsideFlow(
|
||||
conditionExitFlow,
|
||||
conditionVariable eq true,
|
||||
shouldForkFlow = true,
|
||||
shouldRemoveSynthetics = false
|
||||
)
|
||||
} ?: logicSystem.forkFlow(conditionExitFlow)
|
||||
val conditionExitFlow = loopConditionExitNode.mergeIncomingFlow()
|
||||
loopBlockEnterNode.flow = conditionExitFlow.fork().also {
|
||||
val conditionVariable = variableStorage.getVariable(conditionExitFlow, loop.condition)
|
||||
if (conditionVariable != null) {
|
||||
logicSystem.commitOperationStatement(it, conditionVariable eq true, shouldRemoveSynthetics = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun exitWhileLoop(loop: FirLoop) {
|
||||
@@ -838,9 +814,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
fun enterTryExpression(tryExpression: FirTryExpression) {
|
||||
val (tryExpressionEnterNode, tryMainBlockEnterNode) = graphBuilder.enterTryExpression(tryExpression)
|
||||
tryExpressionEnterNode.mergeIncomingFlow()
|
||||
// NB: fork to isolate effects inside the try main block
|
||||
// Otherwise, changes in the try main block could affect the try expression enter node as well as its previous nodes.
|
||||
tryMainBlockEnterNode.mergeIncomingFlow(shouldForkFlow = true)
|
||||
tryMainBlockEnterNode.mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun exitTryMainBlock() {
|
||||
@@ -848,9 +822,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
}
|
||||
|
||||
fun enterCatchClause(catch: FirCatch) {
|
||||
// NB: fork to isolate effects inside the catch clause
|
||||
// Otherwise, changes in the catch clause could affect the previous node: try main block.
|
||||
graphBuilder.enterCatchClause(catch).mergeIncomingFlow(updateReceivers = true, shouldForkFlow = true)
|
||||
graphBuilder.enterCatchClause(catch).mergeIncomingFlow(updateReceivers = true)
|
||||
}
|
||||
|
||||
fun exitCatchClause(catch: FirCatch) {
|
||||
@@ -858,9 +830,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
}
|
||||
|
||||
fun enterFinallyBlock() {
|
||||
// NB: fork to isolate effects inside the finally block
|
||||
// Otherwise, changes in the finally block could affect the previous nodes: try main block and catch clauses.
|
||||
graphBuilder.enterFinallyBlock().mergeIncomingFlow(updateReceivers = true, shouldForkFlow = true)
|
||||
graphBuilder.enterFinallyBlock().mergeIncomingFlow(updateReceivers = true)
|
||||
}
|
||||
|
||||
fun exitFinallyBlock() {
|
||||
@@ -869,10 +839,8 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
|
||||
fun exitTryExpression(callCompleted: Boolean) {
|
||||
val (tryExpressionExitNode, unionNode) = graphBuilder.exitTryExpression(callCompleted)
|
||||
// NB: fork to prevent effects after the try expression from being flown into the try expression
|
||||
// Otherwise, changes in any following nodes could affect the previous nodes, including try main block and finally block if any.
|
||||
tryExpressionExitNode.mergeIncomingFlow(shouldForkFlow = true)
|
||||
unionNode?.let { unionFlowFromArguments(it) }
|
||||
tryExpressionExitNode.mergeIncomingFlow()
|
||||
unionNode?.unionFlowFromArguments()
|
||||
}
|
||||
|
||||
// ----------------------------------- Resolvable call -----------------------------------
|
||||
@@ -890,48 +858,31 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
}
|
||||
|
||||
fun enterSafeCallAfterNullCheck(safeCall: FirSafeCallExpression) {
|
||||
val node = graphBuilder.enterSafeCall(safeCall).mergeIncomingFlow()
|
||||
val previousNode = node.firstPreviousNode
|
||||
val shouldFork: Boolean
|
||||
var flow = if (previousNode is ExitSafeCallNode) {
|
||||
shouldFork = false
|
||||
previousNode.secondPreviousNode?.flow ?: node.flow
|
||||
} else {
|
||||
shouldFork = true
|
||||
node.flow
|
||||
val node = graphBuilder.enterSafeCall(safeCall)
|
||||
val flow = node.mergeIncomingFlow()
|
||||
// When calling `c` in `a?.b?.c`, all type information obtained after calling `b` is valid as we know `a`
|
||||
// is non-null. In theory, this should be unnecessary if the TODOs below are implemented.
|
||||
val flowFromPreviousSafeCall = (node.firstPreviousNode as? ExitSafeCallNode)?.lastNodeInNotNullCase?.flow
|
||||
if (flowFromPreviousSafeCall != null) {
|
||||
logicSystem.copyAllInformation(flowFromPreviousSafeCall, flow)
|
||||
}
|
||||
|
||||
safeCall.receiver.let { receiver ->
|
||||
val type = receiver.coneType.takeIf { it.isMarkedNullable }
|
||||
?.makeConeTypeDefinitelyNotNullOrNotNull(components.session.typeContext)
|
||||
?: return@let
|
||||
|
||||
val variable = variableStorage.getOrCreateVariable(flow, receiver)
|
||||
if (variable.isReal()) {
|
||||
if (shouldFork) {
|
||||
flow = logicSystem.forkFlow(flow)
|
||||
}
|
||||
flow.addTypeStatement(variable typeEq type)
|
||||
}
|
||||
flow = flow.assumeNotNull(variable, shouldFork, shouldRemoveSynthetics = false)
|
||||
}
|
||||
|
||||
node.flow = flow
|
||||
val receiverVariable = variableStorage.getOrCreateVariable(node.flow, safeCall.receiver)
|
||||
flow.assumeNotNull(receiverVariable, shouldRemoveSynthetics = true)
|
||||
}
|
||||
|
||||
fun exitSafeCall(safeCall: FirSafeCallExpression) {
|
||||
val (node, mergePostponedLambdaExitsNode) = graphBuilder.exitSafeCall()
|
||||
val flow = node.mergeIncomingFlow().flow
|
||||
val flow = node.mergeIncomingFlow()
|
||||
mergePostponedLambdaExitsNode?.mergeIncomingFlow()
|
||||
|
||||
val variable = variableStorage.getOrCreateVariable(flow, safeCall)
|
||||
val receiverVariable = when (variable) {
|
||||
// There is some bug with invokes. See KT-36014
|
||||
is RealVariable -> variable.explicitReceiverVariable ?: return
|
||||
is SyntheticVariable -> variableStorage.getOrCreateVariable(flow, safeCall.receiver)
|
||||
}
|
||||
val receiverVariable = variableStorage.getOrCreateVariable(flow, safeCall.receiver)
|
||||
// TODO? if the callee has non-null return type, then (variable eq null) implies (receiverVariable eq null)
|
||||
// if (x?.toString() == null) { /* x == null */ }
|
||||
// TODO? all new statements in previous node's flow are valid here if receiverVariable != null
|
||||
// if (x?.whatever(y as String) != null) { /* y is String */ }
|
||||
// TODO? all new implications in previous node's flow are valid here if receiverVariable != null
|
||||
// (that requires a second level of implications: receiverVariable != null => condition => effect).
|
||||
flow.addImplication((variable notEq null) implies (receiverVariable notEq null))
|
||||
}
|
||||
|
||||
@@ -968,7 +919,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
return
|
||||
}
|
||||
val (functionCallNode, unionNode) = graphBuilder.exitFunctionCall(functionCall, callCompleted)
|
||||
unionNode?.let { unionFlowFromArguments(it) }
|
||||
unionNode?.unionFlowFromArguments()
|
||||
functionCallNode.mergeIncomingFlow()
|
||||
if (functionCall.isBooleanNot()) {
|
||||
exitBooleanNot(functionCall, functionCallNode)
|
||||
@@ -978,19 +929,18 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
|
||||
fun exitDelegatedConstructorCall(call: FirDelegatedConstructorCall, callCompleted: Boolean) {
|
||||
val (callNode, unionNode) = graphBuilder.exitDelegatedConstructorCall(call, callCompleted)
|
||||
unionNode?.let { unionFlowFromArguments(it) }
|
||||
unionNode?.unionFlowFromArguments()
|
||||
callNode.mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun exitStringConcatenationCall(call: FirStringConcatenationCall) {
|
||||
val (callNode, unionNode) = graphBuilder.exitStringConcatenationCall(call)
|
||||
unionNode?.let { unionFlowFromArguments(it) }
|
||||
unionNode?.unionFlowFromArguments()
|
||||
callNode.mergeIncomingFlow()
|
||||
}
|
||||
|
||||
|
||||
private fun unionFlowFromArguments(node: UnionFunctionCallArgumentsNode) {
|
||||
node.flow = logicSystem.unionFlow(node.previousNodes.map { it.flow }).also {
|
||||
private fun UnionFunctionCallArgumentsNode.unionFlowFromArguments() {
|
||||
flow = logicSystem.unionFlow(previousNodes.map { it.flow }).also {
|
||||
logicSystem.updateAllReceivers(it)
|
||||
}
|
||||
}
|
||||
@@ -1037,12 +987,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
val lastNode = graphBuilder.lastNode
|
||||
when (val value = effect.value) {
|
||||
ConeConstantReference.WILDCARD -> {
|
||||
lastNode.flow = logicSystem.approveStatementsInsideFlow(
|
||||
lastNode.flow,
|
||||
argumentVariable eq true,
|
||||
shouldForkFlow = false,
|
||||
shouldRemoveSynthetics = true
|
||||
)
|
||||
logicSystem.commitOperationStatement(lastNode.flow, argumentVariable eq true, shouldRemoveSynthetics = true)
|
||||
}
|
||||
|
||||
is ConeBooleanConstantReference -> {
|
||||
@@ -1084,34 +1029,33 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
}
|
||||
|
||||
fun exitLocalVariableDeclaration(variable: FirProperty, hadExplicitType: Boolean) {
|
||||
val node = graphBuilder.exitVariableDeclaration(variable).mergeIncomingFlow()
|
||||
val flow = graphBuilder.exitVariableDeclaration(variable).mergeIncomingFlow()
|
||||
val initializer = variable.initializer ?: return
|
||||
exitVariableInitialization(node, initializer, variable, assignment = null, hadExplicitType)
|
||||
exitVariableInitialization(flow, initializer, variable, assignment = null, hadExplicitType)
|
||||
}
|
||||
|
||||
fun exitVariableAssignment(assignment: FirVariableAssignment) {
|
||||
val node = graphBuilder.exitVariableAssignment(assignment).mergeIncomingFlow()
|
||||
val flow = graphBuilder.exitVariableAssignment(assignment).mergeIncomingFlow()
|
||||
val property = assignment.lValue.resolvedSymbol?.fir as? FirProperty ?: return
|
||||
if (property.isLocal || property.isVal) {
|
||||
exitVariableInitialization(node, assignment.rValue, property, assignment, hasExplicitType = false)
|
||||
exitVariableInitialization(flow, assignment.rValue, property, assignment, hasExplicitType = false)
|
||||
} else {
|
||||
// TODO: add unstable smartcast for non-local var
|
||||
val variable = variableStorage.getRealVariableWithoutUnwrappingAlias(node.flow, property.symbol, assignment)
|
||||
val variable = variableStorage.getRealVariableWithoutUnwrappingAlias(flow, property.symbol, assignment)
|
||||
if (variable != null) {
|
||||
logicSystem.recordNewAssignment(node.flow, variable, context.newAssignmentIndex())
|
||||
logicSystem.recordNewAssignment(flow, variable, context.newAssignmentIndex())
|
||||
}
|
||||
}
|
||||
processConditionalContract(assignment)
|
||||
}
|
||||
|
||||
private fun exitVariableInitialization(
|
||||
node: CFGNode<*>,
|
||||
flow: FLOW,
|
||||
initializer: FirExpression,
|
||||
property: FirProperty,
|
||||
assignment: FirVariableAssignment?,
|
||||
hasExplicitType: Boolean,
|
||||
) {
|
||||
val flow = node.flow
|
||||
val propertyVariable = variableStorage.getOrCreateRealVariableWithoutUnwrappingAlias(
|
||||
flow,
|
||||
property.symbol,
|
||||
@@ -1198,14 +1142,11 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
|
||||
private fun exitLeftArgumentOfBinaryBooleanOperator(leftNode: CFGNode<*>, rightNode: CFGNode<*>, isAnd: Boolean) {
|
||||
val parentFlow = leftNode.firstPreviousNode.flow
|
||||
leftNode.flow = logicSystem.forkFlow(parentFlow)
|
||||
val leftOperandVariable = variableStorage.getOrCreateVariable(parentFlow, leftNode.firstPreviousNode.fir)
|
||||
rightNode.flow = logicSystem.approveStatementsInsideFlow(
|
||||
parentFlow,
|
||||
leftOperandVariable eq isAnd,
|
||||
shouldForkFlow = true,
|
||||
shouldRemoveSynthetics = false
|
||||
)
|
||||
leftNode.flow = parentFlow.fork()
|
||||
rightNode.flow = parentFlow.fork().also {
|
||||
logicSystem.commitOperationStatement(it, leftOperandVariable eq isAnd, shouldRemoveSynthetics = false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun exitBinaryBooleanOperator(
|
||||
@@ -1220,8 +1161,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
// Naming for all variables was chosen in assumption that we processing && expression
|
||||
val flowFromLeft = node.leftOperandNode.flow
|
||||
val flowFromRight = node.rightOperandNode.flow
|
||||
|
||||
val flow = node.mergeIncomingFlow().flow
|
||||
val flow = node.mergeIncomingFlow()
|
||||
|
||||
/*
|
||||
* TODO: Here we should handle case when one of arguments is dead (e.g. in cases `false && expr` or `true || expr`)
|
||||
@@ -1234,12 +1174,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
if (!node.leftOperandNode.isDead && node.rightOperandNode.isDead) {
|
||||
// If the right operand does not terminate, then we know that the value of the entire expression
|
||||
// has to be `onlyLeftEvaluated`, and it has to be produced by the left operand.
|
||||
logicSystem.approveStatementsInsideFlow(
|
||||
flow,
|
||||
leftVariable eq onlyLeftEvaluated,
|
||||
shouldForkFlow = false,
|
||||
shouldRemoveSynthetics = true
|
||||
)
|
||||
logicSystem.commitOperationStatement(flow, leftVariable eq onlyLeftEvaluated, shouldRemoveSynthetics = true)
|
||||
} else {
|
||||
val (conditionalFromLeft, conditionalFromRight, approvedFromRight) =
|
||||
logicSystem.collectInfoForBooleanOperator(flowFromLeft, leftVariable, flowFromRight, rightVariable)
|
||||
@@ -1299,7 +1234,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
fun enterInitBlock(initBlock: FirAnonymousInitializer) {
|
||||
graphBuilder.enterInitBlock(initBlock).let { (node, prevNode) ->
|
||||
if (prevNode != null) {
|
||||
node.flow = logicSystem.forkFlow(prevNode.flow)
|
||||
node.flow = prevNode.flow.fork()
|
||||
} else {
|
||||
node.mergeIncomingFlow()
|
||||
}
|
||||
@@ -1330,27 +1265,22 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
|
||||
fun exitElvisLhs(elvisExpression: FirElvisExpression) {
|
||||
val (lhsExitNode, lhsIsNotNullNode, rhsEnterNode) = graphBuilder.exitElvisLhs(elvisExpression)
|
||||
lhsExitNode.mergeIncomingFlow()
|
||||
val flow = lhsExitNode.flow
|
||||
val flow = lhsExitNode.mergeIncomingFlow()
|
||||
val lhsVariable = variableStorage.getOrCreateVariable(flow, elvisExpression.lhs)
|
||||
lhsIsNotNullNode.flow = flow.assumeNotNull(lhsVariable, shouldForkFlow = true, shouldRemoveSynthetics = false)
|
||||
rhsEnterNode.flow = logicSystem.approveStatementsInsideFlow(
|
||||
flow,
|
||||
lhsVariable eq null,
|
||||
shouldForkFlow = true,
|
||||
shouldRemoveSynthetics = false
|
||||
).also {
|
||||
lhsIsNotNullNode.flow = flow.fork().also { it.assumeNotNull(lhsVariable, shouldRemoveSynthetics = false) }
|
||||
rhsEnterNode.flow = flow.fork().also {
|
||||
logicSystem.commitOperationStatement(it, lhsVariable eq null, shouldRemoveSynthetics = false)
|
||||
logicSystem.updateAllReceivers(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun exitElvis(elvisExpression: FirElvisExpression, isLhsNotNull: Boolean) {
|
||||
val (node, mergePostponedLambdaExitsNode) = graphBuilder.exitElvis()
|
||||
node.mergeIncomingFlow()
|
||||
val flow = node.mergeIncomingFlow()
|
||||
mergePostponedLambdaExitsNode?.mergeIncomingFlow()
|
||||
if (isLhsNotNull) {
|
||||
val lhsVariable = variableStorage.getOrCreateVariable(node.previousFlow, elvisExpression.lhs)
|
||||
node.flow.assumeNotNull(lhsVariable, shouldForkFlow = false, shouldRemoveSynthetics = true)
|
||||
flow.assumeNotNull(lhsVariable, shouldRemoveSynthetics = true)
|
||||
}
|
||||
|
||||
if (!components.session.languageVersionSettings.supportsFeature(LanguageFeature.BooleanElvisBoundSmartCasts)) return
|
||||
@@ -1359,7 +1289,6 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
val lhs = elvisExpression.lhs
|
||||
if (lhs.typeRef.coneType.classId != StandardClassIds.Boolean) return
|
||||
|
||||
val flow = node.flow
|
||||
// a ?: false == true -> a != null
|
||||
// a ?: true == false -> a != null
|
||||
val elvisVariable = variableStorage.getOrCreateVariable(flow, elvisExpression)
|
||||
@@ -1400,38 +1329,27 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
|
||||
private val CFGNode<*>.origin: CFGNode<*> get() = if (this is StubNode) firstPreviousNode else this
|
||||
|
||||
private fun <T : CFGNode<*>> T.computeIncomingFlow(): Pair<FLOW, Int> {
|
||||
private fun CFGNode<*>.mergeIncomingFlow(
|
||||
// This flag should be set true if we're changing flow branches from one to another (e.g. in when, try->catch)
|
||||
updateReceivers: Boolean = false
|
||||
): FLOW {
|
||||
val previousFlows = mutableListOf<FLOW>()
|
||||
var deadForwardCount = 0
|
||||
for (previousNode in previousNodes) {
|
||||
val incomingEdgeKind = incomingEdges.getValue(previousNode).kind
|
||||
|
||||
if (isDead && incomingEdgeKind.usedInDeadDfa) {
|
||||
previousFlows += previousNode.flow
|
||||
} else if (incomingEdgeKind.usedInDfa) {
|
||||
previousFlows += previousNode.flow
|
||||
}
|
||||
|
||||
if (incomingEdgeKind == EdgeKind.DeadForward) {
|
||||
deadForwardCount++
|
||||
}
|
||||
if (incomingEdgeKind.usedInDfa || (isDead && incomingEdgeKind.usedInDeadDfa)) {
|
||||
previousFlows += previousNode.flow
|
||||
}
|
||||
}
|
||||
return logicSystem.joinFlow(previousFlows) to (previousFlows.size + deadForwardCount)
|
||||
}
|
||||
|
||||
private fun <T : CFGNode<*>> T.mergeIncomingFlow(
|
||||
// This flag should be set true if we're changing flow branches from one to another (e.g. in when, try->catch)
|
||||
updateReceivers: Boolean = false,
|
||||
shouldForkFlow: Boolean = false
|
||||
): T = this.also { node ->
|
||||
var (flow, incomingEdges) = computeIncomingFlow()
|
||||
if (updateReceivers || incomingEdges > 1) {
|
||||
logicSystem.updateAllReceivers(flow)
|
||||
return logicSystem.joinFlow(previousFlows).also {
|
||||
flow = it
|
||||
if (updateReceivers || previousFlows.size + deadForwardCount > 1) {
|
||||
logicSystem.updateAllReceivers(it)
|
||||
}
|
||||
}
|
||||
if (shouldForkFlow) {
|
||||
flow = flow.fork()
|
||||
}
|
||||
node.flow = flow
|
||||
}
|
||||
|
||||
private fun FLOW.addImplication(statement: Implication) {
|
||||
|
||||
+2
-2
@@ -182,7 +182,7 @@ class ControlFlowGraphBuilder {
|
||||
|
||||
// ----------------------------------- Regular function -----------------------------------
|
||||
|
||||
fun enterFunction(function: FirFunction): Triple<FunctionEnterNode, LocalFunctionDeclarationNode?, CFGNode<*>?> {
|
||||
fun enterFunction(function: FirFunction): Pair<FunctionEnterNode, LocalFunctionDeclarationNode?> {
|
||||
require(function !is FirAnonymousFunction)
|
||||
val name = when (function) {
|
||||
is FirSimpleFunction -> function.name.asString()
|
||||
@@ -226,7 +226,7 @@ class ControlFlowGraphBuilder {
|
||||
exitTargetsForTry.push(it)
|
||||
}
|
||||
|
||||
return Triple(enterNode, localFunctionNode, previousNode)
|
||||
return Pair(enterNode, localFunctionNode)
|
||||
}
|
||||
|
||||
fun exitFunction(function: FirFunction): Pair<FunctionExitNode, ControlFlowGraph> {
|
||||
|
||||
@@ -30,17 +30,14 @@ abstract class LogicSystem<FLOW : Flow>(protected val context: ConeInferenceCont
|
||||
transform: (Implication) -> Implication? = { it },
|
||||
)
|
||||
|
||||
abstract fun approveStatementsInsideFlow(
|
||||
flow: FLOW,
|
||||
approvedStatement: OperationStatement,
|
||||
shouldForkFlow: Boolean,
|
||||
shouldRemoveSynthetics: Boolean,
|
||||
): FLOW
|
||||
abstract fun commitOperationStatement(flow: FLOW, statement: OperationStatement, shouldRemoveSynthetics: Boolean)
|
||||
|
||||
abstract fun addLocalVariableAlias(flow: FLOW, alias: RealVariable, underlyingVariable: RealVariable)
|
||||
|
||||
abstract fun recordNewAssignment(flow: FLOW, variable: RealVariable, index: Int)
|
||||
|
||||
abstract fun copyAllInformation(from: FLOW, to: FLOW)
|
||||
|
||||
protected abstract fun getImplicationsWithVariable(flow: FLOW, variable: DataFlowVariable): Collection<Implication>
|
||||
|
||||
protected abstract fun ConeKotlinType.isAcceptableForSmartcast(): Boolean
|
||||
|
||||
+45
-86
@@ -26,7 +26,6 @@ class PersistentFlow : Flow {
|
||||
var approvedTypeStatements: PersistentApprovedTypeStatements
|
||||
var logicStatements: PersistentImplications
|
||||
val level: Int
|
||||
var approvedTypeStatementsDiff: PersistentApprovedTypeStatements = persistentHashMapOf()
|
||||
|
||||
/*
|
||||
* val x = a
|
||||
@@ -75,12 +74,18 @@ class PersistentFlow : Flow {
|
||||
abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSystem<PersistentFlow>(context) {
|
||||
abstract val variableStorage: VariableStorageImpl
|
||||
|
||||
override fun createEmptyFlow(): PersistentFlow {
|
||||
return PersistentFlow()
|
||||
}
|
||||
override fun createEmptyFlow(): PersistentFlow =
|
||||
PersistentFlow()
|
||||
|
||||
override fun forkFlow(flow: PersistentFlow): PersistentFlow {
|
||||
return PersistentFlow(flow)
|
||||
override fun forkFlow(flow: PersistentFlow): PersistentFlow =
|
||||
PersistentFlow(flow)
|
||||
|
||||
override fun copyAllInformation(from: PersistentFlow, to: PersistentFlow) {
|
||||
to.approvedTypeStatements = from.approvedTypeStatements
|
||||
to.logicStatements = from.logicStatements
|
||||
to.directAliasMap = from.directAliasMap
|
||||
to.backwardsAliasMap = from.backwardsAliasMap
|
||||
to.assignmentIndex = from.assignmentIndex
|
||||
}
|
||||
|
||||
override fun joinFlow(flows: Collection<PersistentFlow>): PersistentFlow =
|
||||
@@ -92,10 +97,10 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
|
||||
private fun foldFlow(flows: Collection<PersistentFlow>, allExecute: Boolean): PersistentFlow {
|
||||
when (flows.size) {
|
||||
0 -> return createEmptyFlow()
|
||||
1 -> return flows.first()
|
||||
1 -> return forkFlow(flows.first())
|
||||
}
|
||||
|
||||
val commonFlow = flows.reduce(::lowestCommonFlow)
|
||||
val commonFlow = forkFlow(flows.reduce(::lowestCommonFlow))
|
||||
// Aliases that have occurred before branching should already be in `commonFlow`,
|
||||
// but it might be useful to also add aliases that happen in all branches, e.g.
|
||||
// the aliasing of `y` to `a.x` after `if (p) { y = a.x } else { y = a.x }`.
|
||||
@@ -137,9 +142,6 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
|
||||
}
|
||||
|
||||
commonFlow.approvedTypeStatements += statements
|
||||
if (commonFlow.previousFlow != null) {
|
||||
commonFlow.approvedTypeStatementsDiff += statements
|
||||
}
|
||||
|
||||
for ((alias, underlyingVariable) in commonAliases) {
|
||||
addLocalVariableAlias(commonFlow, alias, underlyingVariable)
|
||||
@@ -168,7 +170,6 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
|
||||
assert(variable !in backwardsAliasMap)
|
||||
assert(variable !in logicStatements)
|
||||
assert(variable !in approvedTypeStatements)
|
||||
assert(variable !in approvedTypeStatementsDiff)
|
||||
}
|
||||
// `variable.dependentVariables` is not separated by flow, so it may be non-empty if aliasing of this variable
|
||||
// was broken in another flow. However, in *this* flow dependent variables should have no information attached to them.
|
||||
@@ -194,7 +195,6 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
|
||||
}
|
||||
logicStatements = logicStatements.replaceVariable(variable, replacementOrNext)
|
||||
approvedTypeStatements = approvedTypeStatements.replaceVariable(variable, replacementOrNext)
|
||||
approvedTypeStatementsDiff = approvedTypeStatementsDiff.replaceVariable(variable, replacementOrNext)
|
||||
if (aliases != null) {
|
||||
backwardsAliasMap -= variable
|
||||
if (replacementOrNext != null) {
|
||||
@@ -214,24 +214,23 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
|
||||
}
|
||||
|
||||
override fun addTypeStatement(flow: PersistentFlow, statement: TypeStatement) {
|
||||
if (statement.isEmpty) return
|
||||
with(flow) {
|
||||
approvedTypeStatements = approvedTypeStatements.addTypeStatement(statement)
|
||||
if (previousFlow != null) {
|
||||
approvedTypeStatementsDiff = approvedTypeStatementsDiff.addTypeStatement(statement)
|
||||
}
|
||||
if (statement.variable.isThisReference) {
|
||||
processUpdatedReceiverVariable(flow, statement.variable)
|
||||
}
|
||||
if (statement.exactType.isEmpty()) return
|
||||
val variable = statement.variable
|
||||
val oldExactType = flow.approvedTypeStatements[variable]?.exactType
|
||||
val newExactType = oldExactType?.addAll(statement.exactType) ?: statement.exactType.toPersistentSet()
|
||||
if (newExactType === oldExactType) return
|
||||
flow.approvedTypeStatements = flow.approvedTypeStatements.put(variable, PersistentTypeStatement(variable, newExactType))
|
||||
if (variable.isThisReference) {
|
||||
processUpdatedReceiverVariable(flow, variable)
|
||||
}
|
||||
}
|
||||
|
||||
private fun PersistentApprovedTypeStatements.addTypeStatement(info: TypeStatement): PersistentApprovedTypeStatements =
|
||||
put(info.variable, { info.toPersistent() }, { and(listOf(it, info)).toPersistent() })
|
||||
|
||||
override fun addImplication(flow: PersistentFlow, implication: Implication) {
|
||||
if ((implication.effect as? TypeStatement)?.isEmpty == true) return
|
||||
if (implication.condition == implication.effect) return
|
||||
val effect = implication.effect
|
||||
if (effect == implication.condition) return
|
||||
if (effect is TypeStatement && (effect.isEmpty ||
|
||||
flow.approvedTypeStatements[effect.variable]?.exactType?.containsAll(effect.exactType) == true)
|
||||
) return
|
||||
val variable = implication.condition.variable
|
||||
flow.logicStatements = flow.logicStatements.put(variable, { persistentListOf(implication) }, { it + implication })
|
||||
}
|
||||
@@ -257,35 +256,10 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
|
||||
}
|
||||
}
|
||||
|
||||
override fun approveStatementsInsideFlow(
|
||||
flow: PersistentFlow,
|
||||
approvedStatement: OperationStatement,
|
||||
shouldForkFlow: Boolean,
|
||||
shouldRemoveSynthetics: Boolean
|
||||
): PersistentFlow {
|
||||
val approvedFacts = approveOperationStatementsInternal(
|
||||
flow,
|
||||
approvedStatement,
|
||||
initialStatements = null,
|
||||
shouldRemoveSynthetics
|
||||
)
|
||||
|
||||
val resultFlow = if (shouldForkFlow) forkFlow(flow) else flow
|
||||
if (approvedFacts.isEmpty) return resultFlow
|
||||
|
||||
val updatedReceivers = mutableSetOf<RealVariable>()
|
||||
approvedFacts.asMap().forEach { (variable, infos) ->
|
||||
if (variable.isThisReference) {
|
||||
updatedReceivers += variable
|
||||
}
|
||||
addTypeStatement(resultFlow, and(infos))
|
||||
override fun commitOperationStatement(flow: PersistentFlow, statement: OperationStatement, shouldRemoveSynthetics: Boolean) {
|
||||
approveOperationStatementsInternal(flow, statement, initialStatements = null, shouldRemoveSynthetics).values.forEach {
|
||||
addTypeStatement(flow, it)
|
||||
}
|
||||
|
||||
updatedReceivers.forEach {
|
||||
processUpdatedReceiverVariable(resultFlow, it)
|
||||
}
|
||||
|
||||
return resultFlow
|
||||
}
|
||||
|
||||
private fun approveOperationStatementsInternal(
|
||||
@@ -293,55 +267,40 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
|
||||
approvedStatement: OperationStatement,
|
||||
initialStatements: Collection<Implication>?,
|
||||
shouldRemoveSynthetics: Boolean
|
||||
): ArrayListMultimap<RealVariable, TypeStatement> {
|
||||
val approvedFacts: ArrayListMultimap<RealVariable, TypeStatement> = ArrayListMultimap.create()
|
||||
val approvedStatements = LinkedList<OperationStatement>().apply { this += approvedStatement }
|
||||
approveOperationStatementsInternal(flow, approvedStatements, initialStatements, shouldRemoveSynthetics, approvedFacts)
|
||||
return approvedFacts
|
||||
}
|
||||
|
||||
private fun approveOperationStatementsInternal(
|
||||
flow: PersistentFlow,
|
||||
approvedStatements: LinkedList<OperationStatement>,
|
||||
initialStatements: Collection<Implication>?,
|
||||
shouldRemoveSynthetics: Boolean,
|
||||
approvedTypeStatements: ArrayListMultimap<RealVariable, TypeStatement>
|
||||
) {
|
||||
if (approvedStatements.isEmpty()) return
|
||||
val approvedOperationStatements = mutableSetOf<OperationStatement>()
|
||||
): TypeStatements {
|
||||
val approvedTypeStatements: ArrayListMultimap<RealVariable, TypeStatement> = ArrayListMultimap.create()
|
||||
val queue = LinkedList<OperationStatement>().apply { this += approvedStatement }
|
||||
val approved = mutableSetOf<OperationStatement>()
|
||||
var firstIteration = true
|
||||
while (approvedStatements.isNotEmpty()) {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val approvedStatement: OperationStatement = approvedStatements.removeFirst()
|
||||
while (queue.isNotEmpty()) {
|
||||
val next: OperationStatement = queue.removeFirst()
|
||||
// Defense from cycles in facts
|
||||
if (!approvedOperationStatements.add(approvedStatement)) {
|
||||
continue
|
||||
}
|
||||
if (!approved.add(next)) continue
|
||||
val variable = next.variable
|
||||
val statements = initialStatements?.takeIf { firstIteration }
|
||||
?: flow.logicStatements[approvedStatement.variable]?.takeIf { it.isNotEmpty() }
|
||||
?: flow.logicStatements[variable]?.takeIf { it.isNotEmpty() }
|
||||
?: continue
|
||||
if (shouldRemoveSynthetics && approvedStatement.variable.isSynthetic()) {
|
||||
flow.logicStatements -= approvedStatement.variable
|
||||
if (shouldRemoveSynthetics && variable.isSynthetic()) {
|
||||
flow.logicStatements -= variable
|
||||
}
|
||||
for (statement in statements) {
|
||||
if (statement.condition == approvedStatement) {
|
||||
if (statement.condition == next) {
|
||||
when (val effect = statement.effect) {
|
||||
is OperationStatement -> approvedStatements += effect
|
||||
is OperationStatement -> queue += effect
|
||||
is TypeStatement -> approvedTypeStatements.put(effect.variable, effect)
|
||||
}
|
||||
}
|
||||
}
|
||||
firstIteration = false
|
||||
}
|
||||
return approvedTypeStatements.asMap().mapValues { and(it.value) }
|
||||
}
|
||||
|
||||
override fun approveOperationStatement(
|
||||
flow: PersistentFlow,
|
||||
approvedStatement: OperationStatement,
|
||||
statementsForVariable: Collection<Implication>?
|
||||
): TypeStatements =
|
||||
approveOperationStatementsInternal(flow, approvedStatement, statementsForVariable, shouldRemoveSynthetics = false)
|
||||
.asMap().mapValues { and(it.value) }
|
||||
): TypeStatements = approveOperationStatementsInternal(flow, approvedStatement, statementsForVariable, shouldRemoveSynthetics = false)
|
||||
|
||||
override fun collectInfoForBooleanOperator(
|
||||
leftFlow: PersistentFlow,
|
||||
@@ -352,7 +311,7 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
|
||||
return InfoForBooleanOperator(
|
||||
leftFlow.logicStatements[leftVariable] ?: emptyList(),
|
||||
rightFlow.logicStatements[rightVariable] ?: emptyList(),
|
||||
rightFlow.approvedTypeStatementsDiff
|
||||
rightFlow.approvedTypeStatements
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -780,8 +780,7 @@ class EnterSafeCallNode(owner: ControlFlowGraph, override val fir: FirSafeCallEx
|
||||
}
|
||||
}
|
||||
class ExitSafeCallNode(owner: ControlFlowGraph, override val fir: FirSafeCallExpression, level: Int, id: Int) : CFGNode<FirSafeCallExpression>(owner, level, id) {
|
||||
val lastPreviousNode: CFGNode<*> get() = previousNodes.last()
|
||||
val secondPreviousNode: CFGNode<*>? get() = previousNodes.getOrNull(1)
|
||||
val lastNodeInNotNullCase: CFGNode<*>? get() = previousNodes.getOrNull(1)
|
||||
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitExitSafeCallNode(this, data)
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ fun calc(x: String?, y: String?): Int {
|
||||
// x is not null in condition but we do not see it yet
|
||||
} while (x.length > 0)
|
||||
// y is nullable because of break
|
||||
y.length
|
||||
y<!UNSAFE_CALL!>.<!>length
|
||||
// x is not null, at least in theory
|
||||
return x.length
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -7,5 +7,5 @@ fun test(foo: Foo?) {
|
||||
// Unnecessary
|
||||
foo<!UNNECESSARY_SAFE_CALL!>?.<!>bar?.length
|
||||
}
|
||||
foo.bar<!UNNECESSARY_SAFE_CALL!>?.<!>length
|
||||
foo.bar?.length
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ fun foo(y: String?) {
|
||||
if (x != null) {
|
||||
with(y?.let { x = null; it }) {
|
||||
this<!UNSAFE_CALL!>.<!>length
|
||||
x<!UNSAFE_CALL!>.<!>length
|
||||
<!SMARTCAST_IMPOSSIBLE!>x<!>.length
|
||||
}
|
||||
x<!UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
|
||||
@@ -61,8 +61,8 @@ fun case_4(x: Boolean?) {
|
||||
} while (x!!)
|
||||
} while (true)
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>.not()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!><!UNSAFE_CALL!>.<!>not()
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 5
|
||||
@@ -73,8 +73,8 @@ fun case_5(x: Boolean?) {
|
||||
} while (x!!)
|
||||
} while (true)
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>.not()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!><!UNSAFE_CALL!>.<!>not()
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 6
|
||||
@@ -85,8 +85,8 @@ fun case_6(x: Boolean?) {
|
||||
} while (true)
|
||||
} while (x!!)
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>.not()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!><!UNSAFE_CALL!>.<!>not()
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 7
|
||||
@@ -126,8 +126,8 @@ fun case_9(x: Boolean?) {
|
||||
}
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>.not()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!><!UNSAFE_CALL!>.<!>not()
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -204,8 +204,8 @@ fun case_16(x: Boolean?) {
|
||||
break
|
||||
} while (x!!)
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>.not()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!><!UNSAFE_CALL!>.<!>not()
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -343,8 +343,8 @@ fun case_26(x: Boolean?) {
|
||||
}
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>.not()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!><!UNSAFE_CALL!>.<!>not()
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -357,8 +357,8 @@ fun case_27(x: Int?, y: Class) {
|
||||
y[break, x!!]
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>.inv()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!><!UNSAFE_CALL!>.<!>inv()
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -371,6 +371,6 @@ fun case_28(x: Int?, y: List<List<Int>>) {
|
||||
y[break][x!!]
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>.inv()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!><!UNSAFE_CALL!>.<!>inv()
|
||||
}
|
||||
|
||||
@@ -61,8 +61,8 @@ fun case_4(x: Boolean?) {
|
||||
} while (x as Boolean)
|
||||
} while (true)
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>.not()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!><!UNSAFE_CALL!>.<!>not()
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 5
|
||||
@@ -73,8 +73,8 @@ fun case_5(x: Boolean?) {
|
||||
} while (x as Boolean)
|
||||
} while (true)
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>.not()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!><!UNSAFE_CALL!>.<!>not()
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 6
|
||||
@@ -85,8 +85,8 @@ fun case_6(x: Boolean?) {
|
||||
} while (true)
|
||||
} while (x as Boolean)
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>.not()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!><!UNSAFE_CALL!>.<!>not()
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 7
|
||||
@@ -126,8 +126,8 @@ fun case_9(x: Boolean?) {
|
||||
}
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>.not()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!><!UNSAFE_CALL!>.<!>not()
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -204,8 +204,8 @@ fun case_16(x: Boolean?) {
|
||||
break
|
||||
} while (x as Boolean)
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>.not()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!><!UNSAFE_CALL!>.<!>not()
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -343,8 +343,8 @@ fun case_26(x: Boolean?) {
|
||||
}
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>.not()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!><!UNSAFE_CALL!>.<!>not()
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -357,8 +357,8 @@ fun case_27(x: Int?, y: Class) {
|
||||
y[break, x as Int]
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>.inv()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!><!UNSAFE_CALL!>.<!>inv()
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -371,8 +371,8 @@ fun case_28(x: Int?, y: List<List<Int>>) {
|
||||
y[break][x as Int]
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>.inv()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!><!UNSAFE_CALL!>.<!>inv()
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -386,6 +386,6 @@ fun case_29(x: Boolean?, y: MutableList<Int>) {
|
||||
x!!
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>.not()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!><!UNSAFE_CALL!>.<!>not()
|
||||
}
|
||||
|
||||
@@ -68,8 +68,8 @@ fun case_5(x: Boolean?, y: Boolean?) {
|
||||
x!!
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>.not()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!><!UNSAFE_CALL!>.<!>not()
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 6
|
||||
@@ -79,8 +79,8 @@ fun case_6(x: Boolean?, y: ((x: Nothing) -> Unit)?) {
|
||||
x!!
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>.not()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!><!UNSAFE_CALL!>.<!>not()
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 7
|
||||
@@ -120,8 +120,8 @@ fun case_9(x: Int?) {
|
||||
y = break as Int + x!!
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>.inv()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!><!UNSAFE_CALL!>.<!>inv()
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -135,8 +135,8 @@ fun case_10(x: Int?) {
|
||||
break as Int + x as Int
|
||||
} while (true)
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>.inv()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!><!UNSAFE_CALL!>.<!>inv()
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -152,8 +152,8 @@ fun case_11(x: Int?) {
|
||||
break(x!!)
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>.inv()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!><!UNSAFE_CALL!>.<!>inv()
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -169,8 +169,8 @@ fun case_12(x: Int?) {
|
||||
<!NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>break[x!!]<!>
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>.inv()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!><!UNSAFE_CALL!>.<!>inv()
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -186,8 +186,8 @@ fun case_13(x: Int?) {
|
||||
break[x!!] = 10
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>.inv()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!><!UNSAFE_CALL!>.<!>inv()
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -203,8 +203,8 @@ fun case_14(x: Int?) {
|
||||
break[10] = x!!
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>.inv()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!><!UNSAFE_CALL!>.<!>inv()
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 15
|
||||
|
||||
@@ -23,8 +23,8 @@ fun case_2(x: Boolean?, y: Int?) {
|
||||
x!!
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>.not()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!><!UNSAFE_CALL!>.<!>not()
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 3
|
||||
@@ -92,8 +92,8 @@ fun case_7(x: Boolean?, y: Boolean?) {
|
||||
x!!
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Boolean")!>x<!>.not()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean?")!>x<!><!UNSAFE_CALL!>.<!>not()
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 8
|
||||
|
||||
@@ -105,8 +105,8 @@ fun case_9() {
|
||||
x = null
|
||||
break
|
||||
} while (x!!.length > 1)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String? & kotlin.Nothing")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String? & kotlin.Nothing")!>x<!>.length
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String? & kotlin.Nothing?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String? & kotlin.Nothing?")!>x<!><!UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 10
|
||||
@@ -116,8 +116,8 @@ fun case_10() {
|
||||
x = null
|
||||
break
|
||||
} while ((x as String).length > 1)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String? & kotlin.Nothing")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String? & kotlin.Nothing")!>x<!>.length
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String? & kotlin.Nothing?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String? & kotlin.Nothing?")!>x<!><!UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 11
|
||||
@@ -127,8 +127,8 @@ fun case_11() {
|
||||
x = null
|
||||
break
|
||||
} while (x!!)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Nothing")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Nothing")!>x<!>.not()
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Nothing?")!>x<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean? & kotlin.Nothing?")!>x<!><!UNSAFE_CALL!>.<!>not()
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 12
|
||||
|
||||
Reference in New Issue
Block a user