[FIR] Add boolean constant folding for DFA

This commit is contained in:
Dmitriy Novozhilov
2019-08-27 11:22:51 +03:00
parent 78f7211587
commit 7e43a0d9ef
6 changed files with 646 additions and 13 deletions
@@ -19,6 +19,8 @@ enum class ConditionValue(val token: String) {
}
}
fun Boolean.toConditionValue(): ConditionValue = if (this) ConditionValue.True else ConditionValue.False
enum class ConditionOperator(val token: String) {
Eq("=="), NotEq("!=");
@@ -184,9 +184,9 @@ class FirDataFlowAnalyzerImpl(transformer: FirBodyResolveTransformer) : FirDataF
when {
leftConst?.kind == IrConstKind.Null -> processEqNull(node, rightOperand, operation)
rightConst?.kind == IrConstKind.Null -> processEqNull(node, leftOperand, operation)
leftConst != null -> processEqWithConst(node, rightOperand, leftConst, operation)
rightConst != null -> processEqWithConst(node, leftOperand, rightConst, operation)
operation != FirOperation.EQ && operation != FirOperation.IDENTITY -> return
leftConst != null -> processEqWithConst(node, rightOperand)
rightConst != null -> processEqWithConst(node, leftOperand)
else -> processEq(node, leftOperand, rightOperand, operation)
}
}
@@ -196,19 +196,41 @@ class FirDataFlowAnalyzerImpl(transformer: FirBodyResolveTransformer) : FirDataF
}
}
private fun processEqWithConst(node: OperatorCallNode, operand: FirExpression) {
val operandVariables = getRealVariablesForSafeCallChain(operand).takeIf { it.isNotEmpty() } ?: return
private fun processEqWithConst(node: OperatorCallNode, operand: FirExpression, const: FirConstExpression<*>, operation: FirOperation) {
val isEq = when (operation) {
FirOperation.EQ, FirOperation.IDENTITY -> true
FirOperation.NOT_EQ, FirOperation.NOT_IDENTITY -> false
else -> return
}
val expressionVariable = getVariable(node.fir)
var flow = node.flow
operandVariables.forEach { operandVariable ->
flow = flow.addNotApprovedFact(
expressionVariable, UnapprovedFirDataFlowInfo(
eq(True),
operandVariable,
FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet())
// not null for comparisons with constants
getRealVariablesForSafeCallChain(operand).takeIf { it.isNotEmpty() }?.let { operandVariables ->
operandVariables.forEach { operandVariable ->
flow = flow.addNotApprovedFact(
expressionVariable, UnapprovedFirDataFlowInfo(
eq(isEq.toConditionValue()),
operandVariable,
FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet())
)
)
)
}
}
// propagating facts for (... == true) and (... == false)
variableStorage[operand]?.let { operandVariable ->
if (const.kind != IrConstKind.Boolean) return@let
val constValue = (const.value as Boolean)
val shouldInvert = isEq xor constValue
flow.notApprovedFacts[operandVariable].forEach { info ->
flow = flow.addNotApprovedFact(expressionVariable, info.let { if (shouldInvert) it.invert() else it })
}
}
node.flow = flow
}
@@ -217,8 +239,8 @@ class FirDataFlowAnalyzerImpl(transformer: FirBodyResolveTransformer) : FirDataF
val rightType = rightOperand.typeRef.coneTypeSafe<ConeKotlinType>() ?: return
when {
leftType.isMarkedNullable && rightType.isMarkedNullable -> return
leftType.isMarkedNullable -> processEqWithConst(node, leftOperand)
rightType.isMarkedNullable -> processEqWithConst(node, rightOperand)
leftType.isMarkedNullable -> processEqNull(node, leftOperand, FirOperation.NOT_EQ)
rightType.isMarkedNullable -> processEqNull(node, rightOperand, FirOperation.NOT_EQ)
}
// TODO: process EQUALITY
}
@@ -0,0 +1,440 @@
digraph equalsToBoolean_kt {
subgraph foo {
0 [shape=box label="Enter function foo"];
1 [shape=box label="Exit function foo"];
0 -> {1};
}
subgraph val_b {
2 [shape=box label="Enter property"];
3 [shape=box label="Exit property"];
2 -> {3};
}
subgraph test_1 {
4 [shape=box label="Enter function test_1"];
5 [shape=box label="Enter block"];
6 [shape=box label="Enter when"];
7 [shape=box label="Enter when branch condition "];
8 [shape=box label="Access variable R|<local>/b|"];
9 [shape=box label="Const: Boolean(true)"];
10 [shape=box label="Operator =="];
11 [shape=box label="Const: Boolean(true)"];
12 [shape=box label="Operator =="];
13 [shape=box label="Exit when branch condition"];
14 [shape=box label="Enter block"];
15 [shape=box label="Access variable R|<local>/b|"];
16 [shape=box label="Function call: R|<local>/b|.R|kotlin/Boolean.not|()"];
17 [shape=box label="Exit block"];
18 [shape=box label="Exit when branch result"];
19 [shape=box label="Enter when branch condition else"];
20 [shape=box label="Exit when branch condition"];
21 [shape=box label="Enter block"];
22 [shape=box label="Access variable R|<local>/b|"];
23 [shape=box label="Function call: R|<local>/b|.<Inapplicable(WRONG_RECEIVER): [kotlin/Boolean.not]>#()"];
24 [shape=box label="Exit block"];
25 [shape=box label="Exit when branch result"];
26 [shape=box label="Exit when"];
27 [shape=box label="Exit block"];
28 [shape=box label="Exit function test_1"];
4 -> {5};
5 -> {6};
6 -> {7};
7 -> {8};
8 -> {9};
9 -> {10};
10 -> {11};
11 -> {12};
12 -> {13};
13 -> {14 19};
14 -> {15};
15 -> {16};
16 -> {17};
17 -> {18};
18 -> {26};
19 -> {20};
20 -> {21};
21 -> {22};
22 -> {23};
23 -> {24};
24 -> {25};
25 -> {26};
26 -> {27};
27 -> {28};
}
subgraph test_2 {
29 [shape=box label="Enter function test_2"];
30 [shape=box label="Enter block"];
31 [shape=box label="Enter when"];
32 [shape=box label="Enter when branch condition "];
33 [shape=box label="Access variable R|<local>/b|"];
34 [shape=box label="Const: Boolean(true)"];
35 [shape=box label="Operator =="];
36 [shape=box label="Const: Boolean(true)"];
37 [shape=box label="Operator !="];
38 [shape=box label="Exit when branch condition"];
39 [shape=box label="Enter block"];
40 [shape=box label="Access variable R|<local>/b|"];
41 [shape=box label="Function call: R|<local>/b|.<Inapplicable(WRONG_RECEIVER): [kotlin/Boolean.not]>#()"];
42 [shape=box label="Exit block"];
43 [shape=box label="Exit when branch result"];
44 [shape=box label="Enter when branch condition else"];
45 [shape=box label="Exit when branch condition"];
46 [shape=box label="Enter block"];
47 [shape=box label="Access variable R|<local>/b|"];
48 [shape=box label="Function call: R|<local>/b|.R|kotlin/Boolean.not|()"];
49 [shape=box label="Exit block"];
50 [shape=box label="Exit when branch result"];
51 [shape=box label="Exit when"];
52 [shape=box label="Exit block"];
53 [shape=box label="Exit function test_2"];
29 -> {30};
30 -> {31};
31 -> {32};
32 -> {33};
33 -> {34};
34 -> {35};
35 -> {36};
36 -> {37};
37 -> {38};
38 -> {39 44};
39 -> {40};
40 -> {41};
41 -> {42};
42 -> {43};
43 -> {51};
44 -> {45};
45 -> {46};
46 -> {47};
47 -> {48};
48 -> {49};
49 -> {50};
50 -> {51};
51 -> {52};
52 -> {53};
}
subgraph test_3 {
54 [shape=box label="Enter function test_3"];
55 [shape=box label="Enter block"];
56 [shape=box label="Enter when"];
57 [shape=box label="Enter when branch condition "];
58 [shape=box label="Access variable R|<local>/b|"];
59 [shape=box label="Const: Boolean(true)"];
60 [shape=box label="Operator =="];
61 [shape=box label="Const: Boolean(false)"];
62 [shape=box label="Operator =="];
63 [shape=box label="Exit when branch condition"];
64 [shape=box label="Enter block"];
65 [shape=box label="Access variable R|<local>/b|"];
66 [shape=box label="Function call: R|<local>/b|.<Inapplicable(WRONG_RECEIVER): [kotlin/Boolean.not]>#()"];
67 [shape=box label="Exit block"];
68 [shape=box label="Exit when branch result"];
69 [shape=box label="Enter when branch condition else"];
70 [shape=box label="Exit when branch condition"];
71 [shape=box label="Enter block"];
72 [shape=box label="Access variable R|<local>/b|"];
73 [shape=box label="Function call: R|<local>/b|.R|kotlin/Boolean.not|()"];
74 [shape=box label="Exit block"];
75 [shape=box label="Exit when branch result"];
76 [shape=box label="Exit when"];
77 [shape=box label="Exit block"];
78 [shape=box label="Exit function test_3"];
54 -> {55};
55 -> {56};
56 -> {57};
57 -> {58};
58 -> {59};
59 -> {60};
60 -> {61};
61 -> {62};
62 -> {63};
63 -> {64 69};
64 -> {65};
65 -> {66};
66 -> {67};
67 -> {68};
68 -> {76};
69 -> {70};
70 -> {71};
71 -> {72};
72 -> {73};
73 -> {74};
74 -> {75};
75 -> {76};
76 -> {77};
77 -> {78};
}
subgraph test_4 {
79 [shape=box label="Enter function test_4"];
80 [shape=box label="Enter block"];
81 [shape=box label="Enter when"];
82 [shape=box label="Enter when branch condition "];
83 [shape=box label="Access variable R|<local>/b|"];
84 [shape=box label="Const: Boolean(true)"];
85 [shape=box label="Operator =="];
86 [shape=box label="Const: Boolean(false)"];
87 [shape=box label="Operator !="];
88 [shape=box label="Exit when branch condition"];
89 [shape=box label="Enter block"];
90 [shape=box label="Access variable R|<local>/b|"];
91 [shape=box label="Function call: R|<local>/b|.R|kotlin/Boolean.not|()"];
92 [shape=box label="Exit block"];
93 [shape=box label="Exit when branch result"];
94 [shape=box label="Enter when branch condition else"];
95 [shape=box label="Exit when branch condition"];
96 [shape=box label="Enter block"];
97 [shape=box label="Access variable R|<local>/b|"];
98 [shape=box label="Function call: R|<local>/b|.<Inapplicable(WRONG_RECEIVER): [kotlin/Boolean.not]>#()"];
99 [shape=box label="Exit block"];
100 [shape=box label="Exit when branch result"];
101 [shape=box label="Exit when"];
102 [shape=box label="Exit block"];
103 [shape=box label="Exit function test_4"];
79 -> {80};
80 -> {81};
81 -> {82};
82 -> {83};
83 -> {84};
84 -> {85};
85 -> {86};
86 -> {87};
87 -> {88};
88 -> {89 94};
89 -> {90};
90 -> {91};
91 -> {92};
92 -> {93};
93 -> {101};
94 -> {95};
95 -> {96};
96 -> {97};
97 -> {98};
98 -> {99};
99 -> {100};
100 -> {101};
101 -> {102};
102 -> {103};
}
subgraph test_5 {
104 [shape=box label="Enter function test_5"];
105 [shape=box label="Enter block"];
106 [shape=box label="Enter when"];
107 [shape=box label="Enter when branch condition "];
108 [shape=box label="Access variable R|<local>/b|"];
109 [shape=box label="Const: Boolean(true)"];
110 [shape=box label="Operator !="];
111 [shape=box label="Const: Boolean(true)"];
112 [shape=box label="Operator =="];
113 [shape=box label="Exit when branch condition"];
114 [shape=box label="Enter block"];
115 [shape=box label="Access variable R|<local>/b|"];
116 [shape=box label="Function call: R|<local>/b|.<Inapplicable(WRONG_RECEIVER): [kotlin/Boolean.not]>#()"];
117 [shape=box label="Exit block"];
118 [shape=box label="Exit when branch result"];
119 [shape=box label="Enter when branch condition else"];
120 [shape=box label="Exit when branch condition"];
121 [shape=box label="Enter block"];
122 [shape=box label="Access variable R|<local>/b|"];
123 [shape=box label="Function call: R|<local>/b|.R|kotlin/Boolean.not|()"];
124 [shape=box label="Exit block"];
125 [shape=box label="Exit when branch result"];
126 [shape=box label="Exit when"];
127 [shape=box label="Exit block"];
128 [shape=box label="Exit function test_5"];
104 -> {105};
105 -> {106};
106 -> {107};
107 -> {108};
108 -> {109};
109 -> {110};
110 -> {111};
111 -> {112};
112 -> {113};
113 -> {114 119};
114 -> {115};
115 -> {116};
116 -> {117};
117 -> {118};
118 -> {126};
119 -> {120};
120 -> {121};
121 -> {122};
122 -> {123};
123 -> {124};
124 -> {125};
125 -> {126};
126 -> {127};
127 -> {128};
}
subgraph test_6 {
129 [shape=box label="Enter function test_6"];
130 [shape=box label="Enter block"];
131 [shape=box label="Enter when"];
132 [shape=box label="Enter when branch condition "];
133 [shape=box label="Access variable R|<local>/b|"];
134 [shape=box label="Const: Boolean(true)"];
135 [shape=box label="Operator !="];
136 [shape=box label="Const: Boolean(true)"];
137 [shape=box label="Operator !="];
138 [shape=box label="Exit when branch condition"];
139 [shape=box label="Enter block"];
140 [shape=box label="Access variable R|<local>/b|"];
141 [shape=box label="Function call: R|<local>/b|.R|kotlin/Boolean.not|()"];
142 [shape=box label="Exit block"];
143 [shape=box label="Exit when branch result"];
144 [shape=box label="Enter when branch condition else"];
145 [shape=box label="Exit when branch condition"];
146 [shape=box label="Enter block"];
147 [shape=box label="Access variable R|<local>/b|"];
148 [shape=box label="Function call: R|<local>/b|.<Inapplicable(WRONG_RECEIVER): [kotlin/Boolean.not]>#()"];
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_6"];
129 -> {130};
130 -> {131};
131 -> {132};
132 -> {133};
133 -> {134};
134 -> {135};
135 -> {136};
136 -> {137};
137 -> {138};
138 -> {139 144};
139 -> {140};
140 -> {141};
141 -> {142};
142 -> {143};
143 -> {151};
144 -> {145};
145 -> {146};
146 -> {147};
147 -> {148};
148 -> {149};
149 -> {150};
150 -> {151};
151 -> {152};
152 -> {153};
}
subgraph test_7 {
154 [shape=box label="Enter function test_7"];
155 [shape=box label="Enter block"];
156 [shape=box label="Enter when"];
157 [shape=box label="Enter when branch condition "];
158 [shape=box label="Access variable R|<local>/b|"];
159 [shape=box label="Const: Boolean(true)"];
160 [shape=box label="Operator !="];
161 [shape=box label="Const: Boolean(false)"];
162 [shape=box label="Operator =="];
163 [shape=box label="Exit when branch condition"];
164 [shape=box label="Enter block"];
165 [shape=box label="Access variable R|<local>/b|"];
166 [shape=box label="Function call: R|<local>/b|.R|kotlin/Boolean.not|()"];
167 [shape=box label="Exit block"];
168 [shape=box label="Exit when branch result"];
169 [shape=box label="Enter when branch condition else"];
170 [shape=box label="Exit when branch condition"];
171 [shape=box label="Enter block"];
172 [shape=box label="Access variable R|<local>/b|"];
173 [shape=box label="Function call: R|<local>/b|.<Inapplicable(WRONG_RECEIVER): [kotlin/Boolean.not]>#()"];
174 [shape=box label="Exit block"];
175 [shape=box label="Exit when branch result"];
176 [shape=box label="Exit when"];
177 [shape=box label="Exit block"];
178 [shape=box label="Exit function test_7"];
154 -> {155};
155 -> {156};
156 -> {157};
157 -> {158};
158 -> {159};
159 -> {160};
160 -> {161};
161 -> {162};
162 -> {163};
163 -> {164 169};
164 -> {165};
165 -> {166};
166 -> {167};
167 -> {168};
168 -> {176};
169 -> {170};
170 -> {171};
171 -> {172};
172 -> {173};
173 -> {174};
174 -> {175};
175 -> {176};
176 -> {177};
177 -> {178};
}
subgraph test_8 {
179 [shape=box label="Enter function test_8"];
180 [shape=box label="Enter block"];
181 [shape=box label="Enter when"];
182 [shape=box label="Enter when branch condition "];
183 [shape=box label="Access variable R|<local>/b|"];
184 [shape=box label="Const: Boolean(true)"];
185 [shape=box label="Operator !="];
186 [shape=box label="Const: Boolean(false)"];
187 [shape=box label="Operator !="];
188 [shape=box label="Exit when branch condition"];
189 [shape=box label="Enter block"];
190 [shape=box label="Access variable R|<local>/b|"];
191 [shape=box label="Function call: R|<local>/b|.<Inapplicable(WRONG_RECEIVER): [kotlin/Boolean.not]>#()"];
192 [shape=box label="Exit block"];
193 [shape=box label="Exit when branch result"];
194 [shape=box label="Enter when branch condition else"];
195 [shape=box label="Exit when branch condition"];
196 [shape=box label="Enter block"];
197 [shape=box label="Access variable R|<local>/b|"];
198 [shape=box label="Function call: R|<local>/b|.R|kotlin/Boolean.not|()"];
199 [shape=box label="Exit block"];
200 [shape=box label="Exit when branch result"];
201 [shape=box label="Exit when"];
202 [shape=box label="Exit block"];
203 [shape=box label="Exit function test_8"];
179 -> {180};
180 -> {181};
181 -> {182};
182 -> {183};
183 -> {184};
184 -> {185};
185 -> {186};
186 -> {187};
187 -> {188};
188 -> {189 194};
189 -> {190};
190 -> {191};
191 -> {192};
192 -> {193};
193 -> {201};
194 -> {195};
195 -> {196};
196 -> {197};
197 -> {198};
198 -> {199};
199 -> {200};
200 -> {201};
201 -> {202};
202 -> {203};
}
}
@@ -0,0 +1,68 @@
interface A {
fun foo()
val b: Boolean
}
fun test_1(b: Boolean?) {
if ((b == true) == true) {
b.not() // OK
} else {
b.not() // Bad
}
}
fun test_2(b: Boolean?) {
if ((b == true) != true) {
b.not() // Bad
} else {
b.not() // OK
}
}
fun test_3(b: Boolean?) {
if ((b == true) == false) {
b.not() // Bad
} else {
b.not() // OK
}
}
fun test_4(b: Boolean?) {
if ((b == true) != false) {
b.not() // OK
} else {
b.not() // Bad
}
}
fun test_5(b: Boolean?) {
if ((b != true) == true) {
b.not() // Bad
} else {
b.not() // OK
}
}
fun test_6(b: Boolean?) {
if ((b != true) != true) {
b.not() // OK
} else {
b.not() // Bad
}
}
fun test_7(b: Boolean?) {
if ((b != true) == false) {
b.not() // OK
} else {
b.not() // Bad
}
}
fun test_8(b: Boolean?) {
if ((b != true) != false) {
b.not() // Bad
} else {
b.not() // OK
}
}
@@ -0,0 +1,96 @@
FILE: equalsToBoolean.kt
public abstract interface A : R|kotlin/Any| {
public abstract fun foo(): R|kotlin/Unit|
public abstract val b: R|kotlin/Boolean|
public get(): R|kotlin/Boolean|
}
public final fun test_1(b: R|kotlin/Boolean?|): R|kotlin/Unit| {
when () {
==(==(R|<local>/b|, Boolean(true)), Boolean(true)) -> {
R|<local>/b|.R|kotlin/Boolean.not|()
}
else -> {
R|<local>/b|.<Inapplicable(WRONG_RECEIVER): [kotlin/Boolean.not]>#()
}
}
}
public final fun test_2(b: R|kotlin/Boolean?|): R|kotlin/Unit| {
when () {
!=(==(R|<local>/b|, Boolean(true)), Boolean(true)) -> {
R|<local>/b|.<Inapplicable(WRONG_RECEIVER): [kotlin/Boolean.not]>#()
}
else -> {
R|<local>/b|.R|kotlin/Boolean.not|()
}
}
}
public final fun test_3(b: R|kotlin/Boolean?|): R|kotlin/Unit| {
when () {
==(==(R|<local>/b|, Boolean(true)), Boolean(false)) -> {
R|<local>/b|.<Inapplicable(WRONG_RECEIVER): [kotlin/Boolean.not]>#()
}
else -> {
R|<local>/b|.R|kotlin/Boolean.not|()
}
}
}
public final fun test_4(b: R|kotlin/Boolean?|): R|kotlin/Unit| {
when () {
!=(==(R|<local>/b|, Boolean(true)), Boolean(false)) -> {
R|<local>/b|.R|kotlin/Boolean.not|()
}
else -> {
R|<local>/b|.<Inapplicable(WRONG_RECEIVER): [kotlin/Boolean.not]>#()
}
}
}
public final fun test_5(b: R|kotlin/Boolean?|): R|kotlin/Unit| {
when () {
==(!=(R|<local>/b|, Boolean(true)), Boolean(true)) -> {
R|<local>/b|.<Inapplicable(WRONG_RECEIVER): [kotlin/Boolean.not]>#()
}
else -> {
R|<local>/b|.R|kotlin/Boolean.not|()
}
}
}
public final fun test_6(b: R|kotlin/Boolean?|): R|kotlin/Unit| {
when () {
!=(!=(R|<local>/b|, Boolean(true)), Boolean(true)) -> {
R|<local>/b|.R|kotlin/Boolean.not|()
}
else -> {
R|<local>/b|.<Inapplicable(WRONG_RECEIVER): [kotlin/Boolean.not]>#()
}
}
}
public final fun test_7(b: R|kotlin/Boolean?|): R|kotlin/Unit| {
when () {
==(!=(R|<local>/b|, Boolean(true)), Boolean(false)) -> {
R|<local>/b|.R|kotlin/Boolean.not|()
}
else -> {
R|<local>/b|.<Inapplicable(WRONG_RECEIVER): [kotlin/Boolean.not]>#()
}
}
}
public final fun test_8(b: R|kotlin/Boolean?|): R|kotlin/Unit| {
when () {
!=(!=(R|<local>/b|, Boolean(true)), Boolean(false)) -> {
R|<local>/b|.<Inapplicable(WRONG_RECEIVER): [kotlin/Boolean.not]>#()
}
else -> {
R|<local>/b|.R|kotlin/Boolean.not|()
}
}
}
@@ -119,6 +119,11 @@ public class FirCfgBuildingTestGenerated extends AbstractFirCfgBuildingTest {
runTest("compiler/fir/resolve/testData/resolve/smartcasts/equalsAndIdentity.kt");
}
@TestMetadata("equalsToBoolean.kt")
public void testEqualsToBoolean() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/smartcasts/equalsToBoolean.kt");
}
@TestMetadata("inPlaceLambdas.kt")
public void testInPlaceLambdas() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/smartcasts/inPlaceLambdas.kt");