Verification Features
The Annotations section explains how to write specifications. This section explains how LiquidJava uses them when checking Java code, without running the program.
At a check, the verifier gathers facts about the values in scope and asks an SMT solver whether those facts imply the required refinement. For example, knowing x == 5 is enough to prove x > 0. Knowing only x >= 0 is not: x could be zero.
Variables and Assignments
A local variable’s initializer must satisfy its declared refinement. Later assignments must satisfy that same refinement, even when the value changes.
import liquidjava.specification.Refinement;
public class AssignmentExample {
public static void example() {
@Refinement("_ > 0") int count = 1;
count = 2; // accepted: 2 > 0
count = 0; // Refinement Error: 0 is not positive
}
}
The verifier also tracks information from expressions and assignments. An unannotated local such as int count = 1 can therefore carry useful information; it does not declare a requirement that every later value must equal 1.
When a Check Fails
A refinement error means the verifier could not establish the required predicate from the available facts. The code may violate the requirement, or the verifier may need a stronger parameter refinement, a branch condition, or a return contract to establish it. See Understanding Refinement Errors for interpreting the diagnostic and its counterexample.
Method Calls and Returns
Learn how parameter and return refinements connect a method's implementation to its callers.
Conditionals
Learn how if and else conditions provide facts for refinement checks.
Recursion
Learn how LiquidJava checks recursive calls using parameter and return refinements.