CBMC
|
Back to Code Contracts User Documentation
An invariant clause specifies a property that must be preserved by every iteration of a loop. In general a loop has infinitely many invariants. For instance, true
is a trivial invariant that holds before the loop, and after each iteration of the loop (thus, inductive). However, true
is rarely useful – it is an extremely imprecise abstraction of a loop, which is generally not sufficient to prove any subsequent assertions.
An invariant clause accepts a valid Boolean expression over the variables visible at the same scope as the loop. Additionally, [history variables] are also allowed within invariant clauses.
Invariant clauses may be specified just after the loop guard. Multiple invariant clauses on the same loop are allowed, and is equivalent to a single invariant clause that is a conjunction of those clauses. A few examples are shown below.
Important. Invariant clauses must be free of side effects, for example, mutation of local or global variables. Otherwise, CBMC raises an error message during compilation:
A loop invariant clause expands to several assumptions and assertions:
Mathematical induction is the working principle here. (1) establishes the base case for induction, and (2) & (3) establish the inductive case. Therefore, the invariant must hold after the loop execution for any number of iterations. The invariant, together with the negation of the loop guard, must be sufficient to establish subsequent assertions. If it is not, the abstraction is too imprecise and the user must supply a stronger invariant.
To illustrate the key idea, consider the following [binary search] loop with invariant clauses:
The instrumented GOTO program is conceptually similar to the following high-level C program:
A few things to note here:
havoc
the modified variables, this set of modified variables is automatically computed using alias analysis of l-values appearing in the loop body. However, the analysis is incomplete and may fail to characterize the set for complex loops. In such cases, the user must manually annotate the set of modified variables using an _assigns clause_.false
, observe that this assumption only exists within the lb <= ub
conditional. Therefore, only the symbolic execution path inside this conditional block terminates. The code outside of the conditional block continues to be symbolically executed, and subsequent assertions do not become vacuously true
.