diff --git a/liquidjava-example/src/main/java/testSuite/ErrorAssignmentBeforeReturn.java b/liquidjava-example/src/main/java/testSuite/ErrorAssignmentBeforeReturn.java new file mode 100644 index 00000000..8b5eff5a --- /dev/null +++ b/liquidjava-example/src/main/java/testSuite/ErrorAssignmentBeforeReturn.java @@ -0,0 +1,12 @@ +// Refinement Error +package testSuite; + +import liquidjava.specification.Refinement; + +public class ErrorAssignmentBeforeReturn { + @Refinement("_ > 0") + static int example(int x) { + x = x + 1; + return x; + } +} diff --git a/liquidjava-verifier/src/main/java/liquidjava/processor/refinement_checker/VCChecker.java b/liquidjava-verifier/src/main/java/liquidjava/processor/refinement_checker/VCChecker.java index 51084672..706dc4e3 100644 --- a/liquidjava-verifier/src/main/java/liquidjava/processor/refinement_checker/VCChecker.java +++ b/liquidjava-verifier/src/main/java/liquidjava/processor/refinement_checker/VCChecker.java @@ -1,7 +1,9 @@ package liquidjava.processor.refinement_checker; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.Stack; import java.util.function.Consumer; @@ -12,6 +14,9 @@ import liquidjava.processor.VCImplication; import liquidjava.processor.context.*; import liquidjava.rj_language.Predicate; +import liquidjava.rj_language.ast.Expression; +import liquidjava.rj_language.ast.Ite; +import liquidjava.rj_language.ast.Var; import liquidjava.smt.Counterexample; import liquidjava.smt.SMTEvaluator; import liquidjava.smt.SMTResult; @@ -191,7 +196,18 @@ private VCImplication joinPredicates(Predicate expectedType, List lastInst = v.getLastInstance(); + if (lastInst.isPresent() && vars.contains(lastInst.get()) + && hasDependencyCycle(lastInst.get(), var.getName(), vars, new HashSet<>())) + refinement = v.getMainRefinement(); + } + VCImplication si = new VCImplication(var.getName(), var.getType(), refinement); if (lastSi != null) { lastSi.setNext(si); lastSi = si; @@ -268,6 +284,22 @@ void removePathVariableThatIncludes(String otherVar) { .forEach(pathVariables::remove); } + private boolean hasDependencyCycle(RefinedVariable rv, String var, List vars, Set seen) { + if (!seen.add(rv.getName())) + return false; + Expression e = rv.getRefinement().getExpression(); + return hasVariable(e, var) || vars.stream().filter(o -> hasVariable(e, o.getName())) + .anyMatch(o -> hasDependencyCycle(o, var, vars, seen)); + } + + private boolean hasVariable(Expression exp, String var) { + if (exp instanceof Var v) + return v.getName().equals(var); + if (exp instanceof Ite ite) + return hasVariable(ite.getThen(), var) || hasVariable(ite.getElse(), var); + return exp.getChildren().stream().anyMatch(c -> hasVariable(c, var)); + } + // Errors--------------------------------------------------------------------------------------------------- protected void throwRefinementError(SourcePosition position, Predicate expected, Predicate found,