Skip to main content

creusot_std/invariant/
guarded.rs

1use crate::{logic::Mapping, prelude::*};
2
3/// Guards that can be used in [`Guarded`].
4pub trait Guard<T> {
5    /// Property that must be satisfied by the end of `Guarded`.
6    ///
7    /// `initial` is the initial guarded value remembered by `Guarded`,
8    /// `current` is the current guarded value (the `inner` field of `Guarded`).
9    #[logic(prophetic)]
10    fn guards(self, initial: T, current: T) -> bool;
11}
12
13/// Guard predicate under mutable references.
14///
15/// Notably, `G: GuardRef<T>` implies `G: Guard<&mut T>`
16/// and `G: Guard<FullBorrow<T>>`.
17pub trait GuardRef<T> {
18    #[logic(prophetic)]
19    fn guards_ref(self, x: T) -> bool;
20}
21
22impl<'a, G: GuardRef<T>, T> Guard<&'a mut T> for G {
23    #[logic(open, prophetic, inline)]
24    fn guards(self, initial: &'a mut T, current: &'a mut T) -> bool {
25        pearlite! { self.guards_ref(*current) && ^initial == ^current }
26    }
27}
28
29/// `Guarded` is a wrapper around an object of type `T`, which makes sure that a
30/// guard (a predicate on `T`) is satisfied when the guarded object disappears.
31/// It works by making the guard part of the type invariant of `Guarded`,
32/// and uses some internal Creusot machinery to ensure that the guard
33/// never changes during the lifetime of the `Guarded` object.
34///
35/// As such, the guard can be broken locally, but it must be restored before the
36/// `Guarded` object is dropped, passed as a parameter, returned or when a
37/// borrow of the `Guarded` is resolved.
38///
39/// The guard is represented by a value `guard: G` whose type implements the trait
40/// [`Guard<T>`]. The relation [`guard.guards(inner, initial)`][Guard::guards]
41/// is the property that must be satisfied when the guarded object disappears,
42/// where `inner` is the current value of the guarded object, and `initial` is
43/// its initial value when the `Guarded` object is created. The initial value is
44/// used to guarantee that the guarded object is not replaced with another (it
45/// may be mutated, but not replaced with another) using e.g., [`std::mem::swap`].
46///
47/// `Guarded` is typically used with a `T` mutable borrow. In this case,
48/// `Guard<&mut T>` guarantees that the prophecies never change, and the
49/// guard typically guarantees that the final value of the borrow satisfies
50/// some property. Other uses include types that behave like mutable borrows,
51/// such as `FullBorrow`.
52///
53/// # Example
54///
55/// ```
56/// use creusot_std::prelude::*;
57/// use creusot_std::invariant::Guarded;
58///
59/// #[ensures(^b == 0i32)]
60/// fn breaks_inv(b: &mut i32) { *b = 0; }
61///
62/// let mut x = 1;
63/// let guarded = Guarded::new_m(&mut x, snapshot!(|x: i32| x == 1i32));
64/// // break the guard...
65/// breaks_inv(&mut *guarded.inner);
66/// // but restore it before we are done
67/// *guarded.inner = 1;
68/// ```
69#[repr(transparent)]
70#[intrinsic("guarded")]
71pub struct Guarded<T, G: Guard<T>> {
72    /// Payload of this `Guarded` value.
73    pub inner: T,
74    _guard: Snapshot<G>,
75    _initial: Snapshot<T>,
76}
77
78impl<T, G: Guard<T>> Invariant for Guarded<T, G> {
79    #[logic(open, prophetic, inline)]
80    fn invariant(self) -> bool {
81        pearlite! { (*self._guard).guards(*self._initial, self.inner) }
82    }
83}
84
85impl<T, G: Guard<T>> Guarded<T, G> {
86    #[logic(open, inline)]
87    pub fn guard(self) -> G {
88        pearlite! { *self._guard }
89    }
90}
91
92impl<'a, T, G: GuardRef<T>> Guarded<&'a mut T, G> {
93    /// Create a new guarded borrow.
94    ///
95    /// The borrow contained in the result is guaranteed to satisfy the
96    /// [`guard`](Guard::guard) at the end of its lifetime. Thus, we get the
97    /// guard for its prophecy.
98    ///
99    /// Note that the type of the second argument may not be inferred during
100    /// compilation with rustc if it is directly constructed using
101    /// the `snapshot!` macro. You may want to spell out the first type
102    /// argument of `Guarded`, or use `Guarded::new_m` if the guard is a `Mapping`.
103    #[trusted]
104    #[requires(guard.guards_ref(*borrow))]
105    #[ensures(result.inner == borrow)]
106    #[ensures(result.guard() == *guard)]
107    #[ensures(guard.guards_ref(^borrow))]
108    #[check(ghost)]
109    pub fn new(borrow: &'a mut T, guard: Snapshot<G>) -> Self {
110        Self { inner: borrow, _guard: guard, _initial: snapshot!(borrow) }
111    }
112}
113
114impl<'a, T> Guarded<&'a mut T, Mapping<T, bool>> {
115    /// Variant of [`Guarded::new`] specialized with `Mapping` as the guard.
116    #[requires(guard.guards_ref(*borrow))]
117    #[ensures(result.inner == borrow)]
118    #[ensures(result.guard() == *guard)]
119    #[ensures(guard.guards_ref(^borrow))]
120    #[check(ghost)]
121    pub fn new_m(borrow: &'a mut T, guard: Snapshot<Mapping<T, bool>>) -> Self {
122        Self::new(borrow, guard)
123    }
124}
125
126impl<'a, T> Guarded<&'a mut Option<T>, IsSome> {
127    #[trusted]
128    #[check(ghost)]
129    #[ensures(*result.inner == Some(**borrow))]
130    #[ensures(^result.inner == Some(^borrow))]
131    pub fn some(borrow: Ghost<&'a mut T>) -> Ghost<Self> {
132        let _ = borrow;
133        Ghost::conjure()
134    }
135}
136
137impl<T> GuardRef<T> for Mapping<T, bool> {
138    #[logic(open, inline)]
139    fn guards_ref(self, x: T) -> bool {
140        pearlite! { self[x] }
141    }
142}
143
144/// Guard an `Option` that must remain `Some`.
145pub struct IsSome;
146
147impl<T> GuardRef<Option<T>> for IsSome {
148    #[logic(open, inline)]
149    fn guards_ref(self, x: Option<T>) -> bool {
150        pearlite! { x != None }
151    }
152}