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