Skip to content

Commit 3705a01

Browse files
authored
Update termination.md
- Removed imports of well-founded orders - Added naming convention for domains for custom well-founded orders - Added example showing that ADTs can be used as termination measures
1 parent b270b2f commit 3705a01

1 file changed

Lines changed: 29 additions & 34 deletions

File tree

tutorial/termination.md

Lines changed: 29 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ Here, `<tuple>` denotes the termination measure: a tuple of comma-separated expr
2323
A common example for termination is the standard `factorial` function, which terminates because its argument decreases with respect to the usual well-founded order over non-negative numbers.
2424

2525
```silver-runnable
26-
import <decreases/int.vpr>
27-
2826
function factorial(n:Int) : Int
2927
requires 0 <= n
3028
decreases n
@@ -37,27 +35,24 @@ Viper successfully verifies that `factorial` terminates: at each recursive invoc
3735

3836
### Predefined Well-Founded Orders {#term_prov_wfo}
3937

40-
Viper's standard library provides definitions of well-founded orders for most types built into Viper, all of which can be imported from the `decreases` folder. The following table lists all provided orders; we write `s1 <_ s2` if `s1` is less than `s2` with respect to the order.
38+
Viper's standard library provides definitions of well-founded orders for most types built into Viper. The following table lists all provided orders; we write `s1 <_ s2` if `s1` is less than `s2` with respect to the order.
4139

42-
| Build-In Type<br>(file name) | Provided Well-Founded Order |
40+
| Built-In Type | Provided Well-Founded Order |
4341
| ---- | ---- |
44-
|`Ref`<br>(`ref.vpr`)| `r1 <_ r2 <==> r1 == null && r2 != null`
45-
|`Bool`<br>(`bool.vpr`)| `b1 <_ b2 <==> b1 == false && b2 == true`
46-
|`Int`<br>(`int.vpr`)| `i1 <_ i2 <==> i1 < i2 && 0 <= i2`
47-
|`Rational`<br>(`rational.vpr`, rationals will be deprecated in the summer 2023 release):| `r1 <_ r2 <==> r1 <= r2 - 1/1 && 0/1 <= r2`
48-
|`Perm`<br>(`perm.vpr`)| `p1 <_ p2 <==> p1 <= p2 - write && none <= p2`
49-
|`Seq[T]`<br>(`seq.vpr`)| `s1 <_ s2 <==> \|s1\| < \|s2\|`
50-
|`Set[T]`<br>(`set.vpr`)| `s1 <_ s2 <==> \|s1\| < \|s2\|`
51-
|`Multiset[T]`<br>(`multiset.vpr`)| `s1 <_ s2 <==> \|s1\| < \|s2\|`
52-
|`PredicateInstance`<br>(`predicate_instance.vpr`)| `p1 <_ p2 <==> nested(p1, p2)`
42+
|`Ref`| `r1 <_ r2 <==> r1 == null && r2 != null`
43+
|`Bool`| `b1 <_ b2 <==> b1 == false && b2 == true`
44+
|`Int`| `i1 <_ i2 <==> i1 < i2 && 0 <= i2`
45+
|`Perm`| `p1 <_ p2 <==> p1 <= p2 - write && none <= p2`
46+
|`Seq[T]`| `s1 <_ s2 <==> \|s1\| < \|s2\|`
47+
|`Set[T]`| `s1 <_ s2 <==> \|s1\| < \|s2\|`
48+
|`Multiset[T]`| `s1 <_ s2 <==> \|s1\| < \|s2\|`
49+
|`PredicateInstance`| `p1 <_ p2 <==> nested(p1, p2)`
5350

5451
All definitions are straightforward, except the last one, which is concerned with using predicate instances as termination measures. Due to the least fixed-point interpretation of predicates, any predicate instance has a finite depth, i.e., can be unfolded only a finite number of times. This implies that a predicate instance `p1`, which is nested inside a predicate instance `p2`, has a smaller (and non-negative) depth than `p2`.
5552

5653
Viper uses this nesting depth to enable termination checks based on predicate instances, as illustrated by the next example, the recursive computation of the length of a linked list: intuitively, the remainder of the linked list, represented by predicate instance `list(this)`, is used as the termination measure. This works because the recursive call is nested under the unfolding of `list(this)`, and takes the smaller predicate instance `list(this.next)`.
5754

5855
```silver-runnable
59-
import <decreases/predicate_instance.vpr>
60-
6156
field next: Ref
6257
6358
predicate list(this: Ref) {
@@ -77,10 +72,24 @@ function length(this: Ref): Int
7772
Change the body of `length` to just the recursive call `length(this)`. Which error message do you expect?
7873
///
7974

75+
Additionally, Viper's ADT plugin automatically generated well-founded orders for any ADT defined in the program. Thus, ADT values can also be used as termination measures:
76+
77+
```silver-runnable
78+
adt List[T] {
79+
Nil()
80+
Cons(hd: T, tl: List[T])
81+
}
82+
83+
function llen(l: List[Int]): Int
84+
decreases l
85+
{
86+
l.isNil ? 0 : 1 + llen(l.tl)
87+
}
88+
```
89+
8090
Final remarks:
8191

8292
* Note that `PredicateInstance` is an internal Viper type, and currently supported only in decreases tuples. The `nested` function is also internal and cannot be used by users.
83-
* For simplicity, all standard well-founded orders can be imported via `decreases/all.vpr`.
8493
* Users can also define [custom well-founded orders](#term_custom_orders).
8594

8695
## General Syntax of Decreases Tuples
@@ -100,8 +109,6 @@ Special cases, such as empty tuples, tuples of different length, and tuples of d
100109
A typical example of a function for which a tuple as termination measure is used, is the Ackermann function:
101110

102111
```silver-runnable
103-
import <decreases/int.vpr>
104-
105112
function ack(m:Int, n:Int):Int
106113
decreases m, n
107114
requires m >= 0
@@ -120,8 +127,6 @@ For the first recursive call `ack(m - 1, 1)`, and the second (outer) recursive c
120127
Swap the tuple elements, i.e., change the decreases clause to `n, m`. For which of the recursive calls do you expect error messages?
121128
///
122129

123-
The well-founded order over tuples need not be imported (and cannot be customised). However, the well-founded orders of the types appearing in the tuple must be.
124-
125130
## Special Decreases Tuples
126131

127132
Viper supports three decreases tuples whose semantics require further explanation.
@@ -189,8 +194,6 @@ As previously mentioned, Viper offers [predefined orders](#term_prov_wfo) for it
189194
In the remainder of this subsection, both approaches will be illustrated using a combination of the `MyInt` example (from the earlier subsection on domains) and a `factorial` function operating on `MyInt`. In the example below, the destructor `get` is used to map a `MyInt` to a regular `Int`, which indirectly allows using `MyInt` in the function's decreases clause.
190195

191196
```silver-runnable
192-
import <decreases/int.vpr>
193-
194197
domain MyInt {
195198
function put(i: Int): MyInt // Constructor
196199
function get(m: MyInt): Int // Destructor
@@ -227,6 +230,8 @@ v1 <_ v2 <==> decreasing(v1, v2) && bounded(v2)
227230
The necessary properties of `decreasing` and `bounded` for values of type `T` can be defined via axioms. For the `MyInt` type from before, suitable axioms would be:
228231

229232
```silver
233+
import <decreases/declaration.vpr>
234+
230235
domain MyIntWellFoundedOrder {
231236
axiom {
232237
forall i1: MyInt, i2: MyInt :: {decreasing(i1, i2)}
@@ -240,7 +245,8 @@ domain MyIntWellFoundedOrder {
240245
```
241246

242247
//note//
243-
The functions `decreasing` and `bounded` must be declared by the Viper program to verify, which is easiest done by importing `decreases/declaration.vpr`. This is also what the predefined orders, e.g., `decreases/int.vpr`, do.
248+
The functions `decreasing` and `bounded` must be declared by the Viper program to verify, which is easiest done by importing `decreases/declaration.vpr`, as shown in the example. This is also what the predefined orders do.
249+
Viper uses a naming convention where the well-founded order for type `T` should be defined in a domain called `TWellFoundedOrder`; giving it a different name will result in a warning.
244250
///
245251

246252
//exercise//
@@ -257,8 +263,6 @@ For mutually recursive functions, Viper implements the following approach (as, e
257263
A simple case of mutual recursion is illustrated next, by functions `is_even` and `is_odd`:
258264

259265
```silver-runnable
260-
import <decreases/int.vpr>
261-
262266
function is_even(x: Int): Bool
263267
requires x >= 0
264268
decreases x
@@ -279,9 +283,6 @@ Consider function `is_even`: its termination measure `x` decreases at the indire
279283
In the example above, the two termination measures are tuples of equal length and type. However, this is not required of mutually recursive functions in order to prove their termination. Consider the next example (which verifies successfully):
280284

281285
```silver-runnable
282-
import <decreases/int.vpr>
283-
import <decreases/bool.vpr>
284-
285286
function fun1(y: Int, b: Bool): Int
286287
decreases y, b
287288
{
@@ -300,8 +301,6 @@ At indirectly recursive calls, two decreases tuples are compared by lexicographi
300301

301302
//exercise//
302303

303-
* Comment the import of `bool.vpr`, and reverify the program. Can you explain the resulting verification error?
304-
305304
* In the above example, change the call `fun1(x-1, true)` to `fun1(x, true)` -- the program still verifies. That's because Viper appends a `Top` element (an internal value of any type, larger than any other value) to each tuple, a neat trick also implemented by, e.g., Dafny and F*. Can you explain how this helps with checking termination of the call `fun1(x, true)`?
306305

307306
///
@@ -326,8 +325,6 @@ To ensure soundness, only a *single* clause per kind of measure is allowed. More
326325
The following example illustrates combined conditional termination clauses: function `sign` promises to decrease `x` if positive, and something (wildcard) if `x` is negative. In case `x` is zero, function `sign` does not (promise to) terminate.
327326

328327
```silver-runnable
329-
import <decreases/int.vpr>
330-
331328
function sign(x: Int): Int
332329
decreases x if 1 <= x
333330
decreases _ if x <= -1
@@ -431,8 +428,6 @@ The currently implemented approach to checking termination of methods is similar
431428
A straightforward example is method `sum`, shown next:
432429

433430
```silver-runnable
434-
import <decreases/int.vpr>
435-
436431
method sum(n: Int) returns (res: Int)
437432
requires 0 <= n
438433
decreases

0 commit comments

Comments
 (0)