[FIR] Fix building CFG and resolving when expression without branches

This commit is contained in:
Dmitriy Novozhilov
2019-10-17 12:32:51 +03:00
parent f8f2d8b9a1
commit cfa9950c22
7 changed files with 145 additions and 20 deletions
@@ -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)
@@ -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)
@@ -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)
+84
View File
@@ -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};
}
+11
View File
@@ -0,0 +1,11 @@
fun test_1() {
when {}
}
fun test_2(x: Int) {
when (x) {}
}
fun test_3(x: Int) {
when (val y = x) {}
}
+16
View File
@@ -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|) {
}
}
@@ -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");