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