Skip to content

Commit eeb2728

Browse files
authored
Special Variable Substitution (#201)
1 parent ac31356 commit eeb2728

3 files changed

Lines changed: 51 additions & 16 deletions

File tree

liquidjava-verifier/src/main/java/liquidjava/rj_language/ast/Var.java

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,4 @@ public boolean equals(Object obj) {
7070
return name.equals(other.name);
7171
}
7272
}
73-
74-
public boolean isInternal() {
75-
return name.startsWith("#");
76-
}
77-
78-
public int getCounter() {
79-
if (!isInternal())
80-
throw new IllegalStateException("Cannot get counter of non-internal variable");
81-
int lastUnderscore = name.lastIndexOf('_');
82-
return Integer.parseInt(name.substring(lastUnderscore + 1));
83-
}
8473
}

liquidjava-verifier/src/main/java/liquidjava/rj_language/opt/VariableResolver.java

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,17 @@ private static void resolveRecursive(Expression exp, Map<String, Expression> map
5454
map.put(var.getName(), left.clone());
5555
} else if (left instanceof Var leftVar && right instanceof Var rightVar) {
5656
// to substitute internal variable with user-facing variable
57-
if (leftVar.isInternal() && !rightVar.isInternal()) {
57+
if (isInternal(leftVar) && !isInternal(rightVar) && !isReturnVar(leftVar)) {
5858
map.put(leftVar.getName(), right.clone());
59-
} else if (rightVar.isInternal() && !leftVar.isInternal()) {
59+
} else if (isInternal(rightVar) && !isInternal(leftVar) && !isReturnVar(rightVar)) {
6060
map.put(rightVar.getName(), left.clone());
61-
} else if (leftVar.isInternal() && rightVar.isInternal()) {
61+
} else if (isInternal(leftVar) && isInternal(rightVar)) {
6262
// to substitute the lower-counter variable with the higher-counter one
63-
boolean isLeftCounterLower = leftVar.getCounter() <= rightVar.getCounter();
63+
boolean isLeftCounterLower = getCounter(leftVar) <= getCounter(rightVar);
6464
Var lowerVar = isLeftCounterLower ? leftVar : rightVar;
6565
Var higherVar = isLeftCounterLower ? rightVar : leftVar;
66-
map.putIfAbsent(lowerVar.getName(), higherVar.clone());
66+
if (!isReturnVar(lowerVar) && !isFreshVar(higherVar))
67+
map.putIfAbsent(lowerVar.getName(), higherVar.clone());
6768
}
6869
}
6970
}
@@ -144,4 +145,23 @@ private static boolean hasUsage(Expression exp, String name) {
144145
// usage not found
145146
return false;
146147
}
148+
149+
private static int getCounter(Var var) {
150+
if (!isInternal(var))
151+
throw new IllegalStateException("Cannot get counter of non-internal variable");
152+
int lastUnderscore = var.getName().lastIndexOf('_');
153+
return Integer.parseInt(var.getName().substring(lastUnderscore + 1));
154+
}
155+
156+
private static boolean isInternal(Var var) {
157+
return var.getName().startsWith("#");
158+
}
159+
160+
private static boolean isReturnVar(Var var) {
161+
return var.getName().startsWith("#ret_");
162+
}
163+
164+
private static boolean isFreshVar(Var var) {
165+
return var.getName().startsWith("#fresh_");
166+
}
147167
}

liquidjava-verifier/src/test/java/liquidjava/rj_language/opt/VariableResolverTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,30 @@ void testUnusedEqualitiesShouldBeIgnored() {
142142
assertEquals(1, result.size(), "Should only extract used variable z");
143143
assertEquals("3", result.get("z").toString());
144144
}
145+
146+
@Test
147+
void testReturnVariableIsNotSubstituted() {
148+
// #ret_1 == x && x > 0 should not substitute #ret_1 with x
149+
Expression ret = new Var("#ret_1");
150+
Expression x = new Var("x");
151+
Expression xGreaterZero = new BinaryExpression(x, ">", new LiteralInt(0));
152+
Expression retEqualsX = new BinaryExpression(ret, "==", x);
153+
Expression fullExpr = new BinaryExpression(xGreaterZero, "&&", retEqualsX);
154+
155+
Map<String, Expression> result = VariableResolver.resolve(fullExpr);
156+
assertTrue(result.isEmpty(), "Return variables should not be substituted with another variable");
157+
}
158+
159+
@Test
160+
void testFreshVariableIsNotUsedAsSubstitutionTarget() {
161+
// #tmp_1 > 0 && #tmp_1 == #fresh_2 should not substitute #tmp_1 with #fresh_2
162+
Expression internal = new Var("#tmp_1");
163+
Expression fresh = new Var("#fresh_2");
164+
Expression internalGreaterZero = new BinaryExpression(internal, ">", new LiteralInt(0));
165+
Expression internalEqualsFresh = new BinaryExpression(internal, "==", fresh);
166+
Expression fullExpr = new BinaryExpression(internalGreaterZero, "&&", internalEqualsFresh);
167+
168+
Map<String, Expression> result = VariableResolver.resolve(fullExpr);
169+
assertTrue(result.isEmpty(), "Fresh variables should not replace another variable");
170+
}
145171
}

0 commit comments

Comments
 (0)