[FIR] Fix building CFG and resolving when expression without branches
This commit is contained in:
+13
-8
@@ -353,15 +353,20 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
|
|||||||
fun exitWhenExpression(whenExpression: FirWhenExpression) {
|
fun exitWhenExpression(whenExpression: FirWhenExpression) {
|
||||||
val (whenExitNode, syntheticElseNode) = graphBuilder.exitWhenExpression(whenExpression)
|
val (whenExitNode, syntheticElseNode) = graphBuilder.exitWhenExpression(whenExpression)
|
||||||
if (syntheticElseNode != null) {
|
if (syntheticElseNode != null) {
|
||||||
val previousConditionExitNode = syntheticElseNode.previousNodes.single() as WhenBranchConditionExitNode
|
|
||||||
syntheticElseNode.mergeIncomingFlow()
|
syntheticElseNode.mergeIncomingFlow()
|
||||||
syntheticElseNode.flow = logicSystem.approveFactsInsideFlow(
|
val previousConditionExitNode = syntheticElseNode.previousNodes.single() as? WhenBranchConditionExitNode
|
||||||
variablesForWhenConditions.remove(previousConditionExitNode)!!,
|
// previous node for syntheticElseNode can be not WhenBranchConditionExitNode in case of `when` without any branches
|
||||||
EqFalse,
|
// in that case there will be when enter or subject access node
|
||||||
syntheticElseNode.flow,
|
if (previousConditionExitNode != null) {
|
||||||
shouldForkFlow = true,
|
syntheticElseNode.flow = logicSystem.approveFactsInsideFlow(
|
||||||
shouldRemoveSynthetics = true
|
variablesForWhenConditions.remove(previousConditionExitNode)!!,
|
||||||
)
|
EqFalse,
|
||||||
|
syntheticElseNode.flow,
|
||||||
|
shouldForkFlow = true,
|
||||||
|
shouldRemoveSynthetics = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
val previousFlows = whenExitNode.alivePreviousNodes.map { it.flow }
|
val previousFlows = whenExitNode.alivePreviousNodes.map { it.flow }
|
||||||
val flow = logicSystem.joinFlow(previousFlows)
|
val flow = logicSystem.joinFlow(previousFlows)
|
||||||
|
|||||||
-1
@@ -225,7 +225,6 @@ class ControlFlowGraphBuilder {
|
|||||||
// exit from last condition node still on stack
|
// exit from last condition node still on stack
|
||||||
// we should remove it
|
// we should remove it
|
||||||
val lastWhenConditionExit = lastNodes.pop()
|
val lastWhenConditionExit = lastNodes.pop()
|
||||||
assert(lastWhenConditionExit is WhenBranchConditionExitNode)
|
|
||||||
val syntheticElseBranchNode = if (!whenExpression.isExhaustive) {
|
val syntheticElseBranchNode = if (!whenExpression.isExhaustive) {
|
||||||
createWhenSyntheticElseBranchNode(whenExpression).apply {
|
createWhenSyntheticElseBranchNode(whenExpression).apply {
|
||||||
addEdge(lastWhenConditionExit, this)
|
addEdge(lastWhenConditionExit, this)
|
||||||
|
|||||||
+16
-11
@@ -532,20 +532,25 @@ open class FirBodyResolveTransformer(
|
|||||||
}
|
}
|
||||||
@Suppress("NAME_SHADOWING")
|
@Suppress("NAME_SHADOWING")
|
||||||
var whenExpression = whenExpression.transformSubject(this, noExpectedType)
|
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 {
|
when {
|
||||||
dataFlowAnalyzer.exitWhenExpression(whenExpression)
|
whenExpression.branches.isEmpty() -> {}
|
||||||
whenExpression.resultType = FirErrorTypeRefImpl(null, "")
|
whenExpression.isOneBranch() -> {
|
||||||
return@with whenExpression.compose()
|
whenExpression = whenExpression.transformBranches(this, noExpectedType)
|
||||||
|
whenExpression.resultType = whenExpression.branches.first().result.resultType
|
||||||
}
|
}
|
||||||
|
else -> {
|
||||||
|
whenExpression = whenExpression.transformBranches(this, null)
|
||||||
|
|
||||||
val expectedTypeRef = data as FirTypeRef?
|
whenExpression = syntheticCallGenerator.generateCalleeForWhenExpression(whenExpression) ?: run {
|
||||||
whenExpression = callCompleter.completeCall(whenExpression, expectedTypeRef)
|
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)
|
whenExpression = whenExpression.transformSingle(whenExhaustivenessTransformer, null)
|
||||||
dataFlowAnalyzer.exitWhenExpression(whenExpression)
|
dataFlowAnalyzer.exitWhenExpression(whenExpression)
|
||||||
|
|||||||
@@ -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|<local>/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|<local>/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};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fun test_1() {
|
||||||
|
when {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test_2(x: Int) {
|
||||||
|
when (x) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test_3(x: Int) {
|
||||||
|
when (val y = x) {}
|
||||||
|
}
|
||||||
@@ -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|<local>/x|) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public final fun test_3(x: R|kotlin/Int|): R|kotlin/Unit| {
|
||||||
|
when (lval y: R|kotlin/Int| = R|<local>/x|) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+5
@@ -46,6 +46,11 @@ public class FirCfgBuildingTestGenerated extends AbstractFirCfgBuildingTest {
|
|||||||
runTest("compiler/fir/resolve/testData/resolve/cfg/complex.kt");
|
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")
|
@TestMetadata("initBlock.kt")
|
||||||
public void testInitBlock() throws Exception {
|
public void testInitBlock() throws Exception {
|
||||||
runTest("compiler/fir/resolve/testData/resolve/cfg/initBlock.kt");
|
runTest("compiler/fir/resolve/testData/resolve/cfg/initBlock.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user