From 8214e4f8064f23261002a8ec71f81052c1dfe2cf Mon Sep 17 00:00:00 2001 From: pyos Date: Wed, 15 Jun 2022 12:21:31 +0200 Subject: [PATCH] FIR CFG: check lambda concurrency by data flow, not control flow var p: String? = something if (p != null) { foo( run { p = null; n() }, // This lambda executes strictly after the one above by CFG, // but data flow for type inference comes from before the call // so p would get smartcasted if not forbidden. run { p.length; 123 } ) } --- .../resolve/cfg/flowFromInplaceLambda2.dot | 2 +- .../cfg/flowFromInplaceLambda2.fir.txt | 2 +- .../resolve/cfg/flowFromInplaceLambda2.kt | 2 +- .../resolve/cfg/flowFromTwoInplaceLambdas.dot | 513 +++++++++++------- .../cfg/flowFromTwoInplaceLambdas.fir.txt | 18 + .../resolve/cfg/flowFromTwoInplaceLambdas.kt | 11 + .../fir/resolve/dfa/FirDataFlowAnalyzer.kt | 23 +- .../dfa/FirLocalVariableAssignmentAnalyzer.kt | 57 +- 8 files changed, 407 insertions(+), 221 deletions(-) diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.dot b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.dot index 2c41bf3bf84..4b391873460 100644 --- a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.dot +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.dot @@ -870,7 +870,7 @@ digraph flowFromInplaceLambda2_kt { color=blue 325 [label="Enter block"]; 326 [label="Access variable R|/p|"]; - 327 [label="Access variable R|kotlin/String.length|"]; + 327 [label="Access variable #"]; 328 [label="Const: Int(123)"]; 329 [label="Exit block"]; } 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 a8069e3b29e..56c23cc5648 100644 --- a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.fir.txt @@ -127,7 +127,7 @@ FILE: flowFromInplaceLambda2.kt ^ R|/n|() } )), Int(1), R|kotlin/run|( = run@fun (): R|kotlin/Int| { - R|/p|.R|kotlin/String.length| + 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 c66012b6998..fd81182bcee 100644 --- a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.kt +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.kt @@ -71,7 +71,7 @@ fun test7(x: String?) { foo( id(run { p = null; n() }), 1, - run { p.length; 123 } // Bad (p = null) + run { p.length; 123 } // Bad (p = null) ) p.length // Bad (p = null) } diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromTwoInplaceLambdas.dot b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromTwoInplaceLambdas.dot index 8ef0708d244..d854907d704 100644 --- a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromTwoInplaceLambdas.dot +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromTwoInplaceLambdas.dot @@ -157,55 +157,70 @@ digraph flowFromTwoInplaceLambdas_kt { subgraph cluster_13 { color=red - 53 [label="Enter function test2" style="filled" fillcolor=red]; + 53 [label="Enter function test1_tail" style="filled" fillcolor=red]; subgraph cluster_14 { color=blue 54 [label="Enter block"]; 55 [label="Access variable R|/x|"]; - 56 [label="Variable declaration: lvar p: R|kotlin/Any?|"]; - 57 [label="Access variable R|/p|"]; - 58 [label="Access variable #"]; - 59 [label="Postponed enter to lambda"]; + 56 [label="Variable declaration: lvar p: R|kotlin/String?|"]; subgraph cluster_15 { color=blue - 73 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 57 [label="Enter when"]; subgraph cluster_16 { color=blue - 74 [label="Enter block"]; - 75 [label="Const: Null(null)"]; - 76 [label="Assignment: R|/p|"]; - 77 [label="Function call: R|/n|()"]; - 78 [label="Exit block"]; + 58 [label="Enter when branch condition "]; + 59 [label="Access variable R|/p|"]; + 60 [label="Const: Null(null)"]; + 61 [label="Equality operator !="]; + 62 [label="Exit when branch condition"]; } - 79 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; - } - 61 [label="Postponed exit from lambda"]; - 62 [label="Postponed enter to lambda"]; - subgraph cluster_17 { - color=blue - 80 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; - subgraph cluster_18 { + 63 [label="Synthetic else branch"]; + 64 [label="Enter when branch result"]; + subgraph cluster_17 { color=blue - 81 [label="Enter block"]; - 82 [label="Access variable R|/p|"]; - 83 [label="Type operator: (R|/p| as R|kotlin/String|)"]; - 84 [label="Const: Int(123)"]; - 85 [label="Exit block"]; + 65 [label="Enter block"]; + 66 [label="Postponed enter to lambda"]; + subgraph cluster_18 { + color=blue + 86 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + subgraph cluster_19 { + color=blue + 87 [label="Enter block"]; + 88 [label="Access variable R|/p|"]; + 89 [label="Access variable #"]; + 90 [label="Const: Int(123)"]; + 91 [label="Exit block"]; + } + 92 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 68 [label="Postponed exit from lambda"]; + 69 [label="Postponed enter to lambda"]; + subgraph cluster_20 { + color=blue + 79 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + subgraph cluster_21 { + color=blue + 80 [label="Enter block"]; + 81 [label="Const: Null(null)"]; + 82 [label="Assignment: R|/p|"]; + 83 [label="Function call: R|/n|()"]; + 84 [label="Exit block"]; + } + 85 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 67 [label="Call arguments union" style="filled" fillcolor=yellow]; + 70 [label="Postponed exit from lambda"]; + 71 [label="Function call: R|/run2|(...)"]; + 72 [label="Access variable R|/p|"]; + 73 [label="Access variable #"]; + 74 [label="Exit block"]; } - 86 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + 75 [label="Exit when branch result"]; + 76 [label="Exit when"]; } - 60 [label="Call arguments union" style="filled" fillcolor=yellow]; - 63 [label="Postponed exit from lambda"]; - 64 [label="Function call: R|/run2|(...)"]; - 65 [label="Access variable R|/p|"]; - 66 [label="Access variable #"]; - 67 [label="Access variable R|/p|"]; - 68 [label="Enter safe call"]; - 69 [label="Access variable R|kotlin/String.length|"]; - 70 [label="Exit safe call"]; - 71 [label="Exit block"]; + 77 [label="Exit block"]; } - 72 [label="Exit function test2" style="filled" fillcolor=red]; + 78 [label="Exit function test1_tail" style="filled" fillcolor=red]; } 53 -> {54}; 54 -> {55}; @@ -213,240 +228,338 @@ digraph flowFromTwoInplaceLambdas_kt { 56 -> {57}; 57 -> {58}; 58 -> {59}; - 59 -> {73}; - 59 -> {61} [color=red]; - 59 -> {73} [style=dashed]; - 60 -> {64} [color=red]; + 59 -> {60}; + 60 -> {61}; 61 -> {62}; - 62 -> {80}; - 62 -> {63} [color=red]; - 62 -> {80} [style=dashed]; - 63 -> {64} [color=green]; + 62 -> {64 63}; + 63 -> {76}; 64 -> {65}; 65 -> {66}; - 66 -> {67}; - 67 -> {68 70}; + 66 -> {86}; + 66 -> {68} [color=red]; + 66 -> {86} [style=dashed]; + 67 -> {71} [color=red]; 68 -> {69}; - 69 -> {70}; - 70 -> {71}; + 69 -> {79}; + 69 -> {70} [color=red]; + 69 -> {79} [style=dashed]; + 70 -> {71} [color=green]; 71 -> {72}; + 72 -> {73}; 73 -> {74}; 74 -> {75}; 75 -> {76}; 76 -> {77}; 77 -> {78}; - 78 -> {79}; - 79 -> {60} [color=red]; - 79 -> {61} [color=green]; + 79 -> {80}; 80 -> {81}; 81 -> {82}; 82 -> {83}; 83 -> {84}; 84 -> {85}; - 85 -> {86}; - 86 -> {60} [color=red]; - 86 -> {63} [color=green]; - - subgraph cluster_19 { - color=red - 87 [label="Enter function test3" style="filled" fillcolor=red]; - subgraph cluster_20 { - color=blue - 88 [label="Enter block"]; - 89 [label="Access variable R|/x|"]; - 90 [label="Variable declaration: lvar p: R|kotlin/Any?|"]; - 91 [label="Access variable R|/p|"]; - 92 [label="Access variable #"]; - 93 [label="Postponed enter to lambda"]; - subgraph cluster_21 { - color=blue - 107 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; - subgraph cluster_22 { - color=blue - 108 [label="Enter block"]; - 109 [label="Const: Null(null)"]; - 110 [label="Assignment: R|/p|"]; - 111 [label="Function call: R|/n|()"]; - 112 [label="Exit block"]; - } - 113 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; - } - 95 [label="Postponed exit from lambda"]; - 96 [label="Postponed enter to lambda"]; - subgraph cluster_23 { - color=blue - 114 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; - subgraph cluster_24 { - color=blue - 115 [label="Enter block"]; - 116 [label="Const: String()"]; - 117 [label="Assignment: R|/p|"]; - 118 [label="Const: Int(123)"]; - 119 [label="Exit block"]; - } - 120 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; - } - 94 [label="Call arguments union" style="filled" fillcolor=yellow]; - 97 [label="Postponed exit from lambda"]; - 98 [label="Function call: R|/run2|(...)"]; - 99 [label="Access variable R|/p|"]; - 100 [label="Access variable #"]; - 101 [label="Access variable R|/p|"]; - 102 [label="Enter safe call"]; - 103 [label="Access variable R|kotlin/String.length|"]; - 104 [label="Exit safe call"]; - 105 [label="Exit block"]; - } - 106 [label="Exit function test3" style="filled" fillcolor=red]; - } + 85 -> {67} [color=red]; + 85 -> {70} [color=green]; + 86 -> {87}; 87 -> {88}; 88 -> {89}; 89 -> {90}; 90 -> {91}; 91 -> {92}; - 92 -> {93}; - 93 -> {107}; - 93 -> {95} [color=red]; - 93 -> {107} [style=dashed]; - 94 -> {98} [color=red]; + 92 -> {67} [color=red]; + 92 -> {68} [color=green]; + + subgraph cluster_22 { + color=red + 93 [label="Enter function test2" style="filled" fillcolor=red]; + subgraph cluster_23 { + color=blue + 94 [label="Enter block"]; + 95 [label="Access variable R|/x|"]; + 96 [label="Variable declaration: lvar p: R|kotlin/Any?|"]; + 97 [label="Access variable R|/p|"]; + 98 [label="Access variable #"]; + 99 [label="Postponed enter to lambda"]; + subgraph cluster_24 { + color=blue + 113 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + subgraph cluster_25 { + color=blue + 114 [label="Enter block"]; + 115 [label="Const: Null(null)"]; + 116 [label="Assignment: R|/p|"]; + 117 [label="Function call: R|/n|()"]; + 118 [label="Exit block"]; + } + 119 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 101 [label="Postponed exit from lambda"]; + 102 [label="Postponed enter to lambda"]; + subgraph cluster_26 { + color=blue + 120 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + subgraph cluster_27 { + color=blue + 121 [label="Enter block"]; + 122 [label="Access variable R|/p|"]; + 123 [label="Type operator: (R|/p| as R|kotlin/String|)"]; + 124 [label="Const: Int(123)"]; + 125 [label="Exit block"]; + } + 126 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 100 [label="Call arguments union" style="filled" fillcolor=yellow]; + 103 [label="Postponed exit from lambda"]; + 104 [label="Function call: R|/run2|(...)"]; + 105 [label="Access variable R|/p|"]; + 106 [label="Access variable #"]; + 107 [label="Access variable R|/p|"]; + 108 [label="Enter safe call"]; + 109 [label="Access variable R|kotlin/String.length|"]; + 110 [label="Exit safe call"]; + 111 [label="Exit block"]; + } + 112 [label="Exit function test2" style="filled" fillcolor=red]; + } + 93 -> {94}; + 94 -> {95}; 95 -> {96}; - 96 -> {114}; - 96 -> {97} [color=red]; - 96 -> {114} [style=dashed]; - 97 -> {98} [color=green]; + 96 -> {97}; + 97 -> {98}; 98 -> {99}; - 99 -> {100}; - 100 -> {101}; - 101 -> {102 104}; - 102 -> {103}; - 103 -> {104}; + 99 -> {113}; + 99 -> {101} [color=red]; + 99 -> {113} [style=dashed]; + 100 -> {104} [color=red]; + 101 -> {102}; + 102 -> {120}; + 102 -> {103} [color=red]; + 102 -> {120} [style=dashed]; + 103 -> {104} [color=green]; 104 -> {105}; 105 -> {106}; - 107 -> {108}; + 106 -> {107}; + 107 -> {108 110}; 108 -> {109}; 109 -> {110}; 110 -> {111}; 111 -> {112}; - 112 -> {113}; - 113 -> {94} [color=red]; - 113 -> {95} [color=green]; + 113 -> {114}; 114 -> {115}; 115 -> {116}; 116 -> {117}; 117 -> {118}; 118 -> {119}; - 119 -> {120}; - 120 -> {94} [color=red]; - 120 -> {97} [color=green]; + 119 -> {100} [color=red]; + 119 -> {101} [color=green]; + 120 -> {121}; + 121 -> {122}; + 122 -> {123}; + 123 -> {124}; + 124 -> {125}; + 125 -> {126}; + 126 -> {100} [color=red]; + 126 -> {103} [color=green]; - subgraph cluster_25 { + subgraph cluster_28 { color=red - 121 [label="Enter class I1" style="filled" fillcolor=red]; - 122 [label="Exit class I1" style="filled" fillcolor=red]; - } - 121 -> {122} [color=green]; - - subgraph cluster_26 { - color=red - 123 [label="Enter class I2" style="filled" fillcolor=red]; - 124 [label="Exit class I2" style="filled" fillcolor=red]; - } - 123 -> {124} [color=green]; - - subgraph cluster_27 { - color=red - 125 [label="Enter function test4" style="filled" fillcolor=red]; - subgraph cluster_28 { + 127 [label="Enter function test3" style="filled" fillcolor=red]; + subgraph cluster_29 { color=blue - 126 [label="Enter block"]; - 127 [label="Access variable R|/x|"]; - 128 [label="Access variable #"]; + 128 [label="Enter block"]; 129 [label="Access variable R|/x|"]; - 130 [label="Access variable #"]; - 131 [label="Postponed enter to lambda"]; - subgraph cluster_29 { + 130 [label="Variable declaration: lvar p: R|kotlin/Any?|"]; + 131 [label="Access variable R|/p|"]; + 132 [label="Access variable #"]; + 133 [label="Postponed enter to lambda"]; + subgraph cluster_30 { color=blue - 143 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; - subgraph cluster_30 { + 147 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + subgraph cluster_31 { color=blue - 144 [label="Enter block"]; - 145 [label="Access variable R|/x|"]; - 146 [label="Type operator: (R|/x| as R|I1|)"]; - 147 [label="Access variable R|/x|"]; - 148 [label="Access variable #"]; - 149 [label="Function call: R|/n|()"]; - 150 [label="Exit block"]; + 148 [label="Enter block"]; + 149 [label="Const: Null(null)"]; + 150 [label="Assignment: R|/p|"]; + 151 [label="Function call: R|/n|()"]; + 152 [label="Exit block"]; } - 151 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + 153 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } - 133 [label="Postponed exit from lambda"]; - 134 [label="Postponed enter to lambda"]; - subgraph cluster_31 { + 135 [label="Postponed exit from lambda"]; + 136 [label="Postponed enter to lambda"]; + subgraph cluster_32 { color=blue - 152 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; - subgraph cluster_32 { + 154 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + subgraph cluster_33 { color=blue - 153 [label="Enter block"]; - 154 [label="Access variable R|/x|"]; - 155 [label="Type operator: (R|/x| as R|I2|)"]; - 156 [label="Access variable R|/x|"]; - 157 [label="Access variable #"]; + 155 [label="Enter block"]; + 156 [label="Const: String()"]; + 157 [label="Assignment: R|/p|"]; 158 [label="Const: Int(123)"]; 159 [label="Exit block"]; } 160 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } - 132 [label="Call arguments union" style="filled" fillcolor=yellow]; - 135 [label="Postponed exit from lambda"]; - 136 [label="Function call: R|/run2|(...)"]; - 137 [label="Access variable R|/x|"]; - 138 [label="Access variable R|/I1.x|"]; - 139 [label="Access variable R|/x|"]; - 140 [label="Access variable R|/I2.y|"]; - 141 [label="Exit block"]; + 134 [label="Call arguments union" style="filled" fillcolor=yellow]; + 137 [label="Postponed exit from lambda"]; + 138 [label="Function call: R|/run2|(...)"]; + 139 [label="Access variable R|/p|"]; + 140 [label="Access variable #"]; + 141 [label="Access variable R|/p|"]; + 142 [label="Enter safe call"]; + 143 [label="Access variable R|kotlin/String.length|"]; + 144 [label="Exit safe call"]; + 145 [label="Exit block"]; } - 142 [label="Exit function test4" style="filled" fillcolor=red]; + 146 [label="Exit function test3" style="filled" fillcolor=red]; } - 125 -> {126}; - 126 -> {127}; 127 -> {128}; 128 -> {129}; 129 -> {130}; 130 -> {131}; - 131 -> {143}; - 131 -> {133} [color=red]; - 131 -> {143} [style=dashed]; - 132 -> {136} [color=red]; - 133 -> {134}; - 134 -> {152}; - 134 -> {135} [color=red]; - 134 -> {152} [style=dashed]; - 135 -> {136} [color=green]; - 136 -> {137}; - 137 -> {138}; + 131 -> {132}; + 132 -> {133}; + 133 -> {147}; + 133 -> {135} [color=red]; + 133 -> {147} [style=dashed]; + 134 -> {138} [color=red]; + 135 -> {136}; + 136 -> {154}; + 136 -> {137} [color=red]; + 136 -> {154} [style=dashed]; + 137 -> {138} [color=green]; 138 -> {139}; 139 -> {140}; 140 -> {141}; - 141 -> {142}; + 141 -> {142 144}; + 142 -> {143}; 143 -> {144}; 144 -> {145}; 145 -> {146}; - 146 -> {147}; 147 -> {148}; 148 -> {149}; 149 -> {150}; 150 -> {151}; - 151 -> {132} [color=red]; - 151 -> {133} [color=green]; + 151 -> {152}; 152 -> {153}; - 153 -> {154}; + 153 -> {134} [color=red]; + 153 -> {135} [color=green]; 154 -> {155}; 155 -> {156}; 156 -> {157}; 157 -> {158}; 158 -> {159}; 159 -> {160}; - 160 -> {132} [color=red]; - 160 -> {135} [color=green]; + 160 -> {134} [color=red]; + 160 -> {137} [color=green]; + + subgraph cluster_34 { + color=red + 161 [label="Enter class I1" style="filled" fillcolor=red]; + 162 [label="Exit class I1" style="filled" fillcolor=red]; + } + 161 -> {162} [color=green]; + + subgraph cluster_35 { + color=red + 163 [label="Enter class I2" style="filled" fillcolor=red]; + 164 [label="Exit class I2" style="filled" fillcolor=red]; + } + 163 -> {164} [color=green]; + + subgraph cluster_36 { + color=red + 165 [label="Enter function test4" style="filled" fillcolor=red]; + subgraph cluster_37 { + color=blue + 166 [label="Enter block"]; + 167 [label="Access variable R|/x|"]; + 168 [label="Access variable #"]; + 169 [label="Access variable R|/x|"]; + 170 [label="Access variable #"]; + 171 [label="Postponed enter to lambda"]; + subgraph cluster_38 { + color=blue + 183 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + subgraph cluster_39 { + color=blue + 184 [label="Enter block"]; + 185 [label="Access variable R|/x|"]; + 186 [label="Type operator: (R|/x| as R|I1|)"]; + 187 [label="Access variable R|/x|"]; + 188 [label="Access variable #"]; + 189 [label="Function call: R|/n|()"]; + 190 [label="Exit block"]; + } + 191 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 173 [label="Postponed exit from lambda"]; + 174 [label="Postponed enter to lambda"]; + subgraph cluster_40 { + color=blue + 192 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + subgraph cluster_41 { + color=blue + 193 [label="Enter block"]; + 194 [label="Access variable R|/x|"]; + 195 [label="Type operator: (R|/x| as R|I2|)"]; + 196 [label="Access variable R|/x|"]; + 197 [label="Access variable #"]; + 198 [label="Const: Int(123)"]; + 199 [label="Exit block"]; + } + 200 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 172 [label="Call arguments union" style="filled" fillcolor=yellow]; + 175 [label="Postponed exit from lambda"]; + 176 [label="Function call: R|/run2|(...)"]; + 177 [label="Access variable R|/x|"]; + 178 [label="Access variable R|/I1.x|"]; + 179 [label="Access variable R|/x|"]; + 180 [label="Access variable R|/I2.y|"]; + 181 [label="Exit block"]; + } + 182 [label="Exit function test4" style="filled" fillcolor=red]; + } + 165 -> {166}; + 166 -> {167}; + 167 -> {168}; + 168 -> {169}; + 169 -> {170}; + 170 -> {171}; + 171 -> {183}; + 171 -> {173} [color=red]; + 171 -> {183} [style=dashed]; + 172 -> {176} [color=red]; + 173 -> {174}; + 174 -> {192}; + 174 -> {175} [color=red]; + 174 -> {192} [style=dashed]; + 175 -> {176} [color=green]; + 176 -> {177}; + 177 -> {178}; + 178 -> {179}; + 179 -> {180}; + 180 -> {181}; + 181 -> {182}; + 183 -> {184}; + 184 -> {185}; + 185 -> {186}; + 186 -> {187}; + 187 -> {188}; + 188 -> {189}; + 189 -> {190}; + 190 -> {191}; + 191 -> {172} [color=red]; + 191 -> {173} [color=green]; + 192 -> {193}; + 193 -> {194}; + 194 -> {195}; + 195 -> {196}; + 196 -> {197}; + 197 -> {198}; + 198 -> {199}; + 199 -> {200}; + 200 -> {172} [color=red]; + 200 -> {175} [color=green]; } diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromTwoInplaceLambdas.fir.txt b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromTwoInplaceLambdas.fir.txt index 08447c27bc3..8c824c9b1a3 100644 --- a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromTwoInplaceLambdas.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromTwoInplaceLambdas.fir.txt @@ -30,6 +30,24 @@ FILE: flowFromTwoInplaceLambdas.kt } } + } + public final fun test1_tail(x: R|kotlin/String?|): R|kotlin/Unit| { + lvar p: R|kotlin/String?| = R|/x| + when () { + !=(R|/p|, Null(null)) -> { + R|/run2|(run2@fun (): R|kotlin/Int?| { + R|/p| = Null(null) + ^ R|/n|() + } + , = run2@fun (): R|kotlin/Int?| { + R|/p|.# + ^ Int(123) + } + ) + R|/p|.# + } + } + } public final fun test2(x: R|kotlin/Any?|): R|kotlin/Unit| { lvar p: R|kotlin/Any?| = R|/x| diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromTwoInplaceLambdas.kt b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromTwoInplaceLambdas.kt index 9c3211e6165..3c94f0bc94c 100644 --- a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromTwoInplaceLambdas.kt +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromTwoInplaceLambdas.kt @@ -23,6 +23,17 @@ fun test1(x: String?) { } } +fun test1_tail(x: String?) { + var p = x + if (p != null) { + run2({ p = null; n() }) { + p.length // Bad: may or may not not be called first + 123 + } + p.length // Bad: p = null + } +} + fun test2(x: Any?) { var p: Any? = x p.length // Bad 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 7ac2edde0dc..df0fbd524bf 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 @@ -1047,19 +1047,26 @@ abstract class FirDataFlowAnalyzer( graphBuilder.enterCall() } + private tailrec fun FirExpression.getAnonymousFunction(): FirAnonymousFunction? = when (this) { + is FirAnonymousFunctionExpression -> anonymousFunction + is FirLambdaArgumentExpression -> expression.getAnonymousFunction() + else -> null + } + + private var functionCallLevel = 0 + fun enterFunctionCall(functionCall: FirFunctionCall) { - val lambdaArgs = functionCall.arguments.mapNotNull { (it as? FirAnonymousFunctionExpression)?.anonymousFunction } - if (lambdaArgs.size > 1) { - getOrCreateLocalVariableAssignmentAnalyzer(lambdaArgs.first())?.enterFunctionCallWithMultipleLambdaArgs(lambdaArgs) - } + val lambdaArgs = functionCall.arguments.mapNotNullTo(mutableSetOf()) { it.getAnonymousFunction() } + val localVariableAssignmentAnalyzer = context.firLocalVariableAssignmentAnalyzer + ?: if (lambdaArgs.isNotEmpty()) getOrCreateLocalVariableAssignmentAnalyzer(lambdaArgs.first()) else null + localVariableAssignmentAnalyzer?.enterFunctionCall(lambdaArgs, functionCallLevel) + functionCallLevel++ } @OptIn(PrivateForInline::class) fun exitFunctionCall(functionCall: FirFunctionCall, callCompleted: Boolean) { - val lambdaArgs = functionCall.arguments.mapNotNull { (it as? FirAnonymousFunctionExpression)?.anonymousFunction } - if (lambdaArgs.size > 1) { - getOrCreateLocalVariableAssignmentAnalyzer(lambdaArgs.first())?.exitFunctionCallWithMultipleLambdaArgs() - } + functionCallLevel-- + context.firLocalVariableAssignmentAnalyzer?.exitFunctionCall(callCompleted) if (ignoreFunctionCalls) { graphBuilder.exitIgnoredCall(functionCall) return diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirLocalVariableAssignmentAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirLocalVariableAssignmentAnalyzer.kt index c7b6227db4f..27a459d5e09 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirLocalVariableAssignmentAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirLocalVariableAssignmentAnalyzer.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.fir.resolve.dfa.FirLocalVariableAssignmentAnalyzer.C import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol import org.jetbrains.kotlin.fir.visitors.FirVisitor import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.utils.addToStdlib.popLast /** * Helper that checks if an access to a local variable access is stable. @@ -37,7 +38,7 @@ internal class FirLocalVariableAssignmentAnalyzer( * From the call, it's nondeterministic whether `l1` runs before `l2` or vice versa. So when handling `l1`, we must mark all variables * touched in `l2` unstable. */ - private val concurrentLambdaArgsStack: MutableList> = mutableListOf() + private val concurrentLambdaArgsStack: MutableList> = mutableListOf() /** * Stack whose element tracks all concurrently modified variables in execution paths other than this one. It's a stack because after @@ -85,11 +86,13 @@ internal class FirLocalVariableAssignmentAnalyzer( val concurrentlyAssignedLocalVariables = concurrentlyAssignedLocalVariablesStack.last().toMutableSet() concurrentlyAssignedLocalVariablesStack.add(concurrentlyAssignedLocalVariables) - val concurrentLambdasInCurrentCall = concurrentLambdaArgsStack.lastOrNull() - if (concurrentLambdasInCurrentCall != null && function in concurrentLambdasInCurrentCall) { - for (otherLambda in concurrentLambdasInCurrentCall) { - // As mentioned in the comment above, we don't know whether other lambda arguments passed to the same call will be - // called before or after this lambda, so their assignments might have executed. Unless they're not called at all. + // 1. As mentioned in the comment above, we don't know whether other lambda arguments passed to the same call will be + // called before or after this lambda, so their assignments might have executed. Unless they're not called at all. + // 2. While lambdas from outer calls are not concurrent from control flow point of view, they are concurrent in data flow + // because the way this lambda resolves may affect the way those lambdas resolve, thus we need to forbid dependencies + // from smartcasts in this lambda to statements in these other lambdas. + for (concurrentLambdas in concurrentLambdaArgsStack) { + for (otherLambda in concurrentLambdas) { if (otherLambda != function && otherLambda.invocationKind != EventOccurrencesRange.ZERO) { assignedLocalVariablesByFunction[otherLambda.symbol]?.insideLocalFunction?.let { concurrentlyAssignedLocalVariables += it @@ -117,6 +120,18 @@ internal class FirLocalVariableAssignmentAnalyzer( EventOccurrencesRange.UNKNOWN, null -> // The function may be stored and then called later, so any access to the variables it touches // is no longer smartcastable ever. + // + // TODO: this incorrectly affects separate branches that are visited after this one: + // if (p is Something) { + // if (condition)) { + // foo { p = whatever } + // p.memberOfSomething // Bad + // } else { + // p.memberOfSomething // Marked as an error, but actually OK + // } + // p.memberOfSomething // Bad + // } + // FE1.0 has the same behavior. assignedLocalVariablesByFunction[function.symbol]?.insideLocalFunction?.let { for (outerScope in concurrentlyAssignedLocalVariablesStack) { outerScope += it @@ -126,12 +141,34 @@ internal class FirLocalVariableAssignmentAnalyzer( } } - fun enterFunctionCallWithMultipleLambdaArgs(lambdaArgs: List) { - concurrentLambdaArgsStack.add(lambdaArgs.toSet()) + fun enterFunctionCall(lambdaArgs: MutableSet, level: Int) { + while (concurrentLambdaArgsStack.size < level) { + // This object is only created on first local anonymous function, so we might have missed some + // `enterFunctionCall`s. None of them have lambda arguments. + concurrentLambdaArgsStack.add(mutableSetOf()) + } + concurrentLambdaArgsStack.add(lambdaArgs) } - fun exitFunctionCallWithMultipleLambdaArgs() { - concurrentLambdaArgsStack.removeLast() + fun exitFunctionCall(callCompleted: Boolean) { + // If we had anonymous functions but no calls with lambdas, the stack might have never been initialized. + if (concurrentLambdaArgsStack.isEmpty()) return + + val lambdasInCall = concurrentLambdaArgsStack.popLast() + if (!callCompleted) { + // TODO: this has the same problem as above: + // if (p is Something) { + // foo( + // if (condition) + // someNotCompletedCall { p = whatever } + // else + // someNotCompletedCall { p.memberOfSomething }, // Marked as an error, but actually OK + // someCall { p.memberOfSomething } // Bad + // ) + // } + // And also as above, FE1.0 produces the same error. + concurrentLambdaArgsStack.lastOrNull()?.addAll(lambdasInCall) + } } companion object {