Ghost structures
Using imperative data structures in ghost code
Usual imperative structures like Vec
, HashMap
or HashSet
cannot be used in ghost code. To be precise, you may notice that functions like Vec::push
are not marked with the #[pure]
attribute, and so they cannot be called in ghost code.
This is because this function (and other like it) allocate memory, which on the actual machine, is finite. This currently translates to a possible inconsistency when using Vec
in ghost code:
#![allow(unused)] fn main() { use creusot_contracts::{proof_assert, ghost}; ghost! { let mut v = Vec::new(); for _ in 0..=usize::MAX as u128 + 1 { v.push(0); } proof_assert!(v@.len() <= usize::MAX@); // by definition proof_assert!(v@.len() > usize::MAX@); // uh-oh } }
This case might be forbidden in the future by adding a stronger precondition to Vec::push
, but the point still stands: the capacity of a ghost vector should be infinite.
Ghost structures
As such, ghost code uses the mathematical structures Seq
, FMap
and FSet
, and adds program functions onto them.
The above snippet becomes:
#![allow(unused)] fn main() { use creusot_contracts::{proof_assert, ghost, Int, logic::Seq}; ghost! { let mut s: Seq<Int> = Seq::new(); for _ in 0..=usize::MAX as u128 + 1 { s.push_ghost(0); } // proof_assert!(s.len() <= usize::MAX@); // fails proof_assert!(s.len() > usize::MAX@); } }
A few things to note:
Seq
,FMap
andFSet
are the counterparts forVec
,HashMap
andHashSet
respectively. They all live in thelogic
module.- Since
s
in directly the logical typeSeq
, we don't need the view operator@
. - To avoid name clashes, these additional "ghost" functions are almost all suffixed with
_ghost
. See the documentation for each type to know the available methods, ghost and logical.