-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #975 from arnaudgolfouse/guide
Guide with mdbook
- Loading branch information
Showing
18 changed files
with
456 additions
and
158 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,17 @@ | ||
# Summary | ||
|
||
- [Quickstart](./quickstart.md) | ||
- [Basic concepts](./basic_concepts.md) | ||
- [`requires` and `ensures`](basic_concepts/requires_ensures.md) | ||
- [Invariants](basic_concepts/invariants.md) | ||
- [Variants](basic_concepts/variants.md) | ||
- [`proof_assert`](basic_concepts/proof_assert.md) | ||
- [Trusted](./trusted.md) | ||
- [Representation of types](representation_of_types.md) | ||
- [Most types](representation_of_types/most_types.md) | ||
- [Mutable borrows](representation_of_types/mutable_borrows.md) | ||
- [Pearlite](./pearlite.md) | ||
- [Logic functions](logic_functions.md) | ||
- [Shallow model](./shallow_model.md) | ||
- [Snapshots](snapshots.md) | ||
- [Type invariants](./type_invariants.md) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Basic concepts | ||
|
||
Every Creusot macro will erase themselves when compiled normally: they only exist when using `cargo creusot`. | ||
|
||
If you need to have Creusot-only code, you can use the `#[cfg(creusot)]` attribute. | ||
|
||
Note that you must explicitly use the `creusot_contracts` crate in your code (which should be the case once you actually prove things, but not necessarily when you initially set up a project), such as with the line: | ||
|
||
```rust | ||
use creusot_contracts::*; | ||
``` | ||
|
||
or you will get a compilation error complaining that the `creusot_contracts` crate is not loaded. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Invariants | ||
|
||
Inside a function, you can attach `invariant` clauses to loops, these are attached on _top_ of the loop rather than inside, like: | ||
|
||
```rust | ||
#[invariant(... loop invariant ...)] | ||
while ... { ... } | ||
``` | ||
|
||
<!-- TODO: Better documentation on `#[invariant]`: | ||
- precise syntax (mention pearlite) | ||
- meaning | ||
- common patterns, examples --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# `proof_assert!` | ||
|
||
TODO: | ||
|
||
- Syntax | ||
- usage |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# requires and ensure | ||
|
||
Most of what you will be writing with creusot will be `requires` and `ensures` clauses. That is: **preconditions** and **postconditions**. | ||
|
||
Together, those form the **contract** of a function. This is in essence the "logical signature" of your function: when analyzing a function, only the contract of other functions is visible. | ||
|
||
## Preconditions with `requires` | ||
|
||
A _precondition_ is an assertion that must hold when entering the function. For example, the following function accesses a slice at index `0`: | ||
|
||
```rust | ||
fn head(v: &[i32]) -> i32 { | ||
v[0] | ||
} | ||
``` | ||
|
||
So we must require that the slice has at least one element: | ||
|
||
```rust | ||
#[requires(v@.len() >= 1)] | ||
fn head(v: &[i32]) -> i32 { | ||
v[0] | ||
} | ||
``` | ||
|
||
Note the **shallow model** (`@`) operator: it is needed to convert the Rust type `&[i32]` to a logical type `Seq<i32>`. Else, we could not call `[T]::len`, which is a program function (and not a logical one). | ||
|
||
To learn more, see the chapter on [Pearlite](../pearlite.md) and [ShallowModel](../shallow_model.md). | ||
|
||
## Postconditions with `ensures` | ||
|
||
A _postcondition_ is an assertions that is proven true at the end of the function. The return value of the function can be accessed via the `result` keyword. | ||
|
||
In the case of the example above, we want to assert that the returned integer is the first of the slice: | ||
|
||
```rust | ||
#[requires(v@.len() >= 1)] | ||
#[ensures(v@[0] == result)] | ||
fn head(v: &[i32]) -> i32 { | ||
v[0] | ||
} | ||
``` | ||
|
||
Note that we: | ||
- use the `@` operator on the slice to get a `Seq<i32>` | ||
- we can then index this `Seq<i32>` to get a `i32`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Variants | ||
|
||
A `variant` clause can be attached either to a function like `ensures`, or `requires` or to a loop like `invariant`, it should contain a strictly decreasing expression which can prove the termination of the item it is attached to. | ||
|
||
<!-- TODO: be more precise: | ||
- where exactly is `variant` allowed ? | ||
- At least one example --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# Logic functions | ||
|
||
We provide two new attributes on Rust functions: `logic` and `predicate`. | ||
|
||
Marked `#[logic]` or `#[predicate]`, a function can be used in specs and other logical conditions (`requires`/`ensures` and `invariant`). They can use ghost functions. | ||
The two attributes have the following difference: | ||
|
||
- A `logic` function can freely have logical, non-executable operations, such as quantifiers, logic equalities, etc. Instead, this function can't be called in normal Rust code (the function body of a `logic` function is replaced with a panic). | ||
You can use pearlite syntax for any part in the logic function by marking that part with the `pearlite! { ... }` macro. | ||
- A `predicate` is a logical function which returns a proposition (in practice, returns a boolean value). | ||
|
||
## Recursion | ||
|
||
When you write *recursive* `ghost`, `logic` or `predicate` functions, you have to show that the function terminates. | ||
For that, you can add `#[variant(EXPR)]` attribute, which says that the value of the expression `EXPR` strictly decreases (in a known well-founded order) at each recursive call. | ||
The type of `EXPR` should implement the `WellFounded` trait. | ||
|
||
## Prophetic functions | ||
|
||
As seen in the chapter on [mutable borrow](./representation_of_types/mutable_borrows.md), a mutable borrow contains a _prophetic_ value, whose value depends on future execution. In order to preserve the soundness of the logic, `#[logic]` functions are not allowed to observe that value: that is, they cannot call the prophetic `^` operator. | ||
|
||
If you really need a logic function to use that operator, you need to mark it with `#[logic(prophetic)]`/`#[predicate(prophetic)]` instead. In exchange, this function cannot be called from ghost code (unimplemented right now). | ||
|
||
A normal `#[logic]` function cannot call a `#[logic(prophetic)]` function. | ||
|
||
## Examples | ||
|
||
Basic example: | ||
|
||
```rust | ||
#[logic] | ||
fn logic_add(x: Int, y: Int) -> Int { | ||
x + y | ||
} | ||
|
||
#[requires(x < i32::MAX)] | ||
#[ensures(result@ == logic_add(x@, 1))] | ||
pub fn add_one(x: i32) -> i32 { | ||
x + 1 | ||
} | ||
``` | ||
|
||
Pearlite block: | ||
|
||
```rust | ||
#[predicate] | ||
fn all_ones(s: Seq<i32>) -> bool { | ||
pearlite! { | ||
forall<i: Int> i >= 0 && i < s.len() ==> s[i]@ == 1 | ||
} | ||
} | ||
|
||
#[ensures(all_ones(result@))] | ||
#[ensures(result@.len() == n@)] | ||
pub fn make_ones(n: usize) -> Vec<i32> { | ||
creusot_contracts::vec![1; n] | ||
} | ||
``` | ||
|
||
Recursion: | ||
|
||
```rust | ||
TODO | ||
``` | ||
|
||
Prophetic: | ||
|
||
```rust | ||
TODO | ||
``` | ||
|
||
<!-- TODO: Explain `#[open(...)]` --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# Pearlite | ||
|
||
Pearlite is the language used in: | ||
|
||
- contracts (`ensures`, `requires`, `invariant`, `variant`) | ||
- `proof_assert!` | ||
- the `pearlite!` macro | ||
|
||
It can be seen as a pure, immutable fragment of Rust which has access to a few additional logical operations and connectives. In practice you have: | ||
|
||
- Base Rust expressions: matching, function calls, let bindings, binary and unary operators, tuples, structs and enums, projections, primitive casts, and dereferencing | ||
- Logical Expressions: quantifiers (`forall` and `exists`), logical implication `==>`, _logical_ equality `a == b`, labels <!-- TODO: explain labels --> | ||
- Rust specific logical expressions: access to the **final** value of a mutable reference `^`, access to the [shallow model](./shallow_model.md) of an object `@` | ||
|
||
## Logical implication | ||
|
||
Since `=>` is already a used token in Rust, we use `==>` to mean implication: | ||
|
||
```rust | ||
proof_assert!(true ==> true); | ||
proof_assert!(false ==> true); | ||
proof_assert!(false ==> false); | ||
// proof_assert!(true ==> false); // incorrect | ||
``` | ||
|
||
## Quantifiers | ||
|
||
The logical quantifiers ∀ and ∃ are written `forall` and `exists` in Pearlite: | ||
|
||
```rust | ||
#[requires(forall<i: Int> i >= 0 && i < list@.len() ==> list@[i] == 0)] | ||
fn requires_all_zeros(list: &[i32]) { | ||
// ... | ||
} | ||
``` |
Oops, something went wrong.