diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java index 1f4c71760ac..b2db35319f0 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java @@ -1083,6 +1083,12 @@ public class DiagnosisCompilerFirTestdataTestGenerated extends AbstractDiagnosis runTest("compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.kt"); } + @Test + @TestMetadata("flowFromTwoInplaceLambdas.kt") + public void testFlowFromTwoInplaceLambdas() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/cfg/flowFromTwoInplaceLambdas.kt"); + } + @Test @TestMetadata("initBlock.kt") public void testInitBlock() throws Exception { diff --git a/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java b/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java index 9eed385de66..f1596a2dadb 100644 --- a/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java +++ b/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java @@ -933,6 +933,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract runTest("compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.kt"); } + @TestMetadata("flowFromTwoInplaceLambdas.kt") + public void testFlowFromTwoInplaceLambdas() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/cfg/flowFromTwoInplaceLambdas.kt"); + } + @TestMetadata("initBlock.kt") public void testInitBlock() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/cfg/initBlock.kt"); diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.dot b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.dot index 80e4fce43f0..2c41bf3bf84 100644 --- a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.dot +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.dot @@ -847,16 +847,16 @@ digraph flowFromInplaceLambda2_kt { 300 [label="Postponed enter to lambda"]; subgraph cluster_71 { color=blue - 318 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 317 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; subgraph cluster_72 { color=blue - 319 [label="Enter block"]; - 320 [label="Const: Null(null)"]; - 321 [label="Assignment: R|/p|"]; - 322 [label="Function call: R|/n|()"]; - 323 [label="Exit block"]; + 318 [label="Enter block"]; + 319 [label="Const: Null(null)"]; + 320 [label="Assignment: R|/p|"]; + 321 [label="Function call: R|/n|()"]; + 322 [label="Exit block"]; } - 324 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + 323 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } 301 [label="Postponed exit from lambda"]; 302 [label="Function call: R|kotlin/run|(...)"]; @@ -865,32 +865,31 @@ digraph flowFromInplaceLambda2_kt { 305 [label="Postponed enter to lambda"]; subgraph cluster_73 { color=blue - 325 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 324 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; subgraph cluster_74 { color=blue - 326 [label="Enter block"]; - 327 [label="Access variable R|/p|"]; - 328 [label="Access variable R|kotlin/String.length|"]; - 329 [label="Const: Int(123)"]; - 330 [label="Exit block"]; + 325 [label="Enter block"]; + 326 [label="Access variable R|/p|"]; + 327 [label="Access variable R|kotlin/String.length|"]; + 328 [label="Const: Int(123)"]; + 329 [label="Exit block"]; } - 331 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + 330 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; } 306 [label="Postponed exit from lambda"]; 307 [label="Function call: R|kotlin/run|(...)"]; 308 [label="Call arguments union" style="filled" fillcolor=yellow]; 309 [label="Function call: R|/foo|(...)"]; 310 [label="Access variable R|/p|"]; - 311 [label="Stub" style="filled" fillcolor=gray]; - 312 [label="Access variable R|kotlin/String.length|" style="filled" fillcolor=gray]; - 313 [label="Exit block" style="filled" fillcolor=gray]; + 311 [label="Access variable #"]; + 312 [label="Exit block"]; } - 314 [label="Exit when branch result" style="filled" fillcolor=gray]; - 315 [label="Exit when"]; + 313 [label="Exit when branch result"]; + 314 [label="Exit when"]; } - 316 [label="Exit block"]; + 315 [label="Exit block"]; } - 317 [label="Exit function test7" style="filled" fillcolor=red]; + 316 [label="Exit function test7" style="filled" fillcolor=red]; } 287 -> {288}; 288 -> {289}; @@ -902,46 +901,44 @@ digraph flowFromInplaceLambda2_kt { 294 -> {295}; 295 -> {296}; 296 -> {298 297}; - 297 -> {315}; + 297 -> {314}; 298 -> {299}; 299 -> {300}; - 300 -> {318}; + 300 -> {317}; 300 -> {301} [color=red]; - 300 -> {318} [style=dashed]; + 300 -> {317} [style=dashed]; 301 -> {302}; 302 -> {303}; 303 -> {304}; 304 -> {305}; - 305 -> {325}; + 305 -> {324}; 305 -> {306} [color=red]; - 305 -> {325} [style=dashed]; + 305 -> {324} [style=dashed]; 306 -> {307}; 307 -> {308}; 308 -> {309}; 309 -> {310}; - 310 -> {317} [label=onUncaughtException]; - 310 -> {311} [style=dotted]; - 311 -> {312} [style=dotted]; - 312 -> {313} [style=dotted]; - 313 -> {314} [style=dotted]; - 314 -> {315} [style=dotted]; + 310 -> {311}; + 311 -> {312}; + 312 -> {313}; + 313 -> {314}; + 314 -> {315}; 315 -> {316}; - 316 -> {317}; + 317 -> {318}; 318 -> {319}; 319 -> {320}; 320 -> {321}; 321 -> {322}; 322 -> {323}; - 323 -> {324}; - 324 -> {308} [color=red]; - 324 -> {301} [color=green]; + 323 -> {308} [color=red]; + 323 -> {301} [color=green]; + 324 -> {325}; 325 -> {326}; 326 -> {327}; 327 -> {328}; 328 -> {329}; 329 -> {330}; - 330 -> {331}; - 331 -> {308} [color=red]; - 331 -> {306} [color=green]; + 330 -> {308} [color=red]; + 330 -> {306} [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 422e9f49941..a8069e3b29e 100644 --- a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.fir.txt @@ -131,7 +131,7 @@ FILE: flowFromInplaceLambda2.kt ^ Int(123) } )) - R|/p|.R|kotlin/String.length| + R|/p|.# } } diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.kt b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.kt index 240e533712f..c66012b6998 100644 --- a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.kt +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.kt @@ -73,6 +73,6 @@ fun test7(x: String?) { 1, run { p.length; 123 } // Bad (p = null) ) - p.length // 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 new file mode 100644 index 00000000000..bd8689cd700 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromTwoInplaceLambdas.dot @@ -0,0 +1,342 @@ +digraph flowFromTwoInplaceLambdas_kt { + graph [nodesep=3] + node [shape=box penwidth=2] + edge [penwidth=2] + + subgraph cluster_0 { + color=red + 0 [label="Enter function n" style="filled" fillcolor=red]; + subgraph cluster_1 { + color=blue + 1 [label="Enter block"]; + 2 [label="Const: Null(null)"]; + 3 [label="Jump: ^n Null(null)"]; + 4 [label="Stub" style="filled" fillcolor=gray]; + 5 [label="Exit block" style="filled" fillcolor=gray]; + } + 6 [label="Exit function n" style="filled" fillcolor=red]; + } + 0 -> {1}; + 1 -> {2}; + 2 -> {3}; + 3 -> {6}; + 3 -> {4} [style=dotted]; + 4 -> {5} [style=dotted]; + 5 -> {6} [style=dotted]; + + subgraph cluster_2 { + color=red + 7 [label="Enter function run2" style="filled" fillcolor=red]; + subgraph cluster_3 { + color=blue + 8 [label="Enter block"]; + 9 [label="Function call: R|/x|.R|SubstitutionOverride|()"]; + 10 [label="Function call: R|/y|.R|SubstitutionOverride|()"]; + 11 [label="Exit block"]; + } + 12 [label="Exit function run2" style="filled" fillcolor=red]; + } + 7 -> {8}; + 8 -> {9}; + 9 -> {10}; + 10 -> {11}; + 11 -> {12}; + + subgraph cluster_4 { + color=red + 13 [label="Enter function test1" style="filled" fillcolor=red]; + subgraph cluster_5 { + color=blue + 14 [label="Enter block"]; + 15 [label="Access variable R|/x|"]; + 16 [label="Variable declaration: lvar p: R|kotlin/String?|"]; + subgraph cluster_6 { + color=blue + 17 [label="Enter when"]; + subgraph cluster_7 { + color=blue + 18 [label="Enter when branch condition "]; + 19 [label="Access variable R|/p|"]; + 20 [label="Const: Null(null)"]; + 21 [label="Equality operator !="]; + 22 [label="Exit when branch condition"]; + } + 23 [label="Synthetic else branch"]; + 24 [label="Enter when branch result"]; + subgraph cluster_8 { + color=blue + 25 [label="Enter block"]; + 26 [label="Postponed enter to lambda"]; + subgraph cluster_9 { + color=blue + 39 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + subgraph cluster_10 { + color=blue + 40 [label="Enter block"]; + 41 [label="Const: Null(null)"]; + 42 [label="Assignment: R|/p|"]; + 43 [label="Function call: R|/n|()"]; + 44 [label="Exit block"]; + } + 45 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 28 [label="Postponed exit from lambda"]; + 29 [label="Postponed enter to lambda"]; + subgraph cluster_11 { + color=blue + 46 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + subgraph cluster_12 { + color=blue + 47 [label="Enter block"]; + 48 [label="Access variable R|/p|"]; + 49 [label="Access variable #"]; + 50 [label="Const: Int(123)"]; + 51 [label="Exit block"]; + } + 52 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 27 [label="Call arguments union" style="filled" fillcolor=yellow]; + 30 [label="Postponed exit from lambda"]; + 31 [label="Function call: R|/run2|(...)"]; + 32 [label="Access variable R|/p|"]; + 33 [label="Access variable #"]; + 34 [label="Exit block"]; + } + 35 [label="Exit when branch result"]; + 36 [label="Exit when"]; + } + 37 [label="Exit block"]; + } + 38 [label="Exit function test1" style="filled" fillcolor=red]; + } + 13 -> {14}; + 14 -> {15}; + 15 -> {16}; + 16 -> {17}; + 17 -> {18}; + 18 -> {19}; + 19 -> {20}; + 20 -> {21}; + 21 -> {22}; + 22 -> {24 23}; + 23 -> {36}; + 24 -> {25}; + 25 -> {26}; + 26 -> {39}; + 26 -> {28} [color=red]; + 26 -> {39} [style=dashed]; + 27 -> {31} [color=red]; + 28 -> {29}; + 29 -> {46}; + 29 -> {30} [color=red]; + 29 -> {46} [style=dashed]; + 30 -> {31} [color=green]; + 31 -> {32}; + 32 -> {33}; + 33 -> {34}; + 34 -> {35}; + 35 -> {36}; + 36 -> {37}; + 37 -> {38}; + 39 -> {40}; + 40 -> {41}; + 41 -> {42}; + 42 -> {43}; + 43 -> {44}; + 44 -> {45}; + 45 -> {27} [color=red]; + 45 -> {28} [color=green]; + 46 -> {47}; + 47 -> {48}; + 48 -> {49}; + 49 -> {50}; + 50 -> {51}; + 51 -> {52}; + 52 -> {27} [color=red]; + 52 -> {30} [color=green]; + + subgraph cluster_13 { + color=red + 53 [label="Enter function test2" 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"]; + subgraph cluster_15 { + color=blue + 73 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 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"]; + } + 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 { + 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"]; + } + 86 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 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"]; + } + 72 [label="Exit function test2" style="filled" fillcolor=red]; + } + 53 -> {54}; + 54 -> {55}; + 55 -> {56}; + 56 -> {57}; + 57 -> {58}; + 58 -> {59}; + 59 -> {73}; + 59 -> {61} [color=red]; + 59 -> {73} [style=dashed]; + 60 -> {64} [color=red]; + 61 -> {62}; + 62 -> {80}; + 62 -> {63} [color=red]; + 62 -> {80} [style=dashed]; + 63 -> {64} [color=green]; + 64 -> {65}; + 65 -> {66}; + 66 -> {67}; + 67 -> {68 70}; + 68 -> {69}; + 69 -> {70}; + 70 -> {71}; + 71 -> {72}; + 73 -> {74}; + 74 -> {75}; + 75 -> {76}; + 76 -> {77}; + 77 -> {78}; + 78 -> {79}; + 79 -> {60} [color=red]; + 79 -> {61} [color=green]; + 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]; + } + 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]; + 95 -> {96}; + 96 -> {114}; + 96 -> {97} [color=red]; + 96 -> {114} [style=dashed]; + 97 -> {98} [color=green]; + 98 -> {99}; + 99 -> {100}; + 100 -> {101}; + 101 -> {102 104}; + 102 -> {103}; + 103 -> {104}; + 104 -> {105}; + 105 -> {106}; + 107 -> {108}; + 108 -> {109}; + 109 -> {110}; + 110 -> {111}; + 111 -> {112}; + 112 -> {113}; + 113 -> {94} [color=red]; + 113 -> {95} [color=green]; + 114 -> {115}; + 115 -> {116}; + 116 -> {117}; + 117 -> {118}; + 118 -> {119}; + 119 -> {120}; + 120 -> {94} [color=red]; + 120 -> {97} [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 new file mode 100644 index 00000000000..a38b7340ede --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromTwoInplaceLambdas.fir.txt @@ -0,0 +1,63 @@ +FILE: flowFromTwoInplaceLambdas.kt + public final fun n(): R|T?| { + ^n Null(null) + } + public final fun run2(x: R|() -> T|, y: R|() -> T|): R|kotlin/Unit| + [R|Contract description] + < + CallsInPlace(x, EXACTLY_ONCE) + CallsInPlace(y, EXACTLY_ONCE) + > + { + [StubStatement] + R|/x|.R|SubstitutionOverride|() + R|/y|.R|SubstitutionOverride|() + } + public final fun test1(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| + R|/p|.# + R|/run2|(run2@fun (): R|kotlin/Int?| { + R|/p| = Null(null) + ^ R|/n|() + } + , run2@fun (): R|kotlin/Int?| { + (R|/p| as R|kotlin/String|) + ^ Int(123) + } + ) + R|/p|.# + R|/p|?.{ $subj$.R|kotlin/String.length| } + } + public final fun test3(x: R|kotlin/Any?|): R|kotlin/Unit| { + lvar p: R|kotlin/Any?| = R|/x| + R|/p|.# + R|/run2|(run2@fun (): R|kotlin/Int?| { + R|/p| = Null(null) + ^ R|/n|() + } + , run2@fun (): R|kotlin/Int?| { + R|/p| = String() + ^ Int(123) + } + ) + R|/p|.# + R|/p|?.{ $subj$.R|kotlin/String.length| } + } diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromTwoInplaceLambdas.kt b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromTwoInplaceLambdas.kt new file mode 100644 index 00000000000..e8ac550e11d --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/flowFromTwoInplaceLambdas.kt @@ -0,0 +1,40 @@ +// !DUMP_CFG +import kotlin.contracts.* + +fun n(): T? = null + +fun run2(x: () -> T, y: () -> T) { + contract { + callsInPlace(x, InvocationKind.EXACTLY_ONCE) + callsInPlace(y, InvocationKind.EXACTLY_ONCE) + } + x() + y() +} + +fun test1(x: String?) { + var p = x + if (p != null) { + run2( + { p = null; n() }, + { p.length; 123 } // Bad: may or may not not be called first + ) + p.length // Bad: p = null + } +} + +fun test2(x: Any?) { + var p: Any? = x + p.length // Bad + run2({ p = null; n() }, { p as String; 123 }) + p.length // Bad: p can be null + p?.length // OK: p is either null or String +} + +fun test3(x: Any?) { + var p: Any? = x + p.length // Bad + run2({ p = null; n() }, { p = ""; 123 }) + p.length // Bad: p can be null + p?.length // OK: p is either null or String +} diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java index c673d0d18cd..885535d4c17 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java @@ -1083,6 +1083,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest { runTest("compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.kt"); } + @Test + @TestMetadata("flowFromTwoInplaceLambdas.kt") + public void testFlowFromTwoInplaceLambdas() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/cfg/flowFromTwoInplaceLambdas.kt"); + } + @Test @TestMetadata("initBlock.kt") public void testInitBlock() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java index 69a273f10df..1635d5cc421 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java @@ -1083,6 +1083,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos runTest("compiler/fir/analysis-tests/testData/resolve/cfg/flowFromInplaceLambda2.kt"); } + @Test + @TestMetadata("flowFromTwoInplaceLambdas.kt") + public void testFlowFromTwoInplaceLambdas() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/cfg/flowFromTwoInplaceLambdas.kt"); + } + @Test @TestMetadata("initBlock.kt") public void testInitBlock() throws Exception { 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 eaa7461f4e1..22ff119f67f 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 @@ -108,22 +108,20 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste } override fun joinFlow(flows: Collection): PersistentFlow { - return foldFlow( - flows, - mergeOperation = { statements -> this.or(statements).takeIf { it.isNotEmpty } }, - ) + return foldFlow(flows) { variable -> or(flows.map { it.getApprovedTypeStatements(variable) }).takeIf { it.isNotEmpty } } } override fun unionFlow(flows: Collection): PersistentFlow { - return foldFlow( - flows, - this::and, - ) + return foldFlow(flows) { variable -> + or(flows.groupBy { it.assignmentIndex[variable] ?: -1 }.values.map { flowSubset -> + and(flowSubset.map { it.getApprovedTypeStatements(variable) }) + }) + } } private inline fun foldFlow( flows: Collection, - mergeOperation: (Collection) -> MutableTypeStatement?, + mergeOperation: (RealVariable) -> MutableTypeStatement?, ): PersistentFlow { if (flows.isEmpty()) return createEmptyFlow() flows.singleOrNull()?.let { return it } @@ -134,7 +132,7 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste val variables = flows.flatMap { it.approvedTypeStatements.keys }.toSet() for (variable in variables) { - val info = mergeOperation(flows.map { it.getApprovedTypeStatements(variable) }) ?: continue + val info = mergeOperation(variable) ?: continue commonFlow.approvedTypeStatements -= variable commonFlow.approvedTypeStatementsDiff -= variable val thereWereReassignments = variable.hasDifferentReassignments(flows)