Skip to content

Commit 82bbeb8

Browse files
authored
Merge pull request #8377 from QinyuanWu/develop
Add documentation to loop contracts, __CPROVER_loop_entry
2 parents 2523f2c + 59646b3 commit 82bbeb8

File tree

4 files changed

+86
-6
lines changed

4 files changed

+86
-6
lines changed

src/goto-instrument/contracts/doc/user/contracts-assigns.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ For a loop contract, the condition and target expressions in the assigns clause
4343
may involve any identifier that is in scope at loop entry
4444
(parameters of the surrounding function, local variables, global variables,
4545
type identifiers in `sizeof` or cast expressions, etc.).
46+
In case of nested loops, the assigns clause of the outer loop should contain all assigns targets of the inner loops.
4647
The target expression must be free of function calls and side-effects.
4748
The condition expression may contain calls to side-effect-free functions.
4849

src/goto-instrument/contracts/doc/user/contracts-history-variables.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,34 @@ __CPROVER_assigns(*out)
4242

4343
## In Loop Contracts
4444

45-
TODO: Document `__CPROVER_loop_entry` and `__CPROVER_loop_old`.
45+
### Syntax
46+
47+
```c
48+
__CPROVER_loop_entry(*identifier*)
49+
```
50+
51+
### Parameters
52+
`__CPROVER_loop_entry` takes a variable name that is in scope at the place of the loop contract.
53+
54+
### Semantics
55+
`__CPROVER_loop_entry` takes a snapshot of the variable value right before the **first iteration** of the loop.
56+
57+
### Example
58+
In this example the loop invariant asserts that `x <= 200` is upheld before and after every iteration of the `while` loop:
59+
```c
60+
void my_function()
61+
{
62+
unsigned x = 200;
63+
while( x >= 64 )
64+
__CPROVER_loop_invariant(x <= __CPROVER_loop_entry(x)) //equivalent to x <= 200
65+
{
66+
...
67+
x -= 64;
68+
}
69+
}
70+
```
71+
72+
4673

4774
## Additional Resources
4875

src/goto-instrument/contracts/doc/user/contracts-loops.md

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,24 @@ Back to @ref contracts-user
55
@tableofcontents
66

77
CBMC offers support for loop contracts, which includes four basic clauses:
8+
- an _assigns_ clause for declaring the memory locations assignable by the loop,
89
- an _invariant_ clause for establishing safety properties,
910
- a _decreases_ clause for establishing termination,
10-
- an _assigns_ clause for declaring the memory locations assignable by the loop,
11-
- a _frees_ clause for declaring the pointers freeable by the loop.
1211

13-
These clauses formally describe an abstraction of a loop for the purpose of a proof.
12+
**The three clauses need to be declared in this sequence to avoid errors. Each loop contract
13+
should only have one assigns clause that contains all assigned targets.**
14+
Loop contracts are not used by default. To enable CBMC to check for loop contracts,
15+
add the `--apply-loop-contract` flag at the `goto-instrument` step.
16+
17+
These clauses formally describe an abstraction of a loop for the purpose of an unbounded proof.
1418
CBMC also provides a series of built-in constructs
1519
to aid writing loop contracts (e.g., _history variables_ and _quantifiers_).
20+
CBMC will use the abstraction in place of the loop and prove the invariants of the loop only if
21+
the loop contracts describes a sound and inductive abstraction of the loop.
22+
23+
## Examples
1624

17-
## Overview
25+
### Binary Search Unbounded Proof
1826

1927
Consider an implementation of the [binary search algorithm] below.
2028

@@ -141,6 +149,50 @@ The first command compiles the program to a GOTO binary,
141149
next we instrument the loops using the annotated loop contracts,
142150
and finally we verify the instrumented GOTO binary with desired checks.
143151

152+
### Array Wipe Unbounded Proof
153+
This example uses the __forall__ quantifiers hence requires solving with the `--smt2` flag.
154+
```c
155+
void array_wipe(__CPROVER_size_t len, char * array)
156+
{
157+
__CPROVER_assume(array != NULL); // pre-condition
158+
159+
for (__CPROVER_size_t i = 0; i < len; i++)
160+
__CPROVER_assigns(i, __CPROVER_object_upto(array, len))
161+
__CPROVER_loop_invariant(i >= 0 && i <= len)
162+
__CPROVER_loop_invariant(__CPROVER_forall { size_t j; (0 <= j && j < i) ==> array[j] == 0 } )
163+
{
164+
array[i] = 0; //set all array indices to 0
165+
}
166+
167+
__CPROVER_assert(__CPROVER_forall { size_t j; (0 <= j && j < len) ==> array[j] == 0 }, "array is set to 0"); // post-condition
168+
}
169+
```
170+
171+
### Caution With Nested Loop
172+
Due to the nature of @ref contracts-assigns, we need to be aware of the non-deterministic value of the assigned target.
173+
In the example below,
174+
```c
175+
const unsigned table[256] = {1, 2, 3, ..., 256};
176+
177+
void nested_loop_example() {
178+
unsigned t = 0;
179+
for( j=0; j<18; j++ )
180+
__CPROVER_assigns(t, j, k) // t and k are also assigns targets of the outer loop as they are assigned in the inner loop
181+
__CPROVER_loop_invariant(0 <= j && j <= 18 && t < 256) // without t < 256, the program state for the inductive step allows t to have arbitrary value
182+
__CPROVER_decreases(18 - j)
183+
{
184+
for( k=0; k<48; k++ )
185+
__CPROVER_assigns(t, k)
186+
__CPROVER_loop_invariant(0 <= k && k <= 48 && t < 256)
187+
__CPROVER_decreases(48 - k)
188+
{
189+
t = table[t];
190+
}
191+
}
192+
}
193+
```
194+
If `t < 256` is not included in the outer loop invariant, the inner loop invariant `t < 256` will immediately fail at loop entry because, in the inductive step of the outer loop, the assigns target `t` of the outer loop will be a non-deterministic value which can be greater than 256. With the predicate `t<256` in the outer loop's invariants will restrict `t` to be less than `256` in the proof of the inductive step of the outer loop.
195+
144196
## Additional Resources
145197

146198
[binary search algorithm]: https://en.wikipedia.org/wiki/Binary_search_algorithm

src/goto-instrument/contracts/doc/user/contracts-memory-predicates.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ external environment (in a precondition), or returning it to the calling context
2525
2626
`__CPROVER_is_fresh` takes two arguments: a pointer and an allocation size.
2727
The first argument is the pointer to be checked for "freshness" (i.e., not previously
28-
allocated), and the second is the expected size in bytes for the memory
28+
allocated), and the second is the expected size **in bytes** for the memory
2929
available at the pointer.
3030
3131
#### Return Value

0 commit comments

Comments
 (0)