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