From eae3688c61e3b075a2a98e30969e90aea22e1eb0 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Tue, 12 Nov 2019 18:23:46 +0300 Subject: [PATCH] [FIR] Fix collecting approved data flow info from rhs of boolean operator --- .../fir/resolve/dfa/DelegatingLogicSystem.kt | 28 +- .../testData/resolve/smartcasts/bangbang.dot | 755 ++++++++++++++++++ .../testData/resolve/smartcasts/bangbang.kt | 43 + .../testData/resolve/smartcasts/bangbang.txt | 102 +++ .../FirDiagnosticsWithCfgTestGenerated.java | 5 + 5 files changed, 932 insertions(+), 1 deletion(-) create mode 100644 compiler/fir/resolve/testData/resolve/smartcasts/bangbang.dot create mode 100644 compiler/fir/resolve/testData/resolve/smartcasts/bangbang.kt create mode 100644 compiler/fir/resolve/testData/resolve/smartcasts/bangbang.txt diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DelegatingLogicSystem.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DelegatingLogicSystem.kt index b2b4b5524c4..8cbd06e0d70 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DelegatingLogicSystem.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DelegatingLogicSystem.kt @@ -72,6 +72,30 @@ fun DelegatingFlow.conditionalInfosFromTopFlow(): ConditionalInfos { return ImmutableMultimap(conditionalInfos) } +private fun DelegatingFlow.approvedInfosUntilParent(parent: DelegatingFlow): ApprovedInfos { + return when(level - parent.level) { + 0 -> emptyMap() + 1 -> approvedInfosFromTopFlow() + else -> mutableMapOf().apply { + traverse(parent) { + it.approvedInfosFromTopFlow().forEach { (variable, info) -> + compute(variable) { _, existingInfo -> + existingInfo?.plus(info) ?: info + } + } + } + } + } +} + +private inline fun DelegatingFlow.traverse(untilFlow: DelegatingFlow, block: (DelegatingFlow) -> Unit) { + var flow = this + while (flow != untilFlow) { + block(flow) + flow = flow.previousFlow!! + } +} + private class ImmutableMultimap(private val original: Multimap) : Multimap by original { override fun put(p0: K?, p1: V?): Boolean { throw IllegalStateException() @@ -121,10 +145,12 @@ abstract class DelegatingLogicSystem(context: DataFlowInferenceContext) : LogicS rightVariable: DataFlowVariable ): InfoForBooleanOperator { require(leftFlow is DelegatingFlow && rightFlow is DelegatingFlow) + val parent = lowestCommonFlow(leftFlow, rightFlow) + return InfoForBooleanOperator( leftFlow.previousFlow!!.conditionalInfosFromTopFlow()[leftVariable], rightFlow.conditionalInfosFromTopFlow()[rightVariable], - rightFlow.approvedInfosFromTopFlow() + rightFlow.approvedInfosUntilParent(parent) ) } diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/bangbang.dot b/compiler/fir/resolve/testData/resolve/smartcasts/bangbang.dot new file mode 100644 index 00000000000..d99465f4979 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/bangbang.dot @@ -0,0 +1,755 @@ +digraph bangbang_kt { + graph [splines=ortho nodesep=3] + node [shape=box penwidth=2] + edge [penwidth=2] + + subgraph cluster_0 { + color=red + 0 [label="Enter function foo" style="filled" fillcolor=red]; + 1 [label="Exit function foo" style="filled" fillcolor=red]; + } + + 0 -> {1}; + + subgraph cluster_1 { + color=red + 2 [label="Enter function test_0" style="filled" fillcolor=red]; + subgraph cluster_2 { + color=blue + 3 [label="Enter block"]; + subgraph cluster_3 { + color=blue + 4 [label="Enter when"]; + 5 [label="Access variable R|/a|"]; + 6 [label="Variable declaration: lval : R|A?|"]; + subgraph cluster_4 { + color=blue + 7 [label="Enter when branch condition "]; + 8 [label="Const: Null(null)"]; + 9 [label="Operator =="]; + 10 [label="Exit when branch condition"]; + } + subgraph cluster_5 { + color=blue + 11 [label="Enter when branch condition else"]; + 12 [label="Exit when branch condition"]; + } + 13 [label="Enter when branch result"]; + subgraph cluster_6 { + color=blue + 14 [label="Enter block"]; + 15 [label="Access variable R|/|"]; + 16 [label="Exit block"]; + } + 17 [label="Exit when branch result"]; + 18 [label="Enter when branch result"]; + subgraph cluster_7 { + color=blue + 19 [label="Enter block"]; + 20 [label="Function call: R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; + 21 [label="Throw: throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; + 22 [label="Stub" style="filled" fillcolor=gray]; + 23 [label="Exit block" style="filled" fillcolor=gray]; + } + 24 [label="Exit when branch result" style="filled" fillcolor=gray]; + 25 [label="Exit when"]; + } + 26 [label="Function call: when (lval : R|A?| = R|/a|) { + ==($subj$, Null(null)) -> { + throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() + } + else -> { + R|/| + } +} +.R|/A.foo|()"]; + 27 [label="Access variable R|/a|"]; + 28 [label="Function call: R|/a|.R|/A.foo|()"]; + 29 [label="Exit block"]; + } + 30 [label="Exit function test_0" style="filled" fillcolor=red]; + } + + 2 -> {3}; + 3 -> {4}; + 4 -> {5}; + 5 -> {6}; + 6 -> {7}; + 7 -> {8}; + 8 -> {9}; + 9 -> {10}; + 10 -> {18 11}; + 11 -> {12}; + 12 -> {13}; + 13 -> {14}; + 14 -> {15}; + 15 -> {16}; + 16 -> {17}; + 17 -> {25}; + 18 -> {19}; + 19 -> {20}; + 20 -> {21}; + 21 -> {30}; + 21 -> {22} [style=dotted]; + 22 -> {23} [style=dotted]; + 23 -> {24} [style=dotted]; + 24 -> {25} [style=dotted]; + 25 -> {26}; + 26 -> {27}; + 27 -> {28}; + 28 -> {29}; + 29 -> {30}; + + subgraph cluster_8 { + color=red + 31 [label="Enter function test_1" style="filled" fillcolor=red]; + subgraph cluster_9 { + color=blue + 32 [label="Enter block"]; + subgraph cluster_10 { + color=blue + 33 [label="Enter when"]; + subgraph cluster_11 { + color=blue + 34 [label="Enter when branch condition "]; + subgraph cluster_12 { + color=blue + 35 [label="Enter when"]; + 36 [label="Access variable R|/a|"]; + 37 [label="Variable declaration: lval : R|A?|"]; + subgraph cluster_13 { + color=blue + 38 [label="Enter when branch condition "]; + 39 [label="Const: Null(null)"]; + 40 [label="Operator =="]; + 41 [label="Exit when branch condition"]; + } + subgraph cluster_14 { + color=blue + 42 [label="Enter when branch condition else"]; + 43 [label="Exit when branch condition"]; + } + 44 [label="Enter when branch result"]; + subgraph cluster_15 { + color=blue + 45 [label="Enter block"]; + 46 [label="Access variable R|/|"]; + 47 [label="Exit block"]; + } + 48 [label="Exit when branch result"]; + 49 [label="Enter when branch result"]; + subgraph cluster_16 { + color=blue + 50 [label="Enter block"]; + 51 [label="Function call: R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; + 52 [label="Throw: throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; + 53 [label="Stub" style="filled" fillcolor=gray]; + 54 [label="Exit block" style="filled" fillcolor=gray]; + } + 55 [label="Exit when branch result" style="filled" fillcolor=gray]; + 56 [label="Exit when"]; + } + 57 [label="Function call: when (lval : R|A?| = R|/a|) { + ==($subj$, Null(null)) -> { + throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() + } + else -> { + R|/| + } +} +.R|/A.foo|()"]; + 58 [label="Exit when branch condition"]; + } + 59 [label="Synthetic else branch"]; + 60 [label="Enter when branch result"]; + subgraph cluster_17 { + color=blue + 61 [label="Enter block"]; + 62 [label="Access variable R|/a|"]; + 63 [label="Function call: R|/a|.R|/A.foo|()"]; + 64 [label="Exit block"]; + } + 65 [label="Exit when branch result"]; + 66 [label="Exit when"]; + } + 67 [label="Access variable R|/a|"]; + 68 [label="Function call: R|/a|.R|/A.foo|()"]; + 69 [label="Exit block"]; + } + 70 [label="Exit function test_1" style="filled" fillcolor=red]; + } + + 31 -> {32}; + 32 -> {33}; + 33 -> {34}; + 34 -> {35}; + 35 -> {36}; + 36 -> {37}; + 37 -> {38}; + 38 -> {39}; + 39 -> {40}; + 40 -> {41}; + 41 -> {49 42}; + 42 -> {43}; + 43 -> {44}; + 44 -> {45}; + 45 -> {46}; + 46 -> {47}; + 47 -> {48}; + 48 -> {56}; + 49 -> {50}; + 50 -> {51}; + 51 -> {52}; + 52 -> {70}; + 52 -> {53} [style=dotted]; + 53 -> {54} [style=dotted]; + 54 -> {55} [style=dotted]; + 55 -> {56} [style=dotted]; + 56 -> {57}; + 57 -> {58}; + 58 -> {60 59}; + 59 -> {66}; + 60 -> {61}; + 61 -> {62}; + 62 -> {63}; + 63 -> {64}; + 64 -> {65}; + 65 -> {66}; + 66 -> {67}; + 67 -> {68}; + 68 -> {69}; + 69 -> {70}; + + subgraph cluster_18 { + color=red + 71 [label="Enter function test_2" style="filled" fillcolor=red]; + subgraph cluster_19 { + color=blue + 72 [label="Enter block"]; + subgraph cluster_20 { + color=blue + 73 [label="Enter when"]; + subgraph cluster_21 { + color=blue + 74 [label="Enter when branch condition "]; + subgraph cluster_22 { + color=blue + 75 [label="Enter &&"]; + subgraph cluster_23 { + color=blue + 76 [label="Enter when"]; + 77 [label="Access variable R|/a|"]; + 78 [label="Variable declaration: lval : R|A?|"]; + subgraph cluster_24 { + color=blue + 79 [label="Enter when branch condition "]; + 80 [label="Const: Null(null)"]; + 81 [label="Operator =="]; + 82 [label="Exit when branch condition"]; + } + subgraph cluster_25 { + color=blue + 83 [label="Enter when branch condition else"]; + 84 [label="Exit when branch condition"]; + } + 85 [label="Enter when branch result"]; + subgraph cluster_26 { + color=blue + 86 [label="Enter block"]; + 87 [label="Access variable R|/|"]; + 88 [label="Exit block"]; + } + 89 [label="Exit when branch result"]; + 90 [label="Enter when branch result"]; + subgraph cluster_27 { + color=blue + 91 [label="Enter block"]; + 92 [label="Function call: R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; + 93 [label="Throw: throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; + 94 [label="Stub" style="filled" fillcolor=gray]; + 95 [label="Exit block" style="filled" fillcolor=gray]; + } + 96 [label="Exit when branch result" style="filled" fillcolor=gray]; + 97 [label="Exit when"]; + } + 98 [label="Function call: when (lval : R|A?| = R|/a|) { + ==($subj$, Null(null)) -> { + throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() + } + else -> { + R|/| + } +} +.R|/A.foo|()"]; + 99 [label="Exit left part of &&"]; + 100 [label="Enter right part of &&"]; + 101 [label="Access variable R|/b|"]; + 102 [label="Exit &&"]; + } + 103 [label="Exit when branch condition"]; + } + 104 [label="Synthetic else branch"]; + 105 [label="Enter when branch result"]; + subgraph cluster_28 { + color=blue + 106 [label="Enter block"]; + 107 [label="Access variable R|/a|"]; + 108 [label="Function call: R|/a|.R|/A.foo|()"]; + 109 [label="Exit block"]; + } + 110 [label="Exit when branch result"]; + 111 [label="Exit when"]; + } + 112 [label="Access variable R|/a|"]; + 113 [label="Function call: R|/a|.R|/A.foo|()"]; + 114 [label="Exit block"]; + } + 115 [label="Exit function test_2" style="filled" fillcolor=red]; + } + + 71 -> {72}; + 72 -> {73}; + 73 -> {74}; + 74 -> {75}; + 75 -> {76}; + 76 -> {77}; + 77 -> {78}; + 78 -> {79}; + 79 -> {80}; + 80 -> {81}; + 81 -> {82}; + 82 -> {90 83}; + 83 -> {84}; + 84 -> {85}; + 85 -> {86}; + 86 -> {87}; + 87 -> {88}; + 88 -> {89}; + 89 -> {97}; + 90 -> {91}; + 91 -> {92}; + 92 -> {93}; + 93 -> {115}; + 93 -> {94} [style=dotted]; + 94 -> {95} [style=dotted]; + 95 -> {96} [style=dotted]; + 96 -> {97} [style=dotted]; + 97 -> {98}; + 98 -> {99}; + 99 -> {102 100}; + 100 -> {101}; + 101 -> {102}; + 102 -> {103}; + 103 -> {105 104}; + 104 -> {111}; + 105 -> {106}; + 106 -> {107}; + 107 -> {108}; + 108 -> {109}; + 109 -> {110}; + 110 -> {111}; + 111 -> {112}; + 112 -> {113}; + 113 -> {114}; + 114 -> {115}; + + subgraph cluster_29 { + color=red + 116 [label="Enter function test_3" style="filled" fillcolor=red]; + subgraph cluster_30 { + color=blue + 117 [label="Enter block"]; + subgraph cluster_31 { + color=blue + 118 [label="Enter when"]; + subgraph cluster_32 { + color=blue + 119 [label="Enter when branch condition "]; + subgraph cluster_33 { + color=blue + 120 [label="Enter &&"]; + 121 [label="Access variable R|/b|"]; + 122 [label="Exit left part of &&"]; + 123 [label="Enter right part of &&"]; + subgraph cluster_34 { + color=blue + 124 [label="Enter when"]; + 125 [label="Access variable R|/a|"]; + 126 [label="Variable declaration: lval : R|A?|"]; + subgraph cluster_35 { + color=blue + 127 [label="Enter when branch condition "]; + 128 [label="Const: Null(null)"]; + 129 [label="Operator =="]; + 130 [label="Exit when branch condition"]; + } + subgraph cluster_36 { + color=blue + 131 [label="Enter when branch condition else"]; + 132 [label="Exit when branch condition"]; + } + 133 [label="Enter when branch result"]; + subgraph cluster_37 { + color=blue + 134 [label="Enter block"]; + 135 [label="Access variable R|/|"]; + 136 [label="Exit block"]; + } + 137 [label="Exit when branch result"]; + 138 [label="Enter when branch result"]; + subgraph cluster_38 { + color=blue + 139 [label="Enter block"]; + 140 [label="Function call: R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; + 141 [label="Throw: throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; + 142 [label="Stub" style="filled" fillcolor=gray]; + 143 [label="Exit block" style="filled" fillcolor=gray]; + } + 144 [label="Exit when branch result" style="filled" fillcolor=gray]; + 145 [label="Exit when"]; + } + 146 [label="Function call: when (lval : R|A?| = R|/a|) { + ==($subj$, Null(null)) -> { + throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() + } + else -> { + R|/| + } +} +.R|/A.foo|()"]; + 147 [label="Exit &&"]; + } + 148 [label="Exit when branch condition"]; + } + 149 [label="Synthetic else branch"]; + 150 [label="Enter when branch result"]; + subgraph cluster_39 { + color=blue + 151 [label="Enter block"]; + 152 [label="Access variable R|/a|"]; + 153 [label="Function call: R|/a|.R|/A.foo|()"]; + 154 [label="Exit block"]; + } + 155 [label="Exit when branch result"]; + 156 [label="Exit when"]; + } + 157 [label="Access variable R|/a|"]; + 158 [label="Function call: R|/a|.#()"]; + 159 [label="Exit block"]; + } + 160 [label="Exit function test_3" style="filled" fillcolor=red]; + } + + 116 -> {117}; + 117 -> {118}; + 118 -> {119}; + 119 -> {120}; + 120 -> {121}; + 121 -> {122}; + 122 -> {147 123}; + 123 -> {124}; + 124 -> {125}; + 125 -> {126}; + 126 -> {127}; + 127 -> {128}; + 128 -> {129}; + 129 -> {130}; + 130 -> {138 131}; + 131 -> {132}; + 132 -> {133}; + 133 -> {134}; + 134 -> {135}; + 135 -> {136}; + 136 -> {137}; + 137 -> {145}; + 138 -> {139}; + 139 -> {140}; + 140 -> {141}; + 141 -> {160}; + 141 -> {142} [style=dotted]; + 142 -> {143} [style=dotted]; + 143 -> {144} [style=dotted]; + 144 -> {145} [style=dotted]; + 145 -> {146}; + 146 -> {147}; + 147 -> {148}; + 148 -> {150 149}; + 149 -> {156}; + 150 -> {151}; + 151 -> {152}; + 152 -> {153}; + 153 -> {154}; + 154 -> {155}; + 155 -> {156}; + 156 -> {157}; + 157 -> {158}; + 158 -> {159}; + 159 -> {160}; + + subgraph cluster_40 { + color=red + 161 [label="Enter function test_4" style="filled" fillcolor=red]; + subgraph cluster_41 { + color=blue + 162 [label="Enter block"]; + subgraph cluster_42 { + color=blue + 163 [label="Enter when"]; + subgraph cluster_43 { + color=blue + 164 [label="Enter when branch condition "]; + subgraph cluster_44 { + color=blue + 165 [label="Enter ||"]; + subgraph cluster_45 { + color=blue + 166 [label="Enter when"]; + 167 [label="Access variable R|/a|"]; + 168 [label="Variable declaration: lval : R|A?|"]; + subgraph cluster_46 { + color=blue + 169 [label="Enter when branch condition "]; + 170 [label="Const: Null(null)"]; + 171 [label="Operator =="]; + 172 [label="Exit when branch condition"]; + } + subgraph cluster_47 { + color=blue + 173 [label="Enter when branch condition else"]; + 174 [label="Exit when branch condition"]; + } + 175 [label="Enter when branch result"]; + subgraph cluster_48 { + color=blue + 176 [label="Enter block"]; + 177 [label="Access variable R|/|"]; + 178 [label="Exit block"]; + } + 179 [label="Exit when branch result"]; + 180 [label="Enter when branch result"]; + subgraph cluster_49 { + color=blue + 181 [label="Enter block"]; + 182 [label="Function call: R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; + 183 [label="Throw: throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; + 184 [label="Stub" style="filled" fillcolor=gray]; + 185 [label="Exit block" style="filled" fillcolor=gray]; + } + 186 [label="Exit when branch result" style="filled" fillcolor=gray]; + 187 [label="Exit when"]; + } + 188 [label="Function call: when (lval : R|A?| = R|/a|) { + ==($subj$, Null(null)) -> { + throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() + } + else -> { + R|/| + } +} +.R|/A.foo|()"]; + 189 [label="Exit left part of ||"]; + 190 [label="Enter right part of ||"]; + 191 [label="Access variable R|/b|"]; + 192 [label="Exit ||"]; + } + 193 [label="Exit when branch condition"]; + } + 194 [label="Synthetic else branch"]; + 195 [label="Enter when branch result"]; + subgraph cluster_50 { + color=blue + 196 [label="Enter block"]; + 197 [label="Access variable R|/a|"]; + 198 [label="Function call: R|/a|.R|/A.foo|()"]; + 199 [label="Exit block"]; + } + 200 [label="Exit when branch result"]; + 201 [label="Exit when"]; + } + 202 [label="Access variable R|/a|"]; + 203 [label="Function call: R|/a|.R|/A.foo|()"]; + 204 [label="Exit block"]; + } + 205 [label="Exit function test_4" style="filled" fillcolor=red]; + } + + 161 -> {162}; + 162 -> {163}; + 163 -> {164}; + 164 -> {165}; + 165 -> {166}; + 166 -> {167}; + 167 -> {168}; + 168 -> {169}; + 169 -> {170}; + 170 -> {171}; + 171 -> {172}; + 172 -> {180 173}; + 173 -> {174}; + 174 -> {175}; + 175 -> {176}; + 176 -> {177}; + 177 -> {178}; + 178 -> {179}; + 179 -> {187}; + 180 -> {181}; + 181 -> {182}; + 182 -> {183}; + 183 -> {205}; + 183 -> {184} [style=dotted]; + 184 -> {185} [style=dotted]; + 185 -> {186} [style=dotted]; + 186 -> {187} [style=dotted]; + 187 -> {188}; + 188 -> {189}; + 189 -> {192 190}; + 190 -> {191}; + 191 -> {192}; + 192 -> {193}; + 193 -> {195 194}; + 194 -> {201}; + 195 -> {196}; + 196 -> {197}; + 197 -> {198}; + 198 -> {199}; + 199 -> {200}; + 200 -> {201}; + 201 -> {202}; + 202 -> {203}; + 203 -> {204}; + 204 -> {205}; + + subgraph cluster_51 { + color=red + 206 [label="Enter function test_5" style="filled" fillcolor=red]; + subgraph cluster_52 { + color=blue + 207 [label="Enter block"]; + subgraph cluster_53 { + color=blue + 208 [label="Enter when"]; + subgraph cluster_54 { + color=blue + 209 [label="Enter when branch condition "]; + subgraph cluster_55 { + color=blue + 210 [label="Enter ||"]; + 211 [label="Access variable R|/b|"]; + 212 [label="Exit left part of ||"]; + 213 [label="Enter right part of ||"]; + subgraph cluster_56 { + color=blue + 214 [label="Enter when"]; + 215 [label="Access variable R|/a|"]; + 216 [label="Variable declaration: lval : R|A?|"]; + subgraph cluster_57 { + color=blue + 217 [label="Enter when branch condition "]; + 218 [label="Const: Null(null)"]; + 219 [label="Operator =="]; + 220 [label="Exit when branch condition"]; + } + subgraph cluster_58 { + color=blue + 221 [label="Enter when branch condition else"]; + 222 [label="Exit when branch condition"]; + } + 223 [label="Enter when branch result"]; + subgraph cluster_59 { + color=blue + 224 [label="Enter block"]; + 225 [label="Access variable R|/|"]; + 226 [label="Exit block"]; + } + 227 [label="Exit when branch result"]; + 228 [label="Enter when branch result"]; + subgraph cluster_60 { + color=blue + 229 [label="Enter block"]; + 230 [label="Function call: R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; + 231 [label="Throw: throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; + 232 [label="Stub" style="filled" fillcolor=gray]; + 233 [label="Exit block" style="filled" fillcolor=gray]; + } + 234 [label="Exit when branch result" style="filled" fillcolor=gray]; + 235 [label="Exit when"]; + } + 236 [label="Function call: when (lval : R|A?| = R|/a|) { + ==($subj$, Null(null)) -> { + throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() + } + else -> { + R|/| + } +} +.R|/A.foo|()"]; + 237 [label="Exit ||"]; + } + 238 [label="Exit when branch condition"]; + } + 239 [label="Synthetic else branch"]; + 240 [label="Enter when branch result"]; + subgraph cluster_61 { + color=blue + 241 [label="Enter block"]; + 242 [label="Access variable R|/a|"]; + 243 [label="Function call: R|/a|.#()"]; + 244 [label="Exit block"]; + } + 245 [label="Exit when branch result"]; + 246 [label="Exit when"]; + } + 247 [label="Access variable R|/a|"]; + 248 [label="Function call: R|/a|.#()"]; + 249 [label="Exit block"]; + } + 250 [label="Exit function test_5" style="filled" fillcolor=red]; + } + + 206 -> {207}; + 207 -> {208}; + 208 -> {209}; + 209 -> {210}; + 210 -> {211}; + 211 -> {212}; + 212 -> {237 213}; + 213 -> {214}; + 214 -> {215}; + 215 -> {216}; + 216 -> {217}; + 217 -> {218}; + 218 -> {219}; + 219 -> {220}; + 220 -> {228 221}; + 221 -> {222}; + 222 -> {223}; + 223 -> {224}; + 224 -> {225}; + 225 -> {226}; + 226 -> {227}; + 227 -> {235}; + 228 -> {229}; + 229 -> {230}; + 230 -> {231}; + 231 -> {250}; + 231 -> {232} [style=dotted]; + 232 -> {233} [style=dotted]; + 233 -> {234} [style=dotted]; + 234 -> {235} [style=dotted]; + 235 -> {236}; + 236 -> {237}; + 237 -> {238}; + 238 -> {240 239}; + 239 -> {246}; + 240 -> {241}; + 241 -> {242}; + 242 -> {243}; + 243 -> {244}; + 244 -> {245}; + 245 -> {246}; + 246 -> {247}; + 247 -> {248}; + 248 -> {249}; + 249 -> {250}; + +} diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/bangbang.kt b/compiler/fir/resolve/testData/resolve/smartcasts/bangbang.kt new file mode 100644 index 00000000000..5dfa1c4d698 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/bangbang.kt @@ -0,0 +1,43 @@ +interface A { + fun foo(): Boolean +} + +fun test_0(a: A?) { + a!!.foo() + a.foo() +} + +fun test_1(a: A?) { + if (a!!.foo()) { + a.foo() + } + a.foo() +} + +fun test_2(a: A?, b: Boolean) { + if (a!!.foo() && b) { + a.foo() + } + a.foo() +} + +fun test_3(a: A?, b: Boolean) { + if (b && a!!.foo()) { + a.foo() // OK + } + a.foo() // Bad +} + +fun test_4(a: A?, b: Boolean) { + if (a!!.foo() || b) { + a.foo() + } + a.foo() +} + +fun test_5(a: A?, b: Boolean) { + if (b || a!!.foo()) { + a.foo() + } + a.foo() +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/bangbang.txt b/compiler/fir/resolve/testData/resolve/smartcasts/bangbang.txt new file mode 100644 index 00000000000..3f62be93b68 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/bangbang.txt @@ -0,0 +1,102 @@ +FILE: bangbang.kt + public abstract interface A : R|kotlin/Any| { + public abstract fun foo(): R|kotlin/Boolean| + + } + public final fun test_0(a: R|A?|): R|kotlin/Unit| { + when (lval : R|A?| = R|/a|) { + ==($subj$, Null(null)) -> { + throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() + } + else -> { + R|/| + } + } + .R|/A.foo|() + R|/a|.R|/A.foo|() + } + public final fun test_1(a: R|A?|): R|kotlin/Unit| { + when () { + when (lval : R|A?| = R|/a|) { + ==($subj$, Null(null)) -> { + throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() + } + else -> { + R|/| + } + } + .R|/A.foo|() -> { + R|/a|.R|/A.foo|() + } + } + + R|/a|.R|/A.foo|() + } + public final fun test_2(a: R|A?|, b: R|kotlin/Boolean|): R|kotlin/Unit| { + when () { + when (lval : R|A?| = R|/a|) { + ==($subj$, Null(null)) -> { + throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() + } + else -> { + R|/| + } + } + .R|/A.foo|() && R|/b| -> { + R|/a|.R|/A.foo|() + } + } + + R|/a|.R|/A.foo|() + } + public final fun test_3(a: R|A?|, b: R|kotlin/Boolean|): R|kotlin/Unit| { + when () { + R|/b| && when (lval : R|A?| = R|/a|) { + ==($subj$, Null(null)) -> { + throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() + } + else -> { + R|/| + } + } + .R|/A.foo|() -> { + R|/a|.R|/A.foo|() + } + } + + R|/a|.#() + } + public final fun test_4(a: R|A?|, b: R|kotlin/Boolean|): R|kotlin/Unit| { + when () { + when (lval : R|A?| = R|/a|) { + ==($subj$, Null(null)) -> { + throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() + } + else -> { + R|/| + } + } + .R|/A.foo|() || R|/b| -> { + R|/a|.R|/A.foo|() + } + } + + R|/a|.R|/A.foo|() + } + public final fun test_5(a: R|A?|, b: R|kotlin/Boolean|): R|kotlin/Unit| { + when () { + R|/b| || when (lval : R|A?| = R|/a|) { + ==($subj$, Null(null)) -> { + throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() + } + else -> { + R|/| + } + } + .R|/A.foo|() -> { + R|/a|.#() + } + } + + R|/a|.#() + } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithCfgTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithCfgTestGenerated.java index 3578dabb275..747aff3857d 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithCfgTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithCfgTestGenerated.java @@ -108,6 +108,11 @@ public class FirDiagnosticsWithCfgTestGenerated extends AbstractFirDiagnosticsWi KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/fir/resolve/testData/resolve/smartcasts"), Pattern.compile("^([^.]+)\\.kt$"), true); } + @TestMetadata("bangbang.kt") + public void testBangbang() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/smartcasts/bangbang.kt"); + } + @TestMetadata("booleanOperators.kt") public void testBooleanOperators() throws Exception { runTest("compiler/fir/resolve/testData/resolve/smartcasts/booleanOperators.kt");