Skip to main content

vstd/
invariant.rs

1#[allow(unused_imports)]
2use super::pervasive::*;
3#[allow(unused_imports)]
4use super::prelude::*;
5
6// TODO:
7//  * utility for conveniently creating unique namespaces
8
9// An invariant storing objects of type V needs to be able to have some kind of configurable
10// predicate `V -> bool`. However, doing this naively with a fully configurable
11// predicate function would result in V being reject_recursive_types,
12// which is too limiting and prevents important use cases with recursive types.
13
14//
15// Instead, we allow the user to specify a predicate which is fixed *at the type level*
16// which we do through this trait, InvariantPredicate. However, the predicate still
17// needs to be "dynamically configurable" upon the call to the invariant constructor.
18// To support this, we add another type parameter K, a constant is fixed for a given
19// Invariant object.
20//
21// So each Invariant object has 3 type parameters:
22//  * K - A "constant" which is specified at constructor time
23//  * V - Type of the stored 'tracked' object
24//  * Pred: InvariantPredicate - provides the predicate (K, V) -> bool
25//
26// With this setup, we can now declare both K and V without reject_recursive_types.
27// To be sure, note that the following, based on our trait formalism,
28// is well-formed CIC (Coq), without any type polarity issues:
29//
30// ```
31//    Inductive InvariantPredicate K V :=
32//        | inv_pred : (K -> V -> bool) -> InvariantPredicate K V.
33//
34//    Inductive Inv (K V: Type) (x: InvariantPredicate K V) :=
35//      | inv : K -> Inv K V x.
36//
37//    Definition some_predicate (V: Type) : InvariantPredicate nat V :=
38//      inv_pred nat V (fun k v => false). (* an arbitrary predicate *)
39//
40//    (* example recursive type *)
41//    Inductive T :=
42//      | A : (Inv nat T (some_predicate T)) -> T.
43// ```
44//
45// Note that the user can always just set K to be `V -> bool` in order to make the
46// Invariant's predicate maximally configurable without having to restrict it at the
47// type level. By doing so, the user opts in to the negative usage of V in exchange
48// for the flexibility.
49
50verus! {
51
52/// Trait used to specify an _invariant predicate_ for
53/// [`LocalInvariant`] and [`AtomicInvariant`].
54pub trait InvariantPredicate<K, V> {
55    spec fn inv(k: K, v: V) -> bool;
56}
57
58} // verus!
59// LocalInvariant is NEVER `Sync`.
60//
61// Furthermore, for either type:
62//
63//  * If an Invariant<T> is Sync, then T must be Send
64//      * We could put the T in an Invariant, sync the invariant to another thread,
65//        and then extract the T, having effectively send it to the other thread.
66//  * If Invariant<T> is Send, then T must be Send
67//      * We could put the T in an Invariant, send the invariant to another thread,
68//        and then take the T out.
69//
70// So the Sync/Send-ness of the Invariant depends on the Send-ness of T;
71// however, the Sync-ness of T is unimportant (the invariant doesn't give you an extra
72// ability to share a reference to a T across threads).
73//
74// In conclusion, we should have:
75//
76//    T                   AtomicInvariant<T>  LocalInvariant<T>
77//
78//    {}          ==>     {}                  {}
79//    Send        ==>     Send+Sync           Send
80//    Sync        ==>     {}                  {}
81//    Sync+Send   ==>     Send+Sync           Send
82/// An `AtomicInvariant` is a ghost object that provides "interior mutability"
83/// for ghost objects, specifically, for `tracked` ghost objects.
84/// A reference `&AtomicInvariant` may be shared between clients.
85/// A client holding such a reference may _open_ the invariant
86/// to obtain ghost ownership of `v1: V`, and then _close_ the invariant by returning
87/// ghost ownership of a (potentially) different object `v2: V`.
88///
89/// An `AtomicInvariant` implements [`Sync`](https://doc.rust-lang.org/std/sync/)
90/// and may be shared between threads.
91/// However, this means that an `AtomicInvariant` can be only opened for
92/// the duration of a single _sequentially consistent atomic_ operation.
93/// Such operations are provided by our [`PAtomic`](crate::atomic) library.
94/// For an invariant object without this atomicity restriction,
95/// see [`LocalInvariant`], which gives up thread safety in exchange.
96///
97/// An `AtomicInvariant` consists of:
98///
99///  * A _predicate_ specified via the `InvariantPredicate` type bound, that determines
100///    what values `V` may be saved inside the invariant.
101///  * A _constant_ `K`, specified at construction type. The predicate function takes
102///    this constant as a parameter, so the constant allows users to dynamically configure
103///    the predicate function in a way that can't be done at the type level.
104///  * A _namespace_. This is a bit of a technicality, and you can often just declare
105///    it as an arbitrary integer with no issues. See the [`open_local_invariant!`]
106///    documentation for more details.
107///
108/// The constant and namespace are specified at construction time ([`AtomicInvariant::new`]).
109/// These values are fixed for the lifetime of the `AtomicInvariant` object.
110/// To open the invariant and access the stored object `V`,
111/// use the macro [`open_atomic_invariant!`].
112///
113/// The `AtomicInvariant` API is an instance of the ["invariant" method in Verus's general philosophy on interior mutability](https://verus-lang.github.io/verus/guide/interior_mutability.html).
114///
115/// **Note:** Rather than using `AtomicInvariant` directly, we generally recommend
116/// using the [`atomic_ghost` APIs](crate::atomic_ghost).
117#[cfg_attr(verus_keep_ghost, verifier::proof)]
118#[cfg_attr(verus_keep_ghost, verifier::external_body)] /* vattr */
119#[cfg_attr(verus_keep_ghost, verifier::accept_recursive_types(K))]
120#[cfg_attr(verus_keep_ghost, verifier::accept_recursive_types(V))]
121#[cfg_attr(verus_keep_ghost, verifier::accept_recursive_types(Pred))]
122pub struct AtomicInvariant<K, V, Pred> {
123    dummy: super::prelude::SyncSendIfSend<V>,
124    dummy1: super::prelude::AlwaysSyncSend<(K, Pred, *mut V)>,
125}
126
127/// A `LocalInvariant` is a ghost object that provides "interior mutability"
128/// for ghost objects, specifically, for `tracked` ghost objects.
129/// A reference `&LocalInvariant` may be shared between clients.
130/// A client holding such a reference may _open_ the invariant
131/// to obtain ghost ownership of `v1: V`, and then _close_ the invariant by returning
132/// ghost ownership of a (potentially) different object `v2: V`.
133///
134/// A `LocalInvariant` cannot be shared between threads
135/// (that is, it does not implement [`Sync`](https://doc.rust-lang.org/std/sync/)).
136/// However, this means that a `LocalInvariant` can be opened for an indefinite length
137/// of time, since there is no risk of a race with another thread.
138/// For an invariant object with the opposite properties, see [`AtomicInvariant`].
139///
140/// A `LocalInvariant` consists of:
141///
142///  * A _predicate_ specified via the `InvariantPredicate` type bound, that determines
143///    what values `V` may be saved inside the invariant.
144///  * A _constant_ `K`, specified at construction type. The predicate function takes
145///    this constant as a parameter, so the constant allows users to dynamically configure
146///    the predicate function in a way that can't be done at the type level.
147///  * A _namespace_. This is a bit of a technicality, and you can often just declare
148///    it as an arbitrary integer with no issues. See the [`open_local_invariant!`]
149///    documentation for more details.
150///
151/// The constant and namespace are specified at construction time ([`LocalInvariant::new`]).
152/// These values are fixed for the lifetime of the `LocalInvariant` object.
153/// To open the invariant and access the stored object `V`,
154/// use the macro [`open_local_invariant!`].
155///
156/// The `LocalInvariant` API is an instance of the ["invariant" method in Verus's general philosophy on interior mutability](https://verus-lang.github.io/verus/guide/interior_mutability.html).
157
158#[cfg_attr(verus_keep_ghost, verifier::proof)]
159#[cfg_attr(verus_keep_ghost, verifier::external_body)] /* vattr */
160#[cfg_attr(verus_keep_ghost, verifier::accept_recursive_types(K))]
161#[cfg_attr(verus_keep_ghost, verifier::accept_recursive_types(V))]
162#[cfg_attr(verus_keep_ghost, verifier::accept_recursive_types(Pred))]
163pub struct LocalInvariant<K, V, Pred> {
164    dummy: super::prelude::SendIfSend<V>,
165    dummy1: super::prelude::AlwaysSyncSend<(K, Pred, *mut V)>,
166}
167
168// redundant, just makes the error msg a bit nicer
169#[cfg(verus_keep_ghost)]
170impl<K, V, Pred> !Sync for LocalInvariant<K, V, Pred> {}
171
172macro_rules! declare_invariant_impl {
173    ($invariant:ident => $selfid:ident => $($into_inner_clause:tt)*) => {
174        // note the path names of `inv` and `namespace` are harcoded into the VIR crate.
175
176        verus!{
177
178        impl<K, V, Pred> $invariant<K, V, Pred> {
179            /// The constant specified upon the initialization of this `
180            #[doc = stringify!($invariant)]
181            ///`.
182            pub uninterp spec fn constant(&self) -> K;
183
184            /// Namespace the invariant was declared in.
185            #[rustc_diagnostic_item = concat!("verus::vstd::invariant::", stringify!($invariant), "::namespace")]
186            pub uninterp spec fn namespace(&self) -> int;
187        }
188
189        impl<K, V, Pred: InvariantPredicate<K, V>> $invariant<K, V, Pred> {
190            /// Returns `true` if it is possible to store the value `v` into the `
191            #[doc = stringify!($invariant)]
192            ///`.
193            ///
194            /// This is equivalent to `Pred::inv(self.constant(), v)`.
195
196            #[rustc_diagnostic_item = concat!("verus::vstd::invariant::", stringify!($invariant), "::inv")]
197            pub open spec fn inv(&self, v: V) -> bool {
198                Pred::inv(self.constant(), v)
199            }
200
201            /// Initialize a new `
202            #[doc = stringify!($invariant)]
203            ///` with constant `k`. initial stored (tracked) value `v`,
204            /// and in the namespace `ns`.
205
206            pub axiom fn new(k: K, tracked v: V, ns: int) -> (tracked i: $invariant<K, V, Pred>)
207                requires
208                    Pred::inv(k, v),
209                ensures
210                    i.constant() == k,
211                    i.namespace() == ns;
212
213            // Q. Why does AtomicInvariant::into_inner have an opens_invariant clause
214            // while LocalInvariant::into_inner doesn't?
215            //
216            // A. It has to do with the way we prevent double-opening via into_inner,
217            // i.e., how we prevent the user from calling into_inner on an already-open
218            // invariant:
219            //
220            // open_{atomic|local}_invariant!(&inv => i => {
221            //     inv.into_inner(); // this should error
222            // }
223            //
224            // There are two broad approaches:
225            // 1. Use the mask-checking, treating into_inner the same as an invariant-open
226            // 2. Use lifetimes, ensuring the borrow used to open the block extends through
227            //    the entire block.
228            //
229            // Approach (1) is easier to use, but (1) is not sound for LocalInvariants.
230            // Thus, we use approach (1) for AtomicInvariants and (2) for LocalInvariants.
231            //
232            // Q. Why is approach (1) easier for AtomicInvariants?
233            //
234            // A. This makes it easier to perform an atomic operation that relinquishes
235            //    the permission to access the same atomic.
236            //
237            // Q. Why is approach (1) unsound for LocalInvariants?
238            //
239            // A. Because into_inner is not the only problem. We want to implement
240            // the bound `impl<T: Send> => LocalInvariant<T>: Send`.
241            // Furthermore, moving a local invariant to another thread basically means moving
242            // the invariant from one thread's invariant pool to another, which basically
243            // needs the same restrictions as `into_inner`.
244            //
245            // open_{atomic|local}_invariant!(&inv => i => {
246            //     send_to_another_thread(inv); // this must be disallowed, too
247            // }
248            //
249            // However, we cannot (easily) put a mask bound on the send operation.
250            //
251            // Q. How are the lifetime restrictions implemented for LocalInvariant?
252            // The short answer is that we use the lifetime argument of the InvariantBlockGuard
253            // and force it to remain alive for the duration of the open_local_invariant! block.
254            // However, this requires special support from Verus in the lifetime-erasure system.
255            // See rustc_mir_build_additional_files/verus_builder.rs for more information.
256
257            /// Destroys the `
258            #[doc = stringify!($invariant)]
259            ///`, returning the tracked value contained within.
260
261            pub axiom fn into_inner(tracked $selfid) -> (tracked v: V)
262                ensures $selfid.inv(v),
263                $($into_inner_clause)* ;
264        }
265
266        }
267    };
268}
269
270declare_invariant_impl!(AtomicInvariant => self => opens_invariants [ self.namespace() ] );
271declare_invariant_impl!(LocalInvariant => self => );
272
273#[doc(hidden)]
274#[cfg_attr(verus_keep_ghost, verifier::proof)]
275pub struct InvariantBlockGuard<'a>(core::marker::PhantomData<&'a ()>);
276
277// In the "Logical Paradoxes" section of the Iris 4.1 Reference
278// (`https://plv.mpi-sws.org/iris/appendix-4.1.pdf`), they show that
279// opening invariants carries the risk of unsoundness.
280//
281// The paradox is similar to "Landin's knot", a short program that implements
282// an infinite loop by combining two features: higher-order closures
283// and mutable state:
284//
285//    let r := new_ref();
286//    r := () -> {
287//        let f = !r;
288//        f();
289//    };
290//    let f = !r;
291//    f();
292//
293// Invariants effectively serve as "mutable state"
294// Therefore, in order to implement certain higher-order features
295// like "proof closures" or "dyn", we need to make sure we have an
296// answer to this paradox.
297//
298// One solution to
299// this, described in the paper "Later Credits: Resourceful Reasoning
300// for the Later Modality" by Spies et al. (available at
301// `https://plv.mpi-sws.org/later-credits/paper-later-credits.pdf`) is
302// to use "later credits". That is, require the expenditure of a later
303// credit, only obtainable in exec mode, when opening an invariant. So
304// we require the relinquishment of a tracked
305// `OpenInvariantCredit` to open an invariant, and we provide an
306// exec-mode function `create_open_invariant_credit` to obtain one.
307
308verus! {
309
310#[doc(hidden)]
311#[cfg_attr(verus_keep_ghost, verifier::proof)]
312#[verifier::external_body]
313pub struct OpenInvariantCredit {}
314
315// It's intentional that `create_open_invariant_credit` uses `exec` mode. This prevents
316// creation of an infinite number of credits to open invariants infinitely often.
317#[cfg_attr(verus_keep_ghost, rustc_diagnostic_item = "verus::vstd::invariant::create_open_invariant_credit")]
318#[verifier::external_body]
319#[inline(always)]
320pub fn create_open_invariant_credit() -> Tracked<OpenInvariantCredit>
321    opens_invariants none
322    no_unwind
323{
324    Tracked::<OpenInvariantCredit>::assume_new()
325}
326
327#[cfg(verus_keep_ghost)]
328#[rustc_diagnostic_item = "verus::vstd::invariant::spend_open_invariant_credit_in_proof"]
329#[doc(hidden)]
330#[inline(always)]
331pub proof fn spend_open_invariant_credit_in_proof(tracked credit: OpenInvariantCredit) {
332}
333
334#[cfg_attr(verus_keep_ghost, rustc_diagnostic_item = "verus::vstd::invariant::spend_open_invariant_credit")]
335#[doc(hidden)]
336#[inline(always)]
337pub fn spend_open_invariant_credit(
338    #[allow(unused_variables)]
339    credit: Tracked<OpenInvariantCredit>,
340)
341    opens_invariants none
342    no_unwind
343{
344    proof {
345        spend_open_invariant_credit_in_proof(credit.get());
346    }
347}
348
349} // verus!
350// NOTE: These 3 methods are removed in the conversion to VIR; they are only used
351// for encoding and borrow-checking.
352// In the VIR these are all replaced by the OpenInvariant block.
353// This means that the bodies, preconditions, and even their modes are not important.
354//
355// An example usage of the macro is like
356//
357//   i: AtomicInvariant<X>
358//
359//   open_invariant!(&i => inner => {
360//      { modify `inner` here }
361//   });
362//
363//  where `inner` will have type `X`.
364//
365// Why does this use the 'static type param for the InvariantBlockGuard? This is because
366// AtomicInvariant doesn't need the lifetime-checking like LocalInvariant does. See the explanation
367// over `into_inner`.
368#[cfg(verus_keep_ghost)]
369#[rustc_diagnostic_item = "verus::vstd::invariant::open_atomic_invariant_begin"]
370#[doc(hidden)]
371#[verifier::external] /* vattr */
372pub fn open_atomic_invariant_begin<'a, K, V, Pred: InvariantPredicate<K, V>>(
373    _inv: &'a AtomicInvariant<K, V, Pred>,
374) -> (InvariantBlockGuard<'static>, V) {
375    unimplemented!();
376}
377
378#[cfg(verus_keep_ghost)]
379#[rustc_diagnostic_item = "verus::vstd::invariant::open_local_invariant_begin"]
380#[doc(hidden)]
381#[verifier::external] /* vattr */
382pub fn open_local_invariant_begin<'a, K, V, Pred: InvariantPredicate<K, V>>(
383    _inv: &'a LocalInvariant<K, V, Pred>,
384) -> (InvariantBlockGuard<'a>, V) {
385    unimplemented!();
386}
387
388#[cfg(verus_keep_ghost)]
389#[rustc_diagnostic_item = "verus::vstd::invariant::open_invariant_end"]
390#[doc(hidden)]
391#[verifier::external] /* vattr */
392pub fn open_invariant_end<V>(_guard: InvariantBlockGuard, _v: V) {
393    unimplemented!();
394}
395
396/// Macro used to temporarily "open" an [`AtomicInvariant`] object, obtaining the stored
397/// value within.
398///
399/// ### Usage
400///
401/// The form of the macro looks like,
402///
403/// ```rust
404/// open_atomic_invariant($inv => $id => {
405///     // Inner scope
406/// });
407/// ```
408///
409/// This operation is very similar to [`open_local_invariant!`], so we refer to its
410/// documentation for the basics. There is only one difference, besides
411/// the fact that `$inv` should be an [`&AtomicInvariant`](AtomicInvariant)
412/// rather than a [`&LocalInvariant`](LocalInvariant).
413/// The difference is that `open_atomic_invariant!` has an additional _atomicity constraint_:
414///
415///  * **Atomicity constraint**: The code body of an `open_atomic_invariant!` block
416///    cannot contain any `exec`-mode code with the exception of a _single_ atomic operation.
417///
418/// (Of course, the code block can still contain an arbitrary amount of ghost code.)
419///
420/// The atomicity constraint is needed because an `AtomicInvariant` must be thread-safe;
421/// that is, it can be shared across threads. In order for the ghost state to be shared
422/// safely, it must be restored after each atomic operation.
423///
424/// The atomic operations may be found in the [`PAtomic`](crate::atomic) library.
425/// The user can also mark their own functions as "atomic operations" using
426/// `#[verifier::atomic)]`; however, this is not useful for very much other than defining
427/// wrappers around the existing atomic operations from [`PAtomic`](crate::atomic).
428/// Note that reading and writing through a [`PCell`](crate::cell::PCell)
429/// or a [`PPtr`](crate::simple_pptr::PPtr) are _not_ atomic operations.
430///
431/// **Note:** Rather than using `open_atomic_invariant!` directly, we generally recommend
432/// using the [`atomic_ghost` APIs](crate::atomic_ghost).
433///
434/// It's not legal to use `open_atomic_invariant!` in proof mode. In proof mode, you need
435/// to use `open_atomic_invariant_in_proof!` instead. This takes one extra parameter,
436/// an open-invariant credit, which you can get by calling
437/// `create_open_invariant_credit()` before you enter proof mode.
438
439/// ### Example
440///
441/// TODO fill this in
442
443// TODO the `$eexpr` argument here should be macro'ed in ghost context, not exec
444
445#[macro_export]
446macro_rules! open_atomic_invariant {
447    [$($tail:tt)*] => {
448        #[allow(unexpected_cfgs)] // make sure client crates don't see "unexpected `cfg` condition name: `verus_...`"
449        {
450            $crate::vstd::prelude::verus_exec_inv_macro_exprs!(
451                $crate::vstd::invariant::open_atomic_invariant_internal!($crate::vstd::invariant::create_open_invariant_credit() => $($tail)*)
452            )
453        }
454    };
455}
456
457#[macro_export]
458macro_rules! open_atomic_invariant_in_proof {
459    [$($tail:tt)*] => {
460        $crate::vstd::prelude::verus_ghost_inv_macro_exprs!($crate::vstd::invariant::open_atomic_invariant_in_proof_internal!($($tail)*))
461    };
462}
463
464#[macro_export]
465macro_rules! open_atomic_invariant_internal {
466    ($credit_expr:expr => $eexpr:expr => $iident:ident => $bblock:block) => {
467        #[cfg_attr(verus_keep_ghost, verifier::invariant_block)] /* vattr */ {
468            #[cfg(verus_keep_ghost_body)]
469            $crate::vstd::invariant::spend_open_invariant_credit($credit_expr);
470            #[cfg(verus_keep_ghost_body)]
471            #[allow(unused_mut)] let (guard, mut $iident) =
472                $crate::vstd::invariant::open_atomic_invariant_begin($eexpr);
473            $bblock
474            #[cfg(verus_keep_ghost_body)]
475            $crate::vstd::invariant::open_invariant_end(guard, $iident);
476        }
477    }
478}
479
480#[macro_export]
481macro_rules! open_atomic_invariant_in_proof_internal {
482    ($credit_expr:expr => $eexpr:expr => $iident:ident => $bblock:block) => {
483        #[cfg_attr(verus_keep_ghost, verifier::invariant_block)] /* vattr */ {
484            #[cfg(verus_keep_ghost_body)]
485            $crate::vstd::invariant::spend_open_invariant_credit_in_proof($credit_expr);
486            #[cfg(verus_keep_ghost_body)]
487            #[allow(unused_mut)] let (guard, mut $iident) =
488                $crate::vstd::invariant::open_atomic_invariant_begin($eexpr);
489            $bblock
490            #[cfg(verus_keep_ghost_body)]
491            $crate::vstd::invariant::open_invariant_end(guard, $iident);
492        }
493    }
494}
495
496pub use open_atomic_invariant;
497pub use open_atomic_invariant_in_proof;
498#[doc(hidden)]
499pub use open_atomic_invariant_in_proof_internal;
500#[doc(hidden)]
501pub use open_atomic_invariant_internal;
502
503/// Macro used to temporarily "open" a [`LocalInvariant`] object, obtaining the stored
504/// value within.
505///
506/// ### Usage
507///
508/// The form of the macro looks like,
509///
510/// ```rust
511/// open_local_invariant($inv => $id => {
512///     // Inner scope
513/// });
514/// ```
515///
516/// The operation of opening an invariant is a ghost one; however, the inner code block
517/// may contain arbitrary `exec`-mode code. The invariant remains "open" for the duration
518/// of the inner code block, and it is closed again of the end of the block.
519///
520/// The `$inv` parameter should be an expression of type `&LocalInvariant<K, V, Pred>`,
521/// the invariant object to be opened. The `$id` is an identifier which is bound within
522/// the code block as a `mut` variable of type `V`. This gives the user ownership over
523/// the `V` value, which they may manipulate freely within the code block. At the end
524/// of the code block, the variable `$id` is consumed.
525///
526/// The obtained object `v: V`, will satisfy the `LocalInvariant`'s invariant predicate
527/// [`$inv.inv(v)`](LocalInvariant::inv). Furthermore, the user must prove that this
528/// invariant still holds at the end. In other words, the macro usage is
529/// roughly equivalent to the following:
530///
531/// ```rust
532/// {
533///     let $id: V = /* an arbitrary value */;
534///     assume($inv.inv($id));
535///     /* user code block here */
536///     assert($inv.inv($id));
537///     consume($id);
538/// }
539/// ```
540///
541/// ### Avoiding Reentrancy
542///
543/// Verus adds additional checks to ensure that an invariant is never opened
544/// more than once at the same time. For example, suppose that you attempt to nest
545/// the use of `open_invariant`, supplying the same argument `inv` to each:
546///
547/// ```rust
548/// open_local_invariant(inv => id1 => {
549///     open_local_invariant(inv => id2 => {
550///     });
551/// });
552/// ```
553///
554/// In this situation, Verus would produce an error:
555///
556/// ```
557/// error: possible invariant collision
558///   |
559///   |   open_local_invariant!(&inv => id1 => {
560///   |                           ^ this invariant
561///   |       open_local_invariant!(&inv => id2 => {
562///   |                               ^ might be the same as this invariant
563///   ...
564///   |       }
565///   |   }
566/// ```
567///
568/// When generating these conditions, Verus compares invariants via their
569/// [`namespace()`](LocalInvariant::namespace) values.
570/// An invariant's namespace (represented simply as an integer)
571/// is specified upon the call to [`LocalInvariant::new`].
572/// If you have the need to open multiple invariants at once, make sure to given
573/// them different namespaces.
574///
575/// So that Verus can ensure that there are no nested invariant accesses across function
576/// boundaries, every `proof` and `exec` function has, as part of its specification,
577/// the set of invariant namespaces that it might open.
578///
579/// The invariant set of a function can be specified via the [`opens_invariants` clause](https://verus-lang.github.io/verus/guide/reference-opens-invariants.html).
580/// The default for an `exec`-mode function is to open any, while the default
581/// for a `proof`-mode function is to open none.
582///
583/// It's not legal to use `open_local_invariant!` in proof mode. In proof mode, you need
584/// to use `open_local_invariant_in_proof!` instead. This takes one extra parameter,
585/// an open-invariant credit, which you can get by calling
586/// `create_open_invariant_credit()` before you enter proof mode.
587///
588/// ### Example
589///
590/// TODO fill this in
591///
592/// ### More Examples
593///
594/// TODO fill this in
595
596#[macro_export]
597macro_rules! open_local_invariant {
598    [$($tail:tt)*] => {
599        #[allow(unexpected_cfgs)] // make sure client crates don't see "unexpected `cfg` condition name: `verus_...`"
600        {
601            #[cfg(verus_keep_ghost_body)]
602            let credit = $crate::vstd::invariant::create_open_invariant_credit();
603            $crate::vstd::prelude::verus_exec_inv_macro_exprs!(
604                $crate::vstd::invariant::open_local_invariant_internal!(credit => $($tail)*))
605        }
606    };
607}
608
609#[macro_export]
610macro_rules! open_local_invariant_in_proof {
611    [$($tail:tt)*] => {
612        $crate::vstd::prelude::verus_ghost_inv_macro_exprs!($crate::vstd::invariant::open_local_invariant_in_proof_internal!($($tail)*))
613    };
614}
615
616#[macro_export]
617macro_rules! open_local_invariant_internal {
618    ($credit_expr:expr => $eexpr:expr => $iident:ident => $bblock:block) => {
619        #[cfg_attr(verus_keep_ghost, verifier::invariant_block)] /* vattr */ {
620            #[cfg(verus_keep_ghost_body)]
621            $crate::vstd::invariant::spend_open_invariant_credit($credit_expr);
622            #[cfg(verus_keep_ghost_body)]
623            #[allow(unused_mut)] let (guard, mut $iident) = $crate::vstd::invariant::open_local_invariant_begin($eexpr);
624            $bblock
625            #[cfg(verus_keep_ghost_body)]
626            $crate::vstd::invariant::open_invariant_end(guard, $iident);
627        }
628    }
629}
630
631#[macro_export]
632macro_rules! open_local_invariant_in_proof_internal {
633    ($credit_expr:expr => $eexpr:expr => $iident:ident => $bblock:block) => {
634        #[cfg_attr(verus_keep_ghost, verifier::invariant_block)] /* vattr */ {
635            #[cfg(verus_keep_ghost_body)]
636            $crate::vstd::invariant::spend_open_invariant_credit_in_proof($credit_expr);
637            #[cfg(verus_keep_ghost_body)]
638            #[allow(unused_mut)] let (guard, mut $iident) = $crate::vstd::invariant::open_local_invariant_begin($eexpr);
639            $bblock
640            #[cfg(verus_keep_ghost_body)]
641            $crate::vstd::invariant::open_invariant_end(guard, $iident);
642        }
643    }
644}
645
646pub use open_local_invariant;
647pub use open_local_invariant_in_proof;
648#[doc(hidden)]
649pub use open_local_invariant_in_proof_internal;
650#[doc(hidden)]
651pub use open_local_invariant_internal;