Skip to content

Commit 359f639

Browse files
committed
Fix Self-Referential Assignments
1 parent f8d3f3e commit 359f639

File tree

2 files changed

+45
-1
lines changed

2 files changed

+45
-1
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Refinement Error
2+
package testSuite;
3+
4+
import liquidjava.specification.Refinement;
5+
6+
public class ErrorAssignmentBeforeReturn {
7+
@Refinement("_ > 0")
8+
static int example(int x) {
9+
x = x + 1;
10+
return x;
11+
}
12+
}

liquidjava-verifier/src/main/java/liquidjava/processor/refinement_checker/VCChecker.java

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package liquidjava.processor.refinement_checker;
22

33
import java.util.ArrayList;
4+
import java.util.HashSet;
45
import java.util.List;
6+
import java.util.Optional;
57
import java.util.Set;
68
import java.util.Stack;
79
import java.util.function.Consumer;
@@ -12,6 +14,9 @@
1214
import liquidjava.processor.VCImplication;
1315
import liquidjava.processor.context.*;
1416
import liquidjava.rj_language.Predicate;
17+
import liquidjava.rj_language.ast.Expression;
18+
import liquidjava.rj_language.ast.Ite;
19+
import liquidjava.rj_language.ast.Var;
1520
import liquidjava.smt.Counterexample;
1621
import liquidjava.smt.SMTEvaluator;
1722
import liquidjava.smt.SMTResult;
@@ -191,7 +196,18 @@ private VCImplication joinPredicates(Predicate expectedType, List<RefinedVariabl
191196

192197
for (RefinedVariable var : vars) { // join refinements of vars
193198
addMap(var, map);
194-
VCImplication si = new VCImplication(var.getName(), var.getType(), var.getRefinement());
199+
200+
// if the last instance is already in vars, it is already in the premises
201+
// adding "var == lastInstance" would create a contradictory cycle (e.g. x == x + 1 for x = x + 1)
202+
// so we need to use main refinement to avoid this
203+
Predicate refinement = var.getRefinement();
204+
if (var instanceof Variable v) {
205+
Optional<VariableInstance> lastInst = v.getLastInstance();
206+
if (lastInst.isPresent() && vars.contains(lastInst.get())
207+
&& hasDependencyCycle(lastInst.get(), var.getName(), vars, new HashSet<>()))
208+
refinement = v.getMainRefinement();
209+
}
210+
VCImplication si = new VCImplication(var.getName(), var.getType(), refinement);
195211
if (lastSi != null) {
196212
lastSi.setNext(si);
197213
lastSi = si;
@@ -268,6 +284,22 @@ void removePathVariableThatIncludes(String otherVar) {
268284
.forEach(pathVariables::remove);
269285
}
270286

287+
private boolean hasDependencyCycle(RefinedVariable rv, String var, List<RefinedVariable> vars, Set<String> seen) {
288+
if (!seen.add(rv.getName()))
289+
return false;
290+
Expression e = rv.getRefinement().getExpression();
291+
return hasVariable(e, var) || vars.stream().filter(o -> hasVariable(e, o.getName()))
292+
.anyMatch(o -> hasDependencyCycle(o, var, vars, seen));
293+
}
294+
295+
private boolean hasVariable(Expression exp, String var) {
296+
if (exp instanceof Var v)
297+
return v.getName().equals(var);
298+
if (exp instanceof Ite ite)
299+
return hasVariable(ite.getThen(), var) || hasVariable(ite.getElse(), var);
300+
return exp.getChildren().stream().anyMatch(c -> hasVariable(c, var));
301+
}
302+
271303
// Errors---------------------------------------------------------------------------------------------------
272304

273305
protected void throwRefinementError(SourcePosition position, Predicate expected, Predicate found,

0 commit comments

Comments
 (0)