Skip to content

Commit 21a68d2

Browse files
Mention #[derive(Arbitrary)] and bolero_engine::TypeGenerator in our tutorial (rust-lang#2060)
Expand the section "Custom nondeterministic types" to include the new derive macro and the bolero TypeGenerator trait. Co-authored-by: Zyad Hassan <[email protected]>
1 parent 48569a2 commit 21a68d2

File tree

2 files changed

+52
-28
lines changed

2 files changed

+52
-28
lines changed

docs/src/tutorial-nondeterministic-variables.md

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,26 +41,45 @@ The assertion we wrote in this harness was just an extra check we added to demon
4141

4242
## Custom nondeterministic types
4343

44-
While `kani::any()` is the only method Kani provides to inject non-determinism into a proof harness, Kani only ships with implementations for a few types where we can guarantee safety.
45-
When you need nondeterministic variables of types that `kani::any()` cannot construct, you have two options:
46-
47-
1. Implement the `kani::Arbitrary` trait for your type, so you can use `kani::any()`.
48-
2. Just write a function.
49-
50-
The advantage of the first approach is that it's simple and conventional.
51-
It also means that in addition to being able to use `kani::any()` with your type, you can also use it with `Option<MyType>` (for example).
52-
53-
The advantage of the second approach is that you're able to pass in parameters, like bounds on the size of the data structure.
44+
While `kani::any()` is the only method Kani provides to inject non-determinism into a proof harness, Kani only ships with implementations for a few `std` types where we can guarantee safety.
45+
When you need nondeterministic variables of types that don't have a `kani::any()` implementation available, you have the following options:
46+
47+
1. Implement the `kani::Arbitrary` trait for your type, so you and downstream crates can use `kani::any()` with your type.
48+
2. Implement the [`bolero_generator::TypeGenerator` trait](https://docs.rs/bolero-generator/0.8.0/bolero_generator/trait.TypeGenerator.html).
49+
This will enable you and downstream crates to use Kani via [Bolero](https://camshaft.github.io/bolero/).
50+
3. Write a function that builds an object from non-deterministic variables.
51+
52+
We recommend the first approach for most cases.
53+
The first approach is simple and conventional. This option will also enable you to use it with parameterized types, such as `Option<MyType>` and arrays.
54+
Kani includes a derive macro that allows you to automatically derive `kani::Arbitrary` for structures and enumerations as long as all its fields also implement the `kani::Arbitrary` trait.
55+
One downside of this approach today is that the `kani` crate ships with Kani, but it's not yet available on [crates.io](https://crates.io).
56+
So you need to annotate the Arbitrary implementation with a `#[cfg(kani)]` attribute.
57+
For the derive macro, use `#[cfg_attr(kani, derive(kani::Arbitrary))]`.
58+
59+
The second approach is recommended for cases where you would also like to be able to apply fuzzing or property testing.
60+
The benefits of doing so were described in [this blog post](https://model-checking.github.io/kani-verifier-blog/2022/10/27/using-kani-with-the-bolero-property-testing-framework.html).
61+
Like `kani::Arbitrary`, this trait can also be used with a `derive` macro.
62+
One thing to be aware of is that this type allow users to generate arbitrary values that include pointers.
63+
In those cases, **only the values pointed to are arbitrary**, not the pointers themselves.
64+
65+
Finally, the last approach is recommended when you need to pass in parameters, like bounds on the size of the data structure.
5466
(Which we'll discuss more in the next section.)
55-
This approach is also necessary when you are unable to implement a trait (like `Arbitrary`) on a type you're importing from another crate.
67+
This approach is also necessary when you need to generate a nondeterministic variable of a type that you're importing from another crate, since Rust doesn't allow you to implement a trait defined in an external crate for a type that you don't own.
5668

5769
Either way, inside this function you would simply return an arbitrary value by generating arbitrary values for its components.
5870
To generate a nondeterministic struct, you would just generate nondeterministic values for each of its fields.
5971
For complex data structures like vectors or other containers, you can start with an empty one and add a (bounded) nondeterministic number of entries.
60-
For an enum, you can make use of a simple trick:
72+
73+
For example, for a simple enum you can just annotate it with the Arbitrary derive attribute:
74+
75+
```rust
76+
{{#include tutorial/arbitrary-variables/src/rating.rs:rating_enum}}
77+
```
78+
79+
But if the same enum is defined in an external crate, you can use a simple trick:
6180

6281
```rust
63-
{{#include tutorial/arbitrary-variables/src/rating.rs:rating_invariant}}
82+
{{#include tutorial/arbitrary-variables/src/rating.rs:rating_arbitrary}}
6483
```
6584

6685
All we're doing here is making use of a nondeterministic integer to decide which variant of `Rating` to return.
Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
// Copyright Kani Contributors
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33

4-
// ANCHOR: rating_struct
4+
// ANCHOR: rating_enum
55
#[derive(Copy, Clone)]
6-
enum Rating {
6+
#[cfg_attr(kani, derive(kani::Arbitrary))]
7+
pub enum Rating {
78
One,
89
Two,
910
Three,
1011
}
12+
// ANCHOR_END: rating_enum
1113

1214
impl Rating {
1315
fn as_int(&self) -> u8 {
@@ -18,24 +20,11 @@ impl Rating {
1820
}
1921
}
2022
}
21-
// ANCHOR_END: rating_struct
2223

2324
#[cfg(kani)]
2425
mod verification {
2526
use super::*;
2627

27-
// ANCHOR: rating_invariant
28-
impl kani::Arbitrary for Rating {
29-
fn any() -> Self {
30-
match kani::any::<u8>() {
31-
0 => Rating::One,
32-
1 => Rating::Two,
33-
_ => Rating::Three,
34-
}
35-
}
36-
}
37-
// ANCHOR_END: rating_invariant
38-
3928
// ANCHOR: verify_rating
4029
#[kani::proof]
4130
pub fn check_rating() {
@@ -44,3 +33,19 @@ mod verification {
4433
}
4534
// ANCHOR_END: verify_rating
4635
}
36+
37+
/// Just an example on how the same could be achieved via an aux function
38+
#[cfg(kani)]
39+
mod expanded {
40+
use super::*;
41+
42+
// ANCHOR: rating_arbitrary
43+
pub fn any_rating() -> Rating {
44+
match kani::any() {
45+
0 => Rating::One,
46+
1 => Rating::Two,
47+
_ => Rating::Three,
48+
}
49+
}
50+
// ANCHOR_END: rating_arbitrary
51+
}

0 commit comments

Comments
 (0)