Skip to content

Commit de8249f

Browse files
committed
Add Refinement Predicates Page
1 parent 46ffff9 commit de8249f

File tree

1 file changed

+74
-0
lines changed

1 file changed

+74
-0
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
---
2+
title: Refinement Predicates
3+
parent: Concepts
4+
nav_order: 6
5+
permalink: /concepts/refinement-predicates/
6+
description: Learn how to declare custom ghost functions and how they relate to ghosts and states.
7+
---
8+
9+
# Refinement Predicates
10+
11+
LiquidJava also supports a more general mechanism for declaring ghost functions, through the `@RefinementPredicate` annotation. This is a more flexible but less user-friendly way to declare ghosts. It allows declaring any ghost function with an explicit signature.
12+
13+
The `@Ghost` and `@StateSet` annotations are sugar for the `@RefinementPredicate` annotation, which create specific kinds of ghost functions with a more concise syntax:
14+
15+
| Syntax Sugar | Refinement Predicate |
16+
| --- | --- |
17+
| `@Ghost("int size")` | `@RefinementPredicate("int size(ArrayList)")` |
18+
| `@Ghost("boolean open")` | `@RefinementPredicate("boolean open(InputStreamReader)")` |
19+
| `@StateSet({"open", "closed"})` | `@RefinementPredicate("int state1(File)")`<br>`state1(this) == 0 <=> empty(this)`<br>`state1(this) == 1 <=> full(this)` |
20+
21+
In the current implementation, `@RefinementPredicate` is only supported on methods and constructors, but it is not currently scoped to the annotated method or constructor. Instead, is a global declaration that is available for the whole verification.
22+
23+
## What's a Ghost Function?
24+
25+
A ghost function is an uninterpreted function used only during verification. In practice, that means:
26+
27+
- It has a name, parameter types, and a return type
28+
- It has no implementation
29+
- The verifier does not compute it from program code
30+
- The SMT encoding declares it as a function symbol and then reasons only from the constraints that mention it
31+
32+
So a declaration like:
33+
34+
```java
35+
@RefinementPredicate("int size(ArrayList)")
36+
```
37+
38+
Does not say how `size` is computed. It only says that, during verification, there exists a function `size : ArrayList -> int` and the verifier may constrain it with refinements such as:
39+
40+
```java
41+
@StateRefinement(to = "size(this) == 0")
42+
```
43+
44+
or
45+
46+
```java
47+
@StateRefinement(to = "size(this) == size(old(this)) + 1")
48+
```
49+
50+
The meaning comes from those constraints, not from a body.
51+
52+
## Example
53+
54+
You can model any typestate protocol without using the `@StateSet` annotation, by declaring a ghost function that represents the state and then using aliases to give names to the different states, which is kind of like how `@StateSet` works. Under the hood, states are just ghost functions that return an integer representing current the state. For example:
55+
56+
```java
57+
import liquidjava.specification.*;
58+
59+
@RefinementPredicate("int state(File f)")
60+
@RefinementAlias("Open(File f) { state(f) == 0}")
61+
@RefinementAlias("Closed(File f) { state(f) == 1}")
62+
public class File {
63+
@StateRefinement(to="Open(this)")
64+
public File() {}
65+
66+
@StateRefinement(from="Open(this)")
67+
public void read() {}
68+
69+
@StateRefinement(from="Open(this)", to="Closed(this)")
70+
public void close() {}
71+
}
72+
```
73+
74+
In this case, we must pass the explicit `this` as an argument to the alias.

0 commit comments

Comments
 (0)