Skip to content

Commit 775aa99

Browse files
authored
Document verification features with checked Java examples (#2)
2 parents 2beb54d + 1d03e33 commit 775aa99

9 files changed

Lines changed: 205 additions & 5 deletions

File tree

‎pages/command-line-interface.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Command-Line Interface
3-
nav_order: 5
3+
nav_order: 6
44
permalink: /command-line-interface/
55
description: Run the LiquidJava verifier from the command line for local checks, debugging, and CI workflows.
66
---

‎pages/diagnostics/index.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Diagnostics
3-
nav_order: 3
3+
nav_order: 4
44
has_children: true
55
has_toc: false
66
permalink: /diagnostics/

‎pages/examples/index.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Examples
3-
nav_order: 6
3+
nav_order: 7
44
has_children: true
55
permalink: /examples/
66
description: LiquidJava example usages with focused code snippets.

‎pages/resources.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Resources
3-
nav_order: 7
3+
nav_order: 8
44
has_children: false
55
permalink: /resources/
66
description: Find LiquidJava papers, posters, and source repositories for deeper reading and experimentation.

‎pages/verification/conditionals.md‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---
2+
title: Conditionals
3+
parent: Verification Features
4+
nav_order: 2
5+
permalink: /verification/conditionals/
6+
description: Learn how if and else conditions provide facts for refinement checks.
7+
---
8+
9+
# Conditionals
10+
11+
An `if` condition adds information along each branch. The `then` branch is checked assuming the condition is true; the `else` branch is checked assuming it is false.
12+
13+
```java
14+
import liquidjava.specification.Refinement;
15+
16+
public class ConditionalExample {
17+
public static void requirePositive(
18+
@Refinement("_ > 0") int value) {}
19+
public static void requireNonPositive(
20+
@Refinement("_ <= 0") int value) {}
21+
22+
public static void guarded(int value) {
23+
if (value > 0) {
24+
requirePositive(value); // accepted: value > 0
25+
} else {
26+
requireNonPositive(value); // accepted: value <= 0
27+
}
28+
}
29+
}
30+
```
31+
32+
Although `guarded` accepts any integer, each call is protected by a condition that establishes the required refinement. No extra refinement on its parameter is needed for these calls.
33+
34+
## The Condition Must Be Strong Enough
35+
36+
Changing the guard to `value >= 0` does not prove strict positivity, because zero remains possible:
37+
38+
```java
39+
import liquidjava.specification.Refinement;
40+
41+
public class WeakGuardExample {
42+
public static void requirePositive(
43+
@Refinement("_ > 0") int value) {}
44+
45+
public static void guarded(int value) {
46+
if (value >= 0) {
47+
requirePositive(value); // Refinement Error: value could be 0
48+
}
49+
}
50+
}
51+
```
52+
53+
Branch assumptions apply to the paths on which they hold. After an `if`/`else` where both branches continue, code must work for either outcome; it cannot assume that the `then` condition is still true. Nested conditions can provide additional facts inside their branches.

‎pages/verification/index.md‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
title: Verification Features
3+
nav_order: 3
4+
has_children: true
5+
has_toc: false
6+
permalink: /verification/
7+
description: Understand how LiquidJava checks assignments, method calls, conditionals, and recursive methods.
8+
---
9+
10+
# Verification Features
11+
12+
The [Annotations]({{ '/annotations/' | relative_url }}) section explains how to write specifications. This section explains how LiquidJava uses them when checking Java code, without running the program.
13+
14+
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.
15+
16+
## Variables and Assignments
17+
18+
A local variable's initializer must satisfy its declared refinement. Later assignments must satisfy that same refinement, even when the value changes.
19+
20+
```java
21+
import liquidjava.specification.Refinement;
22+
23+
public class AssignmentExample {
24+
public static void example() {
25+
@Refinement("_ > 0") int count = 1;
26+
count = 2; // accepted: 2 > 0
27+
count = 0; // Refinement Error: 0 is not positive
28+
}
29+
}
30+
```
31+
32+
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`.
33+
34+
## When a Check Fails
35+
36+
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]({{ '/diagnostics/understanding-refinement-errors/' | relative_url }}) for interpreting the diagnostic and its counterexample.

‎pages/verification/method-calls.md‎

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
title: Method Calls and Returns
3+
parent: Verification Features
4+
nav_order: 1
5+
permalink: /verification/method-calls/
6+
description: Learn how parameter and return refinements connect a method's implementation to its callers.
7+
---
8+
9+
# Method Calls and Returns
10+
11+
A method contract has two sides:
12+
13+
- **Parameters:** callers must establish each parameter's refinement. The method body can assume those refinements.
14+
- **Return value:** each return must satisfy the method's declared refinement. Callers can use that refinement for the result.
15+
16+
```java
17+
import liquidjava.specification.Refinement;
18+
19+
public class MethodExample {
20+
@Refinement("_ == value")
21+
public static int positiveIdentity(
22+
@Refinement("value > 0") int value) {
23+
return value;
24+
}
25+
26+
public static void example() {
27+
@Refinement("_ == 3") int result = positiveIdentity(3);
28+
positiveIdentity(0); // Refinement Error: 0 is not positive
29+
}
30+
}
31+
```
32+
33+
For `positiveIdentity(3)`, the verifier checks `3 > 0`. It then substitutes the argument into the return contract `_ == value`, so the result is known to equal `3`. The call with `0` fails the parameter check.
34+
35+
The body is checked separately using its parameter contract. Returning a value that contradicts its return contract also produces an error:
36+
37+
```java
38+
import liquidjava.specification.Refinement;
39+
40+
public class ReturnExample {
41+
@Refinement("_ > 0")
42+
public static int positive() {
43+
return 0; // Refinement Error: 0 is not positive
44+
}
45+
}
46+
```
47+
48+
Write the return properties callers need explicitly. For example, without a return refinement on `positiveIdentity`, callers should not rely on the verifier inspecting its body to discover that the result equals the argument.
49+
50+
## Object State at a Call
51+
52+
For a method with [state refinements]({{ '/annotations/state-refinement/' | relative_url }}), the verifier also checks that the receiver satisfies `from` before the call and uses `to` to describe its state afterward. A constructor's `to` establishes the initial state. This is how a protocol can reject a `read()` call after `close()`.
53+
54+
For library methods whose source is outside the checked code, use [external refinements]({{ '/annotations/external-refinements-for/' | relative_url }}) to supply contracts. Those contracts describe the library behavior the verifier relies on; they do not verify the library's implementation.

‎pages/verification/recursion.md‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
---
2+
title: Recursion
3+
parent: Verification Features
4+
nav_order: 3
5+
permalink: /verification/recursion/
6+
description: Learn how LiquidJava checks recursive calls using parameter and return refinements.
7+
---
8+
9+
# Recursion
10+
11+
A recursive call is checked using the same parameter and return refinements as any other method call. LiquidJava uses the declared contract instead of repeatedly expanding the method body.
12+
13+
```java
14+
import liquidjava.specification.Refinement;
15+
16+
public class RecursionExample {
17+
@Refinement("_ == 0")
18+
public static int untilZero(
19+
@Refinement("k >= 0") int k) {
20+
if (k == 0) {
21+
return 0;
22+
} else {
23+
return untilZero(k - 1);
24+
}
25+
}
26+
}
27+
```
28+
29+
The verifier checks both branches:
30+
31+
1. In the base case, returning `0` satisfies `_ == 0`.
32+
2. In the other branch, `k >= 0` and `k != 0` imply `k > 0`, since `k` is an integer. Therefore `k - 1 >= 0`, so the recursive call satisfies the parameter contract.
33+
3. The recursive call's return contract says its result equals `0`, which satisfies the enclosing method's return contract.
34+
35+
## An Incorrect Base Case
36+
37+
If the base case tests `k == 1`, the other branch can include `k == 0`. Subtracting one then violates the recursive call's parameter refinement:
38+
39+
```java
40+
import liquidjava.specification.Refinement;
41+
42+
public class IncorrectRecursionExample {
43+
@Refinement("_ == 0")
44+
public static int untilZero(
45+
@Refinement("k >= 0") int k) {
46+
if (k == 1) {
47+
return 0;
48+
} else {
49+
// k could be 0, so k - 1 could be negative
50+
return untilZero(k - 1); // Refinement Error
51+
}
52+
}
53+
}
54+
```
55+
56+
{: .note }
57+
Checking recursive contracts does not prove termination or bound recursion depth. An accepted recursive method can still recurse forever or exhaust the Java stack. The return refinement describes the value if the method returns.

‎pages/vscode-extension/index.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: VS Code Extension
3-
nav_order: 4
3+
nav_order: 5
44
has_children: true
55
has_toc: false
66
permalink: /vscode-extension/

0 commit comments

Comments
 (0)