Pseudocode: Generate proper instructions for when conditions. Eliminate unnecessary magics
This commit is contained in:
@@ -120,24 +120,41 @@ public class JetControlFlowProcessor {
|
||||
|
||||
private final JetVisitorVoid conditionVisitor = new JetVisitorVoid() {
|
||||
|
||||
private JetExpression getSubjectExpression(JetWhenCondition condition) {
|
||||
JetWhenExpression whenExpression = PsiTreeUtil.getParentOfType(condition, JetWhenExpression.class);
|
||||
return whenExpression != null ? whenExpression.getSubjectExpression() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitWhenConditionInRange(@NotNull JetWhenConditionInRange condition) {
|
||||
generateInstructions(condition.getRangeExpression());
|
||||
generateInstructions(condition.getOperationReference());
|
||||
|
||||
// TODO : read the call to contains()...
|
||||
createNonSyntheticValue(condition, condition.getRangeExpression(), condition.getOperationReference());
|
||||
if (!generateCall(condition.getOperationReference())) {
|
||||
JetExpression rangeExpression = condition.getRangeExpression();
|
||||
generateInstructions(rangeExpression);
|
||||
createNonSyntheticValue(condition, rangeExpression);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitWhenConditionIsPattern(@NotNull JetWhenConditionIsPattern condition) {
|
||||
// TODO: types in CF?
|
||||
mark(condition);
|
||||
createNonSyntheticValue(condition, getSubjectExpression(condition));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitWhenConditionWithExpression(@NotNull JetWhenConditionWithExpression condition) {
|
||||
generateInstructions(condition.getExpression());
|
||||
copyValue(condition.getExpression(), condition);
|
||||
mark(condition);
|
||||
|
||||
JetExpression expression = condition.getExpression();
|
||||
generateInstructions(expression);
|
||||
|
||||
JetExpression subjectExpression = getSubjectExpression(condition);
|
||||
if (subjectExpression != null) {
|
||||
// todo: this can be replaced by equals() invocation (when corresponding resolved call is recorded)
|
||||
createNonSyntheticValue(condition, subjectExpression, expression);
|
||||
}
|
||||
else {
|
||||
copyValue(expression, condition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1254,19 +1271,14 @@ public class JetControlFlowProcessor {
|
||||
JetWhenCondition condition = conditions[i];
|
||||
condition.accept(conditionVisitor);
|
||||
if (i + 1 < conditions.length) {
|
||||
PseudoValue conditionValue = createSyntheticValue(condition, subjectExpression, condition);
|
||||
builder.nondeterministicJump(bodyLabel, expression, conditionValue);
|
||||
builder.nondeterministicJump(bodyLabel, expression, builder.getBoundValue(condition));
|
||||
}
|
||||
}
|
||||
|
||||
if (!isElse) {
|
||||
nextLabel = builder.createUnboundLabel();
|
||||
PseudoValue conditionValue = null;
|
||||
JetWhenCondition lastCondition = KotlinPackage.lastOrNull(conditions);
|
||||
if (lastCondition != null) {
|
||||
conditionValue = createSyntheticValue(lastCondition, subjectExpression, lastCondition);
|
||||
}
|
||||
builder.nondeterministicJump(nextLabel, expression, conditionValue);
|
||||
builder.nondeterministicJump(nextLabel, expression, builder.getBoundValue(lastCondition));
|
||||
}
|
||||
|
||||
builder.bindLabel(bodyLabel);
|
||||
@@ -1515,7 +1527,9 @@ public class JetControlFlowProcessor {
|
||||
SmartFMap<PseudoValue, ValueParameterDescriptor> parameterValues) {
|
||||
JetExpression expression = valueArgument.getArgumentExpression();
|
||||
if (expression != null) {
|
||||
generateInstructions(expression);
|
||||
if (!valueArgument.isExternal()) {
|
||||
generateInstructions(expression);
|
||||
}
|
||||
|
||||
PseudoValue argValue = builder.getBoundValue(expression);
|
||||
if (argValue != null) {
|
||||
|
||||
@@ -14,6 +14,7 @@ L0:
|
||||
mark(when(a) { is Int -> return a })
|
||||
r(a) -> <v1>
|
||||
mark(is Int -> return a)
|
||||
mark(is Int)
|
||||
magic(is Int|<v1>) -> <v2>
|
||||
jmp?(L4|<v2>) NEXT:[<END>, r(a) -> <v3>]
|
||||
L3:
|
||||
|
||||
@@ -5,8 +5,8 @@ fun illegalWhenBlock(a: Any): Any {
|
||||
}
|
||||
}
|
||||
---------------------
|
||||
<v0>: {<: Any} NEW: magic(a: Any) -> <v0>
|
||||
<v2>: * NEW: magic(is Int|<v1>) -> <v2>
|
||||
a <v1>: * NEW: r(a) -> <v1>
|
||||
a <v3>: {<: Any} NEW: r(a) -> <v3>
|
||||
<v0>: {<: Any} NEW: magic(a: Any) -> <v0>
|
||||
a <v1>: * NEW: r(a) -> <v1>
|
||||
is Int <v2>: * NEW: magic(is Int|<v1>) -> <v2>
|
||||
a <v3>: {<: Any} NEW: r(a) -> <v3>
|
||||
=====================
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
== foo ==
|
||||
fun foo(a: Number) {
|
||||
val t = when (a) {
|
||||
1 -> "1"
|
||||
in Collections.singleton(2) -> "2"
|
||||
is Int -> "Int"
|
||||
!in Collections.singleton(3) -> "!3"
|
||||
!is Number -> "!Number"
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
---------------------
|
||||
L0:
|
||||
1 <START>
|
||||
v(a: Number)
|
||||
magic(a: Number) -> <v0>
|
||||
w(a|<v0>)
|
||||
2 mark({ val t = when (a) { 1 -> "1" in Collections.singleton(2) -> "2" is Int -> "Int" !in Collections.singleton(3) -> "!3" !is Number -> "!Number" else -> null } })
|
||||
v(val t = when (a) { 1 -> "1" in Collections.singleton(2) -> "2" is Int -> "Int" !in Collections.singleton(3) -> "!3" !is Number -> "!Number" else -> null })
|
||||
mark(when (a) { 1 -> "1" in Collections.singleton(2) -> "2" is Int -> "Int" !in Collections.singleton(3) -> "!3" !is Number -> "!Number" else -> null })
|
||||
r(a) -> <v1>
|
||||
mark(1 -> "1")
|
||||
mark(1)
|
||||
r(1) -> <v2>
|
||||
magic(1|<v1>, <v2>) -> <v3>
|
||||
jmp?(L4|<v3>) NEXT:[mark(in Collections.singleton(2) -> "2"), mark("1")]
|
||||
L3:
|
||||
mark("1")
|
||||
r("1") -> <v4>
|
||||
jmp(L2) NEXT:[merge(when (a) { 1 -> "1" in Collections.singleton(2) -> "2" is Int -> "Int" !in Collections.singleton(3) -> "!3" !is Number -> "!Number" else -> null }|<v4>, <v8>, <v10>, <v14>, <v16>, <v17>) -> <v18>]
|
||||
L4:
|
||||
mark(in Collections.singleton(2) -> "2") PREV:[jmp?(L4|<v3>)]
|
||||
mark(Collections.singleton(2))
|
||||
r(2) -> <v5>
|
||||
mark(singleton(2))
|
||||
call(singleton(2), singleton|<v5>) -> <v6>
|
||||
mark(in Collections.singleton(2))
|
||||
call(in Collections.singleton(2), contains|<v6>, <v1>) -> <v7>
|
||||
jmp?(L6|<v7>) NEXT:[mark(is Int -> "Int"), mark("2")]
|
||||
L5:
|
||||
mark("2")
|
||||
r("2") -> <v8>
|
||||
jmp(L2) NEXT:[merge(when (a) { 1 -> "1" in Collections.singleton(2) -> "2" is Int -> "Int" !in Collections.singleton(3) -> "!3" !is Number -> "!Number" else -> null }|<v4>, <v8>, <v10>, <v14>, <v16>, <v17>) -> <v18>]
|
||||
L6:
|
||||
mark(is Int -> "Int") PREV:[jmp?(L6|<v7>)]
|
||||
mark(is Int)
|
||||
magic(is Int|<v1>) -> <v9>
|
||||
jmp?(L8|<v9>) NEXT:[mark(!in Collections.singleton(3) -> "!3"), mark("Int")]
|
||||
L7:
|
||||
mark("Int")
|
||||
r("Int") -> <v10>
|
||||
jmp(L2) NEXT:[merge(when (a) { 1 -> "1" in Collections.singleton(2) -> "2" is Int -> "Int" !in Collections.singleton(3) -> "!3" !is Number -> "!Number" else -> null }|<v4>, <v8>, <v10>, <v14>, <v16>, <v17>) -> <v18>]
|
||||
L8:
|
||||
mark(!in Collections.singleton(3) -> "!3") PREV:[jmp?(L8|<v9>)]
|
||||
mark(Collections.singleton(3))
|
||||
r(3) -> <v11>
|
||||
mark(singleton(3))
|
||||
call(singleton(3), singleton|<v11>) -> <v12>
|
||||
mark(!in Collections.singleton(3))
|
||||
call(!in Collections.singleton(3), contains|<v12>, <v1>) -> <v13>
|
||||
jmp?(L10|<v13>) NEXT:[mark(!is Number -> "!Number"), mark("!3")]
|
||||
L9:
|
||||
mark("!3")
|
||||
r("!3") -> <v14>
|
||||
jmp(L2) NEXT:[merge(when (a) { 1 -> "1" in Collections.singleton(2) -> "2" is Int -> "Int" !in Collections.singleton(3) -> "!3" !is Number -> "!Number" else -> null }|<v4>, <v8>, <v10>, <v14>, <v16>, <v17>) -> <v18>]
|
||||
L10:
|
||||
mark(!is Number -> "!Number") PREV:[jmp?(L10|<v13>)]
|
||||
mark(!is Number)
|
||||
magic(!is Number|<v1>) -> <v15>
|
||||
jmp?(L12|<v15>) NEXT:[mark(else -> null), mark("!Number")]
|
||||
L11:
|
||||
mark("!Number")
|
||||
r("!Number") -> <v16>
|
||||
jmp(L2) NEXT:[merge(when (a) { 1 -> "1" in Collections.singleton(2) -> "2" is Int -> "Int" !in Collections.singleton(3) -> "!3" !is Number -> "!Number" else -> null }|<v4>, <v8>, <v10>, <v14>, <v16>, <v17>) -> <v18>]
|
||||
L12:
|
||||
mark(else -> null) PREV:[jmp?(L12|<v15>)]
|
||||
L13:
|
||||
r(null) -> <v17>
|
||||
jmp(L2)
|
||||
L2:
|
||||
merge(when (a) { 1 -> "1" in Collections.singleton(2) -> "2" is Int -> "Int" !in Collections.singleton(3) -> "!3" !is Number -> "!Number" else -> null }|<v4>, <v8>, <v10>, <v14>, <v16>, <v17>) -> <v18> PREV:[jmp(L2), jmp(L2), jmp(L2), jmp(L2), jmp(L2), jmp(L2)]
|
||||
w(t|<v18>)
|
||||
L1:
|
||||
1 <END> NEXT:[<SINK>]
|
||||
error:
|
||||
<ERROR> PREV:[]
|
||||
sink:
|
||||
<SINK> PREV:[<ERROR>, <END>]
|
||||
=====================
|
||||
@@ -0,0 +1,12 @@
|
||||
import java.util.Collections
|
||||
|
||||
fun foo(a: Number) {
|
||||
val t = when (a) {
|
||||
1 -> "1"
|
||||
in Collections.singleton(2) -> "2"
|
||||
is Int -> "Int"
|
||||
!in Collections.singleton(3) -> "!3"
|
||||
!is Number -> "!Number"
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
== foo ==
|
||||
fun foo(a: Number) {
|
||||
val t = when (a) {
|
||||
1 -> "1"
|
||||
in Collections.singleton(2) -> "2"
|
||||
is Int -> "Int"
|
||||
!in Collections.singleton(3) -> "!3"
|
||||
!is Number -> "!Number"
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
---------------------
|
||||
<v0>: {<: Number} NEW: magic(a: Number) -> <v0>
|
||||
a <v1>: AND{{<: Any?}, *} NEW: r(a) -> <v1>
|
||||
1 <v2>: * NEW: r(1) -> <v2>
|
||||
1 <v3>: * NEW: magic(1|<v1>, <v2>) -> <v3>
|
||||
"1" <v4>: {<: String?} NEW: r("1") -> <v4>
|
||||
2 <v5>: {<: Int?} NEW: r(2) -> <v5>
|
||||
singleton(2) <v6>: OR{{<: Collection<Int>}, {<: Collection<Int>}} NEW: call(singleton(2), singleton|<v5>) -> <v6>
|
||||
Collections.singleton(2) <v6>: OR{{<: Collection<Int>}, {<: Collection<Int>}} COPY
|
||||
in Collections.singleton(2) <v7>: * NEW: call(in Collections.singleton(2), contains|<v6>, <v1>) -> <v7>
|
||||
"2" <v8>: {<: String?} NEW: r("2") -> <v8>
|
||||
is Int <v9>: * NEW: magic(is Int|<v1>) -> <v9>
|
||||
"Int" <v10>: {<: String?} NEW: r("Int") -> <v10>
|
||||
3 <v11>: {<: Int?} NEW: r(3) -> <v11>
|
||||
singleton(3) <v12>: OR{{<: Collection<Int>}, {<: Collection<Int>}} NEW: call(singleton(3), singleton|<v11>) -> <v12>
|
||||
Collections.singleton(3) <v12>: OR{{<: Collection<Int>}, {<: Collection<Int>}} COPY
|
||||
!in Collections.singleton(3) <v13>: * NEW: call(!in Collections.singleton(3), contains|<v12>, <v1>) -> <v13>
|
||||
"!3" <v14>: {<: String?} NEW: r("!3") -> <v14>
|
||||
!is Number <v15>: * NEW: magic(!is Number|<v1>) -> <v15>
|
||||
"!Number" <v16>: {<: String?} NEW: r("!Number") -> <v16>
|
||||
null <v17>: {<: String?} NEW: r(null) -> <v17>
|
||||
when (a) { 1 -> "1" in Collections.singleton(2) -> "2" is Int -> "Int" !in Collections.singleton(3) -> "!3" !is Number -> "!Number" else -> null } <v18>: {<: String?} NEW: merge(when (a) { 1 -> "1" in Collections.singleton(2) -> "2" is Int -> "Int" !in Collections.singleton(3) -> "!3" !is Number -> "!Number" else -> null }|<v4>, <v8>, <v10>, <v14>, <v16>, <v17>) -> <v18>
|
||||
=====================
|
||||
@@ -147,6 +147,11 @@ public class ControlFlowTestGenerated extends AbstractControlFlowTest {
|
||||
doTest("compiler/testData/cfg/controlStructures/returnsInWhen.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("whenConditions.kt")
|
||||
public void testWhenConditions() throws Exception {
|
||||
doTest("compiler/testData/cfg/controlStructures/whenConditions.kt");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/cfg/conventions")
|
||||
|
||||
@@ -149,6 +149,11 @@ public class PseudoValueTestGenerated extends AbstractPseudoValueTest {
|
||||
doTest("compiler/testData/cfg/controlStructures/returnsInWhen.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("whenConditions.kt")
|
||||
public void testWhenConditions() throws Exception {
|
||||
doTest("compiler/testData/cfg/controlStructures/whenConditions.kt");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/cfg/conventions")
|
||||
|
||||
Reference in New Issue
Block a user