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 5d3e6055d07..5245c90359c 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 @@ -1219,6 +1219,12 @@ public class DiagnosisCompilerFirTestdataTestGenerated extends AbstractDiagnosis runTest("compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInConstructor.kt"); } + @Test + @TestMetadata("postponedLambdaInReturn.kt") + public void testPostponedLambdaInReturn() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.kt"); + } + @Test @TestMetadata("postponedLambdas.kt") public void testPostponedLambdas() 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 c58d25e00c4..a7f8e4f859c 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 @@ -1051,6 +1051,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract runTest("compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInConstructor.kt"); } + @TestMetadata("postponedLambdaInReturn.kt") + public void testPostponedLambdaInReturn() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.kt"); + } + @TestMetadata("postponedLambdas.kt") public void testPostponedLambdas() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdas.kt"); diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.dot b/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.dot new file mode 100644 index 00000000000..6bcd09443c9 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.dot @@ -0,0 +1,490 @@ +digraph postponedLambdaInReturn_kt { + graph [nodesep=3] + node [shape=box penwidth=2] + edge [penwidth=2] + + subgraph cluster_0 { + color=red + 0 [label="Enter function " style="filled" fillcolor=red]; + 1 [label="Delegated constructor call: super()" style="filled" fillcolor=yellow]; + 2 [label="Exit function " style="filled" fillcolor=red]; + } + 0 -> {1}; + 1 -> {2}; + + subgraph cluster_1 { + color=red + 3 [label="Enter class Lateinit" style="filled" fillcolor=red]; + 4 [label="Exit class Lateinit" style="filled" fillcolor=red]; + } + 3 -> {4} [color=green]; + + subgraph cluster_2 { + color=red + 5 [label="Enter function build" style="filled" fillcolor=red]; + subgraph cluster_3 { + color=blue + 6 [label="Enter block"]; + 7 [label="Function call: R|/Lateinit.Lateinit|()" style="filled" fillcolor=yellow]; + 8 [label="Access variable R|/builder|"]; + 9 [label="Function call: R|/Lateinit.Lateinit|().R|kotlin/apply||>(...)" style="filled" fillcolor=yellow]; + 10 [label="Access variable R|SubstitutionOverride|"]; + 11 [label="Jump: ^build R|/Lateinit.Lateinit|().R|kotlin/apply||>(R|/builder|).R|SubstitutionOverride|"]; + 12 [label="Stub" style="filled" fillcolor=gray]; + 13 [label="Exit block" style="filled" fillcolor=gray]; + } + 14 [label="Exit function build" style="filled" fillcolor=red]; + } + 5 -> {6}; + 6 -> {7}; + 7 -> {8}; + 8 -> {9}; + 9 -> {10}; + 10 -> {11}; + 11 -> {14}; + 11 -> {12} [style=dotted]; + 12 -> {13} [style=dotted]; + 13 -> {14} [style=dotted]; + + subgraph cluster_4 { + color=red + 15 [label="Enter property" style="filled" fillcolor=red]; + 16 [label="Const: Boolean(false)"]; + 17 [label="Exit property" style="filled" fillcolor=red]; + } + 15 -> {16}; + 16 -> {17}; + + subgraph cluster_5 { + color=red + 18 [label="Enter function test1" style="filled" fillcolor=red]; + subgraph cluster_6 { + color=blue + 19 [label="Enter block"]; + 20 [label="Const: Null(null)"]; + 21 [label="Variable declaration: lvar y: R|kotlin/String?|"]; + 22 [label="Postponed enter to lambda"]; + subgraph cluster_7 { + color=blue + 30 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + subgraph cluster_8 { + color=blue + 31 [label="Enter block"]; + subgraph cluster_9 { + color=blue + 32 [label="Enter when"]; + subgraph cluster_10 { + color=blue + 33 [label="Enter when branch condition "]; + 34 [label="Access variable R|/p|"]; + 35 [label="Exit when branch condition"]; + } + subgraph cluster_11 { + color=blue + 36 [label="Enter when branch condition else"]; + 37 [label="Exit when branch condition"]; + } + 38 [label="Enter when branch result"]; + subgraph cluster_12 { + color=blue + 39 [label="Enter block"]; + 40 [label="Const: String()"]; + 41 [label="Jump: ^@run String()"]; + 42 [label="Stub" style="filled" fillcolor=gray]; + 43 [label="Exit block" style="filled" fillcolor=gray]; + } + 44 [label="Exit when branch result" style="filled" fillcolor=gray]; + 45 [label="Enter when branch result"]; + subgraph cluster_13 { + color=blue + 46 [label="Enter block"]; + 47 [label="Postponed enter to lambda"]; + subgraph cluster_14 { + color=blue + 57 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + subgraph cluster_15 { + color=blue + 58 [label="Enter block"]; + 59 [label="Access variable R|/y|"]; + 60 [label="Type operator: (R|/y| as R|kotlin/String|)"]; + 61 [label="Const: String(...)"]; + 62 [label="Assignment: R|SubstitutionOverride|"]; + 63 [label="Exit block"]; + } + 64 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 48 [label="Postponed exit from lambda"]; + 49 [label="Function call: R|/build|(...)" style="filled" fillcolor=yellow]; + 50 [label="Jump: ^@run R|/build|( = build@fun R|Lateinit|.(): R|kotlin/Unit| { + (R|/y| as R|kotlin/String|) + this@R|special/anonymous|.R|SubstitutionOverride| = String(...) +} +)"]; + 51 [label="Stub" style="filled" fillcolor=gray]; + 52 [label="Exit block" style="filled" fillcolor=gray]; + } + 53 [label="Exit when branch result" style="filled" fillcolor=gray]; + 54 [label="Exit when" style="filled" fillcolor=gray]; + } + 55 [label="Exit block" style="filled" fillcolor=gray]; + } + 56 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 23 [label="Postponed exit from lambda"]; + 24 [label="Function call: R|kotlin/run|(...)" style="filled" fillcolor=yellow]; + 25 [label="Variable declaration: lval x: R|kotlin/String|"]; + 26 [label="Access variable R|/y|"]; + 27 [label="Access variable #"]; + 28 [label="Exit block"]; + } + 29 [label="Exit function test1" style="filled" fillcolor=red]; + } + 18 -> {19}; + 19 -> {20}; + 20 -> {21}; + 21 -> {22}; + 22 -> {30 24}; + 22 -> {23} [style=dotted]; + 22 -> {30} [style=dashed]; + 23 -> {24}; + 24 -> {25}; + 25 -> {26}; + 26 -> {27}; + 27 -> {28}; + 28 -> {29}; + 30 -> {31}; + 31 -> {32}; + 32 -> {33}; + 33 -> {34}; + 34 -> {35}; + 35 -> {45 36}; + 36 -> {37}; + 37 -> {38}; + 38 -> {39}; + 39 -> {40}; + 40 -> {41}; + 41 -> {56}; + 41 -> {42} [style=dotted]; + 42 -> {43} [style=dotted]; + 43 -> {44} [style=dotted]; + 44 -> {54} [style=dotted]; + 45 -> {46}; + 46 -> {47}; + 47 -> {49 57}; + 47 -> {48} [style=dotted]; + 47 -> {57} [style=dashed]; + 48 -> {49} [color=green]; + 49 -> {50}; + 50 -> {56}; + 50 -> {51} [style=dotted]; + 51 -> {52} [style=dotted]; + 52 -> {53} [style=dotted]; + 53 -> {54} [style=dotted]; + 54 -> {55} [style=dotted]; + 55 -> {56} [style=dotted]; + 56 -> {23}; + 57 -> {58}; + 58 -> {59}; + 59 -> {60}; + 60 -> {61}; + 61 -> {62}; + 62 -> {63}; + 63 -> {64}; + 64 -> {48}; + + subgraph cluster_16 { + color=red + 65 [label="Enter function test2" style="filled" fillcolor=red]; + subgraph cluster_17 { + color=blue + 66 [label="Enter block"]; + 67 [label="Postponed enter to lambda"]; + subgraph cluster_18 { + color=blue + 75 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + subgraph cluster_19 { + color=blue + 76 [label="Enter block"]; + subgraph cluster_20 { + color=blue + 77 [label="Enter while loop"]; + subgraph cluster_21 { + color=blue + 78 [label="Enter loop condition"]; + 79 [label="Const: Boolean(true)"]; + 80 [label="Exit loop condition"]; + } + subgraph cluster_22 { + color=blue + 81 [label="Enter loop block"]; + subgraph cluster_23 { + color=blue + 82 [label="Enter block"]; + subgraph cluster_24 { + color=blue + 83 [label="Try expression enter"]; + subgraph cluster_25 { + color=blue + 84 [label="Try main block enter"]; + subgraph cluster_26 { + color=blue + 85 [label="Enter block"]; + 86 [label="Postponed enter to lambda"]; + subgraph cluster_27 { + color=blue + 107 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + subgraph cluster_28 { + color=blue + 108 [label="Enter block"]; + 109 [label="Const: String(...)"]; + 110 [label="Assignment: R|SubstitutionOverride|"]; + 111 [label="Exit block"]; + } + 112 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 87 [label="Postponed exit from lambda"]; + 88 [label="Function call: R|/build|(...)" style="filled" fillcolor=yellow]; + 89 [label="Jump: ^@run R|/build|( = build@fun R|Lateinit|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|SubstitutionOverride| = String(...) +} +)"]; + 90 [label="Stub" style="filled" fillcolor=gray]; + 91 [label="Exit block" style="filled" fillcolor=gray]; + } + 92 [label="Try main block exit" style="filled" fillcolor=gray]; + } + subgraph cluster_29 { + color=blue + 93 [label="Catch enter"]; + 94 [label="Variable declaration: e: R|kotlin/Throwable|"]; + subgraph cluster_30 { + color=blue + 95 [label="Enter block"]; + 96 [label="Exit block"]; + } + 97 [label="Catch exit"]; + } + 98 [label="Try expression exit"]; + } + 99 [label="Exit block"]; + } + 100 [label="Exit loop block"]; + } + 101 [label="Exit whileloop" style="filled" fillcolor=gray]; + } + 102 [label="Function call: R|java/lang/Exception.Exception|()" style="filled" fillcolor=gray]; + 103 [label="Throw: throw R|java/lang/Exception.Exception|()" style="filled" fillcolor=gray]; + 104 [label="Stub" style="filled" fillcolor=gray]; + 105 [label="Exit block" style="filled" fillcolor=gray]; + } + 106 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 68 [label="Postponed exit from lambda"]; + 69 [label="Function call: R|kotlin/run|(...)" style="filled" fillcolor=yellow]; + 70 [label="Variable declaration: lval x: R|kotlin/String|"]; + 71 [label="Access variable R|/x|"]; + 72 [label="Access variable R|kotlin/String.length|"]; + 73 [label="Exit block"]; + } + 74 [label="Exit function test2" style="filled" fillcolor=red]; + } + 65 -> {66}; + 66 -> {67}; + 67 -> {75 69}; + 67 -> {68} [style=dotted]; + 67 -> {75} [style=dashed]; + 68 -> {69}; + 69 -> {70}; + 70 -> {71}; + 71 -> {72}; + 72 -> {73}; + 73 -> {74}; + 75 -> {76}; + 76 -> {77}; + 77 -> {78}; + 78 -> {79}; + 79 -> {80}; + 80 -> {81}; + 80 -> {101} [style=dotted]; + 81 -> {82}; + 82 -> {83}; + 83 -> {84 93}; + 84 -> {85}; + 85 -> {86}; + 86 -> {88 107}; + 86 -> {87} [style=dotted]; + 86 -> {107} [style=dashed]; + 87 -> {88} [color=green]; + 88 -> {89}; + 89 -> {106}; + 89 -> {90} [style=dotted]; + 90 -> {91} [style=dotted]; + 91 -> {92} [style=dotted]; + 92 -> {98 93} [style=dotted]; + 93 -> {94}; + 93 -> {74} [label=onUncaughtException]; + 94 -> {95}; + 95 -> {96}; + 96 -> {97}; + 97 -> {98}; + 98 -> {99}; + 99 -> {100}; + 100 -> {78} [color=green style=dashed]; + 101 -> {102} [style=dotted]; + 102 -> {103} [style=dotted]; + 103 -> {104} [style=dotted]; + 103 -> {74} [style=dotted] [label=onUncaughtException]; + 104 -> {105} [style=dotted]; + 105 -> {106} [style=dotted]; + 106 -> {68}; + 107 -> {108}; + 108 -> {109}; + 109 -> {110}; + 110 -> {111}; + 111 -> {112}; + 112 -> {87}; + + subgraph cluster_31 { + color=red + 113 [label="Enter function test3" style="filled" fillcolor=red]; + subgraph cluster_32 { + color=blue + 114 [label="Enter block"]; + 115 [label="Variable declaration: lvar y: R|kotlin/String?|"]; + 116 [label="Const: String()"]; + 117 [label="Assignment: R|/y|"]; + 118 [label="Postponed enter to lambda"]; + subgraph cluster_33 { + color=blue + 127 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + subgraph cluster_34 { + color=blue + 128 [label="Enter block"]; + subgraph cluster_35 { + color=blue + 129 [label="Enter when"]; + subgraph cluster_36 { + color=blue + 130 [label="Enter when branch condition "]; + 131 [label="Access variable R|/p|"]; + 132 [label="Function call: R|/p|.R|kotlin/Boolean.not|()" style="filled" fillcolor=yellow]; + 133 [label="Exit when branch condition"]; + } + subgraph cluster_37 { + color=blue + 134 [label="Enter when branch condition else"]; + 135 [label="Exit when branch condition"]; + } + 136 [label="Enter when branch result"]; + subgraph cluster_38 { + color=blue + 137 [label="Enter block"]; + 138 [label="Const: String()"]; + 139 [label="Jump: ^@run String()"]; + 140 [label="Stub" style="filled" fillcolor=gray]; + 141 [label="Exit block" style="filled" fillcolor=gray]; + } + 142 [label="Exit when branch result" style="filled" fillcolor=gray]; + 143 [label="Enter when branch result"]; + subgraph cluster_39 { + color=blue + 144 [label="Enter block"]; + 145 [label="Postponed enter to lambda"]; + subgraph cluster_40 { + color=blue + 155 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + subgraph cluster_41 { + color=blue + 156 [label="Enter block"]; + 157 [label="Const: Null(null)"]; + 158 [label="Assignment: R|/y|"]; + 159 [label="Const: String(...)"]; + 160 [label="Assignment: R|SubstitutionOverride|"]; + 161 [label="Exit block"]; + } + 162 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 146 [label="Postponed exit from lambda"]; + 147 [label="Function call: R|/build|(...)" style="filled" fillcolor=yellow]; + 148 [label="Jump: ^@run R|/build|( = build@fun R|Lateinit|.(): R|kotlin/Unit| { + R|/y| = Null(null) + this@R|special/anonymous|.R|SubstitutionOverride| = String(...) +} +)"]; + 149 [label="Stub" style="filled" fillcolor=gray]; + 150 [label="Exit block" style="filled" fillcolor=gray]; + } + 151 [label="Exit when branch result" style="filled" fillcolor=gray]; + 152 [label="Exit when" style="filled" fillcolor=gray]; + } + 153 [label="Exit block" style="filled" fillcolor=gray]; + } + 154 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 119 [label="Postponed exit from lambda"]; + 120 [label="Function call: R|kotlin/run|(...)" style="filled" fillcolor=yellow]; + 121 [label="Variable declaration: lval x: R|kotlin/String|"]; + 122 [label="Access variable R|/y|"]; + 123 [label="Smart cast: R|/y|"]; + 124 [label="Access variable R|kotlin/String.length|"]; + 125 [label="Exit block"]; + } + 126 [label="Exit function test3" style="filled" fillcolor=red]; + } + 113 -> {114}; + 114 -> {115}; + 115 -> {116}; + 116 -> {117}; + 117 -> {118}; + 118 -> {127 120}; + 118 -> {119} [style=dotted]; + 118 -> {127} [style=dashed]; + 119 -> {120}; + 120 -> {121}; + 121 -> {122}; + 122 -> {123}; + 123 -> {124}; + 124 -> {125}; + 125 -> {126}; + 127 -> {128}; + 128 -> {129}; + 129 -> {130}; + 130 -> {131}; + 131 -> {132}; + 132 -> {133}; + 133 -> {143 134}; + 134 -> {135}; + 135 -> {136}; + 136 -> {137}; + 137 -> {138}; + 138 -> {139}; + 139 -> {154}; + 139 -> {140} [style=dotted]; + 140 -> {141} [style=dotted]; + 141 -> {142} [style=dotted]; + 142 -> {152} [style=dotted]; + 143 -> {144}; + 144 -> {145}; + 145 -> {147 155}; + 145 -> {146} [style=dotted]; + 145 -> {155} [style=dashed]; + 146 -> {147} [color=green]; + 147 -> {148}; + 148 -> {154}; + 148 -> {149} [style=dotted]; + 149 -> {150} [style=dotted]; + 150 -> {151} [style=dotted]; + 151 -> {152} [style=dotted]; + 152 -> {153} [style=dotted]; + 153 -> {154} [style=dotted]; + 154 -> {119}; + 155 -> {156}; + 156 -> {157}; + 157 -> {158}; + 158 -> {159}; + 159 -> {160}; + 160 -> {161}; + 161 -> {162}; + 162 -> {146}; + +} diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.fir.txt b/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.fir.txt new file mode 100644 index 00000000000..c4dc566a0e1 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.fir.txt @@ -0,0 +1,82 @@ +FILE: postponedLambdaInReturn.kt + public final class Lateinit : R|kotlin/Any| { + public constructor(): R|Lateinit| { + super() + } + + public final lateinit var value: R|R| + public get(): R|R| + public set(value: R|R|): R|kotlin/Unit| + + } + @R|kotlin/OptIn|(markerClass = vararg((Q|kotlin/contracts/ExperimentalContracts|))) public final inline fun build(crossinline builder: R|Lateinit.() -> kotlin/Unit|): R|R| + [R|Contract description] + < + CallsInPlace(builder, EXACTLY_ONCE) + > + { + [StubStatement] + ^build R|/Lateinit.Lateinit|().R|kotlin/apply||>(R|/builder|).R|SubstitutionOverride| + } + public final val p: R|kotlin/Boolean| = Boolean(false) + public get(): R|kotlin/Boolean| + public final fun test1(): R|kotlin/Unit| { + lvar y: R|kotlin/String?| = Null(null) + lval x: R|kotlin/String| = R|kotlin/run|( = run@fun (): R|kotlin/String| { + when () { + R|/p| -> { + ^@run R|/build|( = build@fun R|Lateinit|.(): R|kotlin/Unit| { + (R|/y| as R|kotlin/String|) + this@R|special/anonymous|.R|SubstitutionOverride| = String(...) + } + ) + } + else -> { + ^@run String() + } + } + + } + ) + R|/y|.# + } + public final fun test2(): R|kotlin/Unit| { + lval x: R|kotlin/String| = R|kotlin/run|( = run@fun (): R|kotlin/String| { + while(Boolean(true)) { + try { + ^@run R|/build|( = build@fun R|Lateinit|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|SubstitutionOverride| = String(...) + } + ) + } + catch (e: R|kotlin/Throwable|) { + } + + } + + throw R|java/lang/Exception.Exception|() + } + ) + R|/x|.R|kotlin/String.length| + } + public final fun test3(): R|kotlin/Unit| { + lvar y: R|kotlin/String?| + R|/y| = String() + lval x: R|kotlin/String| = R|kotlin/run|( = run@fun (): R|kotlin/String| { + when () { + R|/p|.R|kotlin/Boolean.not|() -> { + ^@run R|/build|( = build@fun R|Lateinit|.(): R|kotlin/Unit| { + R|/y| = Null(null) + this@R|special/anonymous|.R|SubstitutionOverride| = String(...) + } + ) + } + else -> { + ^@run String() + } + } + + } + ) + R|/y|.R|kotlin/String.length| + } diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.kt b/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.kt new file mode 100644 index 00000000000..8559de2a0b6 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.kt @@ -0,0 +1,50 @@ +// !DUMP_CFG + +import kotlin.contracts.* + +class Lateinit { + lateinit var value: R +} + +@OptIn(ExperimentalContracts::class) +public inline fun build(crossinline builder: Lateinit.() -> Unit): R { + contract { callsInPlace(builder, InvocationKind.EXACTLY_ONCE) } + return Lateinit().apply(builder).value +} + +val p = false + +fun test1() { + var y: String? = null + val x: String = run { + if (p) + return@run build { y as String; value = "..." } + else + return@run "" + } + y.length // bad +} + +fun test2() { + val x: String = run { + while (true) { + try { + return@run build { value = "..." } + } catch (e: Throwable) {} + } + throw Exception() + } + x.length +} + +fun test3() { + var y: String? + y = "" + val x: String = run { + if (!p) + return@run build { y = null; value = "..." } + else + return@run "" + } + y.length // bad +} 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 2036aec3006..40177b87712 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 @@ -1219,6 +1219,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest { runTest("compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInConstructor.kt"); } + @Test + @TestMetadata("postponedLambdaInReturn.kt") + public void testPostponedLambdaInReturn() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.kt"); + } + @Test @TestMetadata("postponedLambdas.kt") public void testPostponedLambdas() 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 21aac4a2ae1..820045a5b41 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 @@ -1219,6 +1219,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos runTest("compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInConstructor.kt"); } + @Test + @TestMetadata("postponedLambdaInReturn.kt") + public void testPostponedLambdaInReturn() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/cfg/postponedLambdaInReturn.kt"); + } + @Test @TestMetadata("postponedLambdas.kt") public void testPostponedLambdas() throws Exception { 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 a5c61989d23..5ee35974a03 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 @@ -535,6 +535,10 @@ abstract class FirDataFlowAnalyzer( // ----------------------------------- Jump ----------------------------------- + fun enterJump(jump: FirJump<*>) { + graphBuilder.enterJump(jump) + } + fun exitJump(jump: FirJump<*>) { graphBuilder.exitJump(jump).mergeIncomingFlow() } 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 61c826d739e..a5ab2baded4 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 @@ -669,6 +669,15 @@ class ControlFlowGraphBuilder { // ----------------------------------- Jump ----------------------------------- + fun enterJump(jump: FirJump<*>) { + // Data flow from anonymous functions in return values does not merge with any enclosing calls. + // For named functions, the return value has to be a completed call anyway, so there should + // be no postponed lambdas in it. + if (jump is FirReturnExpression && jump.target.labeledElement is FirAnonymousFunction) { + splitDataFlowForPostponedLambdas() + } + } + fun exitJump(jump: FirJump<*>): JumpNode { val node = createJumpNode(jump) val nextNode = when (jump) { @@ -678,6 +687,13 @@ class ControlFlowGraphBuilder { else -> throw IllegalArgumentException("Unknown jump type: ${jump.render()}") } + if (jump is FirReturnExpression && jump.target.labeledElement is FirAnonymousFunction) { + // TODO: these should be DFA-only edges; they should be pointed into the postponed function exit node? + // With builder inference, lambdas are not necessarily resolved starting from the innermost one... + // See analysis test cfg/postponedLambdaInReturn.kt. + postponedLambdaExits.pop() + } + val labelForFinallyBLock = when (jump) { is FirReturnExpression -> ReturnPath(jump.target.labeledElement.symbol) is FirLoopJump -> LoopPath(jump) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt index 656740e05ae..484b79c2939 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt @@ -171,6 +171,7 @@ class FirControlFlowStatementsResolveTransformer(transformer: FirAbstractBodyRes // ------------------------------- Jumps ------------------------------- override fun transformJump(jump: FirJump, data: ResolutionMode): FirStatement { + dataFlowAnalyzer.enterJump(jump) val result = transformer.transformExpression(jump, data) dataFlowAnalyzer.exitJump(jump) return result