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) => {
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 // TODO: the opens_invariants condition can be removed for LocalInvariant
214
215 /// Destroys the `
216 #[doc = stringify!($invariant)]
217 ///`, returning the tracked value contained within.
218
219 pub axiom fn into_inner(#[verifier::proof] self) -> (tracked v: V)
220 ensures self.inv(v),
221 opens_invariants [ self.namespace() ];
222 }
223
224 }
225 };
226}
227
228declare_invariant_impl!(AtomicInvariant);
229declare_invariant_impl!(LocalInvariant);
230
231#[doc(hidden)]
232#[cfg_attr(verus_keep_ghost, verifier::proof)]
233pub struct InvariantBlockGuard<'a>(core::marker::PhantomData<&'a ()>);
234
235// In the "Logical Paradoxes" section of the Iris 4.1 Reference
236// (`https://plv.mpi-sws.org/iris/appendix-4.1.pdf`), they show that
237// opening invariants carries the risk of unsoundness.
238//
239// The paradox is similar to "Landin's knot", a short program that implements
240// an infinite loop by combining two features: higher-order closures
241// and mutable state:
242//
243// let r := new_ref();
244// r := () -> {
245// let f = !r;
246// f();
247// };
248// let f = !r;
249// f();
250//
251// Invariants effectively serve as "mutable state"
252// Therefore, in order to implement certain higher-order features
253// like "proof closures" or "dyn", we need to make sure we have an
254// answer to this paradox.
255//
256// One solution to
257// this, described in the paper "Later Credits: Resourceful Reasoning
258// for the Later Modality" by Spies et al. (available at
259// `https://plv.mpi-sws.org/later-credits/paper-later-credits.pdf`) is
260// to use "later credits". That is, require the expenditure of a later
261// credit, only obtainable in exec mode, when opening an invariant. So
262// we require the relinquishment of a tracked
263// `OpenInvariantCredit` to open an invariant, and we provide an
264// exec-mode function `create_open_invariant_credit` to obtain one.
265
266verus! {
267
268#[doc(hidden)]
269#[cfg_attr(verus_keep_ghost, verifier::proof)]
270#[verifier::external_body]
271pub struct OpenInvariantCredit {}
272
273// It's intentional that `create_open_invariant_credit` uses `exec` mode. This prevents
274// creation of an infinite number of credits to open invariants infinitely often.
275#[cfg_attr(verus_keep_ghost, rustc_diagnostic_item = "verus::vstd::invariant::create_open_invariant_credit")]
276#[verifier::external_body]
277#[inline(always)]
278pub fn create_open_invariant_credit() -> Tracked<OpenInvariantCredit>
279 opens_invariants none
280 no_unwind
281{
282 Tracked::<OpenInvariantCredit>::assume_new()
283}
284
285#[cfg(verus_keep_ghost)]
286#[rustc_diagnostic_item = "verus::vstd::invariant::spend_open_invariant_credit_in_proof"]
287#[doc(hidden)]
288#[inline(always)]
289pub proof fn spend_open_invariant_credit_in_proof(tracked credit: OpenInvariantCredit) {
290}
291
292#[cfg_attr(verus_keep_ghost, rustc_diagnostic_item = "verus::vstd::invariant::spend_open_invariant_credit")]
293#[doc(hidden)]
294#[inline(always)]
295pub fn spend_open_invariant_credit(
296 #[allow(unused_variables)]
297 credit: Tracked<OpenInvariantCredit>,
298)
299 opens_invariants none
300 no_unwind
301{
302 proof {
303 spend_open_invariant_credit_in_proof(credit.get());
304 }
305}
306
307} // verus!
308// NOTE: These 3 methods are removed in the conversion to VIR; they are only used
309// for encoding and borrow-checking.
310// In the VIR these are all replaced by the OpenInvariant block.
311// This means that the bodies, preconditions, and even their modes are not important.
312//
313// An example usage of the macro is like
314//
315// i: AtomicInvariant<X>
316//
317// open_invariant!(&i => inner => {
318// { modify `inner` here }
319// });
320//
321// where `inner` will have type `X`.
322#[cfg(verus_keep_ghost)]
323#[rustc_diagnostic_item = "verus::vstd::invariant::open_atomic_invariant_begin"]
324#[doc(hidden)]
325#[verifier::external] /* vattr */
326pub fn open_atomic_invariant_begin<'a, K, V, Pred: InvariantPredicate<K, V>>(
327 _inv: &'a AtomicInvariant<K, V, Pred>,
328) -> (InvariantBlockGuard<'static>, V) {
329 unimplemented!();
330}
331
332#[cfg(verus_keep_ghost)]
333#[rustc_diagnostic_item = "verus::vstd::invariant::open_local_invariant_begin"]
334#[doc(hidden)]
335#[verifier::external] /* vattr */
336pub fn open_local_invariant_begin<'a, K, V, Pred: InvariantPredicate<K, V>>(
337 _inv: &'a LocalInvariant<K, V, Pred>,
338) -> (InvariantBlockGuard<'a>, V) {
339 unimplemented!();
340}
341
342#[cfg(verus_keep_ghost)]
343#[rustc_diagnostic_item = "verus::vstd::invariant::open_invariant_end"]
344#[doc(hidden)]
345#[verifier::external] /* vattr */
346pub fn open_invariant_end<V>(_guard: InvariantBlockGuard, _v: V) {
347 unimplemented!();
348}
349
350/// Macro used to temporarily "open" an [`AtomicInvariant`] object, obtaining the stored
351/// value within.
352///
353/// ### Usage
354///
355/// The form of the macro looks like,
356///
357/// ```rust
358/// open_atomic_invariant($inv => $id => {
359/// // Inner scope
360/// });
361/// ```
362///
363/// This operation is very similar to [`open_local_invariant!`], so we refer to its
364/// documentation for the basics. There is only one difference, besides
365/// the fact that `$inv` should be an [`&AtomicInvariant`](AtomicInvariant)
366/// rather than a [`&LocalInvariant`](LocalInvariant).
367/// The difference is that `open_atomic_invariant!` has an additional _atomicity constraint_:
368///
369/// * **Atomicity constraint**: The code body of an `open_atomic_invariant!` block
370/// cannot contain any `exec`-mode code with the exception of a _single_ atomic operation.
371///
372/// (Of course, the code block can still contain an arbitrary amount of ghost code.)
373///
374/// The atomicity constraint is needed because an `AtomicInvariant` must be thread-safe;
375/// that is, it can be shared across threads. In order for the ghost state to be shared
376/// safely, it must be restored after each atomic operation.
377///
378/// The atomic operations may be found in the [`PAtomic`](crate::atomic) library.
379/// The user can also mark their own functions as "atomic operations" using
380/// `#[verifier::atomic)]`; however, this is not useful for very much other than defining
381/// wrappers around the existing atomic operations from [`PAtomic`](crate::atomic).
382/// Note that reading and writing through a [`PCell`](crate::cell::PCell)
383/// or a [`PPtr`](crate::simple_pptr::PPtr) are _not_ atomic operations.
384///
385/// **Note:** Rather than using `open_atomic_invariant!` directly, we generally recommend
386/// using the [`atomic_ghost` APIs](crate::atomic_ghost).
387///
388/// It's not legal to use `open_atomic_invariant!` in proof mode. In proof mode, you need
389/// to use `open_atomic_invariant_in_proof!` instead. This takes one extra parameter,
390/// an open-invariant credit, which you can get by calling
391/// `create_open_invariant_credit()` before you enter proof mode.
392
393/// ### Example
394///
395/// TODO fill this in
396
397// TODO the `$eexpr` argument here should be macro'ed in ghost context, not exec
398
399#[macro_export]
400macro_rules! open_atomic_invariant {
401 [$($tail:tt)*] => {
402 #[allow(unexpected_cfgs)] // make sure client crates don't see "unexpected `cfg` condition name: `verus_...`"
403 {
404 $crate::vstd::prelude::verus_exec_inv_macro_exprs!(
405 $crate::vstd::invariant::open_atomic_invariant_internal!($crate::vstd::invariant::create_open_invariant_credit() => $($tail)*)
406 )
407 }
408 };
409}
410
411#[macro_export]
412macro_rules! open_atomic_invariant_in_proof {
413 [$($tail:tt)*] => {
414 $crate::vstd::prelude::verus_ghost_inv_macro_exprs!($crate::vstd::invariant::open_atomic_invariant_in_proof_internal!($($tail)*))
415 };
416}
417
418#[macro_export]
419macro_rules! open_atomic_invariant_internal {
420 ($credit_expr:expr => $eexpr:expr => $iident:ident => $bblock:block) => {
421 #[cfg_attr(verus_keep_ghost, verifier::invariant_block)] /* vattr */ {
422 #[cfg(verus_keep_ghost_body)]
423 $crate::vstd::invariant::spend_open_invariant_credit($credit_expr);
424 #[cfg(verus_keep_ghost_body)]
425 #[allow(unused_mut)] let (guard, mut $iident) =
426 $crate::vstd::invariant::open_atomic_invariant_begin($eexpr);
427 $bblock
428 #[cfg(verus_keep_ghost_body)]
429 $crate::vstd::invariant::open_invariant_end(guard, $iident);
430 }
431 }
432}
433
434#[macro_export]
435macro_rules! open_atomic_invariant_in_proof_internal {
436 ($credit_expr:expr => $eexpr:expr => $iident:ident => $bblock:block) => {
437 #[cfg_attr(verus_keep_ghost, verifier::invariant_block)] /* vattr */ {
438 #[cfg(verus_keep_ghost_body)]
439 $crate::vstd::invariant::spend_open_invariant_credit_in_proof($credit_expr);
440 #[cfg(verus_keep_ghost_body)]
441 #[allow(unused_mut)] let (guard, mut $iident) =
442 $crate::vstd::invariant::open_atomic_invariant_begin($eexpr);
443 $bblock
444 #[cfg(verus_keep_ghost_body)]
445 $crate::vstd::invariant::open_invariant_end(guard, $iident);
446 }
447 }
448}
449
450pub use open_atomic_invariant;
451pub use open_atomic_invariant_in_proof;
452#[doc(hidden)]
453pub use open_atomic_invariant_in_proof_internal;
454#[doc(hidden)]
455pub use open_atomic_invariant_internal;
456
457/// Macro used to temporarily "open" a [`LocalInvariant`] object, obtaining the stored
458/// value within.
459///
460/// ### Usage
461///
462/// The form of the macro looks like,
463///
464/// ```rust
465/// open_local_invariant($inv => $id => {
466/// // Inner scope
467/// });
468/// ```
469///
470/// The operation of opening an invariant is a ghost one; however, the inner code block
471/// may contain arbitrary `exec`-mode code. The invariant remains "open" for the duration
472/// of the inner code block, and it is closed again of the end of the block.
473///
474/// The `$inv` parameter should be an expression of type `&LocalInvariant<K, V, Pred>`,
475/// the invariant object to be opened. The `$id` is an identifier which is bound within
476/// the code block as a `mut` variable of type `V`. This gives the user ownership over
477/// the `V` value, which they may manipulate freely within the code block. At the end
478/// of the code block, the variable `$id` is consumed.
479///
480/// The obtained object `v: V`, will satisfy the `LocalInvariant`'s invariant predicate
481/// [`$inv.inv(v)`](LocalInvariant::inv). Furthermore, the user must prove that this
482/// invariant still holds at the end. In other words, the macro usage is
483/// roughly equivalent to the following:
484///
485/// ```rust
486/// {
487/// let $id: V = /* an arbitrary value */;
488/// assume($inv.inv($id));
489/// /* user code block here */
490/// assert($inv.inv($id));
491/// consume($id);
492/// }
493/// ```
494///
495/// ### Avoiding Reentrancy
496///
497/// Verus adds additional checks to ensure that an invariant is never opened
498/// more than once at the same time. For example, suppose that you attempt to nest
499/// the use of `open_invariant`, supplying the same argument `inv` to each:
500///
501/// ```rust
502/// open_local_invariant(inv => id1 => {
503/// open_local_invariant(inv => id2 => {
504/// });
505/// });
506/// ```
507///
508/// In this situation, Verus would produce an error:
509///
510/// ```
511/// error: possible invariant collision
512/// |
513/// | open_local_invariant!(&inv => id1 => {
514/// | ^ this invariant
515/// | open_local_invariant!(&inv => id2 => {
516/// | ^ might be the same as this invariant
517/// ...
518/// | }
519/// | }
520/// ```
521///
522/// When generating these conditions, Verus compares invariants via their
523/// [`namespace()`](LocalInvariant::namespace) values.
524/// An invariant's namespace (represented simply as an integer)
525/// is specified upon the call to [`LocalInvariant::new`].
526/// If you have the need to open multiple invariants at once, make sure to given
527/// them different namespaces.
528///
529/// So that Verus can ensure that there are no nested invariant accesses across function
530/// boundaries, every `proof` and `exec` function has, as part of its specification,
531/// the set of invariant namespaces that it might open.
532///
533/// 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).
534/// The default for an `exec`-mode function is to open any, while the default
535/// for a `proof`-mode function is to open none.
536///
537/// It's not legal to use `open_local_invariant!` in proof mode. In proof mode, you need
538/// to use `open_local_invariant_in_proof!` instead. This takes one extra parameter,
539/// an open-invariant credit, which you can get by calling
540/// `create_open_invariant_credit()` before you enter proof mode.
541///
542/// ### Example
543///
544/// TODO fill this in
545///
546/// ### More Examples
547///
548/// TODO fill this in
549
550#[macro_export]
551macro_rules! open_local_invariant {
552 [$($tail:tt)*] => {
553 #[allow(unexpected_cfgs)] // make sure client crates don't see "unexpected `cfg` condition name: `verus_...`"
554 {
555 #[cfg(verus_keep_ghost_body)]
556 let credit = $crate::vstd::invariant::create_open_invariant_credit();
557 $crate::vstd::prelude::verus_exec_inv_macro_exprs!(
558 $crate::vstd::invariant::open_local_invariant_internal!(credit => $($tail)*))
559 }
560 };
561}
562
563#[macro_export]
564macro_rules! open_local_invariant_in_proof {
565 [$($tail:tt)*] => {
566 $crate::vstd::prelude::verus_ghost_inv_macro_exprs!($crate::vstd::invariant::open_local_invariant_in_proof_internal!($($tail)*))
567 };
568}
569
570#[macro_export]
571macro_rules! open_local_invariant_internal {
572 ($credit_expr:expr => $eexpr:expr => $iident:ident => $bblock:block) => {
573 #[cfg_attr(verus_keep_ghost, verifier::invariant_block)] /* vattr */ {
574 #[cfg(verus_keep_ghost_body)]
575 $crate::vstd::invariant::spend_open_invariant_credit($credit_expr);
576 #[cfg(verus_keep_ghost_body)]
577 #[allow(unused_mut)] let (guard, mut $iident) = $crate::vstd::invariant::open_local_invariant_begin($eexpr);
578 $bblock
579 #[cfg(verus_keep_ghost_body)]
580 $crate::vstd::invariant::open_invariant_end(guard, $iident);
581 }
582 }
583}
584
585#[macro_export]
586macro_rules! open_local_invariant_in_proof_internal {
587 ($credit_expr:expr => $eexpr:expr => $iident:ident => $bblock:block) => {
588 #[cfg_attr(verus_keep_ghost, verifier::invariant_block)] /* vattr */ {
589 #[cfg(verus_keep_ghost_body)]
590 $crate::vstd::invariant::spend_open_invariant_credit_in_proof($credit_expr);
591 #[cfg(verus_keep_ghost_body)]
592 #[allow(unused_mut)] let (guard, mut $iident) = $crate::vstd::invariant::open_local_invariant_begin($eexpr);
593 $bblock
594 #[cfg(verus_keep_ghost_body)]
595 $crate::vstd::invariant::open_invariant_end(guard, $iident);
596 }
597 }
598}
599
600pub use open_local_invariant;
601pub use open_local_invariant_in_proof;
602#[doc(hidden)]
603pub use open_local_invariant_in_proof_internal;
604#[doc(hidden)]
605pub use open_local_invariant_internal;