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#[cfg(creusot)]
78use crate::sync_view::Objective;
79
80/// Declare a new namespace.
81///
82/// # Example
83///
84/// ```rust
85/// use creusot_std::{ghost::invariant::{declare_namespace, Namespace}, logic::Set, prelude::*};
86/// declare_namespace! { A }
87///
88/// #[requires(ns.contains(A()))]
89/// fn foo(ns: Ghost<&mut Set<Namespace>>) { /* ... */ }
90/// ```
91pub use base_macros::declare_namespace;
92
93/// The type of _namespaces_ of associated with non-atomic invariants.
94///
95/// Can be declared with the [`declare_namespace`] macro, and then attached to a non-atomic
96/// invariant when creating it with [`NonAtomicInvariant::new`].
97#[intrinsic("namespace")]
98pub struct Namespace(());
99
100impl Clone for Namespace {
101    #[check(ghost)]
102    #[ensures(result == *self)]
103    fn clone(&self) -> Self {
104        *self
105    }
106}
107impl Copy for Namespace {}
108
109impl Plain for Namespace {
110    #[trusted]
111    #[ensures(*result == *snap)]
112    #[check(ghost)]
113    #[allow(unused_variables)]
114    fn into_ghost(snap: Snapshot<Self>) -> Ghost<Self> {
115        Ghost::conjure()
116    }
117}
118
119/// Invariant tokens.
120///
121/// This is given at the start of the program, and must be passed along to
122/// [NonAtomicInvariant::open] to prevent opening invariant reentrantly.
123///
124/// # Tokens and `open`
125///
126/// Tokens are used to avoid reentrency in [`open`](NonAtomicInvariant::open).
127/// To ensure this, `Tokens` acts as a special kind of mutable borrow : only
128/// one may exist at a given point in the program, preventing multiple calls to
129/// `open` with the same namespace. This is the reason this type has a lifetime
130/// attached to it.
131///
132/// Note that after the execution of `open`, the token that was used is
133/// restored. Because of this, we never need to talk about the 'final' value
134/// of this borrow, because it never differs from the current value (in places
135/// where we can use it).
136///
137/// To help passing it into functions, it may be [reborrowed](Self::reborrow),
138/// similarly to a normal borrow.
139#[opaque]
140// `*mut ()` so that Tokens are neither Send nor Sync
141pub struct Tokens<'a>(PhantomData<&'a ()>, PhantomData<*mut ()>);
142
143impl Tokens<'_> {
144    /// Get the underlying set of namespaces of this token.
145    ///
146    /// Also accessible via the [`view`](View::view) (`@`) operator.
147    #[logic(opaque)]
148    pub fn namespaces(self) -> Set<Namespace> {
149        dead
150    }
151
152    /// Get the tokens for all the namespaces.
153    ///
154    /// This is only callable _once_, in `main`.
155    #[trusted]
156    #[ensures(forall<ns: Namespace> result.contains(ns))]
157    #[intrinsic("tokens_new")]
158    #[check(ghost)]
159    pub fn new() -> Ghost<Self> {
160        Ghost::conjure()
161    }
162
163    /// Reborrow the token, allowing it to be reused later.
164    ///
165    /// # Example
166    /// ```
167    /// # use creusot_std::{ghost::invariant::Tokens, prelude::*};
168    /// fn foo(tokens: Ghost<Tokens>) {}
169    /// fn bar(tokens: Ghost<Tokens>) {}
170    /// fn baz(mut tokens: Ghost<Tokens>) {
171    ///     foo(ghost!(tokens.reborrow()));
172    ///     bar(tokens);
173    /// }
174    /// ```
175    #[trusted]
176    #[ensures(result == *self && ^self == *self)]
177    #[check(ghost)]
178    pub fn reborrow<'a>(&'a mut self) -> Tokens<'a> {
179        Tokens(PhantomData, PhantomData)
180    }
181
182    /// Split the tokens in two, so that it can be used to access independant invariants.
183    ///
184    /// # Example
185    ///
186    /// ```
187    /// # use creusot_std::{ghost::invariant::{declare_namespace, Tokens}, prelude::*};
188    /// declare_namespace! { FOO }
189    /// declare_namespace! { BAR }
190    ///
191    /// // the lifetime 'locks' the namespace
192    /// #[requires(tokens.contains(FOO()))]
193    /// fn foo<'a>(tokens: Ghost<Tokens<'a>>) -> &'a i32 {
194    /// # todo!()
195    ///     // access some invariant to get the reference
196    /// }
197    /// #[requires(tokens.contains(BAR()))]
198    /// fn bar(tokens: Ghost<Tokens>) {}
199    ///
200    /// #[requires(tokens.contains(FOO()) && tokens.contains(BAR()))]
201    /// fn baz(mut tokens: Ghost<Tokens>) -> i32 {
202    ///      let (ns_foo, ns_bar) = ghost!(tokens.split(snapshot!(FOO()))).split();
203    ///      let x = foo(ns_foo);
204    ///      bar(ns_bar);
205    ///      *x
206    /// }
207    /// ```
208    #[trusted]
209    #[requires(self.contains(*ns))]
210    #[ensures(^self == *self)]
211    #[ensures(result.0.contains(*ns))]
212    #[ensures(result.1.namespaces() == self.namespaces().remove(*ns))]
213    #[check(ghost)]
214    pub fn split<'a>(&'a mut self, ns: Snapshot<Namespace>) -> (Tokens<'a>, Tokens<'a>) {
215        (Tokens(PhantomData, PhantomData), Tokens(PhantomData, PhantomData))
216    }
217
218    #[logic(open)]
219    pub fn contains(self, namespace: Namespace) -> bool {
220        self.namespaces().contains(namespace)
221    }
222}
223
224impl View for Tokens<'_> {
225    type ViewTy = Set<Namespace>;
226    #[logic(open, inline)]
227    fn view(self) -> Set<Namespace> {
228        self.namespaces()
229    }
230}
231
232/// A variant of [`Invariant`] for use in [`AtomicInvariantSC`]s,
233/// [`AtomicInvariant`]s and [`NonAtomicInvariant`]s.
234///
235/// This allows to specify an invariant that depends on some public data
236/// (`AtomicInvariantSC::public`, `AtomicInvariant::public`
237/// `NonAtomicInvariant::public`).
238pub trait Protocol {
239    type Public;
240
241    #[logic(prophetic)]
242    fn protocol(self, data: Self::Public) -> bool;
243}
244
245#[opaque]
246pub struct AtomicInvariantSC<T>(PhantomData<*mut T>);
247
248unsafe impl<T: Send> Sync for AtomicInvariantSC<T> {}
249
250impl<T: Protocol> AtomicInvariantSC<T> {
251    /// Construct a `AtomicInvariant`
252    ///
253    /// # Parameters
254    /// - `value`: the actual data contained in the invariant. Use [`Self::open`] to
255    /// access it. Also called the 'private' part of the invariant.
256    /// - `public`: the 'public' part of the invariant.
257    /// - `namespace`: the namespace of the invariant.
258    ///   This is required to avoid [open](Self::open)ing the same invariant twice.
259    #[trusted]
260    #[requires(value.protocol(*public))]
261    #[ensures(result.public() == *public)]
262    #[ensures(result.namespace() == *namespace)]
263    #[check(ghost)]
264    pub fn new(
265        value: Ghost<T>,
266        public: Snapshot<T::Public>,
267        namespace: Snapshot<Namespace>,
268    ) -> Ghost<Self> {
269        Ghost::conjure()
270    }
271
272    /// Get the namespace associated with this invariant.
273    #[logic(opaque)]
274    pub fn namespace(self) -> Namespace {
275        dead
276    }
277
278    /// Get the 'public' part of this invariant.
279    #[logic(opaque)]
280    pub fn public(self) -> T::Public {
281        dead
282    }
283
284    /// Gives the actual invariant held by the [`AtomicInvariant`].
285    #[trusted]
286    #[ensures(result.protocol(self.public()))]
287    #[check(ghost)]
288    pub fn into_inner(self) -> T {
289        panic!("Should not be called outside ghost code")
290    }
291
292    /// Open the invariant to get the data stored inside.
293    ///
294    /// This will call the closure `f` with the inner data. You must restore the
295    /// contained [`Protocol`] before returning from the closure.
296    ///
297    /// NOTE: This function can only be called from ghost code, because atomic
298    /// invariants are always wrapped in `Ghost`. This guarantees atomicity.
299    #[trusted]
300    #[requires(tokens.contains(self.namespace()))]
301    #[requires(forall<t: &mut T> t.protocol(self.public()) && inv(t) ==>
302        f.precondition((t,)) &&
303        // f must restore the invariant
304        (forall<res: A> f.postcondition_once((t,), res) ==> (^t).protocol(self.public())))]
305    #[ensures(exists<t: &mut T> inv(t) && t.protocol(self.public()) && f.postcondition_once((t,), result))]
306    #[check(ghost)]
307    pub fn open<A>(&self, tokens: Tokens, f: impl FnGhost + for<'a> FnOnce(&'a mut T) -> A) -> A {
308        panic!("Should not be called outside ghost code")
309    }
310}
311
312#[opaque]
313pub struct AtomicInvariant<T>(PhantomData<*mut T>);
314
315// TODO: Find a real hack to achieve this.
316#[cfg(creusot)]
317unsafe impl<T: Send + Objective> Sync for AtomicInvariant<T> {}
318
319#[cfg(not(creusot))]
320unsafe impl<T: Send> Sync for AtomicInvariant<T> {}
321
322impl<T: Protocol> AtomicInvariant<T> {
323    /// Construct a `AtomicInvariant`
324    ///
325    /// # Parameters
326    /// - `value`: the actual data contained in the invariant. Use [`Self::open`] to
327    /// access it. Also called the 'private' part of the invariant.
328    /// - `public`: the 'public' part of the invariant.
329    /// - `namespace`: the namespace of the invariant.
330    ///   This is required to avoid [open](Self::open)ing the same invariant twice.
331    #[trusted]
332    #[requires(value.protocol(*public))]
333    #[ensures(result.public() == *public)]
334    #[ensures(result.namespace() == *namespace)]
335    #[check(ghost)]
336    pub fn new(
337        value: Ghost<T>,
338        public: Snapshot<T::Public>,
339        namespace: Snapshot<Namespace>,
340    ) -> Ghost<Self> {
341        Ghost::conjure()
342    }
343
344    /// Get the namespace associated with this invariant.
345    #[logic(opaque)]
346    pub fn namespace(self) -> Namespace {
347        dead
348    }
349
350    /// Get the 'public' part of this invariant.
351    #[logic(opaque)]
352    pub fn public(self) -> T::Public {
353        dead
354    }
355
356    /// Gives the actual invariant held by the [`AtomicInvariant`].
357    #[trusted]
358    #[ensures(result.protocol(self.public()))]
359    #[check(ghost)]
360    pub fn into_inner(self) -> T {
361        panic!("Should not be called outside ghost code")
362    }
363
364    /// Open the invariant to get the data stored inside.
365    ///
366    /// This will call the closure `f` with the inner data. You must restore the
367    /// contained [`Protocol`] before returning from the closure.
368    ///
369    /// NOTE: This function can only be called from ghost code, because atomic
370    /// invariants are always wrapped in `Ghost`. This guarantees atomicity.
371    #[trusted]
372    #[requires(tokens.contains(self.namespace()))]
373    #[requires(forall<t: &mut T> t.protocol(self.public()) && inv(t) ==>
374        f.precondition((t,)) &&
375        // f must restore the invariant
376        (forall<res: A> f.postcondition_once((t,), res) ==> (^t).protocol(self.public())))]
377    #[ensures(exists<t: &mut T> inv(t) && t.protocol(self.public()) && f.postcondition_once((t,), result))]
378    #[check(ghost)]
379    pub fn open<A>(&self, tokens: Tokens, f: impl FnGhost + for<'a> FnOnce(&'a mut T) -> A) -> A {
380        panic!("Should not be called outside ghost code")
381    }
382}
383
384/// A ghost structure, that holds a piece of data (`T`) together with an
385/// [protocol](Protocol).
386///
387/// # Note
388///
389/// `NonAtomicInvariant` is not `Sync`, and is invariant in the underlying data.
390/// - not `Sync` precisely because it is non-atomic, so access to the data is unsyncronized
391/// - invariant because it gives access to a mutable borrow of this data.
392#[opaque]
393pub struct NonAtomicInvariant<T: Protocol>(PhantomData<*mut T>);
394
395unsafe impl<T: Protocol> Send for NonAtomicInvariant<T> {}
396
397/// Define method call syntax for [`NonAtomicInvariant::open`].
398pub trait NonAtomicInvariantExt<'a> {
399    type Inner: 'a;
400
401    /// Alias for [`NonAtomicInvariant::open`], to use method call syntax (`inv.open(...)`).
402    #[requires(false)]
403    fn open<A, F>(self, tokens: Ghost<Tokens<'a>>, f: F) -> A
404    where
405        F: FnOnce(Ghost<&'a mut Self::Inner>) -> A;
406}
407
408impl<'a, T: Protocol> NonAtomicInvariantExt<'a> for Ghost<&'a NonAtomicInvariant<T>> {
409    type Inner = T;
410
411    #[requires(tokens.contains(self.namespace()))]
412    #[requires(forall<t: Ghost<&mut T>> (**t).protocol(self.public()) && inv(t) ==>
413        f.precondition((t,)) &&
414        // f must restore the invariant
415        (forall<res: A> f.postcondition_once((t,), res) ==> (^t).protocol(self.public())))]
416    #[ensures(exists<t: Ghost<&mut T>> t.protocol(self.public()) && f.postcondition_once((t,), result))]
417    fn open<A, F>(self, tokens: Ghost<Tokens<'a>>, f: F) -> A
418    where
419        F: FnOnce(Ghost<&'a mut Self::Inner>) -> A,
420    {
421        NonAtomicInvariant::open(self, tokens, f)
422    }
423}
424
425impl<'a, T> NonAtomicInvariantExt<'a> for Ghost<&'a T>
426where
427    T: core::ops::Deref,
428    Ghost<&'a T::Target>: NonAtomicInvariantExt<'a>,
429{
430    type Inner = <Ghost<&'a T::Target> as NonAtomicInvariantExt<'a>>::Inner;
431
432    #[requires(T::deref.precondition((*self,)))]
433    #[requires(forall<this> T::deref.postcondition((*self,), this) ==>
434        <Ghost<&'a T::Target> as NonAtomicInvariantExt<'a>>::open.precondition((Ghost::new_logic(this), tokens, f))
435    )]
436    #[ensures(exists<this> T::deref.postcondition((*self,), this) &&
437        <Ghost<&'a T::Target> as NonAtomicInvariantExt<'a>>::open.postcondition((Ghost::new_logic(this), tokens, f), result)
438    )]
439    fn open<A, F>(self, tokens: Ghost<Tokens<'a>>, f: F) -> A
440    where
441        F: FnOnce(Ghost<&'a mut Self::Inner>) -> A,
442    {
443        let this: Ghost<&T::Target> = ghost!(&self);
444        this.open(tokens, f)
445    }
446}
447
448impl<'a, L> NonAtomicInvariantExt<'a> for &'a Ghost<L>
449where
450    Ghost<&'a L>: NonAtomicInvariantExt<'a>,
451{
452    type Inner = <Ghost<&'a L> as NonAtomicInvariantExt<'a>>::Inner;
453
454    #[requires(<Ghost<&'a L> as NonAtomicInvariantExt<'a>>::open.precondition((Ghost::new_logic(&**self), tokens, f)))]
455    #[ensures(<Ghost<&'a L> as NonAtomicInvariantExt<'a>>::open.postcondition((Ghost::new_logic(&**self), tokens, f), result))]
456    fn open<A, F>(self, tokens: Ghost<Tokens<'a>>, f: F) -> A
457    where
458        F: FnOnce(Ghost<&'a mut Self::Inner>) -> A,
459    {
460        self.borrow().open(tokens, f)
461    }
462}
463
464impl<T: Protocol> NonAtomicInvariant<T> {
465    /// Construct a `NonAtomicInvariant`
466    ///
467    /// # Parameters
468    /// - `value`: the actual data contained in the invariant. Use [`Self::open`] to
469    /// access it. Also called the 'private' part of the invariant.
470    /// - `public`: the 'public' part of the invariant.
471    /// - `namespace`: the namespace of the invariant.
472    ///   This is required to avoid [open](Self::open)ing the same invariant twice.
473    #[trusted]
474    #[requires(value.protocol(*public))]
475    #[ensures(result.public() == *public)]
476    #[ensures(result.namespace() == *namespace)]
477    #[check(ghost)]
478    pub fn new(
479        value: Ghost<T>,
480        public: Snapshot<T::Public>,
481        namespace: Snapshot<Namespace>,
482    ) -> Ghost<Self> {
483        Ghost::conjure()
484    }
485
486    /// Gives the actual invariant held by the [`NonAtomicInvariant`].
487    #[trusted]
488    #[ensures(result.protocol(self.public()))]
489    #[check(ghost)]
490    pub fn into_inner(self) -> T {
491        panic!("Should not be called outside ghost code")
492    }
493
494    /// Get the namespace associated with this invariant.
495    #[logic(opaque)]
496    pub fn namespace(self) -> Namespace {
497        dead
498    }
499
500    /// Get the 'public' part of this invariant.
501    #[logic(opaque)]
502    pub fn public(self) -> T::Public {
503        dead
504    }
505
506    /// Open the invariant to get the data stored inside.
507    ///
508    /// This will call the closure `f` with the inner data. You must restore the
509    /// contained [`Protocol`] before returning from the closure.
510    #[trusted]
511    #[requires(tokens.contains(this.namespace()))]
512    #[requires(forall<t: Ghost<&mut T>> t.protocol(this.public()) && inv(t) ==>
513        f.precondition((t,)) &&
514        // f must restore the invariant
515        (forall<res: A> f.postcondition_once((t,), res) ==> (^t).protocol(this.public())))]
516    #[ensures(exists<t: Ghost<&mut T>> inv(t) && t.protocol(this.public()) && f.postcondition_once((t,), result))]
517    pub fn open<'a, A>(
518        this: Ghost<&'a Self>,
519        tokens: Ghost<Tokens<'a>>,
520        f: impl FnOnce(Ghost<&'a mut T>) -> A,
521    ) -> A {
522        f(Ghost::conjure())
523    }
524
525    /// Open the invariant to get the data stored inside, immutably.
526    /// This allows reentrant access to the invariant.
527    #[trusted]
528    #[requires(tokens.contains(self.namespace()))]
529    #[ensures(result.protocol(self.public()))]
530    #[check(ghost)]
531    pub fn open_const<'a>(&'a self, tokens: &'a Tokens) -> &'a T {
532        panic!("Should not be called outside ghost code")
533    }
534}