Skip to content

Commit 77592f7

Browse files
author
Remi Delmas
committed
CONTRACTS: user doc for function pointer clauses
1 parent 4664568 commit 77592f7

10 files changed

+189
-0
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
@@ -502,6 +502,7 @@ int foo()
502502

503503
- @ref contracts-functions
504504
- @ref contracts-requires-ensures
505+
- @ref contracts-requires-ensures-contract
505506
- @ref contracts-assigns
506507
- @ref contracts-frees
507508
- @ref contracts-loops

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ then the weakest possible invariant (i.e, `true`) is used to model an arbitrary
179179

180180
- @ref contracts-functions
181181
- @ref contracts-requires-ensures
182+
- @ref contracts-requires-ensures-contract
182183
- @ref contracts-assigns
183184
- @ref contracts-frees
184185
- @ref contracts-loops

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ program using contracts.
154154

155155
- @ref contracts-functions
156156
- @ref contracts-requires-ensures
157+
- @ref contracts-requires-ensures-contract
157158
- @ref contracts-assigns
158159
- @ref contracts-frees
159160
- @ref contracts-loops

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ TODO: Document `__CPROVER_loop_entry` and `__CPROVER_loop_old`.
4848

4949
- @ref contracts-functions
5050
- @ref contracts-requires-ensures
51+
- @ref contracts-requires-ensures-contract
5152
- @ref contracts-assigns
5253
- @ref contracts-frees
5354
- @ref contracts-loops

src/goto-instrument/contracts/doc/user/contracts-loop-invariants.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ A few things to note here:
157157

158158
- @ref contracts-functions
159159
- @ref contracts-requires-ensures
160+
- @ref contracts-requires-ensures-contract
160161
- @ref contracts-assigns
161162
- @ref contracts-frees
162163
- @ref contracts-loops

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ and finally we verify the instrumented GOTO binary with desired checks.
147147

148148
- @ref contracts-functions
149149
- @ref contracts-requires-ensures
150+
- @ref contracts-requires-ensures-contract
150151
- @ref contracts-assigns
151152
- @ref contracts-frees
152153
- @ref contracts-loops

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ int foo()
124124

125125
- @ref contracts-functions
126126
- @ref contracts-requires-ensures
127+
- @ref contracts-requires-ensures-contract
127128
- @ref contracts-assigns
128129
- @ref contracts-frees
129130
- @ref contracts-loops

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ int bar_sat(int *arr, int len)
9696

