11[ CPROVER Manual TOC] ( ../../ )
22
3- # Assigns Clause
3+ # Assigns Clauses
44
5- ## In Function Contracts
5+ An _ assigns clause_ lets the user to specify which memory locations may be
6+ assigned to by a function or a loop.
67
7- ### Syntax
8+ For a function contract, if no assigns clause is provided, the default set of
9+ locations is the empty set.
10+
11+ For a loop contract, if no assigns clause is provided with the contract, CBMC
12+ attempts to infer the set of locations assigned by the loop from the loop body,
13+ and then checks that this set is correct as if it had been provided by the user.
14+
15+ For both functions and loop contracts, if more than one assigns clause is
16+ provided, they are implicitly merged into a single clause.
17+
18+ Each target listed in the clause gets resolved to a pair of values of type
19+ ` (void *, size_t) ` specifying the assignable memory location's start address
20+ and extent in bytes.
21+
22+ We use the _ assigns_ interpretation for these locations, as opposed to the
23+ _ modifies_ interpretation. This means that memory locations that are part of the
24+ _ frame_ of the function call or the loop that are not listed in the assigns
25+ clause (or in the inferred clause for a loop contract) must not be assigned to
26+ by the function or the loop, even if they end up holding the same value as they
27+ held before the function call or before entering the loop.
28+
29+ In contrast, memory location that are locally stack- or heap-allocated during
30+ function execution or loop execution are by definition not part of the _ frame_
31+ and can always be assigned to by the function or the loop.
32+
33+ ## Syntax
834
935``` c
10- __CPROVER_assigns (* identifier* , ...)
36+ __CPROVER_assigns ( * targets* )
37+ ```
38+
39+ Where targets have the following syntax:
40+
41+ ```
42+ targets ::= conditional-target-group (';' conditional-target-group)* ';'?
43+ conditional-target-group ::= (condition ':')? target (',' target)*
44+ target ::= l-value-expression
45+ | __ CPROVER_typed_target(l-value-expression)
46+ | __ CPROVER_object_whole(pointer-typed-expression)
47+ | __ CPROVER_object_from(pointer-typed-expression)
48+ | __ CPROVER_object_upto(pointer-typed-expression,
49+ unsigned-integer-typed-expression)
50+ ```
51+
52+
53+ For function contracts, the condition and target expressions
54+ in the assigns clause may only involve function parameters,
55+ global variables or type identifiers in `sizeof` or cast expressions.
56+ The target expression must be free of function calls and side-effects.
57+ The condition expression may contain calls to side-effect-free functions.
58+
59+ For a loop contract, the condition and target expressions in the assigns clause
60+ may involve any identifier that is in scope at loop entry
61+ (parameters of the surrounding function, local variables, global variables,
62+ type identifiers in `sizeof` or cast expressions, etc.).
63+ The target expression must be free of function calls and side-effects.
64+ The condition expression may contain calls to side-effect-free functions.
65+
66+ ### Lvalue targets
67+
68+ An l-value target `expr` with a complete type `expr_t` specifies that the range
69+ of `sizeof(expr_t)` bytes starting at `&expr` is assignable.
70+
71+ Similarly, given an l-value expression `expr` with a complete type `expr_t`,
72+ `__CPROVER_typed_target(expr)` specifies that the range
73+ of `sizeof(expr_t)` bytes starting at `&expr` is assignable:
74+ ```c
75+ void __CPROVER_typed_target(expr_t expr);
76+ ```
77+
78+ To specify assignable memory locations
79+ that are interpreted as pointers by the program, one must use
80+ ` __CPROVER_assigns(ptr) ` or ` __CPROVER_assigns(__CPROVER_typed_target(ptr)) ` .
81+
82+ This is to ensure that during call by contract replacement they will be havoced
83+ as pointers, in a type directed way. For instance:
84+
85+ ``` c
86+ struct circular_buffer_t {
87+ int * first;
88+ int * last;
89+ int * current;
90+ }
91+
92+ void step (struct circular_buffer_t * buf)
93+ // correct
94+ __ CPROVER_assigns(__ CPROVER_typed_target(buf->current))
95+ // not correct
96+ __ CPROVER_assigns(__ CPROVER_object_upto(&(buf->current), sizeof(buf->current))
97+ {
98+ if(buf->current == buf->last)
99+ buf->current = buf->first;
100+ else
101+ buf->current += 1;
102+ }
11103```
12104
13- An _assigns_ clause allows the user to specify that a memory location may be written by a function. The set of locations writable by a function is the union of the locations specified by the assigns clauses, or the empty set of no _assigns_ clause is specified. While, in general, an _assigns_ clause could be interpreted with either _writes_ or _modifies_ semantics, this
14- design is based on the former. This means that memory not captured by an
15- _assigns_ clause must not be written within the given function, even if the
16- value(s) therein are not modified.
105+ ### Object slice targets
17106
18- ### Object slice expressions
107+ Given a pointer `ptr` pointing into some object `o`,
108+ `__CPROVER_object_whole(ptr)` specifies that all bytes of the object `o`
109+ are assignable:
110+ ```c
111+ void __CPROVER_object_whole(void *ptr);
112+ ```
113+ REMARK: this also includes bytes in the object that are before the address given
114+ by ` ptr ` ;
19115
20- The following functions can be used in assigns clause to specify ranges of
21- assignable addresses.
22116
23- Given a pointer `ptr` pointing into some object `o`, `__CPROVER_object_from(ptr)`
24- specifies that all bytes starting from the given pointer and until the end of
25- the object are assignable:
117+ Given a pointer ` ptr ` pointing into some object ` o ` , ` __CPROVER_object_from(ptr) `
118+ specifies that the range of bytes starting from the pointer and until the end of
119+ the object ` o ` are assignable:
26120``` c
27- __CPROVER_size_t __CPROVER_object_from(void *ptr);
121+ void __CPROVER_object_from (void * ptr);
28122```
29123
30- Given a pointer ` ptr ` pointing into some object ` o ` , ` __CPROVER_object_from(ptr, size) `
31- specifies that ` size ` bytes starting from the given pointer and until the end of the object are assignable.
32- The ` size ` value must such that ` size <= __CPROVER_object_size(ptr) - __CPROVER_pointer_offset(ptr) ` holds:
124+ ```c
125+ const size_t SIZE = 100;
126+
127+ void init_slice_all(char *arr)
128+ __CPROVER_assigns(__CPROVER_object_from(arr))
129+ {
130+ for(size_t i=0; i<SIZE; i++)
131+ arr[i] = 0;
132+ }
133+ ```
134+
135+ Given a pointer ` ptr ` pointing into some object ` o ` , ` __CPROVER_object_upto(ptr, size) `
136+ specifies that the range of ` size ` bytes of ` o ` starting at ` ptr ` are assignable:
137+ The ` size ` value must such that the range does not exceed the object boundary,
138+ that is, ` __CPROVER_object_size(ptr) - __CPROVER_pointer_offset(ptr) >= size ` must hold:
33139
34140``` c
35- __CPROVER_size_t __CPROVER_object_slice (void * ptr, __ CPROVER_size_t size);
141+ void __CPROVER_object_upto (void * ptr, __ CPROVER_size_t size);
36142```
37143
38- Caveats and limitations: The slices in question must *not*
39- be interpreted as pointers by the program. During call-by-contract replacement,
40- `__CPROVER_havoc_slice(ptr, size)` is used to havoc these targets,
41- and `__CPROVER_havoc_slice` does not support havocing pointers.
42- ### Parameters
144+ ```c
145+ const size_t SIZE = 100;
43146
44- An _assigns_ clause currently supports simple variable types and their pointers,
45- structs, and arrays. Recursive pointer structures are left to future work, as
46- their support would require changes to CBMC's memory model.
147+ void init_slice_n(char *arr, size_t n)
148+ __CPROVER_requires(n <= SIZE)
149+ __CPROVER_assigns(__CPROVER_object_upto(arr, n))
150+ {
151+ for(size_t i=0; i<n; i++)
152+ arr[i] = 0;
153+ }
154+ ```
155+ ### Function parameters
156+
157+ The memory locations storing function parameters values are considered local
158+ to the function and are hence always assignable.
159+
160+ ### Inductive data structures
161+ Inductive data structures are not supported yet in assigns clauses.
47162
163+ ## Examples
48164``` c
49165/* Examples */
50166int err_signal; // Global variable
@@ -59,17 +175,18 @@ int sum(const uint32_t a, const uint32_t b, uint32_t* out)
59175__ CPROVER_assigns(* out, err_signal)
60176```
61177
62- ### Semantics
178+ ## Semantics
63179
64180The semantics of an _assigns_ clause of a given function can be understood
65- in two contexts: enforcement and replacement.
181+ in two contexts: contract enforcement and replacement.
66182
67- #### Enforcement
183+ ### Enforcement
68184
69- In order to determine whether an _ assigns _ clause is a sound abstraction of
70- the write set of a function * f * , the body of the function is instrumented with
185+ In order to determine whether a function (or loop) complies with the _assigns_
186+ clause of the contract , the body of the function (or loop) is instrumented with
71187assertion statements before each statement which may write to memory (e.g., an
72- assignment). These assertions are based on the writable locations identified by the _ assigns_ clauses.
188+ assignment). These assertions check that the location about to be assigned to
189+ is among the targets specified by the _assigns_ clauses.
73190
74191For example, consider the following implementation of `sum` function.
75192
@@ -85,8 +202,11 @@ __CPROVER_assigns(*out)
85202}
86203```
87204
88- Assignable variables in the function are just those specified so with
89- `__CPROVER_assigns`, together with any local variables.
205+ Assignable locations for the ` sum ` function are the locations specified with
206+ ` __CPROVER_assigns ` , together with any location storing a function parameter,
207+ or any location that is locally stack- or heap-allocated as part of function (or loop)
208+ execution.
209+
90210In the case of ` sum ` that is ` *out ` and ` result ` . Each assignment will be
91211instrumented with an assertion to check that the target of the assignment
92212is one of those options.
@@ -119,30 +239,40 @@ int sum(const uint32_t a, const uint32_t b, uint32_t* out)
119239}
120240```
121241
122- Additionally, the set of assignable target expressions is updated while
123- traversing the function body when new memory is allocated. For example, the
124- statement ` x = (int *)malloc(sizeof(int)) ` would create a pointer, stored in
125- ` x ` , to assignable memory. Since the memory is allocated within the current
126- function, there is no way an assignment to this memory can affect the memory of
127- the calling context. If memory is allocated for a struct, the subcomponents are
128- considered assignable as well.
242+ ### Replacement
243+
244+ Assuming the _assigns_ clause of the contract correctly captures the set of
245+ locations assigned to by a function (checked during _contract enforcement_),
246+ CBMC will use the contract's [Requires \& Ensures Clauses](../../contracts/requires-and-ensures/#replacement),
247+ and its _assigns clause_ to generate a sound abstraction of the function behaviour from the contract.
248+
249+ Given the contract:
129250
130- Finally, a set of freely-assignable symbols * free* is tracked during the
131- traversal of the function body. These are locally-defined variables and formal
132- parameters without dereferences. For example, in a variable declaration `<type >
133- x = <initial_value>` , ` x` would be added to the * free* set. Assignment statements
134- where the left-hand-side is in the * free* set are not instrumented with the above assertions.
251+ ```c
252+ int f(params)
253+ __CPROVER_requires(R);
254+ __CPROVER_assigns(A);
255+ __CPROVER_ensures(E);
256+ { ... }
257+ ```
135258
136- #### Replacement
259+ Function calls ` f(args) ` get replaced with a sequence of instuctions equivalent to:
137260
138- Assuming _ assigns_ clauses are a sound abstraction of the write set for
139- a given function, CBMC will use the function contract in place of the function
140- implementation as described by
141- [ Requires \& Ensures Clauses] ( ../../contracts/requires-and-ensures/#replacement ) , and it will add
142- non-deterministic assignments for each object listed in the ` __CPROVER_assigns `
143- clause. Since these objects might be modified by the function, CBMC uses
144- non-deterministic assignments to havoc them and restrict their values only by
145- assuming the postconditions (i.e., ensures clauses).
261+ ``` c
262+ // check preconditions
263+ __CPROVER_assert (R[ args/params] , "Check f preconditions");
264+
265+ // havoc the assignable targets
266+ // for each target t1, t2, ... of A[ args/params] ;
267+ t1 = nondet();
268+ t2 = nondet();
269+ ...
270+ // assume post conditions
271+ __ CPROVER_assume(E[ args/params] );
272+ ```
273+ Where `R[args/params]`, `A[args/params]`, `E[args/params]` denote the contract
274+ clause expressions rewritten by substituting
275+ function parameters with the argyments passed at the call site.
146276
147277In our example, consider that a function `foo` may call `sum`.
148278
@@ -202,7 +332,3 @@ int foo()
202332 return rval;
203333}
204334```
205-
206- ## In Loop Contracts
207-
208- TODO: Document ` __CPROVER_assigns ` for loops.
0 commit comments