|
66 | 66 |
|
67 | 67 | Integration test examples are located under `tests/ui/` and can be executed using `cargo test`. You can review these examples to understand what the current Thrust implementation can handle. |
68 | 68 |
|
| 69 | +## Annotation |
| 70 | + |
| 71 | +Thrust can verify a wide range of programs without explicit annotations, but you can use `#[thrust::requires(expr)]` and `#[thrust::ensures(expr)]` to annotate the precondition and postcondition of a function, aiding in verification or specifying the specification. Here, `expr` is a logical expression that supports basic integer, boolean, and reference operations. |
| 72 | + |
| 73 | +```rust |
| 74 | +#[thrust::requires(n >= 0)] |
| 75 | +#[thrust::ensures((result * 2) == n * (n + 1))] |
| 76 | +fn sum(n: i32) -> i32 { |
| 77 | + if n == 0 { |
| 78 | + 0 |
| 79 | + } else { |
| 80 | + n + sum(n - 1) |
| 81 | + } |
| 82 | +} |
| 83 | +``` |
| 84 | + |
| 85 | +You can use the `^` unary operator to denote the value of a mutable reference after the function is called. Note that in the current implementation, you need to specify both `requires` and `ensures` when using either one. |
| 86 | + |
| 87 | +```rust |
| 88 | +#[thrust::requires(true)] |
| 89 | +#[thrust::ensures(^ma == *ma + a)] |
| 90 | +fn add(ma: &mut i32, a: i32) { |
| 91 | + *ma += a; |
| 92 | +} |
| 93 | +``` |
| 94 | + |
| 95 | +The conditions on `thrust::requires` and `thrust::ensures` are internally encoded as refinement types for the parameter and return types. You can also specify these refinement types directly using `#[thrust::param(param: type)]` and `#[thrust::ret(type)]`. |
| 96 | + |
| 97 | +```rust |
| 98 | +#[thrust::param(n: { x: int | x >= 0 })] |
| 99 | +#[thrust::ret({ x: int | (x * 2) == n * (n + 1) })] |
| 100 | +fn sum(n: i32) -> i32 { |
| 101 | + if n == 0 { |
| 102 | + 0 |
| 103 | + } else { |
| 104 | + n + sum(n - 1) |
| 105 | + } |
| 106 | +} |
| 107 | +``` |
| 108 | + |
| 109 | +The bodies of functions marked with `#[thrust::trusted]` are not analyzed by Thrust. Additionally, `#[thrust::callable]` is an alias for `#[thrust::requires(true)]` and `#[thrust::ensures(true)]`. |
| 110 | + |
| 111 | +```rust |
| 112 | +#[thrust::trusted] |
| 113 | +#[thrust::callable] |
| 114 | +fn rand() -> i32 { unimplemented!() } |
| 115 | +``` |
| 116 | + |
| 117 | +## Configuration |
| 118 | + |
| 119 | +Several environment variables are used by Thrust to configure its behavior: |
| 120 | + |
| 121 | +- `THRUST_SOLVER`: A CHC solver command used to solve CHC constraints generated by Thrust. Default: `z3` |
| 122 | +- `THRUST_SOLVER_ARGS`: Whitespace-separated command-line flags passed to the solver. The default is `fp.spacer.global=true fp.validate=true` when the solver is `z3`. |
| 123 | +- `THRUST_SOLVER_TIMEOUT_SECS`: Timeout for waiting on results from the solver. Default: `30` |
| 124 | +- `THRUST_OUTPUT_DIR`: When configured, Thrust outputs intermediate smtlib2 files into this directory. |
| 125 | +- `THRUST_ENUM_EXPANSION_DEPTH_LIMIT`: When Thrust works with enums, it "expands" the structure of the enum value onto its environment. This configuration value sets the limit on the depth of recursion during this expansion to handle enums that are defined recursively. It is our future work to discover a sensible value for this automatically. Default: `2` |
| 126 | + |
69 | 127 | ## Development |
70 | 128 |
|
71 | 129 | The implementation of the Thrust is largely divided into the following modules. |
|
0 commit comments