[FIR] Add detecting dead edges for boolean operators with consts

This commit is contained in:
Dmitriy Novozhilov
2019-08-27 14:36:06 +03:00
parent fd1347f2b7
commit 006cb536e0
8 changed files with 681 additions and 6 deletions
@@ -301,13 +301,18 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
fun exitLeftBinaryAndArgument(binaryLogicExpression: FirBinaryLogicExpression) {
assert(binaryLogicExpression.kind == FirBinaryLogicExpression.OperationKind.AND)
addEdge(lastNode, binaryAndExitNodes.top())
addEdge(lastNode, binaryAndExitNodes.top(), propagateDeadness = false, isDead = lastNode.booleanConstValue == true)
}
fun exitBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression): BinaryAndExitNode {
levelCounter--
assert(binaryLogicExpression.kind == FirBinaryLogicExpression.OperationKind.AND)
return binaryAndExitNodes.pop().also { addNewSimpleNode(it) }
return binaryAndExitNodes.pop().also {
val rightNode = lastNodes.pop()
addEdge(rightNode, it, propagateDeadness = false, isDead = it.leftOperandNode.booleanConstValue == false)
it.isDead = it.previousNodes.all { it.isDead }
lastNodes.push(it)
}
}
fun enterBinaryOr(binaryLogicExpression: FirBinaryLogicExpression): BinaryOrEnterNode {
@@ -322,9 +327,10 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
levelCounter--
assert(binaryLogicExpression.kind == FirBinaryLogicExpression.OperationKind.OR)
val previousNode = lastNodes.pop()
addEdge(previousNode, binaryOrExitNodes.top())
val leftBooleanValue = previousNode.booleanConstValue
addEdge(previousNode, binaryOrExitNodes.top(), isDead = leftBooleanValue == false)
return createBinaryOrExitLeftOperandNode(binaryLogicExpression).also {
addEdge(previousNode, it)
addEdge(previousNode, it, isDead = leftBooleanValue == true)
lastNodes.push(it)
levelCounter++
}
@@ -334,11 +340,15 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
assert(binaryLogicExpression.kind == FirBinaryLogicExpression.OperationKind.OR)
levelCounter--
return binaryOrExitNodes.pop().also {
addEdge(lastNodes.pop(), it)
val rightNode = lastNodes.pop()
addEdge(rightNode, it, propagateDeadness = false)
it.isDead = it.previousNodes.all { it.isDead }
lastNodes.push(it)
}
}
private val CFGNode<*>.booleanConstValue: Boolean? get() = (fir as? FirConstExpression<*>)?.value as? Boolean?
// ----------------------------------- Try-catch-finally -----------------------------------
private val finallyEnterNodes: Stack<FinallyBlockEnterNode> = stackOf()
@@ -525,7 +535,17 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
return oldNode
}
private fun addEdge(from: CFGNode<*>, to: CFGNode<*>, propagateDeadness: Boolean = true) {
private fun addDeadEdge(from: CFGNode<*>, to: CFGNode<*>) {
val stub = createStubNode()
addEdge(from, stub)
addEdge(stub, to)
}
private fun addEdge(from: CFGNode<*>, to: CFGNode<*>, propagateDeadness: Boolean = true, isDead: Boolean = false) {
if (isDead) {
addDeadEdge(from, to)
return
}
if (propagateDeadness && from.isDead) {
to.isDead = true
}
@@ -0,0 +1,381 @@
digraph booleanOperatorsWithConsts_kt {
subgraph test_1 {
0 [shape=box label="Enter function test_1"];
1 [shape=box label="Enter block"];
2 [shape=box label="Enter when"];
3 [shape=box label="Enter when branch condition "];
4 [shape=box label="Enter ||"];
5 [shape=box label="Access variable R|<local>/b|"];
6 [shape=box label="Exit left part of ||"];
7 [shape=box label="Const: Boolean(false)"];
8 [shape=box label="Exit ||"];
9 [shape=box label="Exit when branch condition"];
10 [shape=box label="Enter block"];
11 [shape=box label="Const: Int(1)"];
12 [shape=box label="Exit block"];
13 [shape=box label="Exit when branch result"];
14 [shape=box label="Enter when branch condition else"];
15 [shape=box label="Exit when branch condition"];
16 [shape=box label="Enter block"];
17 [shape=box label="Exit block"];
18 [shape=box label="Exit when branch result"];
19 [shape=box label="Exit when"];
20 [shape=box label="Exit block"];
21 [shape=box label="Exit function test_1"];
0 -> {1};
1 -> {2};
2 -> {3};
3 -> {4};
4 -> {5};
5 -> {8 6};
6 -> {7};
7 -> {8};
8 -> {9};
9 -> {10 14};
10 -> {11};
11 -> {12};
12 -> {13};
13 -> {19};
14 -> {15};
15 -> {16};
16 -> {17};
17 -> {18};
18 -> {19};
19 -> {20};
20 -> {21};
}
subgraph test_2 {
22 [shape=box label="Enter function test_2"];
23 [shape=box label="Enter block"];
24 [shape=box label="Enter when"];
25 [shape=box label="Enter when branch condition "];
26 [shape=box label="Enter ||"];
27 [shape=box label="Const: Boolean(false)"];
28 [shape=box label="Exit left part of ||"];
29 [shape=box label="Access variable R|<local>/b|"];
30 [shape=box label="Stub[DEAD]"];
31 [shape=box label="Exit ||"];
32 [shape=box label="Exit when branch condition"];
33 [shape=box label="Enter block"];
34 [shape=box label="Const: Int(1)"];
35 [shape=box label="Exit block"];
36 [shape=box label="Exit when branch result"];
37 [shape=box label="Enter when branch condition else"];
38 [shape=box label="Exit when branch condition"];
39 [shape=box label="Enter block"];
40 [shape=box label="Exit block"];
41 [shape=box label="Exit when branch result"];
42 [shape=box label="Exit when"];
43 [shape=box label="Exit block"];
44 [shape=box label="Exit function test_2"];
22 -> {23};
23 -> {24};
24 -> {25};
25 -> {26};
26 -> {27};
27 -> {28};
27 -> {30} [style=dotted];
28 -> {29};
29 -> {31};
30 -> {31} [style=dotted];
31 -> {32};
32 -> {33 37};
33 -> {34};
34 -> {35};
35 -> {36};
36 -> {42};
37 -> {38};
38 -> {39};
39 -> {40};
40 -> {41};
41 -> {42};
42 -> {43};
43 -> {44};
}
subgraph test_3 {
45 [shape=box label="Enter function test_3"];
46 [shape=box label="Enter block"];
47 [shape=box label="Enter when"];
48 [shape=box label="Enter when branch condition "];
49 [shape=box label="Enter ||"];
50 [shape=box label="Access variable R|<local>/b|"];
51 [shape=box label="Exit left part of ||"];
52 [shape=box label="Const: Boolean(true)"];
53 [shape=box label="Exit ||"];
54 [shape=box label="Exit when branch condition"];
55 [shape=box label="Enter block"];
56 [shape=box label="Const: Int(1)"];
57 [shape=box label="Exit block"];
58 [shape=box label="Exit when branch result"];
59 [shape=box label="Enter when branch condition else"];
60 [shape=box label="Exit when branch condition"];
61 [shape=box label="Enter block"];
62 [shape=box label="Exit block"];
63 [shape=box label="Exit when branch result"];
64 [shape=box label="Exit when"];
65 [shape=box label="Exit block"];
66 [shape=box label="Exit function test_3"];
45 -> {46};
46 -> {47};
47 -> {48};
48 -> {49};
49 -> {50};
50 -> {53 51};
51 -> {52};
52 -> {53};
53 -> {54};
54 -> {55 59};
55 -> {56};
56 -> {57};
57 -> {58};
58 -> {64};
59 -> {60};
60 -> {61};
61 -> {62};
62 -> {63};
63 -> {64};
64 -> {65};
65 -> {66};
}
subgraph test_4 {
67 [shape=box label="Enter function test_4"];
68 [shape=box label="Enter block"];
69 [shape=box label="Enter when"];
70 [shape=box label="Enter when branch condition "];
71 [shape=box label="Enter ||"];
72 [shape=box label="Const: Boolean(true)"];
73 [shape=box label="Stub[DEAD]"];
74 [shape=box label="Exit left part of ||[DEAD]"];
75 [shape=box label="Access variable R|<local>/b|[DEAD]"];
76 [shape=box label="Exit ||"];
77 [shape=box label="Exit when branch condition"];
78 [shape=box label="Enter block"];
79 [shape=box label="Const: Int(1)"];
80 [shape=box label="Exit block"];
81 [shape=box label="Exit when branch result"];
82 [shape=box label="Enter when branch condition else"];
83 [shape=box label="Exit when branch condition"];
84 [shape=box label="Enter block"];
85 [shape=box label="Exit block"];
86 [shape=box label="Exit when branch result"];
87 [shape=box label="Exit when"];
88 [shape=box label="Exit block"];
89 [shape=box label="Exit function test_4"];
67 -> {68};
68 -> {69};
69 -> {70};
70 -> {71};
71 -> {72};
72 -> {76};
72 -> {73} [style=dotted];
73 -> {74} [style=dotted];
74 -> {75} [style=dotted];
75 -> {76} [style=dotted];
76 -> {77};
77 -> {78 82};
78 -> {79};
79 -> {80};
80 -> {81};
81 -> {87};
82 -> {83};
83 -> {84};
84 -> {85};
85 -> {86};
86 -> {87};
87 -> {88};
88 -> {89};
}
subgraph test_5 {
90 [shape=box label="Enter function test_5"];
91 [shape=box label="Enter block"];
92 [shape=box label="Enter when"];
93 [shape=box label="Enter when branch condition "];
94 [shape=box label="Enter &&"];
95 [shape=box label="Access variable R|<local>/b|"];
96 [shape=box label="Const: Boolean(false)"];
97 [shape=box label="Exit &&"];
98 [shape=box label="Exit when branch condition"];
99 [shape=box label="Enter block"];
100 [shape=box label="Const: Int(1)"];
101 [shape=box label="Exit block"];
102 [shape=box label="Exit when branch result"];
103 [shape=box label="Enter when branch condition else"];
104 [shape=box label="Exit when branch condition"];
105 [shape=box label="Enter block"];
106 [shape=box label="Exit block"];
107 [shape=box label="Exit when branch result"];
108 [shape=box label="Exit when"];
109 [shape=box label="Exit block"];
110 [shape=box label="Exit function test_5"];
90 -> {91};
91 -> {92};
92 -> {93};
93 -> {94};
94 -> {95};
95 -> {97 96};
96 -> {97};
97 -> {98};
98 -> {99 103};
99 -> {100};
100 -> {101};
101 -> {102};
102 -> {108};
103 -> {104};
104 -> {105};
105 -> {106};
106 -> {107};
107 -> {108};
108 -> {109};
109 -> {110};
}
subgraph test_6 {
111 [shape=box label="Enter function test_6"];
112 [shape=box label="Enter block"];
113 [shape=box label="Enter when"];
114 [shape=box label="Enter when branch condition "];
115 [shape=box label="Enter &&"];
116 [shape=box label="Const: Boolean(false)"];
117 [shape=box label="Access variable R|<local>/b|"];
118 [shape=box label="Stub[DEAD]"];
119 [shape=box label="Exit &&"];
120 [shape=box label="Exit when branch condition"];
121 [shape=box label="Enter block"];
122 [shape=box label="Const: Int(1)"];
123 [shape=box label="Exit block"];
124 [shape=box label="Exit when branch result"];
125 [shape=box label="Enter when branch condition else"];
126 [shape=box label="Exit when branch condition"];
127 [shape=box label="Enter block"];
128 [shape=box label="Exit block"];
129 [shape=box label="Exit when branch result"];
130 [shape=box label="Exit when"];
131 [shape=box label="Exit block"];
132 [shape=box label="Exit function test_6"];
111 -> {112};
112 -> {113};
113 -> {114};
114 -> {115};
115 -> {116};
116 -> {119 117};
117 -> {118} [style=dotted];
118 -> {119} [style=dotted];
119 -> {120};
120 -> {121 125};
121 -> {122};
122 -> {123};
123 -> {124};
124 -> {130};
125 -> {126};
126 -> {127};
127 -> {128};
128 -> {129};
129 -> {130};
130 -> {131};
131 -> {132};
}
subgraph test_7 {
133 [shape=box label="Enter function test_7"];
134 [shape=box label="Enter block"];
135 [shape=box label="Enter when"];
136 [shape=box label="Enter when branch condition "];
137 [shape=box label="Enter &&"];
138 [shape=box label="Access variable R|<local>/b|"];
139 [shape=box label="Const: Boolean(true)"];
140 [shape=box label="Exit &&"];
141 [shape=box label="Exit when branch condition"];
142 [shape=box label="Enter block"];
143 [shape=box label="Const: Int(1)"];
144 [shape=box label="Exit block"];
145 [shape=box label="Exit when branch result"];
146 [shape=box label="Enter when branch condition else"];
147 [shape=box label="Exit when branch condition"];
148 [shape=box label="Enter block"];
149 [shape=box label="Exit block"];
150 [shape=box label="Exit when branch result"];
151 [shape=box label="Exit when"];
152 [shape=box label="Exit block"];
153 [shape=box label="Exit function test_7"];
133 -> {134};
134 -> {135};
135 -> {136};
136 -> {137};
137 -> {138};
138 -> {140 139};
139 -> {140};
140 -> {141};
141 -> {142 146};
142 -> {143};
143 -> {144};
144 -> {145};
145 -> {151};
146 -> {147};
147 -> {148};
148 -> {149};
149 -> {150};
150 -> {151};
151 -> {152};
152 -> {153};
}
subgraph test_8 {
154 [shape=box label="Enter function test_8"];
155 [shape=box label="Enter block"];
156 [shape=box label="Enter when"];
157 [shape=box label="Enter when branch condition "];
158 [shape=box label="Enter &&"];
159 [shape=box label="Const: Boolean(true)"];
160 [shape=box label="Access variable R|<local>/b|"];
161 [shape=box label="Stub[DEAD]"];
162 [shape=box label="Exit &&"];
163 [shape=box label="Exit when branch condition"];
164 [shape=box label="Enter block"];
165 [shape=box label="Const: Int(1)"];
166 [shape=box label="Exit block"];
167 [shape=box label="Exit when branch result"];
168 [shape=box label="Enter when branch condition else"];
169 [shape=box label="Exit when branch condition"];
170 [shape=box label="Enter block"];
171 [shape=box label="Exit block"];
172 [shape=box label="Exit when branch result"];
173 [shape=box label="Exit when"];
174 [shape=box label="Exit block"];
175 [shape=box label="Exit function test_8"];
154 -> {155};
155 -> {156};
156 -> {157};
157 -> {158};
158 -> {159};
159 -> {160};
159 -> {161} [style=dotted];
160 -> {162};
161 -> {162} [style=dotted];
162 -> {163};
163 -> {164 168};
164 -> {165};
165 -> {166};
166 -> {167};
167 -> {173};
168 -> {169};
169 -> {170};
170 -> {171};
171 -> {172};
172 -> {173};
173 -> {174};
174 -> {175};
}
}
@@ -0,0 +1,47 @@
fun test_1(b: Boolean) {
if (b || false) {
1
}
}
fun test_2(b: Boolean) {
if (false || b) {
1
}
}
fun test_3(b: Boolean) {
if (b || true) {
1
}
}
fun test_4(b: Boolean) {
if (true || b) {
1
}
}
fun test_5(b: Boolean) {
if (b && false) {
1
}
}
fun test_6(b: Boolean) {
if (false && b) {
1
}
}
fun test_7(b: Boolean) {
if (b && true) {
1
}
}
fun test_8(b: Boolean) {
if (true && b) {
1
}
}
@@ -0,0 +1,81 @@
FILE: booleanOperatorsWithConsts.kt
public final fun test_1(b: R|kotlin/Boolean|): R|kotlin/Unit| {
when () {
R|<local>/b| || Boolean(false) -> {
Int(1)
}
else -> {
}
}
}
public final fun test_2(b: R|kotlin/Boolean|): R|kotlin/Unit| {
when () {
Boolean(false) || R|<local>/b| -> {
Int(1)
}
else -> {
}
}
}
public final fun test_3(b: R|kotlin/Boolean|): R|kotlin/Unit| {
when () {
R|<local>/b| || Boolean(true) -> {
Int(1)
}
else -> {
}
}
}
public final fun test_4(b: R|kotlin/Boolean|): R|kotlin/Unit| {
when () {
Boolean(true) || R|<local>/b| -> {
Int(1)
}
else -> {
}
}
}
public final fun test_5(b: R|kotlin/Boolean|): R|kotlin/Unit| {
when () {
R|<local>/b| && Boolean(false) -> {
Int(1)
}
else -> {
}
}
}
public final fun test_6(b: R|kotlin/Boolean|): R|kotlin/Unit| {
when () {
Boolean(false) && R|<local>/b| -> {
Int(1)
}
else -> {
}
}
}
public final fun test_7(b: R|kotlin/Boolean|): R|kotlin/Unit| {
when () {
R|<local>/b| && Boolean(true) -> {
Int(1)
}
else -> {
}
}
}
public final fun test_8(b: R|kotlin/Boolean|): R|kotlin/Unit| {
when () {
Boolean(true) && R|<local>/b| -> {
Int(1)
}
else -> {
}
}
}
@@ -246,4 +246,113 @@ subgraph test_4 {
111 -> {112};
}
subgraph test_5 {
113 [shape=box label="Enter function test_5"];
114 [shape=box label="Enter block"];
115 [shape=box label="Enter when"];
116 [shape=box label="Enter when branch condition "];
117 [shape=box label="Enter ||"];
118 [shape=box label="Access variable R|<local>/x|"];
119 [shape=box label="Const: Null(null)"];
120 [shape=box label="Operator !="];
121 [shape=box label="Exit left part of ||"];
122 [shape=box label="Const: Boolean(false)"];
123 [shape=box label="Exit ||"];
124 [shape=box label="Exit when branch condition"];
125 [shape=box label="Enter block"];
126 [shape=box label="Access variable R|<local>/x|"];
127 [shape=box label="Function call: R|<local>/x|.<Inapplicable(WRONG_RECEIVER): [/A.foo]>#()"];
128 [shape=box label="Exit block"];
129 [shape=box label="Exit when branch result"];
130 [shape=box label="Enter when branch condition else"];
131 [shape=box label="Exit when branch condition"];
132 [shape=box label="Enter block"];
133 [shape=box label="Exit block"];
134 [shape=box label="Exit when branch result"];
135 [shape=box label="Exit when"];
136 [shape=box label="Exit block"];
137 [shape=box label="Exit function test_5"];
113 -> {114};
114 -> {115};
115 -> {116};
116 -> {117};
117 -> {118};
118 -> {119};
119 -> {120};
120 -> {123 121};
121 -> {122};
122 -> {123};
123 -> {124};
124 -> {125 130};
125 -> {126};
126 -> {127};
127 -> {128};
128 -> {129};
129 -> {135};
130 -> {131};
131 -> {132};
132 -> {133};
133 -> {134};
134 -> {135};
135 -> {136};
136 -> {137};
}
subgraph test_6 {
138 [shape=box label="Enter function test_6"];
139 [shape=box label="Enter block"];
140 [shape=box label="Enter when"];
141 [shape=box label="Enter when branch condition "];
142 [shape=box label="Enter ||"];
143 [shape=box label="Const: Boolean(false)"];
144 [shape=box label="Exit left part of ||"];
145 [shape=box label="Access variable R|<local>/x|"];
146 [shape=box label="Const: Null(null)"];
147 [shape=box label="Operator !="];
148 [shape=box label="Stub[DEAD]"];
149 [shape=box label="Exit ||"];
150 [shape=box label="Exit when branch condition"];
151 [shape=box label="Enter block"];
152 [shape=box label="Access variable R|<local>/x|"];
153 [shape=box label="Function call: R|<local>/x|.<Inapplicable(WRONG_RECEIVER): [/A.foo]>#()"];
154 [shape=box label="Exit block"];
155 [shape=box label="Exit when branch result"];
156 [shape=box label="Enter when branch condition else"];
157 [shape=box label="Exit when branch condition"];
158 [shape=box label="Enter block"];
159 [shape=box label="Exit block"];
160 [shape=box label="Exit when branch result"];
161 [shape=box label="Exit when"];
162 [shape=box label="Exit block"];
163 [shape=box label="Exit function test_6"];
138 -> {139};
139 -> {140};
140 -> {141};
141 -> {142};
142 -> {143};
143 -> {144};
143 -> {148} [style=dotted];
144 -> {145};
145 -> {146};
146 -> {147};
147 -> {149};
148 -> {149} [style=dotted];
149 -> {150};
150 -> {151 156};
151 -> {152};
152 -> {153};
153 -> {154};
154 -> {155};
155 -> {161};
156 -> {157};
157 -> {158};
158 -> {159};
159 -> {160};
160 -> {161};
161 -> {162};
162 -> {163};
}
}
@@ -37,4 +37,16 @@ fun test_4(x: Any) {
x.length
}
x.length
}
fun test_5(x: A?) {
if (x != null || false) {
x.foo()
}
}
fun test_6(x: A?) {
if (false || x != null) {
x.foo()
}
}
@@ -56,3 +56,23 @@ FILE: booleanOperators.kt
R|<local>/x|.<Unresolved name: length>#
}
public final fun test_5(x: R|A?|): R|kotlin/Unit| {
when () {
!=(R|<local>/x|, Null(null)) || Boolean(false) -> {
R|<local>/x|.<Inapplicable(WRONG_RECEIVER): [/A.foo]>#()
}
else -> {
}
}
}
public final fun test_6(x: R|A?|): R|kotlin/Unit| {
when () {
Boolean(false) || !=(R|<local>/x|, Null(null)) -> {
R|<local>/x|.<Inapplicable(WRONG_RECEIVER): [/A.foo]>#()
}
else -> {
}
}
}
@@ -36,6 +36,11 @@ public class FirCfgBuildingTestGenerated extends AbstractFirCfgBuildingTest {
runTest("compiler/fir/resolve/testData/resolve/cfg/binaryOperations.kt");
}
@TestMetadata("booleanOperatorsWithConsts.kt")
public void testBooleanOperatorsWithConsts() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/cfg/booleanOperatorsWithConsts.kt");
}
@TestMetadata("complex.kt")
public void testComplex() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/cfg/complex.kt");