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