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