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 alogic
function is replaced with a panic). You can use pearlite syntax for any part in the logic function by marking that part with thepearlite! { ... }
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, 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:
#![allow(unused)] fn main() { #[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:
#![allow(unused)] fn main() { #[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:
#![allow(unused)] fn main() { TODO }
Prophetic:
#![allow(unused)] fn main() { TODO }