9797
- @ref contracts-functions
9898
- @ref contracts-requires-ensures
99+
- @ref contracts-requires-ensures-contract
99100
- @ref contracts-assigns
100101
- @ref contracts-frees
101102
- @ref contracts-loops
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
# Requires and Ensures Clauses {#contracts-requires-ensures-contract}
2+
3+
Back to @ref contracts-user
4+
5+
@tableofcontents
6+
7+
## Syntax
8+
9+
```c
10+
__CPROVER_requires_contract(function_pointer, contract_id (, NULL)?)
11+
```
12+
13+
A _requires contract_ clause specifies the precondition that a function pointer
14+
points to a function satisfying a given contract, or is optionally NULL.
15+
16+
```c
17+
__CPROVER_ensures_contract(function_pointer, contract_id (, NULL)?)
18+
```
19+
20+
A _ensures contract_ clause specifies the postcondition that a function pointer
21+
points to a function satisfying a given contract, or is optionally NULL.
22+
23+
## Semantics
24+
25+
The semantics of _requires contract_ and _ensures contract_ clauses can be
26+
understood in two contexts: enforcement and replacement.
27+
28+
To illustrate these two perspectives, consider the following program:
29+
30+
```c
31+
#include <assert.h>
32+
#include <stdbool.h>
33+
#include <stdlib.h>
34+
35+
typedef int (*fun_t)(int);
36+
37+
int add_one(int x)
38+
__CPROVER_ensures(__CPROVER_return_value == __CPROVER_old(x) + 1);
39+
40+
// returns either a pointer to a function that satisfies add_one or NULL
41+
fun_t get_add_one()
42+
__CPROVER_ensures_contract(__CPROVER_return_value, add_one);
43+
44+
int foo(fun_t f_in, int x, fun_t *f_out)
45+
__CPROVER_requires_contract(f_in, add_one, NULL)
46+
__CPROVER_assigns(*f_out)
47+
__CPROVER_ensures(__CPROVER_return_value == __CPROVER_old(x) + 1)
48+
__CPROVER_ensures_contract(*f_out, add_one)
49+
{
50+
if (f_in)
51+
{
52+
*f_out = f_in;
53+
}
54+
else
55+
{
56+
*f_out = get_add_one();
57+
}
58+
59+
CALL:
60+
return (*f_out)(x);
61+
}
62+
63+
int main()
64+
{
65+
fun_t f_in;
66+
int x;
67+
fun_t f_out;
68+
foo(f_in, x, &f_out);
69+
CALL:
70+
__CPROVER_assert(f_out(1) == 2, "f_out satisfies add_one");
71+
}
72+
```
73+
74+
The function `add_one` is a contract that describes a function that adds one
75+
to its input.
76+
The function `get_add_one` is a contract that describes a function returning a
77+
pointer to a function satisfying the contract `add_one`.
78+
79+
The Function `foo` takes a function pointer `f_in` as input and requires that
80+
it either satisfies `add_one` or is NULL;
81+
If `f_in` is not NULL, `foo` set `f_out` to `f_in`;
82+
If `f_in` is NULL, `foo` calls `get_add_one` to set `f_out` to a non-NULL
83+
pointer to a function that satisfies `add_one`;
84+
The function `foo` returns the result of applying `f_out` to its input, which
85+
allows to establish its post condition.
86+
The `main` function calls `f_out(1)` and checks if the `add_one` contract holds
87+
for this particular input.
88+
89+
This program is instrumented using the following commands:
90+
91+
```
92+
goto-cc main.c -o a.out
93+
goto-instrument --dfcc main --restrict-function-pointer foo.CALL/add_one --restrict-function-pointer main.CALL/add_one --enforce-contract foo --replace-call-with-contract get_add_one a.out b.out
94+
```
95+
96+
The function pointer restrictions are let CBMC know that the function pointers
97+
in `foo` and `main` must be pointing to the function `add_one`, which serves as
98+
the cannonical representation of the assumed contract. These assumptions will be
99+
checked by CBMC, i.e. if it is possible that the function pointer points to
100+
another function the analysis will fail.
101+
102+
The analysis results are the following:
103+
104+
```
105+
cbmc b.out
106+
107+
...
108+
109+
main.c function foo
110+
[foo.postcondition.1] line 15 Check ensures clause of contract contract::foo for function foo: SUCCESS
111+
[foo.postcondition.2] line 16 Assert function pointer '*f_out_wrapper' obeys contract 'add_one': SUCCESS
112+
[foo.assigns.1] line 18 Check that *f_out is assignable: SUCCESS
113+
[foo.assigns.3] line 20 Check that *f_out is assignable: SUCCESS
114+
[foo.pointer_dereference.1] line 22 dereferenced function pointer must be add_one: SUCCESS
115+
116+
main.c function main
117+
[main.assertion.1] line 31 f_out satisfies add_one: SUCCESS
118+
[main.pointer_dereference.1] line 31 dereferenced function pointer must be add_one: SUCCESS
119+
120+
** 0 of 56 failed (1 iterations)
121+
VERIFICATION SUCCESSFUL
122+
```
123+
124+
### Enforcement
125+
126+
When enforcing a contract, a clause
127+
128+
```c
129+
__CPROVER_requires(function_pointer, contract_id, NULL)
130+
```
131+
132+
is turned into a non-deterministic assignment and inserted before the call site
133+
of the function under verification:
134+
135+
136+
```c
137+
function_pointer = nondet() ? &contract_id : NULL;
138+
```
139+
140+
That way, the function under verification receives a pointer to the
141+
`contract_id` function. The instrumentation pass also generates a body for the
142+
function `contract_id` from its contract clauses, so it becomes the canonical
143+
representation of the contract.
144+
145+
An _ensures contract_ clause:
146+
147+
```c
148+
__CPROVER_ensures(function_pointer, contract_id, NULL)
149+
```
150+
151+
is turned into an assertion:
152+
153+
```c
154+
assert(function_pointer ==> function_pointer == &contract_id);
155+
```
156+
157+
That checks that whenever the `function_pointer` is not null, it points to the
158+
function `contract_id`, the canonical representation of the contract.
159+
160+
### Replacement
161+
162+
For contract replacement, the dual transformation is used: _requires contract_
163+
clauses are turned into assertions, and _ensures contract_ clauses are turned
164+
into nondeterministic assignments.
165+
166+
## Additional Resources
167+
168+
- @ref contracts-functions
169+
- @ref contracts-requires-ensures
170+
- @ref contracts-requires-ensures-contract
171+
- @ref contracts-assigns
172+
- @ref contracts-frees
173+
- @ref contracts-loops
174+
- @ref contracts-loop-invariants
175+
- @ref contracts-decreases
176+
- @ref contracts-assigns
177+
- @ref contracts-frees
178+
- @ref contracts-memory-predicates
179+
- @ref contracts-history-variables
180+
- @ref contracts-quantifiers

src/goto-instrument/contracts/doc/user/contracts-requires-ensures.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ int foo()
179179

180180
- @ref contracts-functions
181181
- @ref contracts-requires-ensures
182+
- @ref contracts-requires-ensures-contract
182183
- @ref contracts-assigns
183184
- @ref contracts-frees
184185
- @ref contracts-loops

0 commit comments

Comments
 (0)