Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package testSuite;

import liquidjava.specification.Refinement;

public class ErrorWarningUnsatRefinement {

public void example1() {
@Refinement("x == 1 && x != 1") // Unsat Refinement Warning
int x = 1; // Refinement Error
}

public void example2() {
@Refinement("x % 2 > 1") // Unsat Refinement Warning
int x = 5; // Refinement Error
}

public void example3() {
@Refinement("false") // Unsat Refinement Warning
int x = 0; // Refinement Error
}

public void example4(@Refinement("x > 0 && x < 0") int x) {} // Unsat Refinement Warning

@Refinement("_ == true && _ == false") // Unsat Refinement Warning
public boolean example5() {
return true; // Refinement Error
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package liquidjava.diagnostics.warnings;

import spoon.reflect.cu.SourcePosition;

/**
* Warning indicating that a refinement predicate is unsatisfiable
*
* @see LJWarning
*/
public class UnsatRefinementWarning extends LJWarning {

private final String refinement;

public UnsatRefinementWarning(SourcePosition position, String message, String refinement) {
super(message, position);
this.refinement = refinement;
}

public String getRefinement() {
return refinement;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
import java.util.Map;
import java.util.Optional;

import liquidjava.diagnostics.Diagnostics;
import liquidjava.diagnostics.errors.*;
import liquidjava.diagnostics.warnings.UnsatRefinementWarning;
import liquidjava.processor.context.AliasWrapper;
import liquidjava.processor.context.Context;
import liquidjava.processor.context.GhostFunction;
Expand All @@ -16,6 +18,7 @@
import liquidjava.processor.facade.GhostDTO;
import liquidjava.rj_language.Predicate;
import liquidjava.rj_language.parsing.RefinementsParser;
import liquidjava.smt.SMTEvaluator;
import liquidjava.smt.SMTResult;
import liquidjava.utils.Utils;
import liquidjava.utils.constants.Formats;
Expand All @@ -29,6 +32,9 @@
import spoon.reflect.declaration.CtClass;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtInterface;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtTypedElement;
import spoon.reflect.declaration.CtVariable;
import spoon.reflect.factory.Factory;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.visitor.CtScanner;
Expand All @@ -40,6 +46,7 @@ public abstract class TypeChecker extends CtScanner {
protected final Context context;
protected final Factory factory;
protected final VCChecker vcChecker;
private final Diagnostics diagnostics = Diagnostics.getInstance();

public TypeChecker(Context context, Factory factory) {
this.context = context;
Expand Down Expand Up @@ -87,11 +94,48 @@ public Optional<Predicate> getRefinementFromAnnotation(CtElement element) throws
throw new InvalidRefinementError(position, "Refinement predicate must be a boolean expression",
ref.get());
}
if (!Boolean.TRUE.equals(element.getMetadata(Keys.REFINEMENT_SAT_CHECK)))
checkRefinementSatisfiability(ref.get(), p, element);
constr = Optional.of(p);
}
return constr;
}

private void checkRefinementSatisfiability(String refinement, Predicate predicate, CtElement element) {
context.enterContext();
try {
Predicate p = new Predicate();
CtTypeReference<?> annotationType = element instanceof CtTypedElement<?> typedElement
? typedElement.getType() : null;
if (annotationType != null && !context.hasVariable(Keys.WILDCARD))
context.addVarToContext(Keys.WILDCARD, annotationType, p, element);

if (element instanceof CtVariable<?> variable && !context.hasVariable(variable.getSimpleName()))
context.addVarToContext(variable.getSimpleName(), variable.getType(), p, element);

if (element instanceof CtMethod<?> method && method.getType() != null && !context.hasVariable("return"))
context.addVarToContext("return", method.getType(), p, element);

String qualifiedClassName = getQualifiedClassName(element);
if (qualifiedClassName != null && !context.hasVariable(Keys.THIS))
context.addVarToContext(Keys.THIS, factory.Type().createReference(qualifiedClassName), p, element);

Predicate refinementPredicate = predicate.changeStatesToRefinements(context.getGhostStates(),
new String[] { Keys.WILDCARD, Keys.THIS });
refinementPredicate = refinementPredicate.changeAliasToRefinement(context, factory);

if (new SMTEvaluator().isUnsatisfiable(refinementPredicate, context)) {
SourcePosition position = Utils.getLJAnnotationPosition(element, refinement);
diagnostics.add(new UnsatRefinementWarning(position, "This refinement can never be true", refinement));
}
element.putMetadata(Keys.REFINEMENT_SAT_CHECK, true); // for caching the satisfiability check result
} catch (Exception e) {
// ignore
} finally {
context.exitContext();
}
}

@SuppressWarnings({ "rawtypes" })
public Optional<String> getMessageFromAnnotation(CtElement element) {
for (CtAnnotation<? extends Annotation> ann : element.getAnnotations()) {
Expand Down
14 changes: 14 additions & 0 deletions liquidjava-verifier/src/main/java/liquidjava/smt/SMTEvaluator.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,18 @@ public SMTResult verifySubtype(Predicate subRef, Predicate supRef, Context conte
}
return SMTResult.ok();
}

public boolean isUnsatisfiable(Predicate predicate, Context context) throws Exception {
try {
Expression exp = predicate.getExpression();
try (TranslatorToZ3 tz3 = new TranslatorToZ3(context)) {
ExpressionToZ3Visitor visitor = new ExpressionToZ3Visitor(tz3);
Expr<?> e = exp.accept(visitor);
Solver solver = tz3.makeSolverForExpression(e);
return solver.check().equals(Status.UNSATISFIABLE);
}
} catch (Z3Exception e) {
throw new Z3Exception(e.getLocalizedMessage());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

public final class Keys {
public static final String REFINEMENT = "refinement";
public static final String REFINEMENT_SAT_CHECK = "refinement_sat_check";
public static final String TARGET = "target";
public static final String THIS = "this";
public static final String WILDCARD = "_";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class TestUtils {
* @param path
*/
public static boolean shouldPass(String path) {
return path.toLowerCase().contains("correct") || path.toLowerCase().contains("warning");
return path.toLowerCase().contains("correct");
}

/**
Expand Down
Loading