|
1 | 1 | package liquidjava.processor.refinement_checker; |
2 | 2 |
|
3 | 3 | import java.util.ArrayList; |
| 4 | +import java.util.HashSet; |
4 | 5 | import java.util.List; |
| 6 | +import java.util.Optional; |
5 | 7 | import java.util.Set; |
6 | 8 | import java.util.Stack; |
7 | 9 | import java.util.function.Consumer; |
|
12 | 14 | import liquidjava.processor.VCImplication; |
13 | 15 | import liquidjava.processor.context.*; |
14 | 16 | 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; |
15 | 20 | import liquidjava.smt.Counterexample; |
16 | 21 | import liquidjava.smt.SMTEvaluator; |
17 | 22 | import liquidjava.smt.SMTResult; |
@@ -191,7 +196,18 @@ private VCImplication joinPredicates(Predicate expectedType, List<RefinedVariabl |
191 | 196 |
|
192 | 197 | for (RefinedVariable var : vars) { // join refinements of vars |
193 | 198 | 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); |
195 | 211 | if (lastSi != null) { |
196 | 212 | lastSi.setNext(si); |
197 | 213 | lastSi = si; |
@@ -268,6 +284,22 @@ void removePathVariableThatIncludes(String otherVar) { |
268 | 284 | .forEach(pathVariables::remove); |
269 | 285 | } |
270 | 286 |
|
| 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 | + |
271 | 303 | // Errors--------------------------------------------------------------------------------------------------- |
272 | 304 |
|
273 | 305 | protected void throwRefinementError(SourcePosition position, Predicate expected, Predicate found, |
|
0 commit comments