Skip to main content

creusot_std/ghost/
invariant.rs

1//! Resource invariants.
2//!
3//! Resource invariants allow the use of a shared piece of data to be used in
4//! the invariant (see [`Protocol`]), but in return they impose a much more
5//! restricted access to the underlying data, as well as the use of [`Tokens`].
6//!
7//! [Atomic invariants](AtomicInvariant) are used to specify concurrent
8//! operations.
9//!
10//! [Non-atomic invariants](NonAtomicInvariant) are used to specify thread-local
11//! operations.
12//!
13//! Not to be confused with [loop invariants][crate::macros::invariant] or
14//! [type invariants][crate::invariant::Invariant].
15//!
16//! # Example
17//!
18//! Building a simplified `Cell`, that only asserts its content's type invariant.
19//! ```
20//! # use creusot_std::{
21//! #     cell::PermCell,
22//! #     ghost::{
23//! #         invariant::{NonAtomicInvariant, Protocol, Tokens, declare_namespace},
24//! #         perm::Perm,
25//! #     },
26//! #     logic::Id,
27//! #     prelude::*,
28//! # };
29//! declare_namespace! { PERMCELL }
30//!
31//! /// A cell that simply asserts its content's type invariant.
32//! pub struct CellInv<T: Invariant> {
33//!     data: PermCell<T>,
34//!     permission: Ghost<NonAtomicInvariant<PermCellNAInv<T>>>,
35//! }
36//! impl<T: Invariant> Invariant for CellInv<T> {
37//!     #[logic]
38//!     fn invariant(self) -> bool {
39//!         self.permission.namespace() == PERMCELL() && self.permission.public() == self.data.id()
40//!     }
41//! }
42//!
43//! struct PermCellNAInv<T>(Box<Perm<PermCell<T>>>);
44//! impl<T: Invariant> Protocol for PermCellNAInv<T> {
45//!     type Public = Id;
46//!
47//!     #[logic]
48//!     fn protocol(self, id: Id) -> bool {
49//!         self.0.id() == id
50//!     }
51//! }
52//!
53//! impl<T: Invariant> CellInv<T> {
54//!     #[requires(tokens.contains(PERMCELL()))]
55//!     pub fn write(&self, x: T, tokens: Ghost<Tokens>) {
56//!         NonAtomicInvariant::open(self.permission.borrow(), tokens, move |perm| unsafe {
57//!             *self.data.borrow_mut(ghost!(&mut perm.into_inner().0)) = x
58//!         })
59//!     }
60//! }
61//! ```
62//!
63//! # Explicit tokens
64//!
65//! For now, [`Tokens`] must be explicitely passed to [`open`](NonAtomicInvariant::open).
66//! We plan to relax this limitation at some point.
67
68#![allow(unused_variables)]
69
70use crate::{
71    ghost::{FnGhost, Plain},
72    logic::Set,
73    prelude::*,
74};
75use core::marker::PhantomData;
76
77/// Declare a new namespace.
78///
79/// # Example
80///
81/// ```rust
82/// use creusot_std::{ghost::invariant::{declare_namespace, Namespace}, logic::Set, prelude::*};
83/// declare_namespace! { A }
84///
85/// #[requires(ns.contains(A()))]
86/// fn foo(ns: Ghost<&mut Set<Namespace>>) { /* ... */ }
87/// ```
88pub use base_macros::declare_namespace;
89
90/// The type of _namespaces_ of associated with non-atomic invariants.
91///
92/// Can be declared with the [`declare_namespace`] macro, and then attached to a non-atomic
93/// invariant when creating it with [`NonAtomicInvariant::new`].
94#[intrinsic("namespace")]
95pub struct Namespace(());
96
97impl Clone for Namespace {
98    #[check(ghost)]
99    #[ensures(result == *self)]
100    fn clone(&self) -> Self {
101        *self
102    }
103}
104impl Copy for Namespace {}
105
106impl Plain for Namespace {
107    #[trusted]
108    #[ensures(*result == *snap)]
109    #[check(ghost)]
110    #[allow(unused_variables)]
111    fn into_ghost(snap: Snapshot<Self>) -> Ghost<Self> {
112        Ghost::conjure()
113    }
114}
115
116/// Invariant tokens.
117///
118/// This is given at the start of the program, and must be passed along to
119/// [NonAtomicInvariant::open] to prevent opening invariant reentrantly.
120///
121/// # Tokens and `open`
122///
123/// Tokens are used to avoid reentrency in [`open`](NonAtomicInvariant::open).
124/// To ensure this, `Tokens` acts as a special kind of mutable borrow : only
125/// one may exist at a given point in the program, preventing multiple calls to
126/// `open` with the same namespace. This is the reason this type has a lifetime
127/// attached to it.
128///
129/// Note that after the execution of `open`, the token that was used is
130/// restored. Because of this, we never need to talk about the 'final' value
131/// of this borrow, because it never differs from the current value (in places
132/// where we can use it).
133///
134/// To help passing it into functions, it may be [reborrowed](Self::reborrow),
135/// similarly to a normal borrow.
136#[opaque]
137// `*mut ()` so that Tokens are neither Send nor Sync
138pub struct Tokens<'a>(PhantomData<&'a ()>, PhantomData<*mut ()>);
139
140impl Tokens<'_> {
141    /// Get the underlying set of namespaces of this token.
142    ///
143    /// Also accessible via the [`view`](View::view) (`@`) operator.
144    #[logic(opaque)]
145    pub fn namespaces(self) -> Set<Namespace> {
146        dead
147    }
148
149    /// Get the tokens for all the namespaces.
150    ///
151    /// This is only callable _once_, in `main`.
152    #[trusted]
153    #[ensures(forall<ns: Namespace> result.contains(ns))]
154    #[intrinsic("tokens_new")]
155    #[check(ghost)]
156    pub fn new() -> Ghost<Self> {
157        Ghost::conjure()
158    }
159
160    /// Reborrow the token, allowing it to be reused later.
161    ///
162    /// # Example
163    /// ```
164    /// # use creusot_std::{ghost::invariant::Tokens, prelude::*};
165    /// fn foo(tokens: Ghost<Tokens>) {}
166    /// fn bar(tokens: Ghost<Tokens>) {}
167    /// fn baz(mut tokens: Ghost<Tokens>) {
168    ///     foo(ghost!(tokens.reborrow()));
169    ///     bar(tokens);
170    /// }
171    /// ```
172    #[trusted]
173    #[ensures(result == *self && ^self == *self)]
174    #[check(ghost)]
175    pub fn reborrow<'a>(&'a mut self) -> Tokens<'a> {
176        Tokens(PhantomData, PhantomData)
177    }
178
179    /// Split the tokens in two, so that it can be used to access independant invariants.
180    ///
181    /// # Example
182    ///
183    /// ```
184    /// # use creusot_std::{ghost::invariant::{declare_namespace, Tokens}, prelude::*};
185    /// declare_namespace! { FOO }
186    /// declare_namespace! { BAR }
187    ///
188    /// // the lifetime 'locks' the namespace
189    /// #[requires(tokens.contains(FOO()))]
190    /// fn foo<'a>(tokens: Ghost<Tokens<'a>>) -> &'a i32 {
191    /// # todo!()
192    ///     // access some invariant to get the reference
193    /// }
194    /// #[requires(tokens.contains(BAR()))]
195    /// fn bar(tokens: Ghost<Tokens>) {}
196    ///
197    /// #[requires(tokens.contains(FOO()) && tokens.contains(BAR()))]
198    /// fn baz(mut tokens: Ghost<Tokens>) -> i32 {
199    ///      let (ns_foo, ns_bar) = ghost!(tokens.split(snapshot!(FOO()))).split();
200    ///      let x = foo(ns_foo);
201    ///      bar(ns_bar);
202    ///      *x
203    /// }
204    /// ```
205    #[trusted]
206    #[requires(self.contains(*ns))]
207    #[ensures(^self == *self)]
208    #[ensures(result.0.contains(*ns))]
209    #[ensures(result.1.namespaces() == self.namespaces().remove(*ns))]
210    #[check(ghost)]
211    pub fn split<'a>(&'a mut self, ns: Snapshot<Namespace>) -> (Tokens<'a>, Tokens<'a>) {
212        (Tokens(PhantomData, PhantomData), Tokens(PhantomData, PhantomData))
213    }
214
215    #[logic(open)]
216    pub fn contains(self, namespace: Namespace) -> bool {
217        self.namespaces().contains(namespace)
218    }
219}
220
221impl View for Tokens<'_> {
222    type ViewTy = Set<Namespace>;
223    #[logic(open, inline)]
224    fn view(self) -> Set<Namespace> {
225        self.namespaces()
226    }
227}
228
229/// A variant of [`Invariant`] for use in [`AtomicInvariant`]s and [`NonAtomicInvariant`]s.
230///
231/// This allows to specify an invariant that depends on some public data
232/// (`AtomicInvariant::public`, `NonAtomicInvariant::public`).
233pub trait Protocol {
234    type Public;
235
236    #[logic(prophetic)]
237    fn protocol(self, data: Self::Public) -> bool;
238}
239
240#[opaque]
241pub struct AtomicInvariant<T>(PhantomData<*mut T>);
242
243unsafe impl<T: Send> Sync for AtomicInvariant<T> {}
244
245impl<T: Protocol> AtomicInvariant<T> {
246    /// Construct a `AtomicInvariant`
247    ///
248    /// # Parameters
249    /// - `value`: the actual data contained in the invariant. Use [`Self::open`] to
250    /// access it. Also called the 'private' part of the invariant.
251    /// - `public`: the 'public' part of the invariant.
252    /// - `namespace`: the namespace of the invariant.
253    ///   This is required to avoid [open](Self::open)ing the same invariant twice.
254    #[trusted]
255    #[requires(value.protocol(*public))]
256    #[ensures(result.public() == *public)]
257    #[ensures(result.namespace() == *namespace)]
258    #[check(ghost)]
259    pub fn new(
260        value: Ghost<T>,
261        public: Snapshot<T::Public>,
262        namespace: Snapshot<Namespace>,
263    ) -> Ghost<Self> {
264        Ghost::conjure()
265    }
266
267    /// Get the namespace associated with this invariant.
268    #[logic(opaque)]
269    pub fn namespace(self) -> Namespace {
270        dead
271    }
272
273    /// Get the 'public' part of this invariant.
274    #[logic(opaque)]
275    pub fn public(self) -> T::Public {
276        dead
277    }
278
279    /// Gives the actual invariant held by the [`AtomicInvariant`].
280    #[trusted]
281    #[ensures(result.protocol(self.public()))]
282    #[check(ghost)]
283    pub fn into_inner(self) -> T {
284        panic!("Should not be called outside ghost code")
285    }
286
287    /// Open the invariant to get the data stored inside.
288    ///
289    /// This will call the closure `f` with the inner data. You must restore the
290    /// contained [`Protocol`] before returning from the closure.
291    ///
292    /// NOTE: This function can only be called from ghost code, because atomic
293    /// invariants are always wrapped in `Ghost`. This guarantees atomicity.
294    #[trusted]
295    #[requires(tokens.contains(self.namespace()))]
296    #[requires(forall<t: &mut T> t.protocol(self.public()) && inv(t) ==>
297        f.precondition((t,)) &&
298        // f must restore the invariant
299        (forall<res: A> f.postcondition_once((t,), res) ==> (^t).protocol(self.public())))]
300    #[ensures(exists<t: &mut T> inv(t) && t.protocol(self.public()) && f.postcondition_once((t,), result))]
301    #[check(ghost)]
302    pub fn open<A>(&self, tokens: Tokens, f: impl FnGhost + for<'a> FnOnce(&'a mut T) -> A) -> A {
303        panic!("Should not be called outside ghost code")
304    }
305}
306
307/// A ghost structure, that holds a piece of data (`T`) together with an
308/// [protocol](Protocol).
309///
310/// # Note
311///
312/// `NonAtomicInvariant` is not `Sync`, and is invariant in the underlying data.
313/// - not `Sync` precisely because it is non-atomic, so access to the data is unsyncronized
314/// - invariant because it gives access to a mutable borrow of this data.
315#[opaque]
316pub struct NonAtomicInvariant<T: Protocol>(PhantomData<*mut T>);
317
318/// Define method call syntax for [`NonAtomicInvariant::open`].
319pub trait NonAtomicInvariantExt<'a> {
320    type Inner: 'a;
321
322    /// Alias for [`NonAtomicInvariant::open`], to use method call syntax (`inv.open(...)`).
323    #[requires(false)]
324    fn open<A, F>(self, tokens: Ghost<Tokens<'a>>, f: F) -> A
325    where
326        F: FnOnce(Ghost<&'a mut Self::Inner>) -> A;
327}
328
329impl<'a, T: Protocol> NonAtomicInvariantExt<'a> for Ghost<&'a NonAtomicInvariant<T>> {
330    type Inner = T;
331
332    #[requires(tokens.contains(self.namespace()))]
333    #[requires(forall<t: Ghost<&mut T>> (**t).protocol(self.public()) && inv(t) ==>
334        f.precondition((t,)) &&
335        // f must restore the invariant
336        (forall<res: A> f.postcondition_once((t,), res) ==> (^t).protocol(self.public())))]
337    #[ensures(exists<t: Ghost<&mut T>> t.protocol(self.public()) && f.postcondition_once((t,), result))]
338    fn open<A, F>(self, tokens: Ghost<Tokens<'a>>, f: F) -> A
339    where
340        F: FnOnce(Ghost<&'a mut Self::Inner>) -> A,
341    {
342        NonAtomicInvariant::open(self, tokens, f)
343    }
344}
345
346impl<'a, T> NonAtomicInvariantExt<'a> for Ghost<&'a T>
347where
348    T: core::ops::Deref,
349    Ghost<&'a T::Target>: NonAtomicInvariantExt<'a>,
350{
351    type Inner = <Ghost<&'a T::Target> as NonAtomicInvariantExt<'a>>::Inner;
352
353    #[requires(T::deref.precondition((*self,)))]
354    #[requires(forall<this> T::deref.postcondition((*self,), this) ==>
355        <Ghost<&'a T::Target> as NonAtomicInvariantExt<'a>>::open.precondition((Ghost::new_logic(this), tokens, f))
356    )]
357    #[ensures(exists<this> T::deref.postcondition((*self,), this) &&
358        <Ghost<&'a T::Target> as NonAtomicInvariantExt<'a>>::open.postcondition((Ghost::new_logic(this), tokens, f), result)
359    )]
360    fn open<A, F>(self, tokens: Ghost<Tokens<'a>>, f: F) -> A
361    where
362        F: FnOnce(Ghost<&'a mut Self::Inner>) -> A,
363    {
364        let this: Ghost<&T::Target> = ghost!(&self);
365        this.open(tokens, f)
366    }
367}
368
369impl<'a, L> NonAtomicInvariantExt<'a> for &'a Ghost<L>
370where
371    Ghost<&'a L>: NonAtomicInvariantExt<'a>,
372{
373    type Inner = <Ghost<&'a L> as NonAtomicInvariantExt<'a>>::Inner;
374
375    #[requires(<Ghost<&'a L> as NonAtomicInvariantExt<'a>>::open.precondition((Ghost::new_logic(&**self), tokens, f)))]
376    #[ensures(<Ghost<&'a L> as NonAtomicInvariantExt<'a>>::open.postcondition((Ghost::new_logic(&**self), tokens, f), result))]
377    fn open<A, F>(self, tokens: Ghost<Tokens<'a>>, f: F) -> A
378    where
379        F: FnOnce(Ghost<&'a mut Self::Inner>) -> A,
380    {
381        self.borrow().open(tokens, f)
382    }
383}
384
385impl<T: Protocol> NonAtomicInvariant<T> {
386    /// Construct a `NonAtomicInvariant`
387    ///
388    /// # Parameters
389    /// - `value`: the actual data contained in the invariant. Use [`Self::open`] to
390    /// access it. Also called the 'private' part of the invariant.
391    /// - `public`: the 'public' part of the invariant.
392    /// - `namespace`: the namespace of the invariant.
393    ///   This is required to avoid [open](Self::open)ing the same invariant twice.
394    #[trusted]
395    #[requires(value.protocol(*public))]
396    #[ensures(result.public() == *public)]
397    #[ensures(result.namespace() == *namespace)]
398    #[check(ghost)]
399    pub fn new(
400        value: Ghost<T>,
401        public: Snapshot<T::Public>,
402        namespace: Snapshot<Namespace>,
403    ) -> Ghost<Self> {
404        Ghost::conjure()
405    }
406
407    /// Gives the actual invariant held by the [`NonAtomicInvariant`].
408    #[trusted]
409    #[ensures(result.protocol(self.public()))]
410    #[check(ghost)]
411    pub fn into_inner(self) -> T {
412        panic!("Should not be called outside ghost code")
413    }
414
415    /// Get the namespace associated with this invariant.
416    #[logic(opaque)]
417    pub fn namespace(self) -> Namespace {
418        dead
419    }
420
421    /// Get the 'public' part of this invariant.
422    #[logic(opaque)]
423    pub fn public(self) -> T::Public {
424        dead
425    }
426
427    /// Open the invariant to get the data stored inside.
428    ///
429    /// This will call the closure `f` with the inner data. You must restore the
430    /// contained [`Protocol`] before returning from the closure.
431    #[trusted]
432    #[requires(tokens.contains(this.namespace()))]
433    #[requires(forall<t: Ghost<&mut T>> t.protocol(this.public()) && inv(t) ==>
434        f.precondition((t,)) &&
435        // f must restore the invariant
436        (forall<res: A> f.postcondition_once((t,), res) ==> (^t).protocol(this.public())))]
437    #[ensures(exists<t: Ghost<&mut T>> inv(t) && t.protocol(this.public()) && f.postcondition_once((t,), result))]
438    pub fn open<'a, A>(
439        this: Ghost<&'a Self>,
440        tokens: Ghost<Tokens<'a>>,
441        f: impl FnOnce(Ghost<&'a mut T>) -> A,
442    ) -> A {
443        f(Ghost::conjure())
444    }
445
446    /// Open the invariant to get the data stored inside, immutably.
447    /// This allows reentrant access to the invariant.
448    #[trusted]
449    #[requires(tokens.contains(self.namespace()))]
450    #[ensures(result.protocol(self.public()))]
451    #[check(ghost)]
452    pub fn open_const<'a>(&'a self, tokens: &'a Tokens) -> &'a T {
453        panic!("Should not be called outside ghost code")
454    }
455}