creusot_contracts/ghost/local_invariant.rs
1//! Define local invariants.
2//!
3//! [Local invariants](LocalInvariant) are not the same as [type invariants](Invariant): they
4//! allow the use of a shared piece of data to be used in the invariant (see
5//! [`Protocol`]), but in return they impose a much more restricted access to
6//! the underlying data, as well as the use of [`Tokens`].
7//!
8//! # Example
9//!
10//! Building a simplified `Cell`, that only asserts its content's type invariant.
11//! ```
12//! # use creusot_contracts::{
13//! # cell::{PermCell, PermCellOwn},
14//! # ghost::local_invariant::{LocalInvariant, Protocol, Tokens, declare_namespace},
15//! # logic::Id,
16//! # prelude::*,
17//! # };
18//! declare_namespace! { PERMCELL }
19//!
20//! /// A cell that simply asserts its content's type invariant.
21//! pub struct CellInv<T: Invariant> {
22//! data: PermCell<T>,
23//! permission: Ghost<LocalInvariant<PermCellLocalInv<T>>>,
24//! }
25//! impl<T: Invariant> Invariant for CellInv<T> {
26//! #[logic]
27//! fn invariant(self) -> bool {
28//! self.permission.namespace() == PERMCELL() && self.permission.public() == self.data.id()
29//! }
30//! }
31//!
32//! struct PermCellLocalInv<T>(PermCellOwn<T>);
33//! impl<T: Invariant> Protocol for PermCellLocalInv<T> {
34//! type Public = Id;
35//!
36//! #[logic]
37//! fn protocol(self, id: Id) -> bool {
38//! self.0.id() == id
39//! }
40//! }
41//!
42//! impl<T: Invariant> CellInv<T> {
43//! #[requires(tokens.contains(PERMCELL()))]
44//! pub fn write(&self, x: T, tokens: Ghost<Tokens>) {
45//! LocalInvariant::open(self.permission.borrow(), tokens, move |perm| unsafe {
46//! *self.data.borrow_mut(ghost!(&mut perm.into_inner().0)) = x
47//! })
48//! }
49//! }
50//! ```
51//!
52//! # Explicit tokens
53//!
54//! For now, [`Tokens`] must be explicitely passed to [`open`](LocalInvariant::open).
55//! We plan to relax this limitation at some point.
56
57use crate::{logic::Set, prelude::*};
58use std::{cell::UnsafeCell, marker::PhantomData};
59
60/// Declare a new namespace.
61///
62/// # Example
63///
64/// ```rust
65/// use creusot_contracts::{ghost::local_invariant::{declare_namespace, Namespace}, logic::Set, prelude::*};
66/// declare_namespace! { A }
67///
68/// #[requires(ns.contains(A()))]
69/// fn foo(ns: Ghost<&mut Set<Namespace>>) { /* ... */ }
70/// ```
71pub use base_macros::declare_namespace;
72
73/// The type of _namespaces_ of associated with local invariants.
74///
75/// Can be declared with the [`declare_namespace`] macro, and then attached to a local
76/// invariant when creating it with [`LocalInvariant::new`].
77#[intrinsic("namespace")]
78pub struct Namespace(());
79
80/// Invariant tokens.
81///
82/// This is given at the start of the program, and must be passed along to
83/// [LocalInvariant::open] to prevent opening invariant reentrantly.
84///
85/// # Tokens and `open`
86///
87/// Tokens are used to avoid reentrency in [`open`](LocalInvariant::open).
88/// To ensure this, `Tokens` acts as a special kind of mutable borrow : only
89/// one may exist at a given point in the program, preventing multiple calls to
90/// `open` with the same namespace. This is the reason this type has a lifetime
91/// attached to it.
92///
93/// Note that after the execution of `open`, the token that was used is
94/// restored. Because of this, we never need to talk about the 'final' value
95/// of this borrow, because it never differs from the current value (in places
96/// where we can use it).
97///
98/// To help passing it into functions, it may be [reborrowed](Self::reborrow),
99/// similarly to a normal borrow.
100#[opaque]
101pub struct Tokens<'a>(PhantomData<&'a ()>);
102
103impl Tokens<'_> {
104 /// Get the underlying set of namespaces of this token.
105 ///
106 /// Also accessible via the [`view`](View::view) (`@`) operator.
107 #[logic(opaque)]
108 pub fn namespaces(self) -> Set<Namespace> {
109 dead
110 }
111
112 /// Get the tokens for all the namespaces.
113 ///
114 /// This is only callable _once_, in `main`.
115 #[trusted]
116 #[ensures(forall<ns: Namespace> result.contains(ns))]
117 #[intrinsic("tokens_new")]
118 #[check(ghost)]
119 pub fn new() -> Ghost<Self> {
120 Ghost::conjure()
121 }
122
123 /// Reborrow the token, allowing it to be reused later.
124 ///
125 /// # Example
126 /// ```
127 /// # use creusot_contracts::{ghost::local_invariant::Tokens, prelude::*};
128 /// fn foo(tokens: Ghost<Tokens>) {}
129 /// fn bar(tokens: Ghost<Tokens>) {}
130 /// fn baz(mut tokens: Ghost<Tokens>) {
131 /// foo(ghost!(tokens.reborrow()));
132 /// bar(tokens);
133 /// }
134 /// ```
135 #[trusted]
136 #[ensures(result == *self && ^self == *self)]
137 #[check(ghost)]
138 pub fn reborrow<'a>(&'a mut self) -> Tokens<'a> {
139 Tokens(PhantomData)
140 }
141
142 /// Split the tokens in two, so that it can be used to access independant invariants.
143 ///
144 /// # Example
145 ///
146 /// ```
147 /// # use creusot_contracts::{ghost::local_invariant::{declare_namespace, Tokens}, prelude::*};
148 /// declare_namespace! { FOO }
149 /// declare_namespace! { BAR }
150 ///
151 /// // the lifetime 'locks' the namespace
152 /// #[requires(tokens.contains(FOO()))]
153 /// fn foo<'a>(tokens: Ghost<Tokens<'a>>) -> &'a i32 {
154 /// # todo!()
155 /// // access some invariant to get the reference
156 /// }
157 /// #[requires(tokens.contains(BAR()))]
158 /// fn bar(tokens: Ghost<Tokens>) {}
159 ///
160 /// #[requires(tokens.contains(FOO()) && tokens.contains(BAR()))]
161 /// fn baz(mut tokens: Ghost<Tokens>) -> i32 {
162 /// let (ns_foo, ns_bar) = ghost!(tokens.split(snapshot!(FOO()))).split();
163 /// let x = foo(ns_foo);
164 /// bar(ns_bar);
165 /// *x
166 /// }
167 /// ```
168 #[trusted]
169 #[requires(self.contains(*ns))]
170 #[ensures(^self == *self)]
171 #[ensures(result.0.contains(*ns))]
172 #[ensures(result.1.namespaces() == self.namespaces().remove(*ns))]
173 #[check(ghost)]
174 #[allow(unused_variables)]
175 pub fn split<'a>(&'a mut self, ns: Snapshot<Namespace>) -> (Tokens<'a>, Tokens<'a>) {
176 (Tokens(PhantomData), Tokens(PhantomData))
177 }
178
179 #[logic(open)]
180 pub fn contains(self, namespace: Namespace) -> bool {
181 self.namespaces().contains(namespace)
182 }
183}
184
185impl View for Tokens<'_> {
186 type ViewTy = Set<Namespace>;
187 #[logic(open, inline)]
188 fn view(self) -> Set<Namespace> {
189 self.namespaces()
190 }
191}
192
193/// A ghost structure, that holds a piece of data (`T`) together with an
194/// [protocol](Protocol).
195#[opaque]
196pub struct LocalInvariant<T: Protocol> {
197 value: UnsafeCell<T>,
198}
199
200/// A variant of [`Invariant`] for use in [`LocalInvariant`]s.
201///
202/// This allows to specify an invariant that depends on some [public data](LocalInvariant::public).
203pub trait Protocol {
204 type Public;
205
206 #[logic(prophetic)]
207 fn protocol(self, data: Self::Public) -> bool;
208}
209
210/// Define method call syntax for [`LocalInvariant::open`].
211pub trait LocalInvariantExt<'a> {
212 type Inner: 'a;
213
214 /// Alias for [`LocalInvariant::open`], to use method call syntax (`inv.open(...)`).
215 #[requires(false)]
216 fn open<A, F>(self, tokens: Ghost<Tokens<'a>>, f: F) -> A
217 where
218 F: FnOnce(Ghost<&'a mut Self::Inner>) -> A;
219}
220
221impl<'a, T: Protocol> LocalInvariantExt<'a> for Ghost<&'a LocalInvariant<T>> {
222 type Inner = T;
223
224 #[requires(tokens.contains(self.namespace()))]
225 #[requires(forall<t: Ghost<&mut T>> (**t).protocol(self.public()) && inv(t) ==>
226 f.precondition((t,)) &&
227 // f must restore the invariant
228 (forall<res: A> f.postcondition_once((t,), res) ==> (^t).protocol(self.public())))]
229 #[ensures(exists<t: Ghost<&mut T>> t.protocol(self.public()) && f.postcondition_once((t,), result))]
230 fn open<A, F>(self, tokens: Ghost<Tokens<'a>>, f: F) -> A
231 where
232 F: FnOnce(Ghost<&'a mut Self::Inner>) -> A,
233 {
234 LocalInvariant::open(self, tokens, f)
235 }
236}
237
238impl<'a, T> LocalInvariantExt<'a> for Ghost<&'a T>
239where
240 T: std::ops::Deref,
241 Ghost<&'a T::Target>: LocalInvariantExt<'a>,
242{
243 type Inner = <Ghost<&'a T::Target> as LocalInvariantExt<'a>>::Inner;
244
245 #[requires(T::deref.precondition((*self,)))]
246 #[requires(forall<this> T::deref.postcondition((*self,), this) ==>
247 <Ghost<&'a T::Target> as LocalInvariantExt<'a>>::open.precondition((Ghost::new_logic(this), tokens, f))
248 )]
249 #[ensures(exists<this> T::deref.postcondition((*self,), this) &&
250 <Ghost<&'a T::Target> as LocalInvariantExt<'a>>::open.postcondition((Ghost::new_logic(this), tokens, f), result)
251 )]
252 fn open<A, F>(self, tokens: Ghost<Tokens<'a>>, f: F) -> A
253 where
254 F: FnOnce(Ghost<&'a mut Self::Inner>) -> A,
255 {
256 let this: Ghost<&T::Target> = ghost!(&self);
257 this.open(tokens, f)
258 }
259}
260
261impl<'a, L> LocalInvariantExt<'a> for &'a Ghost<L>
262where
263 Ghost<&'a L>: LocalInvariantExt<'a>,
264{
265 type Inner = <Ghost<&'a L> as LocalInvariantExt<'a>>::Inner;
266
267 #[requires(<Ghost<&'a L> as LocalInvariantExt<'a>>::open.precondition((Ghost::new_logic(&**self), tokens, f)))]
268 #[ensures(<Ghost<&'a L> as LocalInvariantExt<'a>>::open.postcondition((Ghost::new_logic(&**self), tokens, f), result))]
269 fn open<A, F>(self, tokens: Ghost<Tokens<'a>>, f: F) -> A
270 where
271 F: FnOnce(Ghost<&'a mut Self::Inner>) -> A,
272 {
273 self.borrow().open(tokens, f)
274 }
275}
276
277impl<T: Protocol> LocalInvariant<T> {
278 /// Construct a `LocalInvariant`
279 ///
280 /// # Parameters
281 /// - `value`: the actual data contained in the invariant. Use [`Self::open`] to
282 /// access it. Also called the 'private' part of the invariant.
283 /// - `public`: the 'public' part of the invariant.
284 /// - `namespace`: the namespace of the invariant.
285 /// This is required to avoid [open](Self::open)ing the same invariant twice.
286 #[trusted]
287 #[requires(value.protocol(*public))]
288 #[ensures(result.public() == *public)]
289 #[ensures(result.namespace() == *namespace)]
290 #[check(ghost)]
291 pub fn new(
292 value: Ghost<T>,
293 #[allow(unused)] public: Snapshot<T::Public>,
294 #[allow(unused)] namespace: Snapshot<Namespace>,
295 ) -> Ghost<Self> {
296 ghost!(Self { value: UnsafeCell::new(value.into_inner()) })
297 }
298
299 /// Get the namespace associated with this invariant.
300 #[logic(opaque)]
301 pub fn namespace(self) -> Namespace {
302 dead
303 }
304
305 /// Get the 'public' part of this invariant.
306 #[logic(opaque)]
307 pub fn public(self) -> T::Public {
308 dead
309 }
310
311 /// Open the invariant to get the data stored inside.
312 ///
313 /// This will call the closure `f` with the inner data. You must restore the
314 /// contained [`Protocol`] before returning from the closure.
315 #[trusted]
316 #[requires(tokens.contains(this.namespace()))]
317 #[requires(forall<t: Ghost<&mut T>> t.protocol(this.public()) && inv(t) ==>
318 f.precondition((t,)) &&
319 // f must restore the invariant
320 (forall<res: A> f.postcondition_once((t,), res) ==> (^t).protocol(this.public())))]
321 #[ensures(exists<t: Ghost<&mut T>> t.protocol(this.public()) && f.postcondition_once((t,), result))]
322 pub fn open<'a, A>(
323 this: Ghost<&'a Self>,
324 tokens: Ghost<Tokens<'a>>,
325 f: impl FnOnce(Ghost<&'a mut T>) -> A,
326 ) -> A {
327 let _ = tokens;
328 // SAFETY: this operation happens in a ghost block, meaning it only
329 // make sense when compiling with creusot: in this case, Creusot will
330 // make sure that this is in fact safe.
331 let borrow = ghost!(unsafe { &mut *this.value.get() });
332 f(borrow)
333 }
334
335 /// Open the invariant to get the data stored inside, immutably.
336 /// This allows reentrant access to the invariant.
337 #[trusted]
338 #[requires(tokens.contains(self.namespace()))]
339 #[ensures(result.protocol(self.public()))]
340 #[check(ghost)]
341 pub fn open_const<'a>(&'a self, tokens: &'a Tokens) -> &'a T {
342 let _ = tokens;
343 unsafe { &*self.value.get() }
344 }
345}