kani/contracts.rs
1// Copyright Kani Contributors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4//! Kani implementation of function contracts.
5//!
6//! Function contracts are still under development. Using the APIs therefore
7//! requires the unstable `-Zfunction-contracts` flag to be passed. You can join
8//! the discussion on contract design by reading our
9//! [RFC](https://model-checking.github.io/kani/rfc/rfcs/0009-function-contracts.html)
10//! and [commenting on the tracking
11//! issue](https://github.com/model-checking/kani/issues/2652).
12//!
13//! The function contract API is expressed as proc-macro attributes, and there
14//! are two parts to it.
15//!
16//! 1. [Contract specification attributes](#specification-attributes-overview):
17//! [`requires`][macro@requires] and [`ensures`][macro@ensures].
18//! 2. [Contract use attributes](#contract-use-attributes-overview):
19//! [`proof_for_contract`][macro@proof_for_contract] and
20//! [`stub_verified`][macro@stub_verified].
21//!
22//! ## Step-by-step Guide
23//!
24//! Let us explore using a workflow involving contracts on the example of a
25//! simple division function `my_div`:
26//!
27//! ```
28//! fn my_div(dividend: usize, divisor: usize) -> usize {
29//! dividend / divisor
30//! }
31//! ```
32//!
33//! With the contract specification attributes we can specify the behavior of
34//! this function declaratively. The [`requires`][macro@requires] attribute
35//! allows us to declare constraints on what constitutes valid inputs to our
36//! function. In this case we would want to disallow a divisor that is `0`.
37//!
38//! ```
39//! # use kani::requires;
40//! #[requires(divisor != 0)]
41//! # fn my_div(dividend: usize, divisor: usize) -> usize {
42//! # dividend / divisor
43//! # }
44//! ```
45//!
46//! This is called a precondition, because it is enforced before (pre-) the
47//! function call. As you can see attribute has access to the functions
48//! arguments. The condition itself is just regular Rust code. You can use any
49//! Rust code, including calling functions and methods. However you may not
50//! perform I/O (like [`println!`]) or mutate memory (like [`Vec::push`]).
51//!
52//! The [`ensures`][macro@ensures] attribute on the other hand lets us describe
53//! the output value in terms of the inputs. You may be as (im)precise as you
54//! like in the [`ensures`][macro@ensures] clause, depending on your needs. One
55//! approximation of the result of division for instance could be this:
56//!
57//! ```
58//! # use kani::ensures;
59//! #[ensures(|result : &usize| *result <= dividend)]
60//! # fn my_div(dividend: usize, divisor: usize) -> usize {
61//! # dividend / divisor
62//! # }
63//! ```
64//!
65//! This is called a postcondition and it also has access to the arguments and
66//! is expressed in regular Rust code. The same restrictions apply as did for
67//! [`requires`][macro@requires]. In addition to the postcondition is expressed
68//! as a closure where the value returned from the function is passed to this
69//! closure by reference.
70//!
71//! You may combine as many [`requires`][macro@requires] and
72//! [`ensures`][macro@ensures] attributes on a single function as you please.
73//! They all get enforced (as if their conditions were `&&`ed together) and the
74//! order does not matter. In our example putting them together looks like this:
75//!
76//! ```
77//! use kani::{requires, ensures};
78//!
79//! #[requires(divisor != 0)]
80//! #[ensures(|result : &usize| *result <= dividend)]
81//! fn my_div(dividend: usize, divisor: usize) -> usize {
82//! dividend / divisor
83//! }
84//! ```
85//!
86//! Once we are finished specifying our contract we can ask Kani to check it's
87//! validity. For this we need to provide a proof harness that exercises the
88//! function. The harness is created like any other, e.g. as a test-like
89//! function with inputs and using `kani::any` to create arbitrary values.
90//! However we do not need to add any assertions or assumptions about the
91//! inputs, Kani will use the pre- and postconditions we have specified for that
92//! and we use the [`proof_for_contract`][macro@proof_for_contract] attribute
93//! instead of [`proof`](crate::proof) and provide it with the path to the
94//! function we want to check.
95//!
96//! ```
97//! # use kani::{requires, ensures};
98//! #
99//! # #[requires(divisor != 0)]
100//! # #[ensures(|result : &usize| *result <= dividend)]
101//! # fn my_div(dividend: usize, divisor: usize) -> usize {
102//! # dividend / divisor
103//! # }
104//! #
105//! #[kani::proof_for_contract(my_div)]
106//! fn my_div_harness() {
107//! my_div(kani::any(), kani::any());
108//! }
109//! ```
110//!
111//! The harness is checked like any other by running `cargo kani` and can be
112//! specifically selected with `--harness my_div_harness`.
113//!
114//! Once we have verified that our contract holds, we can use perhaps it's
115//! coolest feature: verified stubbing. This allows us to use the conditions of
116//! the contract *instead* of it's implementation. This can be very powerful for
117//! expensive implementations (involving loops for instance).
118//!
119//! Verified stubbing is available to any harness via the
120//! [`stub_verified`][macro@stub_verified] harness attribute. We must provide
121//! the attribute with the path to the function to stub, but unlike with
122//! [`stub`](crate::stub) we do not need to provide a function to replace with,
123//! the contract will be used automatically.
124//!
125//! ```
126//! # use kani::{requires, ensures};
127//! #
128//! # #[requires(divisor != 0)]
129//! # #[ensures(|result : &usize| *result <= dividend)]
130//! # fn my_div(dividend: usize, divisor: usize) -> usize {
131//! # dividend / divisor
132//! # }
133//! #
134//! #[kani::proof]
135//! #[kani::stub_verified(my_div)]
136//! fn use_div() {
137//! let v = kani::vec::any_vec::<char, 5>();
138//! let some_idx = my_div(v.len() - 1, 3);
139//! v[some_idx];
140//! }
141//! ```
142//!
143//! In this example the contract is sufficient to prove that the element access
144//! in the last line cannot be out-of-bounds.
145//!
146//! ## Specification Attributes Overview
147//!
148//! The basic two specification attributes available for describing
149//! function behavior are [`requires`][macro@requires] for preconditions and
150//! [`ensures`][macro@ensures] for postconditions. Both admit arbitrary Rust
151//! expressions as their bodies which may also reference the function arguments
152//! but must not mutate memory or perform I/O. The postcondition may
153//! additionally reference the return value of the function as the variable
154//! `result`.
155//!
156//! In addition Kani provides the [`modifies`](macro@modifies) attribute. This
157//! works a bit different in that it does not contain conditions but a comma
158//! separated sequence of expressions that evaluate to pointers. This attribute
159//! constrains to which memory locations the function is allowed to write. Each
160//! expression can contain arbitrary Rust syntax, though it may not perform side
161//! effects and it is also currently unsound if the expression can panic. For more
162//! information see the [write sets](#write-sets) section.
163//!
164//! During verified stubbing the return value of a function with a contract is
165//! replaced by a call to `kani::any`. As such the return value must implement
166//! the `kani::Arbitrary` trait.
167//!
168//! In Kani, function contracts are optional. As such a function with at least
169//! one specification attribute is considered to "have a contract" and any
170//! absent specification type defaults to its most general interpretation
171//! (`true`). All functions with not a single specification attribute are
172//! considered "not to have a contract" and are ineligible for use as the target
173//! of a [`proof_for_contract`][macro@proof_for_contract] of
174//! [`stub_verified`][macro@stub_verified] attribute.
175//!
176//! ## Contract Use Attributes Overview
177//!
178//! Contract are used both to verify function behavior and to leverage the
179//! verification result as a sound abstraction.
180//!
181//! Verifying function behavior currently requires the designation of at least
182//! one checking harness with the
183//! [`proof_for_contract`](macro@proof_for_contract) attribute. A harness may
184//! only have one `proof_for_contract` attribute and it may not also have a
185//! `proof` attribute.
186//!
187//! The checking harness is expected to set up the arguments that `foo` should
188//! be called with and initialized any `static mut` globals that are reachable.
189//! All of these should be initialized to as general value as possible, usually
190//! achieved using `kani::any`. The harness must call e.g. `foo` at least once
191//! and if `foo` has type parameters, only one instantiation of those parameters
192//! is admissible. Violating either results in a compile error.
193//!
194//! If any inputs have special invariants you *can* use `kani::assume` to
195//! enforce them but this may introduce unsoundness. In general all restrictions
196//! on input parameters should be part of the [`requires`](macro@requires)
197//! clause of the function contract.
198//!
199//! Once the contract has been verified it may be used as a verified stub. For
200//! this the [`stub_verified`](macro@stub_verified) attribute is used.
201//! `stub_verified` is a harness attribute, like
202//! [`unwind`](macro@crate::unwind), meaning it is used on functions that are
203//! annotated with [`proof`](macro@crate::proof). It may also be used on a
204//! `proof_for_contract` proof.
205//!
206//! Unlike `proof_for_contract` multiple `stub_verified` attributes are allowed
207//! on the same proof harness though they must target different functions.
208//!
209//! ## Inductive Verification
210//!
211//! Function contracts by default use inductive verification to efficiently
212//! verify recursive functions. In inductive verification a recursive function
213//! is executed once and every recursive call instead uses the contract
214//! replacement. In this way many recursive calls can be checked with a
215//! single verification pass.
216//!
217//! The downside of inductive verification is that the return value of a
218//! contracted function must implement `kani::Arbitrary`. Due to restrictions to
219//! code generation in proc macros, the contract macros cannot determine reliably
220//! in all cases whether a given function with a contract is recursive. As a
221//! result it conservatively sets up inductive verification for every function
222//! and requires the `kani::Arbitrary` constraint for contract checks.
223//!
224//! If you feel strongly about this issue you can join the discussion on issue
225//! [#2823](https://github.com/model-checking/kani/issues/2823) to enable
226//! opt-out of inductive verification.
227//!
228//! ## Write Sets
229//!
230//! The [`modifies`](macro@modifies) attribute is used to describe which
231//! locations in memory a function may assign to. The attribute contains a comma
232//! separated series of expressions that reference the function arguments.
233//! Syntactically any expression is permissible, though it may not perform side
234//! effects (I/O, mutation) or panic. As an example consider this super simple
235//! function:
236//!
237//! ```
238//! #[kani::modifies(ptr, my_box.as_ref())]
239//! fn a_function(ptr: &mut u32, my_box: &mut Box<u32>) {
240//! *ptr = 80;
241//! *my_box.as_mut() = 90;
242//! }
243//! ```
244//!
245//! Because the function performs an observable side-effect (setting both the
246//! value behind the pointer and the value pointed-to by the box) we need to
247//! provide a `modifies` attribute. Otherwise Kani will reject a contract on
248//! this function.
249//!
250//! An expression used in a `modifies` clause must return a pointer to the
251//! location that you would like to allow to be modified. This can be any basic
252//! Rust pointer type (`&T`, `&mut T`, `*const T` or `*mut T`). In addition `T`
253//! must implement [`Arbitrary`](super::Arbitrary). This is used to assign
254//! `kani::any()` to the location when the function is used in a `stub_verified`.
255//!
256//! ## History Expressions
257//!
258//! Additionally, an ensures clause is allowed to refer to the state of the function arguments before function execution and perform simple computations on them
259//! via an `old` monad. Any instance of `old(computation)` will evaluate the
260//! computation before the function is called. It is required that this computation
261//! is effect free and closed with respect to the function arguments.
262//!
263//! For example, the following code passes kani tests:
264//!
265//! ```
266//! #[kani::modifies(a)]
267//! #[kani::ensures(|result| old(*a).wrapping_add(1) == *a)]
268//! #[kani::ensures(|result : &u32| old(*a).wrapping_add(1) == *result)]
269//! fn add1(a : &mut u32) -> u32 {
270//! *a=a.wrapping_add(1);
271//! *a
272//! }
273//! ```
274//!
275//! Here, the value stored in `a` is precomputed and remembered after the function
276//! is called, even though the contents of `a` changed during the function execution.
277//!
278pub use super::{ensures, modifies, proof_for_contract, requires, stub_verified};