diff --git a/compiler/fir/analysis-tests/testData/resolve/inference/kt53494.fir.txt b/compiler/fir/analysis-tests/testData/resolve/inference/kt53494.fir.txt index 25893b73a87..5c9468abd4e 100644 --- a/compiler/fir/analysis-tests/testData/resolve/inference/kt53494.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/inference/kt53494.fir.txt @@ -46,11 +46,16 @@ FILE: kt53494.kt !=(R|/t|, Null(null)) -> { R|/Wrapped.Wrapped|(R|/t|) } - (R|/t| is R|T|) -> { - R|/Wrapped.Wrapped|(R|/t|) - } else -> { - Q|Default| + when () { + (R|/t| is R|T|) -> { + R|/Wrapped.Wrapped|(R|/t|) + } + else -> { + Q|Default| + } + } + } } diff --git a/compiler/fir/analysis-tests/testData/resolve/smartcasts/receivers/implicitReceivers.dot b/compiler/fir/analysis-tests/testData/resolve/smartcasts/receivers/implicitReceivers.dot index e139e5b7f3f..03316ac6ee0 100644 --- a/compiler/fir/analysis-tests/testData/resolve/smartcasts/receivers/implicitReceivers.dot +++ b/compiler/fir/analysis-tests/testData/resolve/smartcasts/receivers/implicitReceivers.dot @@ -506,151 +506,168 @@ digraph implicitReceivers_kt { } subgraph cluster_56 { color=blue - 166 [label="Enter when branch condition "]; - 167 [label="Access variable this@R|/test_4|"]; - 168 [label="Smart cast: this@R|/test_4|"]; - 169 [label="Type operator: (this@R|/test_4| !is R|B|)"]; - 170 [label="Exit when branch condition"]; + 166 [label="Enter when branch condition else"]; + 167 [label="Exit when branch condition"]; } + 168 [label="Enter when branch result"]; subgraph cluster_57 { color=blue - 171 [label="Enter when branch condition else"]; - 172 [label="Exit when branch condition"]; + 169 [label="Enter block"]; + subgraph cluster_58 { + color=blue + 170 [label="Enter when"]; + subgraph cluster_59 { + color=blue + 171 [label="Enter when branch condition "]; + 172 [label="Access variable this@R|/test_4|"]; + 173 [label="Smart cast: this@R|/test_4|"]; + 174 [label="Type operator: (this@R|/test_4| !is R|B|)"]; + 175 [label="Exit when branch condition"]; + } + subgraph cluster_60 { + color=blue + 176 [label="Enter when branch condition else"]; + 177 [label="Exit when branch condition"]; + } + 178 [label="Enter when branch result"]; + subgraph cluster_61 { + color=blue + 179 [label="Enter block"]; + subgraph cluster_62 { + color=blue + 180 [label="Function call arguments enter"]; + 181 [label="Access variable this@R|/test_4|"]; + 182 [label="Smart cast: this@R|/test_4|"]; + 183 [label="Function call arguments exit"]; + } + 184 [label="Function call: this@R|/test_4|.R|/A.foo|()" style="filled" fillcolor=yellow]; + subgraph cluster_63 { + color=blue + 185 [label="Function call arguments enter"]; + 186 [label="Function call arguments exit"]; + } + 187 [label="Function call: this@R|/test_4|.R|/A.foo|()" style="filled" fillcolor=yellow]; + subgraph cluster_64 { + color=blue + 188 [label="Function call arguments enter"]; + 189 [label="Access variable this@R|/test_4|"]; + 190 [label="Smart cast: this@R|/test_4|"]; + 191 [label="Function call arguments exit"]; + } + 192 [label="Function call: this@R|/test_4|.R|/B.bar|()" style="filled" fillcolor=yellow]; + subgraph cluster_65 { + color=blue + 193 [label="Function call arguments enter"]; + 194 [label="Function call arguments exit"]; + } + 195 [label="Function call: this@R|/test_4|.R|/B.bar|()" style="filled" fillcolor=yellow]; + 196 [label="Exit block"]; + } + 197 [label="Exit when branch result"]; + 198 [label="Enter when branch result"]; + subgraph cluster_66 { + color=blue + 199 [label="Enter block"]; + subgraph cluster_67 { + color=blue + 200 [label="Function call arguments enter"]; + 201 [label="Access variable this@R|/test_4|"]; + 202 [label="Smart cast: this@R|/test_4|"]; + 203 [label="Function call arguments exit"]; + } + 204 [label="Function call: this@R|/test_4|.#()" style="filled" fillcolor=yellow]; + subgraph cluster_68 { + color=blue + 205 [label="Function call arguments enter"]; + 206 [label="Function call arguments exit"]; + } + 207 [label="Function call: #()" style="filled" fillcolor=yellow]; + subgraph cluster_69 { + color=blue + 208 [label="Function call arguments enter"]; + 209 [label="Access variable this@R|/test_4|"]; + 210 [label="Smart cast: this@R|/test_4|"]; + 211 [label="Function call arguments exit"]; + } + 212 [label="Function call: this@R|/test_4|.R|/A.foo|()" style="filled" fillcolor=yellow]; + subgraph cluster_70 { + color=blue + 213 [label="Function call arguments enter"]; + 214 [label="Function call arguments exit"]; + } + 215 [label="Function call: this@R|/test_4|.R|/A.foo|()" style="filled" fillcolor=yellow]; + 216 [label="Exit block"]; + } + 217 [label="Exit when branch result"]; + 218 [label="Exit when"]; + } + 219 [label="Exit block"]; } - 173 [label="Enter when branch result"]; - subgraph cluster_58 { + 220 [label="Exit when branch result"]; + 221 [label="Enter when branch result"]; + subgraph cluster_71 { color=blue - 174 [label="Enter block"]; - subgraph cluster_59 { - color=blue - 175 [label="Function call arguments enter"]; - 176 [label="Access variable this@R|/test_4|"]; - 177 [label="Smart cast: this@R|/test_4|"]; - 178 [label="Function call arguments exit"]; - } - 179 [label="Function call: this@R|/test_4|.R|/A.foo|()" style="filled" fillcolor=yellow]; - subgraph cluster_60 { - color=blue - 180 [label="Function call arguments enter"]; - 181 [label="Function call arguments exit"]; - } - 182 [label="Function call: this@R|/test_4|.R|/A.foo|()" style="filled" fillcolor=yellow]; - subgraph cluster_61 { - color=blue - 183 [label="Function call arguments enter"]; - 184 [label="Access variable this@R|/test_4|"]; - 185 [label="Smart cast: this@R|/test_4|"]; - 186 [label="Function call arguments exit"]; - } - 187 [label="Function call: this@R|/test_4|.R|/B.bar|()" style="filled" fillcolor=yellow]; - subgraph cluster_62 { - color=blue - 188 [label="Function call arguments enter"]; - 189 [label="Function call arguments exit"]; - } - 190 [label="Function call: this@R|/test_4|.R|/B.bar|()" style="filled" fillcolor=yellow]; - 191 [label="Exit block"]; - } - 192 [label="Exit when branch result"]; - 193 [label="Enter when branch result"]; - subgraph cluster_63 { - color=blue - 194 [label="Enter block"]; - subgraph cluster_64 { - color=blue - 195 [label="Function call arguments enter"]; - 196 [label="Access variable this@R|/test_4|"]; - 197 [label="Smart cast: this@R|/test_4|"]; - 198 [label="Function call arguments exit"]; - } - 199 [label="Function call: this@R|/test_4|.#()" style="filled" fillcolor=yellow]; - subgraph cluster_65 { - color=blue - 200 [label="Function call arguments enter"]; - 201 [label="Function call arguments exit"]; - } - 202 [label="Function call: #()" style="filled" fillcolor=yellow]; - subgraph cluster_66 { - color=blue - 203 [label="Function call arguments enter"]; - 204 [label="Access variable this@R|/test_4|"]; - 205 [label="Smart cast: this@R|/test_4|"]; - 206 [label="Function call arguments exit"]; - } - 207 [label="Function call: this@R|/test_4|.R|/A.foo|()" style="filled" fillcolor=yellow]; - subgraph cluster_67 { - color=blue - 208 [label="Function call arguments enter"]; - 209 [label="Function call arguments exit"]; - } - 210 [label="Function call: this@R|/test_4|.R|/A.foo|()" style="filled" fillcolor=yellow]; - 211 [label="Exit block"]; - } - 212 [label="Exit when branch result"]; - 213 [label="Enter when branch result"]; - subgraph cluster_68 { - color=blue - 214 [label="Enter block"]; - subgraph cluster_69 { - color=blue - 215 [label="Function call arguments enter"]; - 216 [label="Access variable this@R|/test_4|"]; - 217 [label="Function call arguments exit"]; - } - 218 [label="Function call: this@R|/test_4|.#()" style="filled" fillcolor=yellow]; - subgraph cluster_70 { - color=blue - 219 [label="Function call arguments enter"]; - 220 [label="Function call arguments exit"]; - } - 221 [label="Function call: #()" style="filled" fillcolor=yellow]; - subgraph cluster_71 { - color=blue - 222 [label="Function call arguments enter"]; - 223 [label="Access variable this@R|/test_4|"]; - 224 [label="Function call arguments exit"]; - } - 225 [label="Function call: this@R|/test_4|.#()" style="filled" fillcolor=yellow]; + 222 [label="Enter block"]; subgraph cluster_72 { color=blue - 226 [label="Function call arguments enter"]; - 227 [label="Function call arguments exit"]; + 223 [label="Function call arguments enter"]; + 224 [label="Access variable this@R|/test_4|"]; + 225 [label="Function call arguments exit"]; } - 228 [label="Function call: #()" style="filled" fillcolor=yellow]; - 229 [label="Exit block"]; + 226 [label="Function call: this@R|/test_4|.#()" style="filled" fillcolor=yellow]; + subgraph cluster_73 { + color=blue + 227 [label="Function call arguments enter"]; + 228 [label="Function call arguments exit"]; + } + 229 [label="Function call: #()" style="filled" fillcolor=yellow]; + subgraph cluster_74 { + color=blue + 230 [label="Function call arguments enter"]; + 231 [label="Access variable this@R|/test_4|"]; + 232 [label="Function call arguments exit"]; + } + 233 [label="Function call: this@R|/test_4|.#()" style="filled" fillcolor=yellow]; + subgraph cluster_75 { + color=blue + 234 [label="Function call arguments enter"]; + 235 [label="Function call arguments exit"]; + } + 236 [label="Function call: #()" style="filled" fillcolor=yellow]; + 237 [label="Exit block"]; } - 230 [label="Exit when branch result"]; - 231 [label="Exit when"]; + 238 [label="Exit when branch result"]; + 239 [label="Exit when"]; } - subgraph cluster_73 { - color=blue - 232 [label="Function call arguments enter"]; - 233 [label="Access variable this@R|/test_4|"]; - 234 [label="Function call arguments exit"]; - } - 235 [label="Function call: this@R|/test_4|.#()" style="filled" fillcolor=yellow]; - subgraph cluster_74 { - color=blue - 236 [label="Function call arguments enter"]; - 237 [label="Function call arguments exit"]; - } - 238 [label="Function call: #()" style="filled" fillcolor=yellow]; - subgraph cluster_75 { - color=blue - 239 [label="Function call arguments enter"]; - 240 [label="Access variable this@R|/test_4|"]; - 241 [label="Function call arguments exit"]; - } - 242 [label="Function call: this@R|/test_4|.#()" style="filled" fillcolor=yellow]; subgraph cluster_76 { color=blue - 243 [label="Function call arguments enter"]; - 244 [label="Function call arguments exit"]; + 240 [label="Function call arguments enter"]; + 241 [label="Access variable this@R|/test_4|"]; + 242 [label="Function call arguments exit"]; } - 245 [label="Function call: #()" style="filled" fillcolor=yellow]; - 246 [label="Exit block"]; + 243 [label="Function call: this@R|/test_4|.#()" style="filled" fillcolor=yellow]; + subgraph cluster_77 { + color=blue + 244 [label="Function call arguments enter"]; + 245 [label="Function call arguments exit"]; + } + 246 [label="Function call: #()" style="filled" fillcolor=yellow]; + subgraph cluster_78 { + color=blue + 247 [label="Function call arguments enter"]; + 248 [label="Access variable this@R|/test_4|"]; + 249 [label="Function call arguments exit"]; + } + 250 [label="Function call: this@R|/test_4|.#()" style="filled" fillcolor=yellow]; + subgraph cluster_79 { + color=blue + 251 [label="Function call arguments enter"]; + 252 [label="Function call arguments exit"]; + } + 253 [label="Function call: #()" style="filled" fillcolor=yellow]; + 254 [label="Exit block"]; } - 247 [label="Exit function test_4" style="filled" fillcolor=red]; + 255 [label="Exit function test_4" style="filled" fillcolor=red]; } 159 -> {160}; 160 -> {161}; @@ -658,17 +675,17 @@ digraph implicitReceivers_kt { 162 -> {163}; 163 -> {164}; 164 -> {165}; - 165 -> {166 213}; + 165 -> {166 221}; 166 -> {167}; 167 -> {168}; 168 -> {169}; 169 -> {170}; - 170 -> {171 193}; + 170 -> {171}; 171 -> {172}; 172 -> {173}; 173 -> {174}; 174 -> {175}; - 175 -> {176}; + 175 -> {176 198}; 176 -> {177}; 177 -> {178}; 178 -> {179}; @@ -685,12 +702,12 @@ digraph implicitReceivers_kt { 189 -> {190}; 190 -> {191}; 191 -> {192}; - 192 -> {231}; + 192 -> {193}; 193 -> {194}; 194 -> {195}; 195 -> {196}; 196 -> {197}; - 197 -> {198}; + 197 -> {218}; 198 -> {199}; 199 -> {200}; 200 -> {201}; @@ -705,7 +722,7 @@ digraph implicitReceivers_kt { 209 -> {210}; 210 -> {211}; 211 -> {212}; - 212 -> {231}; + 212 -> {213}; 213 -> {214}; 214 -> {215}; 215 -> {216}; @@ -713,7 +730,7 @@ digraph implicitReceivers_kt { 217 -> {218}; 218 -> {219}; 219 -> {220}; - 220 -> {221}; + 220 -> {239}; 221 -> {222}; 222 -> {223}; 223 -> {224}; @@ -740,62 +757,70 @@ digraph implicitReceivers_kt { 244 -> {245}; 245 -> {246}; 246 -> {247}; + 247 -> {248}; + 248 -> {249}; + 249 -> {250}; + 250 -> {251}; + 251 -> {252}; + 252 -> {253}; + 253 -> {254}; + 254 -> {255}; - subgraph cluster_77 { + subgraph cluster_80 { color=red - 248 [label="Enter function test_5" style="filled" fillcolor=red]; - subgraph cluster_78 { + 256 [label="Enter function test_5" style="filled" fillcolor=red]; + subgraph cluster_81 { color=blue - 249 [label="Enter block"]; - subgraph cluster_79 { + 257 [label="Enter block"]; + subgraph cluster_82 { color=blue - 250 [label="Enter when"]; - subgraph cluster_80 { - color=blue - 251 [label="Enter when branch condition "]; - 252 [label="Access variable this@R|/test_5|"]; - 253 [label="Type operator: (this@R|/test_5| is R|kotlin/collections/List<*>|)"]; - 254 [label="Exit when branch condition"]; - } - subgraph cluster_81 { - color=blue - 255 [label="Enter when branch condition "]; - 256 [label="Access variable this@R|/test_5|"]; - 257 [label="Type operator: (this@R|/test_5| is R|kotlin/String|)"]; - 258 [label="Exit when branch condition"]; - } - subgraph cluster_82 { - color=blue - 259 [label="Enter when branch condition else"]; - 260 [label="Exit when branch condition"]; - } - 261 [label="Enter when branch result"]; + 258 [label="Enter when"]; subgraph cluster_83 { color=blue - 262 [label="Enter block"]; - 263 [label="Const: Int(0)"]; - 264 [label="Exit block"]; + 259 [label="Enter when branch condition "]; + 260 [label="Access variable this@R|/test_5|"]; + 261 [label="Type operator: (this@R|/test_5| is R|kotlin/collections/List<*>|)"]; + 262 [label="Exit when branch condition"]; } - 265 [label="Exit when branch result"]; - 266 [label="Enter when branch result"]; subgraph cluster_84 { color=blue - 267 [label="Enter block"]; - 268 [label="Access variable R|kotlin/String.length|"]; - 269 [label="Exit block"]; + 263 [label="Enter when branch condition "]; + 264 [label="Access variable this@R|/test_5|"]; + 265 [label="Type operator: (this@R|/test_5| is R|kotlin/String|)"]; + 266 [label="Exit when branch condition"]; } - 270 [label="Exit when branch result"]; - 271 [label="Enter when branch result"]; subgraph cluster_85 { color=blue - 272 [label="Enter block"]; - 273 [label="Access variable R|SubstitutionOverride|"]; - 274 [label="Exit block"]; + 267 [label="Enter when branch condition else"]; + 268 [label="Exit when branch condition"]; } - 275 [label="Exit when branch result"]; - 276 [label="Exit when"]; + 269 [label="Enter when branch result"]; + subgraph cluster_86 { + color=blue + 270 [label="Enter block"]; + 271 [label="Const: Int(0)"]; + 272 [label="Exit block"]; + } + 273 [label="Exit when branch result"]; + 274 [label="Enter when branch result"]; + subgraph cluster_87 { + color=blue + 275 [label="Enter block"]; + 276 [label="Access variable R|kotlin/String.length|"]; + 277 [label="Exit block"]; + } + 278 [label="Exit when branch result"]; + 279 [label="Enter when branch result"]; + subgraph cluster_88 { + color=blue + 280 [label="Enter block"]; + 281 [label="Access variable R|SubstitutionOverride|"]; + 282 [label="Exit block"]; + } + 283 [label="Exit when branch result"]; + 284 [label="Exit when"]; } - 277 [label="Jump: ^test_5 when () { + 285 [label="Jump: ^test_5 when () { (this@R|/test_5| is R|kotlin/collections/List<*>|) -> { this@R|/test_5|.R|SubstitutionOverride| } @@ -807,71 +832,71 @@ digraph implicitReceivers_kt { } } "]; - 278 [label="Stub" style="filled" fillcolor=gray]; - 279 [label="Exit block" style="filled" fillcolor=gray]; + 286 [label="Stub" style="filled" fillcolor=gray]; + 287 [label="Exit block" style="filled" fillcolor=gray]; } - 280 [label="Exit function test_5" style="filled" fillcolor=red]; + 288 [label="Exit function test_5" style="filled" fillcolor=red]; } - 248 -> {249}; - 249 -> {250}; - 250 -> {251}; - 251 -> {252}; - 252 -> {253}; - 253 -> {254}; - 254 -> {255 271}; - 255 -> {256}; 256 -> {257}; 257 -> {258}; - 258 -> {259 266}; + 258 -> {259}; 259 -> {260}; 260 -> {261}; 261 -> {262}; - 262 -> {263}; + 262 -> {263 279}; 263 -> {264}; 264 -> {265}; - 265 -> {276}; - 266 -> {267}; + 265 -> {266}; + 266 -> {267 274}; 267 -> {268}; 268 -> {269}; 269 -> {270}; - 270 -> {276}; + 270 -> {271}; 271 -> {272}; 272 -> {273}; - 273 -> {274}; + 273 -> {284}; 274 -> {275}; 275 -> {276}; 276 -> {277}; - 277 -> {280}; - 277 -> {278} [style=dotted]; - 278 -> {279} [style=dotted]; - 279 -> {280} [style=dotted]; - - subgraph cluster_86 { - color=red - 281 [label="Enter function test_6" style="filled" fillcolor=red]; - subgraph cluster_87 { - color=blue - 282 [label="Enter block"]; - 283 [label="Access variable this@R|/test_6|"]; - 284 [label="Type operator: (this@R|/test_6| as R|kotlin/collections/List<*>|)"]; - 285 [label="Access variable R|SubstitutionOverride|"]; - 286 [label="Access variable this@R|/test_6|"]; - 287 [label="Smart cast: this@R|/test_6|"]; - 288 [label="Type operator: (this@R|/test_6| as R|kotlin/String|)"]; - 289 [label="Access variable R|kotlin/String.length|"]; - 290 [label="Exit block"]; - } - 291 [label="Exit function test_6" style="filled" fillcolor=red]; - } + 277 -> {278}; + 278 -> {284}; + 279 -> {280}; + 280 -> {281}; 281 -> {282}; 282 -> {283}; 283 -> {284}; 284 -> {285}; - 285 -> {286}; - 286 -> {287}; - 287 -> {288}; - 288 -> {289}; + 285 -> {288}; + 285 -> {286} [style=dotted]; + 286 -> {287} [style=dotted]; + 287 -> {288} [style=dotted]; + + subgraph cluster_89 { + color=red + 289 [label="Enter function test_6" style="filled" fillcolor=red]; + subgraph cluster_90 { + color=blue + 290 [label="Enter block"]; + 291 [label="Access variable this@R|/test_6|"]; + 292 [label="Type operator: (this@R|/test_6| as R|kotlin/collections/List<*>|)"]; + 293 [label="Access variable R|SubstitutionOverride|"]; + 294 [label="Access variable this@R|/test_6|"]; + 295 [label="Smart cast: this@R|/test_6|"]; + 296 [label="Type operator: (this@R|/test_6| as R|kotlin/String|)"]; + 297 [label="Access variable R|kotlin/String.length|"]; + 298 [label="Exit block"]; + } + 299 [label="Exit function test_6" style="filled" fillcolor=red]; + } 289 -> {290}; 290 -> {291}; + 291 -> {292}; + 292 -> {293}; + 293 -> {294}; + 294 -> {295}; + 295 -> {296}; + 296 -> {297}; + 297 -> {298}; + 298 -> {299}; } diff --git a/compiler/fir/analysis-tests/testData/resolve/smartcasts/receivers/implicitReceivers.fir.txt b/compiler/fir/analysis-tests/testData/resolve/smartcasts/receivers/implicitReceivers.fir.txt index d1cd23534a3..e4ef5d01df9 100644 --- a/compiler/fir/analysis-tests/testData/resolve/smartcasts/receivers/implicitReceivers.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/smartcasts/receivers/implicitReceivers.fir.txt @@ -73,17 +73,22 @@ FILE: implicitReceivers.kt this@R|/test_4|.#() #() } - (this@R|/test_4| !is R|B|) -> { - this@R|/test_4|.#() - #() - this@R|/test_4|.R|/A.foo|() - this@R|/test_4|.R|/A.foo|() - } else -> { - this@R|/test_4|.R|/A.foo|() - this@R|/test_4|.R|/A.foo|() - this@R|/test_4|.R|/B.bar|() - this@R|/test_4|.R|/B.bar|() + when () { + (this@R|/test_4| !is R|B|) -> { + this@R|/test_4|.#() + #() + this@R|/test_4|.R|/A.foo|() + this@R|/test_4|.R|/A.foo|() + } + else -> { + this@R|/test_4|.R|/A.foo|() + this@R|/test_4|.R|/A.foo|() + this@R|/test_4|.R|/B.bar|() + this@R|/test_4|.R|/B.bar|() + } + } + } } diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt index 3424cbc4f46..6125d1c4f9c 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt @@ -1248,14 +1248,17 @@ class Fir2IrVisitor( if (whenExpression.branches.isEmpty()) { return@convertWithOffsets IrBlockImpl(startOffset, endOffset, irBuiltIns.unitType, origin) } + val isProperlyExhaustive = whenExpression.isDeeplyProperlyExhaustive() val whenExpressionType = - if (whenExpression.isProperlyExhaustive && whenExpression.branches.none { + if (isProperlyExhaustive && whenExpression.branches.none { it.condition is FirElseIfTrueCondition && it.result.statements.isEmpty() }) whenExpression.resolvedType else session.builtinTypes.unitType.type - val irBranches = whenExpression.branches.mapTo(mutableListOf()) { branch -> - branch.toIrWhenBranch(whenExpressionType) - } - if (whenExpression.isProperlyExhaustive && whenExpression.branches.none { it.condition is FirElseIfTrueCondition }) { + val irBranches = whenExpression.convertWhenBranchesTo( + mutableListOf(), + whenExpressionType, + flattenElse = origin == IrStatementOrigin.IF, + ) + if (isProperlyExhaustive && whenExpression.branches.none { it.condition is FirElseIfTrueCondition }) { val irResult = IrCallImpl( startOffset, endOffset, irBuiltIns.nothingType, irBuiltIns.noWhenBranchMatchedExceptionSymbol, @@ -1273,6 +1276,57 @@ class Fir2IrVisitor( } } + /** + * TODO this shouldn't be required anymore once KT-65997 is fixed. + */ + private fun FirWhenExpression.isDeeplyProperlyExhaustive(): Boolean { + if (!isProperlyExhaustive) { + return false + } + + val nestedElseIfExpression = branches.lastOrNull()?.nestedElseIfOrNull() ?: return true + return nestedElseIfExpression.isDeeplyProperlyExhaustive() + } + + /** + * Converts the branches to [IrBranch]es. + * + * If [flattenElse] is `true` and the else branch contains another [FirWhenExpression] that's built from an `if`, + * its branches will be added directly to the [result] list instead. + * + * TODO this shouldn't be required anymore once KT-65997 is fixed. + */ + private fun FirWhenExpression.convertWhenBranchesTo( + result: MutableList, + whenExpressionType: ConeKotlinType, + flattenElse: Boolean, + ): MutableList { + for (branch in branches) { + if (flattenElse) { + val elseIfExpression = branch.nestedElseIfOrNull() + if (elseIfExpression != null) { + elseIfExpression.convertWhenBranchesTo(result, whenExpressionType, flattenElse = true) + break + } + } + + result.add(branch.toIrWhenBranch(whenExpressionType)) + } + + return result + } + + private fun FirWhenBranch.nestedElseIfOrNull(): FirWhenExpression? { + if (condition is FirElseIfTrueCondition) { + val elseWhenExpression = (result as? FirSingleExpressionBlock)?.statement as? FirWhenExpression + if (elseWhenExpression != null && elseWhenExpression.source?.elementType == KtNodeTypes.IF) { + return elseWhenExpression + } + } + + return null + } + private fun generateWhen( startOffset: Int, endOffset: Int, diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirExpressionBuilder.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirExpressionBuilder.kt index 50e86107ed6..75c5c7c5497 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirExpressionBuilder.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirExpressionBuilder.kt @@ -1335,42 +1335,28 @@ class LightTreeRawFirExpressionBuilder( * @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.visitIfExpression */ private fun convertIfExpression(ifExpression: LighterASTNode): FirExpression { - var components = parseIfExpression(ifExpression) - return buildWhenExpression { source = ifExpression.toFirSourceElement() - whenBranches@ while (true) { - with(components) { - val trueBranch = convertLoopBody(thenBlock) - branches += buildWhenBranch { - source = firCondition?.source - condition = firCondition ?: buildErrorExpression( - null, - ConeSyntaxDiagnostic("If statement should have condition") - ) - result = trueBranch - } + with(parseIfExpression(ifExpression)) { + val trueBranch = convertLoopBody(thenBlock) + branches += buildWhenBranch { + source = firCondition?.source + condition = firCondition ?: buildErrorExpression( + null, + ConeSyntaxDiagnostic("If statement should have condition") + ) + result = trueBranch } - if (components.elseBlock == null) break@whenBranches - var cascadeIf = false - components.elseBlock?.forEachChildren { - if (it.tokenType == IF) { - cascadeIf = true - components = parseIfExpression(it) - } - } - if (!cascadeIf) { - with(components) { - val elseBranch = convertLoopOrIfBody(elseBlock) - if (elseBranch != null) { - branches += buildWhenBranch { - source = elseBlock?.toFirSourceElement() - condition = buildElseIfTrueCondition() - result = elseBranch - } + + if (elseBlock != null) { + val elseBranch = convertLoopOrIfBody(elseBlock) + if (elseBranch != null) { + branches += buildWhenBranch { + source = elseBlock.toFirSourceElement() + condition = buildElseIfTrueCondition() + result = elseBranch } } - break@whenBranches } } usedAsExpression = ifExpression.usedAsExpression diff --git a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/PsiRawFirBuilder.kt b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/PsiRawFirBuilder.kt index 95479a23620..474e792590c 100644 --- a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/PsiRawFirBuilder.kt +++ b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/PsiRawFirBuilder.kt @@ -2555,26 +2555,18 @@ open class PsiRawFirBuilder( return buildWhenExpression { source = expression.toFirSourceElement() - var ktLastIf: KtIfExpression = expression - whenBranches@ while (true) { - val ktCondition = ktLastIf.condition - branches += buildWhenBranch { - source = ktCondition?.toFirSourceElement(KtFakeSourceElementKind.WhenCondition) - condition = ktCondition.toFirExpression("If statement should have condition") - result = ktLastIf.then.toFirBlock() - } + val ktCondition = expression.condition + branches += buildWhenBranch { + source = ktCondition?.toFirSourceElement(KtFakeSourceElementKind.WhenCondition) + condition = ktCondition.toFirExpression("If statement should have condition") + result = expression.then.toFirBlock() + } - when (val ktElse = ktLastIf.`else`) { - null -> break@whenBranches - is KtIfExpression -> ktLastIf = ktElse - else -> { - branches += buildWhenBranch { - source = ktLastIf.elseKeyword?.toKtPsiSourceElement() - condition = buildElseIfTrueCondition() - result = ktLastIf.`else`.toFirBlock() - } - break@whenBranches - } + if (expression.`else` != null) { + branches += buildWhenBranch { + source = expression.elseKeyword?.toKtPsiSourceElement() + condition = buildElseIfTrueCondition() + result = expression.`else`.toFirBlock() } } diff --git a/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/cascadeIf.txt b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/cascadeIf.txt index fc392facd6f..5e41f1e5366 100644 --- a/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/cascadeIf.txt +++ b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/cascadeIf.txt @@ -4,17 +4,22 @@ FILE: cascadeIf.kt first# -> { ^foo IntegerLiteral(4) } - second# -> { - lval x: = IntegerLiteral(3) - ^foo x#.plus#(IntegerLiteral(2)) - } else -> { when () { - third# -> { - ^foo IntegerLiteral(0) + second# -> { + lval x: = IntegerLiteral(3) + ^foo x#.plus#(IntegerLiteral(2)) } else -> { - ^foo IntegerLiteral(-1) + when () { + third# -> { + ^foo IntegerLiteral(0) + } + else -> { + ^foo IntegerLiteral(-1) + } + } + } } diff --git a/compiler/testData/diagnostics/tests/inference/nestedIfWithExpectedType.kt b/compiler/testData/diagnostics/tests/inference/nestedIfWithExpectedType.kt index e8a075aaa37..c422e598ea3 100644 --- a/compiler/testData/diagnostics/tests/inference/nestedIfWithExpectedType.kt +++ b/compiler/testData/diagnostics/tests/inference/nestedIfWithExpectedType.kt @@ -3,7 +3,15 @@ interface Promise fun materializePromise(): Promise = TODO() -fun foo(x: Int, p1: Promise, p2: Promise): Promise<*> { +fun foo0(x: Int, p2: Promise): Promise<*> { + return if (x == 3) { + materializePromise() + } else { + p2 + } +} + +fun foo1(x: Int, p1: Promise, p2: Promise): Promise<*> { return if (x == 0) { p1 } else { @@ -15,10 +23,12 @@ fun foo(x: Int, p1: Promise, p2: Promise): Promise<*> { } } -fun foo2(x: Int, p2: Promise): Promise<*> { - return if (x == 3) { - materializePromise() +fun foo2(x: Int, p1: Promise): Promise<*> { + return if (x == 0) { + p1 + } else if (x == 2) { + materializePromise() } else { - p2 + p1 } -} \ No newline at end of file +} diff --git a/compiler/tests-spec/testData/diagnostics/linked/control--and-data-flow-analysis/control-flow-graph/expressions-1/conditional-expressions/p-1/neg/1.2.fir.kt b/compiler/tests-spec/testData/diagnostics/linked/control--and-data-flow-analysis/control-flow-graph/expressions-1/conditional-expressions/p-1/neg/1.2.fir.kt deleted file mode 100644 index ffd3b4d115f..00000000000 --- a/compiler/tests-spec/testData/diagnostics/linked/control--and-data-flow-analysis/control-flow-graph/expressions-1/conditional-expressions/p-1/neg/1.2.fir.kt +++ /dev/null @@ -1,31 +0,0 @@ -// !DIAGNOSTICS: -IMPLICIT_CAST_TO_ANY -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -UNUSED_VALUE -UNUSED_PARAMETER -UNUSED_EXPRESSION -// SKIP_TXT - -/* - * KOTLIN DIAGNOSTICS SPEC TEST (NEGATIVE) - * - * SPEC VERSION: 0.1-296 - * MAIN LINK: control--and-data-flow-analysis, control-flow-graph, expressions-1, conditional-expressions -> paragraph 1 -> sentence 1 - * NUMBER: 2 - * DESCRIPTION: check if-expressions must have both branches. - */ - -// TESTCASE NUMBER: 1 - -fun case1() { - val b = true - val c = true - val a = if (b) { - "first true" - } else if (c) { - "else if true" - } -} - -// TESTCASE NUMBER: 2 - -fun case2() { - var b = true - val c = true - val a = if (b) 1 else if (c) 2 else ; -} diff --git a/compiler/tests-spec/testData/diagnostics/linked/control--and-data-flow-analysis/control-flow-graph/expressions-1/conditional-expressions/p-1/neg/1.2.kt b/compiler/tests-spec/testData/diagnostics/linked/control--and-data-flow-analysis/control-flow-graph/expressions-1/conditional-expressions/p-1/neg/1.2.kt index 44e2ecf1b49..c31c474043e 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/control--and-data-flow-analysis/control-flow-graph/expressions-1/conditional-expressions/p-1/neg/1.2.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/control--and-data-flow-analysis/control-flow-graph/expressions-1/conditional-expressions/p-1/neg/1.2.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -IMPLICIT_CAST_TO_ANY -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -UNUSED_VALUE -UNUSED_PARAMETER -UNUSED_EXPRESSION // SKIP_TXT diff --git a/compiler/tests-spec/testData/diagnostics/linked/control--and-data-flow-analysis/control-flow-graph/expressions-1/conditional-expressions/p-1/neg/1.3.fir.kt b/compiler/tests-spec/testData/diagnostics/linked/control--and-data-flow-analysis/control-flow-graph/expressions-1/conditional-expressions/p-1/neg/1.3.fir.kt index 9ca8f8a952b..c0c3b6deef6 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/control--and-data-flow-analysis/control-flow-graph/expressions-1/conditional-expressions/p-1/neg/1.3.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/control--and-data-flow-analysis/control-flow-graph/expressions-1/conditional-expressions/p-1/neg/1.3.fir.kt @@ -36,7 +36,7 @@ fun case4() { val x2 = if (TODO()) true - val x0 = if (false) true else if (throw Exception()) ; + val x0 = if (false) true else if (throw Exception()) ; } diff --git a/compiler/tests-spec/testData/diagnostics/linked/control--and-data-flow-analysis/control-flow-graph/expressions-1/conditional-expressions/p-1/neg/1.4.fir.kt b/compiler/tests-spec/testData/diagnostics/linked/control--and-data-flow-analysis/control-flow-graph/expressions-1/conditional-expressions/p-1/neg/1.4.fir.kt index 37e7fa6d089..50dc462a1a8 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/control--and-data-flow-analysis/control-flow-graph/expressions-1/conditional-expressions/p-1/neg/1.4.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/control--and-data-flow-analysis/control-flow-graph/expressions-1/conditional-expressions/p-1/neg/1.4.fir.kt @@ -43,5 +43,5 @@ fun case4(nothing: Nothing) { // TESTCASE NUMBER: 5 fun case5(nothing: Nothing) { - val x = if (false) else if (nothing) { "foo"} else ; + val x = if (false) else if (nothing) { "foo"} else ; }