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