1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
use super::prelude::*;
// This file implements prophecy variables.
//
// A prophecy variable is represented by a Prophecy<T>, and predicts some value
// of type T.
//
// A prophecy can be allocated by calling Prophecy::<T>::alloc() in exec mode.
// The result is a prophecy variable whose view is an arbitrary value of type T.
//
// A prophecy can be resolved by calling Prophecy::<T>::resolve() in exec mode.
// This call ensures that the view of the prophecy variable is equal to the value
// passed to resolve(). This call (and in particular, its argument v) must be
// exec-mode to avoid circular dependency on the value of the prophecy variable.
//
// An informal soundness argument (following the Future-is-ours paper) is that,
// for any execution of the program, there is some sequence of calls to resolve(),
// whose values do not depend on spec- or proof-mode values. Those values can be
// plugged into the arbitrary ghost values chosen by alloc(), for the corresponding
// prophecy variables, to justify the proof accompanying the program. Since both
// alloc() and resolve() are exec-mode, there is no ambiguity about which alloc()
// call corresponds to a particular resolve() value.
verus! {
pub struct Prophecy<T> {
v: Ghost<T>,
}
impl<T> Prophecy<T> where T: Structural {
pub closed spec fn view(self) -> T {
self.v@
}
#[inline(always)]
pub exec fn new() -> (result: Self) {
Prophecy::<T> { v: Ghost(arbitrary()) }
}
#[inline(always)]
#[verifier::external_body]
pub exec fn resolve(self, v: &T)
ensures
self@ == v,
{
}
}
} // verus!