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 b295b663c54..dce2c9c39e5 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 @@ -353,15 +353,20 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC fun exitWhenExpression(whenExpression: FirWhenExpression) { val (whenExitNode, syntheticElseNode) = graphBuilder.exitWhenExpression(whenExpression) if (syntheticElseNode != null) { - val previousConditionExitNode = syntheticElseNode.previousNodes.single() as WhenBranchConditionExitNode syntheticElseNode.mergeIncomingFlow() - syntheticElseNode.flow = logicSystem.approveFactsInsideFlow( - variablesForWhenConditions.remove(previousConditionExitNode)!!, - EqFalse, - syntheticElseNode.flow, - shouldForkFlow = true, - shouldRemoveSynthetics = true - ) + val previousConditionExitNode = syntheticElseNode.previousNodes.single() as? WhenBranchConditionExitNode + // previous node for syntheticElseNode can be not WhenBranchConditionExitNode in case of `when` without any branches + // in that case there will be when enter or subject access node + if (previousConditionExitNode != null) { + syntheticElseNode.flow = logicSystem.approveFactsInsideFlow( + variablesForWhenConditions.remove(previousConditionExitNode)!!, + EqFalse, + syntheticElseNode.flow, + shouldForkFlow = true, + shouldRemoveSynthetics = true + ) + } + } val previousFlows = whenExitNode.alivePreviousNodes.map { it.flow } val flow = logicSystem.joinFlow(previousFlows) 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 11e56f23f1a..837d4d11711 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 @@ -225,7 +225,6 @@ class ControlFlowGraphBuilder { // exit from last condition node still on stack // we should remove it val lastWhenConditionExit = lastNodes.pop() - assert(lastWhenConditionExit is WhenBranchConditionExitNode) val syntheticElseBranchNode = if (!whenExpression.isExhaustive) { createWhenSyntheticElseBranchNode(whenExpression).apply { addEdge(lastWhenConditionExit, this) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt index 3fb063cc31c..9f29a8d8540 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt @@ -532,20 +532,25 @@ open class FirBodyResolveTransformer( } @Suppress("NAME_SHADOWING") var whenExpression = whenExpression.transformSubject(this, noExpectedType) - if (whenExpression.isOneBranch()) { - whenExpression = whenExpression.transformBranches(this, noExpectedType) - whenExpression.resultType = whenExpression.branches.first().result.resultType - } else { - whenExpression = whenExpression.transformBranches(this, null) - whenExpression = syntheticCallGenerator.generateCalleeForWhenExpression(whenExpression) ?: run { - dataFlowAnalyzer.exitWhenExpression(whenExpression) - whenExpression.resultType = FirErrorTypeRefImpl(null, "") - return@with whenExpression.compose() + when { + whenExpression.branches.isEmpty() -> {} + whenExpression.isOneBranch() -> { + whenExpression = whenExpression.transformBranches(this, noExpectedType) + whenExpression.resultType = whenExpression.branches.first().result.resultType } + else -> { + whenExpression = whenExpression.transformBranches(this, null) - val expectedTypeRef = data as FirTypeRef? - whenExpression = callCompleter.completeCall(whenExpression, expectedTypeRef) + whenExpression = syntheticCallGenerator.generateCalleeForWhenExpression(whenExpression) ?: run { + dataFlowAnalyzer.exitWhenExpression(whenExpression) + whenExpression.resultType = FirErrorTypeRefImpl(null, "") + return@with whenExpression.compose() + } + + val expectedTypeRef = data as FirTypeRef? + whenExpression = callCompleter.completeCall(whenExpression, expectedTypeRef) + } } whenExpression = whenExpression.transformSingle(whenExhaustivenessTransformer, null) dataFlowAnalyzer.exitWhenExpression(whenExpression) diff --git a/compiler/fir/resolve/testData/resolve/cfg/emptyWhen.dot b/compiler/fir/resolve/testData/resolve/cfg/emptyWhen.dot new file mode 100644 index 00000000000..652505f1657 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/cfg/emptyWhen.dot @@ -0,0 +1,84 @@ +digraph emptyWhen_kt { + graph [splines=ortho nodesep=3] + node [shape=box penwidth=2] + edge [penwidth=2] + + subgraph cluster_0 { + color=red + 0 [label="Enter function test_1" style="filled" fillcolor=red]; + subgraph cluster_1 { + color=blue + 1 [label="Enter block"]; + subgraph cluster_2 { + color=blue + 2 [label="Enter when"]; + 3 [label="Synthetic else branch"]; + 4 [label="Exit when"]; + } + 5 [label="Exit block"]; + } + 6 [label="Exit function test_1" style="filled" fillcolor=red]; + } + + 0 -> {1}; + 1 -> {2}; + 2 -> {3}; + 3 -> {4}; + 4 -> {5}; + 5 -> {6}; + + subgraph cluster_3 { + color=red + 7 [label="Enter function test_2" style="filled" fillcolor=red]; + subgraph cluster_4 { + color=blue + 8 [label="Enter block"]; + subgraph cluster_5 { + color=blue + 9 [label="Enter when"]; + 10 [label="Access variable R|/x|"]; + 11 [label="Synthetic else branch"]; + 12 [label="Exit when"]; + } + 13 [label="Exit block"]; + } + 14 [label="Exit function test_2" style="filled" fillcolor=red]; + } + + 7 -> {8}; + 8 -> {9}; + 9 -> {10}; + 10 -> {11}; + 11 -> {12}; + 12 -> {13}; + 13 -> {14}; + + subgraph cluster_6 { + color=red + 15 [label="Enter function test_3" style="filled" fillcolor=red]; + subgraph cluster_7 { + color=blue + 16 [label="Enter block"]; + subgraph cluster_8 { + color=blue + 17 [label="Enter when"]; + 18 [label="Access variable R|/x|"]; + 19 [label="Variable declaration: lval y: R|kotlin/Int|"]; + 20 [label="Synthetic else branch"]; + 21 [label="Exit when"]; + } + 22 [label="Exit block"]; + } + 23 [label="Exit function test_3" style="filled" fillcolor=red]; + } + + 15 -> {16}; + 16 -> {17}; + 17 -> {18}; + 18 -> {19}; + 19 -> {20}; + 20 -> {21}; + 21 -> {22}; + 22 -> {23}; + +} diff --git a/compiler/fir/resolve/testData/resolve/cfg/emptyWhen.kt b/compiler/fir/resolve/testData/resolve/cfg/emptyWhen.kt new file mode 100644 index 00000000000..614132c536a --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/cfg/emptyWhen.kt @@ -0,0 +1,11 @@ +fun test_1() { + when {} +} + +fun test_2(x: Int) { + when (x) {} +} + +fun test_3(x: Int) { + when (val y = x) {} +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/cfg/emptyWhen.txt b/compiler/fir/resolve/testData/resolve/cfg/emptyWhen.txt new file mode 100644 index 00000000000..552812a47db --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/cfg/emptyWhen.txt @@ -0,0 +1,16 @@ +FILE: emptyWhen.kt + public final fun test_1(): R|kotlin/Unit| { + when () { + } + + } + public final fun test_2(x: R|kotlin/Int|): R|kotlin/Unit| { + when (R|/x|) { + } + + } + public final fun test_3(x: R|kotlin/Int|): R|kotlin/Unit| { + when (lval y: R|kotlin/Int| = R|/x|) { + } + + } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirCfgBuildingTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirCfgBuildingTestGenerated.java index bed92cbc469..86e18e2c201 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirCfgBuildingTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirCfgBuildingTestGenerated.java @@ -46,6 +46,11 @@ public class FirCfgBuildingTestGenerated extends AbstractFirCfgBuildingTest { runTest("compiler/fir/resolve/testData/resolve/cfg/complex.kt"); } + @TestMetadata("emptyWhen.kt") + public void testEmptyWhen() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/cfg/emptyWhen.kt"); + } + @TestMetadata("initBlock.kt") public void testInitBlock() throws Exception { runTest("compiler/fir/resolve/testData/resolve/cfg/initBlock.kt");