hydro_lang/properties/mod.rs
1//! Types for reasoning about algebraic properties for Rust closures.
2
3use std::marker::PhantomData;
4
5use stageleft::properties::Property;
6
7use crate::live_collections::boundedness::Boundedness;
8use crate::live_collections::keyed_singleton::KeyedSingletonBound;
9use crate::live_collections::singleton::SingletonBound;
10use crate::live_collections::stream::{ExactlyOnce, Ordering, Retries, TotalOrder};
11use crate::sim_hooks::{OnProcess, OrderingHook};
12
13/// A trait for proof mechanisms that can validate commutativity.
14///
15/// `T` and `B` name the element type and boundedness of the stream the commutative
16/// function consumes. The simulator does not trust commutativity proofs — it still
17/// explores the input ordering — so a proof may carry an [`OrderingHook`] for scripting
18/// that exploration, surfaced through [`Self::take_hook`]. `S` is the hook's
19/// [scope](crate::sim_hooks#hook-scopes).
20#[sealed::sealed]
21pub trait CommutativeProof<T, B: Boundedness, S = OnProcess> {
22 /// Registers the expression with the proof mechanism.
23 ///
24 /// This should not perform any blocking analysis; it is only intended to record the expression for later processing.
25 fn register_proof(&self, expr: &syn::Expr);
26
27 /// Takes the simulator ordering hook attached to this proof, if any.
28 fn take_hook(&mut self) -> Option<OrderingHook<T, B, ExactlyOnce, S>>;
29}
30
31/// A trait for proof mechanisms that can validate idempotence.
32#[sealed::sealed]
33pub trait IdempotentProof {
34 /// Registers the expression with the proof mechanism.
35 ///
36 /// This should not perform any blocking analysis; it is only intended to record the expression for later processing.
37 fn register_proof(&self, expr: &syn::Expr);
38}
39
40/// A trait for proof mechanisms that can validate monotonicity.
41#[sealed::sealed]
42pub trait MonotoneProof {
43 /// Registers the expression with the proof mechanism.
44 ///
45 /// This should not perform any blocking analysis; it is only intended to record the expression for later processing.
46 fn register_proof(&self, expr: &syn::Expr);
47}
48
49/// A trait for proof mechanisms that can validate order-preservation (monotonicity of a map function).
50#[sealed::sealed]
51pub trait OrderPreservingProof {
52 /// Registers the expression with the proof mechanism.
53 ///
54 /// This should not perform any blocking analysis; it is only intended to record the expression for later processing.
55 fn register_proof(&self, expr: &syn::Expr);
56}
57
58/// A trait for proof mechanisms that can validate consistency of a collection.
59#[sealed::sealed]
60pub trait ConsistencyProof {}
61
62/// A hand-written human proof of the correctness property.
63///
64/// To create a manual proof, use the [`manual_proof!`] macro, which takes in a doc comment
65/// explaining why the property holds.
66///
67/// Manual proofs are not trusted by the simulator, which still explores the guarded
68/// non-determinism. `H` is the simulator hook payload (like [`crate::nondet::NonDet`]) so a
69/// commutativity proof can carry an ordering hook for scripting that exploration.
70pub struct ManualProof<H = ()> {
71 hook: H,
72}
73
74impl<H> ManualProof<H> {
75 #[doc(hidden)]
76 pub fn unhooked() -> Self
77 where
78 H: Default,
79 {
80 ManualProof { hook: H::default() }
81 }
82}
83
84impl<T, B: Boundedness, S> ManualProof<Option<OrderingHook<T, B, ExactlyOnce, S>>> {
85 #[doc(hidden)]
86 pub fn hooked(hook: impl Into<Option<OrderingHook<T, B, ExactlyOnce, S>>>) -> Self {
87 ManualProof { hook: hook.into() }
88 }
89}
90
91#[sealed::sealed]
92impl<T, B: Boundedness, S> CommutativeProof<T, B, S>
93 for ManualProof<Option<OrderingHook<T, B, ExactlyOnce, S>>>
94{
95 fn register_proof(&self, _expr: &syn::Expr) {}
96
97 fn take_hook(&mut self) -> Option<OrderingHook<T, B, ExactlyOnce, S>> {
98 self.hook.take()
99 }
100}
101
102#[sealed::sealed]
103impl<T, B: Boundedness, S> CommutativeProof<T, B, S> for ManualProof {
104 fn register_proof(&self, _expr: &syn::Expr) {}
105
106 fn take_hook(&mut self) -> Option<OrderingHook<T, B, ExactlyOnce, S>> {
107 None
108 }
109}
110#[sealed::sealed]
111impl IdempotentProof for ManualProof {
112 fn register_proof(&self, _expr: &syn::Expr) {}
113}
114#[sealed::sealed]
115impl MonotoneProof for ManualProof {
116 fn register_proof(&self, _expr: &syn::Expr) {}
117}
118#[sealed::sealed]
119impl OrderPreservingProof for ManualProof {
120 fn register_proof(&self, _expr: &syn::Expr) {}
121}
122#[sealed::sealed]
123impl ConsistencyProof for ManualProof {}
124
125/// A machine-checked proof of **commutativity**, verified by [Verus](https://verus-lang.github.io/verus/guide/).
126///
127/// Created by the [`verus_proof_commutative_fold!`], [`verus_proof_commutative_map!`],
128/// [`verus_proof_commutative_filter!`], and [`verus_proof_commutative_effect!`] macros,
129/// one per closure shape, each generating the precise obligation that makes reordering
130/// unobservable for that shape (final accumulator, output multiset, retained multiset,
131/// or captured state, respectively). The obligation is generated by the macro directly
132/// from the *actual closure body* — it symbolically executes the body in both orders
133/// (`x` then `y`, and `y` then `x`) from equal initial states and requires the
134/// observable results to be equal — so users never write (and cannot weaken) the
135/// assertion. Users only declare the types involved and, optionally, provide a proof
136/// *script* to help the SMT solver, which is itself checked by Verus.
137///
138/// When the crate is verified with `cargo verus verify`, Verus checks the obligation
139/// (which also proves the closure body panic-free, e.g. no arithmetic overflow). Under
140/// normal compilation, the proof is erased and this type simply marks the property as
141/// proven. This type only implements [`CommutativeProof`], so it cannot be used to
142/// fulfill a different property (e.g. `idempotent = ...`; see [`VerusIdempotentProof`]).
143pub struct VerusCommutativeProof {
144 _private: (),
145}
146
147impl VerusCommutativeProof {
148 #[doc(hidden)]
149 pub fn new() -> Self {
150 VerusCommutativeProof { _private: () }
151 }
152}
153
154impl Default for VerusCommutativeProof {
155 fn default() -> Self {
156 Self::new()
157 }
158}
159
160#[sealed::sealed]
161impl<T, B: Boundedness, S> CommutativeProof<T, B, S> for VerusCommutativeProof {
162 fn register_proof(&self, _expr: &syn::Expr) {}
163
164 fn take_hook(&mut self) -> Option<OrderingHook<T, B, ExactlyOnce, S>> {
165 // Verus proofs are still not trusted by the simulator, which explores the
166 // input ordering on its own; no scripting hook is attached.
167 None
168 }
169}
170
171/// A machine-checked proof of **idempotence**, verified by [Verus](https://verus-lang.github.io/verus/guide/).
172///
173/// Created by the [`verus_proof_idempotent_fold!`], [`verus_proof_idempotent_reduce!`],
174/// [`verus_proof_idempotent_map!`], [`verus_proof_idempotent_filter!`], and
175/// [`verus_proof_idempotent_effect!`] macros, one per closure shape, each generating the
176/// precise obligation that makes a retried (duplicated) element unobservable for that
177/// shape (final accumulator, re-emitted output, retention decision, or captured state,
178/// respectively). The obligation is generated by the macro directly from the *actual
179/// closure body* — it symbolically executes the body on an element once, and on the same
180/// element twice in a row, from equal initial states, and requires the observable
181/// results to be equal — so users never write (and cannot weaken) the assertion. Users
182/// only declare the types involved and, optionally, provide a proof *script* to help the
183/// SMT solver, which is itself checked by Verus.
184///
185/// Combined with a commutativity proof (required on unordered streams), the obligation
186/// also covers duplicates that are not adjacent to the original element, since
187/// commutativity allows moving the duplicate next to it.
188///
189/// When the crate is verified with `cargo verus verify`, Verus checks the obligation
190/// (which also proves the closure body panic-free, e.g. no arithmetic overflow). Under
191/// normal compilation, the proof is erased and this type simply marks the property as
192/// proven. Note that the simulator does not inject retries, so (unlike commutativity)
193/// idempotence claims are not explored by simulation tests at all; a Verus proof is the
194/// only mechanical check. This type only implements [`IdempotentProof`], so it cannot be
195/// used to fulfill a different property (e.g. `commutative = ...`).
196pub struct VerusIdempotentProof {
197 _private: (),
198}
199
200impl VerusIdempotentProof {
201 #[doc(hidden)]
202 pub fn new() -> Self {
203 VerusIdempotentProof { _private: () }
204 }
205}
206
207impl Default for VerusIdempotentProof {
208 fn default() -> Self {
209 Self::new()
210 }
211}
212
213#[sealed::sealed]
214impl IdempotentProof for VerusIdempotentProof {
215 fn register_proof(&self, _expr: &syn::Expr) {}
216}
217
218#[doc(inline)]
219pub use crate::__manual_proof__ as manual_proof;
220
221// Verus only sees items declared inside `verus!`, so under the Verus driver (with the
222// `verus` feature) the function backing `verus_panic!` is declared there.
223// `external_body`: Verus trusts the signature without checking the body (which calls
224// `panic!`, whose vstd specification is `requires false`). The signature has no
225// precondition and the default unwind specification (may unwind), so callers need not
226// prove anything, and since the return type is `!`, nothing after the call is reachable.
227#[cfg(all(verus_keep_ghost, feature = "verus"))]
228vstd::prelude::verus! {
229 #[doc(hidden)]
230 #[verifier::external_body]
231 pub fn __verus_panic() -> ! {
232 panic!("verus_panic! was reached")
233 }
234}
235
236#[doc(inline)]
237pub use crate::__verus_panic__ as verus_panic;
238
239#[cfg(all(verus_keep_ghost, feature = "verus"))]
240#[doc(hidden)]
241#[macro_export]
242macro_rules! __verus_panic__ {
243 // The message arguments are dropped: Verus does not support `format_args!`, and they
244 // would only be evaluated on a path that panics anyway.
245 ($($arg:tt)*) => {
246 $crate::properties::__verus_panic()
247 };
248}
249
250#[cfg(not(all(verus_keep_ghost, feature = "verus")))]
251#[macro_export]
252/// Panics, in a way that **Verus proofs of algebraic properties treat as an acceptable
253/// outcome**. Takes the same arguments as [`panic!`].
254///
255/// A Verus proof (e.g. [`verus_proof_commutative_fold!`]) normally requires the closure body
256/// to be panic-free for every input, which rejects closures such as `*acc += x` (which
257/// panics on overflow) even though they are commutative whenever they complete. Calling
258/// `verus_panic!(...)` instead tells Verus that this path ends the program: the property
259/// only has to hold for executions that do **not** reach it. That matches Hydro's
260/// requirement, since a panicking closure crashes the process rather than producing a
261/// result.
262///
263/// The typical use is to guard an operation that could otherwise panic, so that Verus
264/// knows it cannot fail on the paths that continue:
265///
266/// ```rust,ignore
267/// q!(
268/// |acc, x| {
269/// if *acc > u32::MAX - x {
270/// verus_panic!("sum overflowed");
271/// }
272/// *acc += x; // cannot overflow here, so Verus accepts it
273/// },
274/// commutative = verus_proof_commutative_fold!(acc = u32, item = u32)
275/// )
276/// ```
277///
278/// This is sound because the guard really executes: the closure panics at runtime whenever
279/// the guard condition holds, so a guard that is stricter than necessary is still correct.
280/// A guard that is too weak is caught by Verus, which reports the operation that may still
281/// panic. Unlike `assume(...)` in a proof script, nothing is taken on faith beyond the fact
282/// that `verus_panic!` never returns.
283///
284/// Under normal compilation, this forwards its arguments to [`panic!`]. When verifying
285/// with `cargo verus verify`, the `verus` feature of `hydro_lang` must be enabled; Verus
286/// then sees a call to a function that never returns (the message arguments are not
287/// evaluated by the proof).
288macro_rules! __verus_panic__ {
289 ($($arg:tt)*) => {
290 ::core::panic!($($arg)*)
291 };
292}
293
294#[macro_export]
295/// Fulfills a proof parameter by declaring a human-written justification for why
296/// the algebraic property (e.g. commutativity, idempotence) holds.
297///
298/// The argument must be a doc comment explaining why the property is satisfied.
299///
300/// # Examples
301/// ```rust
302/// # #[cfg(feature = "deploy")] {
303/// # use hydro_lang::prelude::*;
304/// # use hydro_lang::live_collections::stream::NoOrder;
305/// # use futures::StreamExt;
306/// # tokio_test::block_on(hydro_lang::test_util::stream_transform_test(|process| {
307/// # let stream = process.source_iter(q!(vec![1, 2, 3])).weaken_ordering::<NoOrder>();
308/// // stream: [1, 2, 3] (unordered)
309/// stream
310/// .fold(
311/// q!(|| 0),
312/// q!(
313/// |acc, x| *acc += x,
314/// commutative = manual_proof!(/** integer addition is commutative */)
315/// ),
316/// )
317/// .into_stream()
318/// # }, |mut stream| async move {
319/// # assert_eq!(stream.next().await.unwrap(), 6);
320/// # }));
321/// # }
322/// ```
323/// An optional trailing `hook = ...` argument attaches a **simulator ordering hook** to a
324/// commutativity proof (see `hydro_lang::sim::hooks`). The simulator does not trust manual
325/// proofs — it still explores the input ordering — so the hook lets a simulation test
326/// script that exploration:
327///
328/// ```rust,ignore
329/// commutative = manual_proof!(/** set insert is commutative */ hook = my_ordering_hook)
330/// ```
331macro_rules! __manual_proof__ {
332 (
333 $(#[doc = $doc:expr])+hook =
334 $hook:expr,__target =
335 { $($target:tt)* } $(,__captures = [$($captures:ident),*])? $(,)?
336 ) => {
337 $crate::properties::ManualProof::hooked($hook)
338 };
339 ($(#[doc = $doc:expr])+hook = $hook:expr $(,)?) => {
340 $crate::properties::ManualProof::hooked($hook)
341 };
342 (
343 $(#[doc = $doc:expr])+,__target =
344 { $($target:tt)* } $(,__captures = [$($captures:ident),*])? $(,)?
345 ) => {
346 $crate::properties::ManualProof::<()>::unhooked()
347 };
348 ($(#[doc = $doc:expr])+) => {
349 $crate::properties::ManualProof::<()>::unhooked()
350 };
351}
352
353#[doc(inline)]
354pub use crate::__verus_proof_commutative_effect__ as verus_proof_commutative_effect;
355#[doc(inline)]
356pub use crate::__verus_proof_commutative_filter__ as verus_proof_commutative_filter;
357#[doc(inline)]
358pub use crate::__verus_proof_commutative_fold__ as verus_proof_commutative_fold;
359#[doc(inline)]
360pub use crate::__verus_proof_commutative_map__ as verus_proof_commutative_map;
361
362#[macro_export]
363/// Fulfills a `commutative = ...` proof parameter for an **aggregation closure**
364/// (`fold` / `reduce`, shape `|acc, item|` with `acc: &mut A`) with a **Verus-checked
365/// proof of commutativity**.
366///
367/// Users never write the proof obligation (so they cannot get it wrong): this macro
368/// receives the quoted closure itself from `q!` (as a trailing `__target = { ... }`
369/// argument) and generates a Verus function that symbolically executes the *actual
370/// closure body* in both orders — `x` then `y`, and `y` then `x` — starting from equal
371/// accumulators, and asserts that the final accumulators are equal. Verifying this
372/// obligation also proves the closure body panic-free (e.g. no arithmetic overflow).
373/// Users only declare the accumulator and item types:
374///
375/// ```rust,ignore
376/// batch.reduce(q!(
377/// |curr, new| { if new > *curr { *curr = new; } },
378/// commutative = verus_proof_commutative_fold!(acc = u32, item = u32)
379/// ));
380/// ```
381///
382/// If the closure captures (read-only) variables from its environment, re-declare them
383/// (with their runtime types) in a `captures = |...|` clause; the obligation is then
384/// universally quantified over the capture values, which is sound since any particular
385/// execution uses some fixed value. `q!` also passes the closure's capture list (as a
386/// trailing `__captures = [...]` argument), and the macro checks at compile time that
387/// every capture is declared.
388///
389/// An optional `proof = |state, x, y| { ... }` clause supplies a proof *script* to help
390/// the SMT solver, with ghost bindings for the initial accumulator and the two items
391/// (e.g. `proof = |s, x, y| { assert((s | x) | y == (s | y) | x) by (bit_vector); }`).
392/// The script is checked by Verus and cannot weaken the obligation (though, as anywhere
393/// in Verus, `assume` is an explicit soundness escape hatch).
394///
395/// The proof is gated on `cfg(verus_keep_ghost)`, so it only exists when the crate is
396/// compiled by the Verus driver (`cargo verus verify`). Under normal compilation the
397/// macro just produces a [`VerusCommutativeProof`] marker, so the types go through the
398/// ordinary `commutative = ...` mechanism with zero overhead.
399macro_rules! __verus_proof_commutative_fold__ {
400 (
401 acc = $accty:ty, item = $itemty:ty
402 $(, captures = |$($cap:ident : $capty:ty),* $(,)?|)?
403 $(, proof = |$pstate:ident, $px:ident, $py:ident| { $($proof_body:tt)* })?
404 , __target = { $(move)? |$tacc:ident $(: $taccty:ty)?, $titem:ident $(: $titemty:ty)?| $target_body:expr }
405 , __captures = [$($fv:ident),* $(,)?] $(,)?
406 ) => {{
407 /// Checks that every free variable captured by the closure is declared (with its
408 /// type) in the `captures = |...|` clause: within this function, the *only* names
409 /// in scope are the declared captures and the closure parameters, so an
410 /// undeclared capture fails to resolve.
411 #[allow(unused, reason = "only checks name resolution")]
412 fn __hydro_verus_captures_declared($($($cap: $capty),*)?) {
413 $(let $fv = &$fv;)*
414 }
415 #[cfg(verus_keep_ghost)]
416 const _: () = {
417 #[allow(unused_imports)]
418 use ::vstd::prelude::*;
419
420 ::vstd::prelude::verus! {
421 /// Commutativity obligation, generated by `verus_proof_commutative_fold!`:
422 /// applying the closure body with `x` then `y` must yield the same
423 /// accumulator as applying it with `y` then `x`, from any equal starting
424 /// accumulators, for all items and capture values. Verifying this also
425 /// proves the body panic-free.
426 fn __hydro_verus_commutative(
427 $($($cap: $capty,)*)?
428 __hydro_acc_xy: $accty,
429 __hydro_acc_yx: $accty,
430 __hydro_x_0: $itemty,
431 __hydro_x_1: $itemty,
432 __hydro_y_0: $itemty,
433 __hydro_y_1: $itemty,
434 )
435 requires
436 __hydro_acc_xy == __hydro_acc_yx,
437 __hydro_x_0 == __hydro_x_1,
438 __hydro_y_0 == __hydro_y_1,
439 {
440 $(
441 let ghost $pstate = __hydro_acc_xy;
442 let ghost $px = __hydro_x_0;
443 let ghost $py = __hydro_y_0;
444 )?
445 let mut __hydro_acc_xy = __hydro_acc_xy;
446 let mut __hydro_acc_yx = __hydro_acc_yx;
447 { let $tacc = &mut __hydro_acc_xy; let $titem = __hydro_x_0; let _ = { $target_body }; }
448 { let $tacc = &mut __hydro_acc_xy; let $titem = __hydro_y_0; let _ = { $target_body }; }
449 { let $tacc = &mut __hydro_acc_yx; let $titem = __hydro_y_1; let _ = { $target_body }; }
450 { let $tacc = &mut __hydro_acc_yx; let $titem = __hydro_x_1; let _ = { $target_body }; }
451 $(proof { $($proof_body)* })?
452 assert(__hydro_acc_xy == __hydro_acc_yx);
453 }
454 }
455 };
456
457 $crate::properties::VerusCommutativeProof::new()
458 }};
459 (
460 $($rest:tt)*
461 ) => {{
462 ::core::compile_error!(
463 "verus_proof_commutative_fold! must be used as a property annotation inside `q!(...)` on a `|acc, item| ...` closure, e.g. `commutative = verus_proof_commutative_fold!(acc = u32, item = u32)`"
464 );
465 $crate::properties::VerusCommutativeProof::new()
466 }};
467}
468
469#[macro_export]
470/// Fulfills a `commutative = ...` proof parameter for a **map-like closure**
471/// (shape `|item| -> out`, e.g. for `map`) that may mutate a captured singleton
472/// reference (from [`Singleton::by_mut`]), with a **Verus-checked proof of
473/// commutativity**.
474///
475/// **What commutativity means for a map closure:** processing any two items in either
476/// order, starting from the same captured state, must (1) leave the captured state in
477/// the same final value, *and* (2) produce the same **multiset of output values** (the
478/// outputs may be swapped in order, but not changed). Both conditions are required for
479/// downstream determinism: outputs that depend on the processing order (e.g. emitting a
480/// running total) are **not** commutative even if the state update is.
481///
482/// The commuting state is the mutable capture, declared (with the type behind the
483/// reference) in the required `captures_mut = |state: S|` clause; additional read-only
484/// captures can be declared in a `captures = |...|` clause. `q!` also passes the
485/// closure's capture list (as a trailing `__captures = [...]` argument), and the macro
486/// checks at compile time that every capture is declared. The `item = ...` type must be
487/// written exactly as the closure receives it.
488///
489/// Users never write the proof obligation: this macro receives the quoted closure itself
490/// from `q!` (as a trailing `__target = { ... }` argument) and generates a Verus function
491/// that symbolically executes the *actual closure body* in both orders — `x` then `y`,
492/// and `y` then `x` — from equal captured states, then asserts that the final states are
493/// equal and that the two runs' outputs are equal as a multiset (pairwise or crossed).
494/// Verifying this also proves the body panic-free.
495///
496/// An optional `proof = |state, x, y| { ... }` clause supplies a proof *script* to help
497/// the SMT solver, with ghost bindings for the initial captured state and the two items.
498/// The script is checked by Verus and cannot weaken the obligation.
499///
500/// The proof is gated on `cfg(verus_keep_ghost)`, so it only exists when the crate is
501/// compiled by the Verus driver (`cargo verus verify`). Under normal compilation the
502/// macro just produces a [`VerusCommutativeProof`] marker.
503///
504/// # Example
505/// ```rust,ignore
506/// let count_mut = my_count.by_mut();
507/// stream.map(q!(
508/// |x| {
509/// *count_mut = count_mut.wrapping_add(x);
510/// x // outputting `*count_mut` here would NOT be commutative!
511/// },
512/// commutative = verus_proof_commutative_map!(
513/// item = i32,
514/// captures_mut = |count_mut: i32|
515/// )
516/// ));
517/// ```
518///
519/// [`Singleton::by_mut`]: crate::live_collections::singleton::Singleton::by_mut
520macro_rules! __verus_proof_commutative_map__ {
521 (
522 item = $itemty:ty
523 $(, captures = |$($cap:ident : $capty:ty),* $(,)?|)?
524 , captures_mut = |$mcap:ident : $mcapty:ty $(,)?|
525 $(, proof = |$pstate:ident, $px:ident, $py:ident| { $($proof_body:tt)* })?
526 , __target = { $(move)? |$titem:ident $(: $titemty:ty)?| $target_body:expr }
527 , __captures = [$($fv:ident),* $(,)?] $(,)?
528 ) => {{
529 /// Checks that every free variable captured by the closure is declared (with its
530 /// type) in the `captures = |...|` or `captures_mut = |...|` clauses: within this
531 /// function, the *only* names in scope are the declared captures, so an
532 /// undeclared capture fails to resolve.
533 #[allow(unused, reason = "only checks name resolution")]
534 fn __hydro_verus_captures_declared($($($cap: $capty,)*)? $mcap: $mcapty) {
535 $(let $fv = &$fv;)*
536 }
537
538 #[cfg(verus_keep_ghost)]
539 const _: () = {
540 #[allow(unused_imports)]
541 use ::vstd::prelude::*;
542
543 ::vstd::prelude::verus! {
544 /// Commutativity obligation, generated by `verus_proof_commutative_map!`:
545 /// from any equal starting captured states, processing `x` then `y` must
546 /// yield the same final captured state as processing `y` then `x`, and
547 /// the two runs must produce the same multiset of outputs (pairwise or
548 /// crossed equality). Verifying this also proves the body panic-free.
549 fn __hydro_verus_commutative(
550 $($($cap: $capty,)*)?
551 __hydro_state_xy: $mcapty,
552 __hydro_state_yx: $mcapty,
553 __hydro_x_0: $itemty,
554 __hydro_x_1: $itemty,
555 __hydro_y_0: $itemty,
556 __hydro_y_1: $itemty,
557 )
558 requires
559 __hydro_state_xy == __hydro_state_yx,
560 __hydro_x_0 == __hydro_x_1,
561 __hydro_y_0 == __hydro_y_1,
562 {
563 $(
564 let ghost $pstate = __hydro_state_xy;
565 let ghost $px = __hydro_x_0;
566 let ghost $py = __hydro_y_0;
567 )?
568 let mut __hydro_state_xy = __hydro_state_xy;
569 let mut __hydro_state_yx = __hydro_state_yx;
570 let __hydro_out_xy_x = { let $mcap = &mut __hydro_state_xy; let $titem = __hydro_x_0; $target_body };
571 let __hydro_out_xy_y = { let $mcap = &mut __hydro_state_xy; let $titem = __hydro_y_0; $target_body };
572 let __hydro_out_yx_y = { let $mcap = &mut __hydro_state_yx; let $titem = __hydro_y_1; $target_body };
573 let __hydro_out_yx_x = { let $mcap = &mut __hydro_state_yx; let $titem = __hydro_x_1; $target_body };
574 $(proof { $($proof_body)* })?
575 assert(__hydro_state_xy == __hydro_state_yx);
576 assert(
577 (__hydro_out_xy_x == __hydro_out_yx_x && __hydro_out_xy_y == __hydro_out_yx_y)
578 || (__hydro_out_xy_x == __hydro_out_yx_y && __hydro_out_xy_y == __hydro_out_yx_x)
579 );
580 }
581 }
582 };
583
584 $crate::properties::VerusCommutativeProof::new()
585 }};
586 (
587 $($rest:tt)*
588 ) => {{
589 ::core::compile_error!(
590 "verus_proof_commutative_map! must be used as a property annotation inside `q!(...)` on a `|item| ...` closure with exactly one `captures_mut` entry, e.g. `commutative = verus_proof_commutative_map!(item = u32, captures_mut = |state: u32|)`"
591 );
592 $crate::properties::VerusCommutativeProof::new()
593 }};
594}
595
596#[macro_export]
597/// Fulfills a `commutative = ...` proof parameter for a **filter predicate**
598/// (shape `|item| -> bool` where `item` is received by reference) that may mutate a
599/// captured singleton reference (from [`Singleton::by_mut`]), with a **Verus-checked
600/// proof of commutativity**.
601///
602/// **What commutativity means for a filter predicate:** processing any two items in
603/// either order, starting from the same captured state, must (1) leave the captured
604/// state in the same final value, *and* (2) retain the same **multiset of elements**.
605/// The second condition is essential for soundness: a stateful predicate like a rate
606/// limiter converges to the same state either way, but *which* element passes depends on
607/// the order, which is **not** commutative (unless the elements are equal).
608///
609/// The commuting state is the mutable capture, declared (with the type behind the
610/// reference) in the required `captures_mut = |state: S|` clause; additional read-only
611/// captures can be declared in a `captures = |...|` clause. `q!` also passes the
612/// closure's capture list (as a trailing `__captures = [...]` argument), and the macro
613/// checks at compile time that every capture is declared. The `item = ...` type must be
614/// written exactly as the closure receives it (for `filter`, a reference like `&u32`).
615///
616/// Users never write the proof obligation: this macro receives the quoted closure itself
617/// from `q!` (as a trailing `__target = { ... }` argument) and generates a Verus function
618/// that symbolically executes the *actual predicate body* in both orders — `x` then `y`,
619/// and `y` then `x` — from equal captured states, then asserts that the final states are
620/// equal and that the retained multisets are equal: either the per-item decisions match
621/// across the two orders, or the two items are equal and the number of retained copies
622/// matches. Verifying this also proves the body panic-free.
623///
624/// An optional `proof = |state, x, y| { ... }` clause supplies a proof *script* to help
625/// the SMT solver, with ghost bindings for the initial captured state and the two items.
626/// The script is checked by Verus and cannot weaken the obligation.
627///
628/// The proof is gated on `cfg(verus_keep_ghost)`, so it only exists when the crate is
629/// compiled by the Verus driver (`cargo verus verify`). Under normal compilation the
630/// macro just produces a [`VerusCommutativeProof`] marker.
631///
632/// # Example
633/// ```rust,ignore
634/// let seen_mut = seen_count.by_mut();
635/// stream.filter(q!(
636/// |x| {
637/// *seen_mut = seen_mut.wrapping_add(1);
638/// *x > 1 // the decision must not depend on the mutable state!
639/// },
640/// commutative = verus_proof_commutative_filter!(
641/// item = &u32,
642/// captures_mut = |seen_mut: u32|
643/// )
644/// ));
645/// ```
646///
647/// [`Singleton::by_mut`]: crate::live_collections::singleton::Singleton::by_mut
648macro_rules! __verus_proof_commutative_filter__ {
649 (
650 item = $itemty:ty
651 $(, captures = |$($cap:ident : $capty:ty),* $(,)?|)?
652 , captures_mut = |$mcap:ident : $mcapty:ty $(,)?|
653 $(, proof = |$pstate:ident, $px:ident, $py:ident| { $($proof_body:tt)* })?
654 , __target = { $(move)? |$titem:ident $(: $titemty:ty)?| $target_body:expr }
655 , __captures = [$($fv:ident),* $(,)?] $(,)?
656 ) => {{
657 /// Checks that every free variable captured by the closure is declared (with its
658 /// type) in the `captures = |...|` or `captures_mut = |...|` clauses: within this
659 /// function, the *only* names in scope are the declared captures, so an
660 /// undeclared capture fails to resolve.
661 #[allow(unused, reason = "only checks name resolution")]
662 fn __hydro_verus_captures_declared($($($cap: $capty,)*)? $mcap: $mcapty) {
663 $(let $fv = &$fv;)*
664 }
665
666 #[cfg(verus_keep_ghost)]
667 const _: () = {
668 #[allow(unused_imports)]
669 use ::vstd::prelude::*;
670
671 ::vstd::prelude::verus! {
672 /// Commutativity obligation, generated by
673 /// `verus_proof_commutative_filter!`: from any equal starting captured
674 /// states, processing `x` then `y` must yield the same final captured
675 /// state as processing `y` then `x`, and the retained multisets must be
676 /// equal: either the per-item decisions match across the two orders, or
677 /// the two items are equal and the retained counts match. Verifying this
678 /// also proves the body panic-free.
679 fn __hydro_verus_commutative(
680 $($($cap: $capty,)*)?
681 __hydro_state_xy: $mcapty,
682 __hydro_state_yx: $mcapty,
683 __hydro_x_0: $itemty,
684 __hydro_x_1: $itemty,
685 __hydro_y_0: $itemty,
686 __hydro_y_1: $itemty,
687 )
688 requires
689 __hydro_state_xy == __hydro_state_yx,
690 __hydro_x_0 == __hydro_x_1,
691 __hydro_y_0 == __hydro_y_1,
692 {
693 $(
694 let ghost $pstate = __hydro_state_xy;
695 let ghost $px = __hydro_x_0;
696 let ghost $py = __hydro_y_0;
697 )?
698 let mut __hydro_state_xy = __hydro_state_xy;
699 let mut __hydro_state_yx = __hydro_state_yx;
700 let __hydro_keep_xy_x: bool = { let $mcap = &mut __hydro_state_xy; let $titem = __hydro_x_0; $target_body };
701 let __hydro_keep_xy_y: bool = { let $mcap = &mut __hydro_state_xy; let $titem = __hydro_y_0; $target_body };
702 let __hydro_keep_yx_y: bool = { let $mcap = &mut __hydro_state_yx; let $titem = __hydro_y_1; $target_body };
703 let __hydro_keep_yx_x: bool = { let $mcap = &mut __hydro_state_yx; let $titem = __hydro_x_1; $target_body };
704 $(proof { $($proof_body)* })?
705 assert(__hydro_state_xy == __hydro_state_yx);
706 assert(
707 (__hydro_keep_xy_x == __hydro_keep_yx_x && __hydro_keep_xy_y == __hydro_keep_yx_y)
708 || (__hydro_x_0 == __hydro_y_0
709 && (__hydro_keep_xy_x as int) + (__hydro_keep_xy_y as int)
710 == (__hydro_keep_yx_x as int) + (__hydro_keep_yx_y as int))
711 );
712 }
713 }
714 };
715
716 $crate::properties::VerusCommutativeProof::new()
717 }};
718 (
719 $($rest:tt)*
720 ) => {{
721 ::core::compile_error!(
722 "verus_proof_commutative_filter! must be used as a property annotation inside `q!(...)` on a `|item| -> bool` closure with exactly one `captures_mut` entry, e.g. `commutative = verus_proof_commutative_filter!(item = &u32, captures_mut = |state: u32|)`"
723 );
724 $crate::properties::VerusCommutativeProof::new()
725 }};
726}
727
728#[macro_export]
729/// Fulfills a `commutative = ...` proof parameter for a **unit-returning, effectful
730/// closure** (shape `|item| -> ()`, e.g. for `for_each` or `inspect`) that mutates a
731/// captured singleton reference (from [`Singleton::by_mut`]), with a **Verus-checked
732/// proof of commutativity** of the captured-state update.
733///
734/// **What commutativity means for an effectful closure:** processing any two items in
735/// either order, starting from the same captured state, must leave the captured state in
736/// the same final value. Because the closure returns `()` (enforced by this macro; use
737/// [`verus_proof_commutative_map!`] or [`verus_proof_commutative_filter!`] for closures
738/// whose return value is observable), the state is the only observable effect.
739///
740/// The commuting state is the mutable capture, declared (with the type behind the
741/// reference) in the required `captures_mut = |state: S|` clause; additional read-only
742/// captures can be declared in a `captures = |...|` clause. `q!` also passes the
743/// closure's capture list (as a trailing `__captures = [...]` argument), and the macro
744/// checks at compile time that every capture is declared. The `item = ...` type must be
745/// written exactly as the closure receives it (e.g. `&u32` for `inspect`).
746///
747/// Users never write the proof obligation: this macro receives the quoted closure itself
748/// from `q!` (as a trailing `__target = { ... }` argument) and generates a Verus function
749/// that symbolically executes the *actual closure body* in both orders — `x` then `y`,
750/// and `y` then `x` — from equal captured states, and asserts that the final states are
751/// equal. Verifying this also proves the body panic-free.
752///
753/// An optional `proof = |state, x, y| { ... }` clause supplies a proof *script* to help
754/// the SMT solver, with ghost bindings for the initial captured state and the two items.
755/// The script is checked by Verus and cannot weaken the obligation.
756///
757/// The proof is gated on `cfg(verus_keep_ghost)`, so it only exists when the crate is
758/// compiled by the Verus driver (`cargo verus verify`). Under normal compilation the
759/// macro just produces a [`VerusCommutativeProof`] marker.
760///
761/// # Example
762/// ```rust,ignore
763/// let flags_mut = flags.by_mut();
764/// stream.for_each(q!(
765/// |x| { *flags_mut |= x; },
766/// commutative = verus_proof_commutative_effect!(
767/// item = u32,
768/// captures_mut = |flags_mut: u32|,
769/// proof = |s, x, y| { assert(((s | x) | y) == ((s | y) | x)) by (bit_vector); }
770/// )
771/// ));
772/// ```
773///
774/// [`Singleton::by_mut`]: crate::live_collections::singleton::Singleton::by_mut
775macro_rules! __verus_proof_commutative_effect__ {
776 (
777 item = $itemty:ty
778 $(, captures = |$($cap:ident : $capty:ty),* $(,)?|)?
779 , captures_mut = |$mcap:ident : $mcapty:ty $(,)?|
780 $(, proof = |$pstate:ident, $px:ident, $py:ident| { $($proof_body:tt)* })?
781 , __target = { $(move)? |$titem:ident $(: $titemty:ty)?| $target_body:expr }
782 , __captures = [$($fv:ident),* $(,)?] $(,)?
783 ) => {{
784 /// Checks that every free variable captured by the closure is declared (with its
785 /// type) in the `captures = |...|` or `captures_mut = |...|` clauses: within this
786 /// function, the *only* names in scope are the declared captures, so an
787 /// undeclared capture fails to resolve.
788 #[allow(unused, reason = "only checks name resolution")]
789 fn __hydro_verus_captures_declared($($($cap: $capty,)*)? $mcap: $mcapty) {
790 $(let $fv = &$fv;)*
791 }
792
793 #[cfg(verus_keep_ghost)]
794 const _: () = {
795 #[allow(unused_imports)]
796 use ::vstd::prelude::*;
797
798 ::vstd::prelude::verus! {
799 /// Commutativity obligation, generated by
800 /// `verus_proof_commutative_effect!`: applying the closure body with `x`
801 /// then `y` must yield the same captured state as applying it with `y`
802 /// then `x`, from any equal starting states. The closure must return
803 /// `()`, so the state is the only observable effect. Verifying this also
804 /// proves the body panic-free.
805 fn __hydro_verus_commutative(
806 $($($cap: $capty,)*)?
807 __hydro_state_xy: $mcapty,
808 __hydro_state_yx: $mcapty,
809 __hydro_x_0: $itemty,
810 __hydro_x_1: $itemty,
811 __hydro_y_0: $itemty,
812 __hydro_y_1: $itemty,
813 )
814 requires
815 __hydro_state_xy == __hydro_state_yx,
816 __hydro_x_0 == __hydro_x_1,
817 __hydro_y_0 == __hydro_y_1,
818 {
819 $(
820 let ghost $pstate = __hydro_state_xy;
821 let ghost $px = __hydro_x_0;
822 let ghost $py = __hydro_y_0;
823 )?
824 let mut __hydro_state_xy = __hydro_state_xy;
825 let mut __hydro_state_yx = __hydro_state_yx;
826 { let $mcap = &mut __hydro_state_xy; let $titem = __hydro_x_0; let __hydro_out: () = { $target_body }; }
827 { let $mcap = &mut __hydro_state_xy; let $titem = __hydro_y_0; let __hydro_out: () = { $target_body }; }
828 { let $mcap = &mut __hydro_state_yx; let $titem = __hydro_y_1; let __hydro_out: () = { $target_body }; }
829 { let $mcap = &mut __hydro_state_yx; let $titem = __hydro_x_1; let __hydro_out: () = { $target_body }; }
830 $(proof { $($proof_body)* })?
831 assert(__hydro_state_xy == __hydro_state_yx);
832 }
833 }
834 };
835
836 $crate::properties::VerusCommutativeProof::new()
837 }};
838 (
839 $($rest:tt)*
840 ) => {{
841 ::core::compile_error!(
842 "verus_proof_commutative_effect! must be used as a property annotation inside `q!(...)` on a `|item| -> ()` closure with exactly one `captures_mut` entry, e.g. `commutative = verus_proof_commutative_effect!(item = u32, captures_mut = |state: u32|)`"
843 );
844 $crate::properties::VerusCommutativeProof::new()
845 }};
846}
847
848#[doc(inline)]
849pub use crate::__verus_proof_idempotent_effect__ as verus_proof_idempotent_effect;
850#[doc(inline)]
851pub use crate::__verus_proof_idempotent_filter__ as verus_proof_idempotent_filter;
852#[doc(inline)]
853pub use crate::__verus_proof_idempotent_fold__ as verus_proof_idempotent_fold;
854#[doc(inline)]
855pub use crate::__verus_proof_idempotent_map__ as verus_proof_idempotent_map;
856#[doc(inline)]
857pub use crate::__verus_proof_idempotent_reduce__ as verus_proof_idempotent_reduce;
858
859#[macro_export]
860/// Fulfills an `idempotent = ...` proof parameter for a **fold closure** (shape
861/// `|acc, item|` with `acc: &mut A`) with a **Verus-checked proof of idempotence**.
862///
863/// Use [`verus_proof_idempotent_reduce!`] for `reduce`, whose accumulator is seeded with
864/// the first element, which needs an additional obligation.
865///
866/// Users never write the proof obligation (so they cannot get it wrong): this macro
867/// receives the quoted closure itself from `q!` (as a trailing `__target = { ... }`
868/// argument) and generates a Verus function that symbolically executes the *actual
869/// closure body* on an item `x` once, and on `x` twice in a row, starting from equal
870/// accumulators, and asserts that the final accumulators are equal. Verifying this
871/// obligation also proves the closure body panic-free (e.g. no arithmetic overflow).
872/// Users only declare the accumulator and item types:
873///
874/// ```rust,ignore
875/// stream.fold(
876/// q!(|| 0u32),
877/// q!(
878/// |acc, x| { if x > *acc { *acc = x; } },
879/// idempotent = verus_proof_idempotent_fold!(acc = u32, item = u32)
880/// ),
881/// );
882/// ```
883///
884/// Read-only captures are declared in a `captures = |...|` clause, exactly as for
885/// [`verus_proof_commutative_fold!`]; the macro checks at compile time that every capture
886/// is declared.
887///
888/// An optional `proof = |state, x| { ... }` clause supplies a proof *script* to help the
889/// SMT solver, with ghost bindings for the initial accumulator and the item (e.g.
890/// `proof = |s, x| { assert(((s | x) | x) == (s | x)) by (bit_vector); }`). The script is
891/// checked by Verus and cannot weaken the obligation.
892///
893/// The proof is gated on `cfg(verus_keep_ghost)`, so it only exists when the crate is
894/// compiled by the Verus driver (`cargo verus verify`). Under normal compilation the
895/// macro just produces a [`VerusIdempotentProof`] marker.
896macro_rules! __verus_proof_idempotent_fold__ {
897 (
898 acc = $accty:ty, item = $itemty:ty
899 $(, captures = |$($cap:ident : $capty:ty),* $(,)?|)?
900 $(, proof = |$pstate:ident, $px:ident| { $($proof_body:tt)* })?
901 , __target = { $(move)? |$tacc:ident $(: $taccty:ty)?, $titem:ident $(: $titemty:ty)?| $target_body:expr }
902 , __captures = [$($fv:ident),* $(,)?] $(,)?
903 ) => {{
904 /// Checks that every free variable captured by the closure is declared (with its
905 /// type) in the `captures = |...|` clause: within this function, the *only* names
906 /// in scope are the declared captures and the closure parameters, so an
907 /// undeclared capture fails to resolve.
908 #[allow(unused, reason = "only checks name resolution")]
909 fn __hydro_verus_captures_declared($($($cap: $capty),*)?) {
910 $(let $fv = &$fv;)*
911 }
912
913 #[cfg(verus_keep_ghost)]
914 const _: () = {
915 #[allow(unused_imports)]
916 use ::vstd::prelude::*;
917
918 ::vstd::prelude::verus! {
919 /// Idempotence obligation, generated by `verus_proof_idempotent_fold!`:
920 /// applying the closure body with `x` twice in a row must yield the same
921 /// accumulator as applying it once, from any equal starting accumulators,
922 /// for all items and capture values. Verifying this also proves the body
923 /// panic-free.
924 fn __hydro_verus_idempotent(
925 $($($cap: $capty,)*)?
926 __hydro_acc_once: $accty,
927 __hydro_acc_twice: $accty,
928 __hydro_x_0: $itemty,
929 __hydro_x_1: $itemty,
930 __hydro_x_2: $itemty,
931 )
932 requires
933 __hydro_acc_once == __hydro_acc_twice,
934 __hydro_x_0 == __hydro_x_1,
935 __hydro_x_0 == __hydro_x_2,
936 {
937 $(
938 let ghost $pstate = __hydro_acc_once;
939 let ghost $px = __hydro_x_0;
940 )?
941 let mut __hydro_acc_once = __hydro_acc_once;
942 let mut __hydro_acc_twice = __hydro_acc_twice;
943 { let $tacc = &mut __hydro_acc_once; let $titem = __hydro_x_0; let _ = { $target_body }; }
944 { let $tacc = &mut __hydro_acc_twice; let $titem = __hydro_x_1; let _ = { $target_body }; }
945 { let $tacc = &mut __hydro_acc_twice; let $titem = __hydro_x_2; let _ = { $target_body }; }
946 $(proof { $($proof_body)* })?
947 assert(__hydro_acc_once == __hydro_acc_twice);
948 }
949 }
950 };
951
952 $crate::properties::VerusIdempotentProof::new()
953 }};
954 (
955 $($rest:tt)*
956 ) => {{
957 ::core::compile_error!(
958 "verus_proof_idempotent_fold! must be used as a property annotation inside `q!(...)` on a `|acc, item| ...` closure, e.g. `idempotent = verus_proof_idempotent_fold!(acc = u32, item = u32)`"
959 );
960 $crate::properties::VerusIdempotentProof::new()
961 }};
962}
963
964#[macro_export]
965/// Fulfills an `idempotent = ...` proof parameter for a **reduce closure** (shape
966/// `|acc, item|` with `acc: &mut T` and `item: T`) with a **Verus-checked proof of
967/// idempotence**.
968///
969/// `reduce` seeds its accumulator with the first element, so in addition to the
970/// [`verus_proof_idempotent_fold!`] obligation (applying `x` twice in a row equals
971/// applying it once, from any accumulator), this macro also proves the **seed**
972/// obligation: if the first element is duplicated, applying the closure to an
973/// accumulator seeded with `x` and the item `x` must leave the accumulator equal to `x`.
974/// The fold obligation alone does not imply this; for example,
975/// `|acc, x| *acc = max(*acc, x + 1)` satisfies it, but reducing `[a, a]` yields `a + 1`
976/// instead of `a`.
977///
978/// ```rust,ignore
979/// stream.reduce(q!(
980/// |curr, new| { if new > *curr { *curr = new; } },
981/// idempotent = verus_proof_idempotent_reduce!(item = u32)
982/// ));
983/// ```
984///
985/// Read-only captures are declared in a `captures = |...|` clause. An optional
986/// `proof = |state, x| { ... }` script is inserted into both obligations (for the seed
987/// obligation, `state` is bound to the item itself).
988///
989/// The proof is gated on `cfg(verus_keep_ghost)`, so it only exists when the crate is
990/// compiled by the Verus driver (`cargo verus verify`). Under normal compilation the
991/// macro just produces a [`VerusIdempotentProof`] marker.
992macro_rules! __verus_proof_idempotent_reduce__ {
993 (
994 item = $itemty:ty
995 $(, captures = |$($cap:ident : $capty:ty),* $(,)?|)?
996 $(, proof = |$pstate:ident, $px:ident| { $($proof_body:tt)* })?
997 , __target = { $(move)? |$tacc:ident $(: $taccty:ty)?, $titem:ident $(: $titemty:ty)?| $target_body:expr }
998 , __captures = [$($fv:ident),* $(,)?] $(,)?
999 ) => {{
1000 /// Checks that every free variable captured by the closure is declared (with its
1001 /// type) in the `captures = |...|` clause: within this function, the *only* names
1002 /// in scope are the declared captures and the closure parameters, so an
1003 /// undeclared capture fails to resolve.
1004 #[allow(unused, reason = "only checks name resolution")]
1005 fn __hydro_verus_captures_declared($($($cap: $capty),*)?) {
1006 $(let $fv = &$fv;)*
1007 }
1008
1009 #[cfg(verus_keep_ghost)]
1010 const _: () = {
1011 #[allow(unused_imports)]
1012 use ::vstd::prelude::*;
1013
1014 ::vstd::prelude::verus! {
1015 /// Idempotence obligation, generated by `verus_proof_idempotent_reduce!`:
1016 /// applying the closure body with `x` twice in a row must yield the same
1017 /// accumulator as applying it once, from any equal starting accumulators,
1018 /// for all items and capture values. Verifying this also proves the body
1019 /// panic-free.
1020 fn __hydro_verus_idempotent(
1021 $($($cap: $capty,)*)?
1022 __hydro_acc_once: $itemty,
1023 __hydro_acc_twice: $itemty,
1024 __hydro_x_0: $itemty,
1025 __hydro_x_1: $itemty,
1026 __hydro_x_2: $itemty,
1027 )
1028 requires
1029 __hydro_acc_once == __hydro_acc_twice,
1030 __hydro_x_0 == __hydro_x_1,
1031 __hydro_x_0 == __hydro_x_2,
1032 {
1033 $(
1034 let ghost $pstate = __hydro_acc_once;
1035 let ghost $px = __hydro_x_0;
1036 )?
1037 let mut __hydro_acc_once = __hydro_acc_once;
1038 let mut __hydro_acc_twice = __hydro_acc_twice;
1039 { let $tacc = &mut __hydro_acc_once; let $titem = __hydro_x_0; let _ = { $target_body }; }
1040 { let $tacc = &mut __hydro_acc_twice; let $titem = __hydro_x_1; let _ = { $target_body }; }
1041 { let $tacc = &mut __hydro_acc_twice; let $titem = __hydro_x_2; let _ = { $target_body }; }
1042 $(proof { $($proof_body)* })?
1043 assert(__hydro_acc_once == __hydro_acc_twice);
1044 }
1045
1046 /// Seed obligation, generated by `verus_proof_idempotent_reduce!`: when
1047 /// the first element `x` is duplicated, the accumulator is seeded with `x`
1048 /// and then combined with `x`, which must leave it equal to `x`.
1049 fn __hydro_verus_idempotent_seed(
1050 $($($cap: $capty,)*)?
1051 __hydro_x_0: $itemty,
1052 __hydro_x_1: $itemty,
1053 )
1054 requires
1055 __hydro_x_0 == __hydro_x_1,
1056 {
1057 let ghost __hydro_seed = __hydro_x_0;
1058 $(
1059 let ghost $pstate = __hydro_x_0;
1060 let ghost $px = __hydro_x_0;
1061 )?
1062 let mut __hydro_acc = __hydro_x_0;
1063 { let $tacc = &mut __hydro_acc; let $titem = __hydro_x_1; let _ = { $target_body }; }
1064 $(proof { $($proof_body)* })?
1065 assert(__hydro_acc == __hydro_seed);
1066 }
1067 }
1068 };
1069
1070 $crate::properties::VerusIdempotentProof::new()
1071 }};
1072 (
1073 $($rest:tt)*
1074 ) => {{
1075 ::core::compile_error!(
1076 "verus_proof_idempotent_reduce! must be used as a property annotation inside `q!(...)` on a `|acc, item| ...` closure, e.g. `idempotent = verus_proof_idempotent_reduce!(item = u32)`"
1077 );
1078 $crate::properties::VerusIdempotentProof::new()
1079 }};
1080}
1081
1082#[macro_export]
1083/// Fulfills an `idempotent = ...` proof parameter for a **map-like closure** (shape
1084/// `|item| -> out`, e.g. for `map`, `filter_map`, or `flat_map_*`) that may mutate a
1085/// captured singleton reference (from [`Singleton::by_mut`]), with a **Verus-checked
1086/// proof of idempotence**.
1087///
1088/// **What idempotence means for a map closure:** processing an item twice in a row,
1089/// starting from the same captured state, must (1) leave the captured state in the same
1090/// final value as processing it once, *and* (2) produce the same output both times as
1091/// processing it once. Because the output stream also has at-least-once semantics, a
1092/// retried element may re-emit its output, but must not emit a *different* one: a closure
1093/// that reports whether an element is new (e.g. `true` then `false`) is **not**
1094/// idempotent even if its state update is.
1095///
1096/// The mutated state is the mutable capture, declared (with the type behind the
1097/// reference) in the required `captures_mut = |state: S|` clause; additional read-only
1098/// captures can be declared in a `captures = |...|` clause. The macro checks at compile
1099/// time that every capture is declared. The `item = ...` type must be written exactly as
1100/// the closure receives it.
1101///
1102/// An optional `proof = |state, x| { ... }` clause supplies a proof *script* to help the
1103/// SMT solver, with ghost bindings for the initial captured state and the item.
1104///
1105/// The proof is gated on `cfg(verus_keep_ghost)`, so it only exists when the crate is
1106/// compiled by the Verus driver (`cargo verus verify`). Under normal compilation the
1107/// macro just produces a [`VerusIdempotentProof`] marker.
1108///
1109/// # Example
1110/// ```rust,ignore
1111/// let max_mut = max_seen.by_mut();
1112/// stream.map(q!(
1113/// |x| {
1114/// if x > *max_mut { *max_mut = x; }
1115/// x // outputting `x > *max_mut` (is this new?) would NOT be idempotent!
1116/// },
1117/// idempotent = verus_proof_idempotent_map!(
1118/// item = u32,
1119/// captures_mut = |max_mut: u32|
1120/// )
1121/// ));
1122/// ```
1123///
1124/// [`Singleton::by_mut`]: crate::live_collections::singleton::Singleton::by_mut
1125macro_rules! __verus_proof_idempotent_map__ {
1126 (
1127 item = $itemty:ty
1128 $(, captures = |$($cap:ident : $capty:ty),* $(,)?|)?
1129 , captures_mut = |$mcap:ident : $mcapty:ty $(,)?|
1130 $(, proof = |$pstate:ident, $px:ident| { $($proof_body:tt)* })?
1131 , __target = { $(move)? |$titem:ident $(: $titemty:ty)?| $target_body:expr }
1132 , __captures = [$($fv:ident),* $(,)?] $(,)?
1133 ) => {{
1134 /// Checks that every free variable captured by the closure is declared (with its
1135 /// type) in the `captures = |...|` or `captures_mut = |...|` clauses: within this
1136 /// function, the *only* names in scope are the declared captures, so an
1137 /// undeclared capture fails to resolve.
1138 #[allow(unused, reason = "only checks name resolution")]
1139 fn __hydro_verus_captures_declared($($($cap: $capty,)*)? $mcap: $mcapty) {
1140 $(let $fv = &$fv;)*
1141 }
1142
1143 #[cfg(verus_keep_ghost)]
1144 const _: () = {
1145 #[allow(unused_imports)]
1146 use ::vstd::prelude::*;
1147
1148 ::vstd::prelude::verus! {
1149 /// Idempotence obligation, generated by `verus_proof_idempotent_map!`:
1150 /// from any equal starting captured states, processing `x` twice in a
1151 /// row must yield the same final captured state as processing it once,
1152 /// and both outputs of the repeated run must equal the output of the
1153 /// single run. Verifying this also proves the body panic-free.
1154 fn __hydro_verus_idempotent(
1155 $($($cap: $capty,)*)?
1156 __hydro_state_once: $mcapty,
1157 __hydro_state_twice: $mcapty,
1158 __hydro_x_0: $itemty,
1159 __hydro_x_1: $itemty,
1160 __hydro_x_2: $itemty,
1161 )
1162 requires
1163 __hydro_state_once == __hydro_state_twice,
1164 __hydro_x_0 == __hydro_x_1,
1165 __hydro_x_0 == __hydro_x_2,
1166 {
1167 $(
1168 let ghost $pstate = __hydro_state_once;
1169 let ghost $px = __hydro_x_0;
1170 )?
1171 let mut __hydro_state_once = __hydro_state_once;
1172 let mut __hydro_state_twice = __hydro_state_twice;
1173 let __hydro_out_once = { let $mcap = &mut __hydro_state_once; let $titem = __hydro_x_0; $target_body };
1174 let __hydro_out_twice_1 = { let $mcap = &mut __hydro_state_twice; let $titem = __hydro_x_1; $target_body };
1175 let __hydro_out_twice_2 = { let $mcap = &mut __hydro_state_twice; let $titem = __hydro_x_2; $target_body };
1176 $(proof { $($proof_body)* })?
1177 assert(__hydro_state_once == __hydro_state_twice);
1178 assert(__hydro_out_twice_1 == __hydro_out_once && __hydro_out_twice_2 == __hydro_out_once);
1179 }
1180 }
1181 };
1182
1183 $crate::properties::VerusIdempotentProof::new()
1184 }};
1185 (
1186 $($rest:tt)*
1187 ) => {{
1188 ::core::compile_error!(
1189 "verus_proof_idempotent_map! must be used as a property annotation inside `q!(...)` on a `|item| ...` closure with exactly one `captures_mut` entry, e.g. `idempotent = verus_proof_idempotent_map!(item = u32, captures_mut = |state: u32|)`"
1190 );
1191 $crate::properties::VerusIdempotentProof::new()
1192 }};
1193}
1194
1195#[macro_export]
1196/// Fulfills an `idempotent = ...` proof parameter for a **predicate** (shape
1197/// `|item| -> bool` where `item` is received by reference, e.g. for `filter` or
1198/// `partition`) that may mutate a captured singleton reference (from
1199/// [`Singleton::by_mut`]), with a **Verus-checked proof of idempotence**.
1200///
1201/// **What idempotence means for a predicate:** processing an item twice in a row,
1202/// starting from the same captured state, must (1) leave the captured state in the same
1203/// final value as processing it once, *and* (2) make the same decision both times as
1204/// processing it once. Condition (2) is required for `partition`, where an element
1205/// rejected on retry lands in the *other* output. (For `filter` alone, dropping a retried
1206/// element that was retained the first time would be harmless; this macro
1207/// conservatively rejects it as well.)
1208///
1209/// The mutated state is the mutable capture, declared (with the type behind the
1210/// reference) in the required `captures_mut = |state: S|` clause; additional read-only
1211/// captures can be declared in a `captures = |...|` clause. The macro checks at compile
1212/// time that every capture is declared. The `item = ...` type must be written exactly as
1213/// the closure receives it (a reference like `&u32`).
1214///
1215/// An optional `proof = |state, x| { ... }` clause supplies a proof *script* to help the
1216/// SMT solver, with ghost bindings for the initial captured state and the item.
1217///
1218/// The proof is gated on `cfg(verus_keep_ghost)`, so it only exists when the crate is
1219/// compiled by the Verus driver (`cargo verus verify`). Under normal compilation the
1220/// macro just produces a [`VerusIdempotentProof`] marker.
1221///
1222/// # Example
1223/// ```rust,ignore
1224/// let max_mut = max_seen.by_mut();
1225/// stream.filter(q!(
1226/// |x| {
1227/// if *x > *max_mut { *max_mut = *x; }
1228/// *x > 1 // the decision must not depend on whether `x` was seen before!
1229/// },
1230/// idempotent = verus_proof_idempotent_filter!(
1231/// item = &u32,
1232/// captures_mut = |max_mut: u32|
1233/// )
1234/// ));
1235/// ```
1236///
1237/// [`Singleton::by_mut`]: crate::live_collections::singleton::Singleton::by_mut
1238macro_rules! __verus_proof_idempotent_filter__ {
1239 (
1240 item = $itemty:ty
1241 $(, captures = |$($cap:ident : $capty:ty),* $(,)?|)?
1242 , captures_mut = |$mcap:ident : $mcapty:ty $(,)?|
1243 $(, proof = |$pstate:ident, $px:ident| { $($proof_body:tt)* })?
1244 , __target = { $(move)? |$titem:ident $(: $titemty:ty)?| $target_body:expr }
1245 , __captures = [$($fv:ident),* $(,)?] $(,)?
1246 ) => {{
1247 /// Checks that every free variable captured by the closure is declared (with its
1248 /// type) in the `captures = |...|` or `captures_mut = |...|` clauses: within this
1249 /// function, the *only* names in scope are the declared captures, so an
1250 /// undeclared capture fails to resolve.
1251 #[allow(unused, reason = "only checks name resolution")]
1252 fn __hydro_verus_captures_declared($($($cap: $capty,)*)? $mcap: $mcapty) {
1253 $(let $fv = &$fv;)*
1254 }
1255
1256 #[cfg(verus_keep_ghost)]
1257 const _: () = {
1258 #[allow(unused_imports)]
1259 use ::vstd::prelude::*;
1260
1261 ::vstd::prelude::verus! {
1262 /// Idempotence obligation, generated by `verus_proof_idempotent_filter!`:
1263 /// from any equal starting captured states, processing `x` twice in a
1264 /// row must yield the same final captured state as processing it once,
1265 /// and both decisions of the repeated run must equal the decision of the
1266 /// single run. Verifying this also proves the body panic-free.
1267 fn __hydro_verus_idempotent(
1268 $($($cap: $capty,)*)?
1269 __hydro_state_once: $mcapty,
1270 __hydro_state_twice: $mcapty,
1271 __hydro_x_0: $itemty,
1272 __hydro_x_1: $itemty,
1273 __hydro_x_2: $itemty,
1274 )
1275 requires
1276 __hydro_state_once == __hydro_state_twice,
1277 __hydro_x_0 == __hydro_x_1,
1278 __hydro_x_0 == __hydro_x_2,
1279 {
1280 $(
1281 let ghost $pstate = __hydro_state_once;
1282 let ghost $px = __hydro_x_0;
1283 )?
1284 let mut __hydro_state_once = __hydro_state_once;
1285 let mut __hydro_state_twice = __hydro_state_twice;
1286 let __hydro_keep_once: bool = { let $mcap = &mut __hydro_state_once; let $titem = __hydro_x_0; $target_body };
1287 let __hydro_keep_twice_1: bool = { let $mcap = &mut __hydro_state_twice; let $titem = __hydro_x_1; $target_body };
1288 let __hydro_keep_twice_2: bool = { let $mcap = &mut __hydro_state_twice; let $titem = __hydro_x_2; $target_body };
1289 $(proof { $($proof_body)* })?
1290 assert(__hydro_state_once == __hydro_state_twice);
1291 assert(__hydro_keep_twice_1 == __hydro_keep_once && __hydro_keep_twice_2 == __hydro_keep_once);
1292 }
1293 }
1294 };
1295
1296 $crate::properties::VerusIdempotentProof::new()
1297 }};
1298 (
1299 $($rest:tt)*
1300 ) => {{
1301 ::core::compile_error!(
1302 "verus_proof_idempotent_filter! must be used as a property annotation inside `q!(...)` on a `|item| -> bool` closure with exactly one `captures_mut` entry, e.g. `idempotent = verus_proof_idempotent_filter!(item = &u32, captures_mut = |state: u32|)`"
1303 );
1304 $crate::properties::VerusIdempotentProof::new()
1305 }};
1306}
1307
1308#[macro_export]
1309/// Fulfills an `idempotent = ...` proof parameter for a **unit-returning, effectful
1310/// closure** (shape `|item| -> ()`, e.g. for `for_each` or `inspect`) that mutates a
1311/// captured singleton reference (from [`Singleton::by_mut`]), with a **Verus-checked
1312/// proof of idempotence** of the captured-state update.
1313///
1314/// **What idempotence means for an effectful closure:** processing an item twice in a
1315/// row, starting from the same captured state, must leave the captured state in the same
1316/// final value as processing it once. Because the closure returns `()` (enforced by this
1317/// macro; use [`verus_proof_idempotent_map!`] or [`verus_proof_idempotent_filter!`] for
1318/// closures whose return value is observable), the state is the only observable effect.
1319///
1320/// The mutated state is the mutable capture, declared (with the type behind the
1321/// reference) in the required `captures_mut = |state: S|` clause; additional read-only
1322/// captures can be declared in a `captures = |...|` clause. The macro checks at compile
1323/// time that every capture is declared. The `item = ...` type must be written exactly as
1324/// the closure receives it (e.g. `&u32` for `inspect`).
1325///
1326/// An optional `proof = |state, x| { ... }` clause supplies a proof *script* to help the
1327/// SMT solver, with ghost bindings for the initial captured state and the item.
1328///
1329/// The proof is gated on `cfg(verus_keep_ghost)`, so it only exists when the crate is
1330/// compiled by the Verus driver (`cargo verus verify`). Under normal compilation the
1331/// macro just produces a [`VerusIdempotentProof`] marker.
1332///
1333/// # Example
1334/// ```rust,ignore
1335/// let flags_mut = flags.by_mut();
1336/// stream.for_each(q!(
1337/// |x| { *flags_mut |= x; },
1338/// idempotent = verus_proof_idempotent_effect!(
1339/// item = u32,
1340/// captures_mut = |flags_mut: u32|,
1341/// proof = |s, x| { assert(((s | x) | x) == (s | x)) by (bit_vector); }
1342/// )
1343/// ));
1344/// ```
1345///
1346/// [`Singleton::by_mut`]: crate::live_collections::singleton::Singleton::by_mut
1347macro_rules! __verus_proof_idempotent_effect__ {
1348 (
1349 item = $itemty:ty
1350 $(, captures = |$($cap:ident : $capty:ty),* $(,)?|)?
1351 , captures_mut = |$mcap:ident : $mcapty:ty $(,)?|
1352 $(, proof = |$pstate:ident, $px:ident| { $($proof_body:tt)* })?
1353 , __target = { $(move)? |$titem:ident $(: $titemty:ty)?| $target_body:expr }
1354 , __captures = [$($fv:ident),* $(,)?] $(,)?
1355 ) => {{
1356 /// Checks that every free variable captured by the closure is declared (with its
1357 /// type) in the `captures = |...|` or `captures_mut = |...|` clauses: within this
1358 /// function, the *only* names in scope are the declared captures, so an
1359 /// undeclared capture fails to resolve.
1360 #[allow(unused, reason = "only checks name resolution")]
1361 fn __hydro_verus_captures_declared($($($cap: $capty,)*)? $mcap: $mcapty) {
1362 $(let $fv = &$fv;)*
1363 }
1364
1365 #[cfg(verus_keep_ghost)]
1366 const _: () = {
1367 #[allow(unused_imports)]
1368 use ::vstd::prelude::*;
1369
1370 ::vstd::prelude::verus! {
1371 /// Idempotence obligation, generated by `verus_proof_idempotent_effect!`:
1372 /// applying the closure body with `x` twice in a row must yield the same
1373 /// captured state as applying it once, from any equal starting states.
1374 /// The closure must return `()`, so the state is the only observable
1375 /// effect. Verifying this also proves the body panic-free.
1376 fn __hydro_verus_idempotent(
1377 $($($cap: $capty,)*)?
1378 __hydro_state_once: $mcapty,
1379 __hydro_state_twice: $mcapty,
1380 __hydro_x_0: $itemty,
1381 __hydro_x_1: $itemty,
1382 __hydro_x_2: $itemty,
1383 )
1384 requires
1385 __hydro_state_once == __hydro_state_twice,
1386 __hydro_x_0 == __hydro_x_1,
1387 __hydro_x_0 == __hydro_x_2,
1388 {
1389 $(
1390 let ghost $pstate = __hydro_state_once;
1391 let ghost $px = __hydro_x_0;
1392 )?
1393 let mut __hydro_state_once = __hydro_state_once;
1394 let mut __hydro_state_twice = __hydro_state_twice;
1395 { let $mcap = &mut __hydro_state_once; let $titem = __hydro_x_0; let __hydro_out: () = { $target_body }; }
1396 { let $mcap = &mut __hydro_state_twice; let $titem = __hydro_x_1; let __hydro_out: () = { $target_body }; }
1397 { let $mcap = &mut __hydro_state_twice; let $titem = __hydro_x_2; let __hydro_out: () = { $target_body }; }
1398 $(proof { $($proof_body)* })?
1399 assert(__hydro_state_once == __hydro_state_twice);
1400 }
1401 }
1402 };
1403
1404 $crate::properties::VerusIdempotentProof::new()
1405 }};
1406 (
1407 $($rest:tt)*
1408 ) => {{
1409 ::core::compile_error!(
1410 "verus_proof_idempotent_effect! must be used as a property annotation inside `q!(...)` on a `|item| -> ()` closure with exactly one `captures_mut` entry, e.g. `idempotent = verus_proof_idempotent_effect!(item = u32, captures_mut = |state: u32|)`"
1411 );
1412 $crate::properties::VerusIdempotentProof::new()
1413 }};
1414}
1415
1416/// Marks that the property is not proved.
1417pub enum NotProved {}
1418
1419/// Marks that the property is proven.
1420pub enum Proved {}
1421
1422/// Algebraic properties for an aggregation function of type (T, &mut A) -> ().
1423///
1424/// Commutativity:
1425/// ```rust,ignore
1426/// let mut state = ???;
1427/// f(a, &mut state); f(b, &mut state) // produces same final state as
1428/// f(b, &mut state); f(a, &mut state)
1429/// ```
1430///
1431/// Idempotence:
1432/// ```rust,ignore
1433/// let mut state = ???;
1434/// f(a, &mut state);
1435/// let state1 = *state;
1436/// f(a, &mut state);
1437/// // state1 must be equal to state
1438/// ```
1439///
1440/// For `reduce`, whose accumulator is seeded with the first element, idempotence
1441/// additionally requires that `f(a, &mut a)` leaves the accumulator equal to `a` (the
1442/// first element may itself be duplicated).
1443pub struct AggFuncAlgebra<
1444 T = (),
1445 B: Boundedness = crate::live_collections::boundedness::Unbounded,
1446 Commutative = NotProved,
1447 Idempotent = NotProved,
1448 Monotone = NotProved,
1449 S = OnProcess,
1450>(
1451 Option<Box<dyn CommutativeProof<T, B, S>>>,
1452 Option<Box<dyn IdempotentProof>>,
1453 Option<Box<dyn MonotoneProof>>,
1454 PhantomData<(Commutative, Idempotent, Monotone)>,
1455);
1456
1457impl<T, B: Boundedness, C, I, M, S> AggFuncAlgebra<T, B, C, I, M, S> {
1458 /// Marks the function as being commutative, with the given proof mechanism.
1459 pub fn commutative(
1460 self,
1461 proof: impl CommutativeProof<T, B, S> + 'static,
1462 ) -> AggFuncAlgebra<T, B, Proved, I, M, S> {
1463 AggFuncAlgebra(Some(Box::new(proof)), self.1, self.2, PhantomData)
1464 }
1465
1466 /// Marks the function as being idempotent, with the given proof mechanism.
1467 pub fn idempotent(
1468 self,
1469 proof: impl IdempotentProof + 'static,
1470 ) -> AggFuncAlgebra<T, B, C, Proved, M, S> {
1471 AggFuncAlgebra(self.0, Some(Box::new(proof)), self.2, PhantomData)
1472 }
1473
1474 /// Marks the function as being monotone, with the given proof mechanism.
1475 pub fn monotone(
1476 self,
1477 proof: impl MonotoneProof + 'static,
1478 ) -> AggFuncAlgebra<T, B, C, I, Proved, S> {
1479 AggFuncAlgebra(self.0, self.1, Some(Box::new(proof)), PhantomData)
1480 }
1481
1482 /// Registers the expression with the underlying proof mechanisms, and takes the
1483 /// simulator ordering hook attached to the commutativity proof, if any.
1484 pub(crate) fn register_proof(
1485 self,
1486 expr: &syn::Expr,
1487 ) -> Option<OrderingHook<T, B, ExactlyOnce, S>> {
1488 let mut hook = None;
1489 if let Some(mut comm_proof) = self.0 {
1490 comm_proof.register_proof(expr);
1491 hook = comm_proof.take_hook();
1492 }
1493
1494 if let Some(idem_proof) = self.1 {
1495 idem_proof.register_proof(expr);
1496 }
1497
1498 if let Some(monotone_proof) = self.2 {
1499 monotone_proof.register_proof(expr);
1500 }
1501
1502 hook
1503 }
1504}
1505
1506impl<T, B: Boundedness, C, I, M, S> Property for AggFuncAlgebra<T, B, C, I, M, S> {
1507 type Root = AggFuncAlgebra<T, B, NotProved, NotProved, NotProved, S>;
1508
1509 fn make_root(_target: &mut Option<Self>) -> Self::Root {
1510 AggFuncAlgebra(None, None, None, PhantomData)
1511 }
1512}
1513
1514/// Algebraic properties for a singleton map function of type T -> U.
1515///
1516/// Order-preserving means that if the input grows monotonically, the output also grows monotonically.
1517pub struct SingletonMapFuncAlgebra<
1518 T = (),
1519 B: Boundedness = crate::live_collections::boundedness::Unbounded,
1520 OrderPreserving = NotProved,
1521 Commutative = NotProved,
1522 Idempotent = NotProved,
1523 S = OnProcess,
1524>(
1525 Option<Box<dyn OrderPreservingProof>>,
1526 Option<Box<dyn CommutativeProof<T, B, S>>>,
1527 Option<Box<dyn IdempotentProof>>,
1528 PhantomData<(OrderPreserving, Commutative, Idempotent)>,
1529);
1530
1531impl<T, B: Boundedness, O, C, I, S> SingletonMapFuncAlgebra<T, B, O, C, I, S> {
1532 /// Marks the function as being order-preserving, with the given proof mechanism.
1533 pub fn order_preserving(
1534 self,
1535 proof: impl OrderPreservingProof + 'static,
1536 ) -> SingletonMapFuncAlgebra<T, B, Proved, C, I, S> {
1537 SingletonMapFuncAlgebra(Some(Box::new(proof)), self.1, self.2, PhantomData)
1538 }
1539
1540 /// Marks the function as being commutative, with the given proof mechanism.
1541 pub fn commutative(
1542 self,
1543 proof: impl CommutativeProof<T, B, S> + 'static,
1544 ) -> SingletonMapFuncAlgebra<T, B, O, Proved, I, S> {
1545 SingletonMapFuncAlgebra(self.0, Some(Box::new(proof)), self.2, PhantomData)
1546 }
1547
1548 /// Marks the function as being idempotent, with the given proof mechanism.
1549 pub fn idempotent(
1550 self,
1551 proof: impl IdempotentProof + 'static,
1552 ) -> SingletonMapFuncAlgebra<T, B, O, C, Proved, S> {
1553 SingletonMapFuncAlgebra(self.0, self.1, Some(Box::new(proof)), PhantomData)
1554 }
1555
1556 /// Registers the expression with the underlying proof mechanisms, and takes the
1557 /// simulator ordering hook attached to the commutativity proof, if any.
1558 pub(crate) fn register_proof(
1559 self,
1560 expr: &syn::Expr,
1561 ) -> Option<OrderingHook<T, B, ExactlyOnce, S>> {
1562 if let Some(proof) = self.0 {
1563 proof.register_proof(expr);
1564 }
1565 self.1.and_then(|mut proof| {
1566 proof.register_proof(expr);
1567 proof.take_hook()
1568 })
1569 }
1570}
1571
1572impl<T, B: Boundedness, O, C, I, S> Property for SingletonMapFuncAlgebra<T, B, O, C, I, S> {
1573 type Root = SingletonMapFuncAlgebra<T, B, NotProved, NotProved, NotProved, S>;
1574
1575 fn make_root(_target: &mut Option<Self>) -> Self::Root {
1576 SingletonMapFuncAlgebra(None, None, None, PhantomData)
1577 }
1578}
1579
1580/// Algebraic properties for a stream map function of type T -> U.
1581pub struct StreamMapFuncAlgebra<
1582 T = (),
1583 B: Boundedness = crate::live_collections::boundedness::Unbounded,
1584 Commutative = NotProved,
1585 Idempotent = NotProved,
1586 S = OnProcess,
1587>(
1588 Option<Box<dyn CommutativeProof<T, B, S>>>,
1589 Option<Box<dyn IdempotentProof>>,
1590 PhantomData<(Commutative, Idempotent)>,
1591);
1592
1593impl<T, B: Boundedness, C, I, S> StreamMapFuncAlgebra<T, B, C, I, S> {
1594 /// Marks the function as being commutative, with the given proof mechanism.
1595 pub fn commutative(
1596 self,
1597 proof: impl CommutativeProof<T, B, S> + 'static,
1598 ) -> StreamMapFuncAlgebra<T, B, Proved, I, S> {
1599 StreamMapFuncAlgebra(Some(Box::new(proof)), self.1, PhantomData)
1600 }
1601
1602 /// Marks the function as being idempotent, with the given proof mechanism.
1603 pub fn idempotent(
1604 self,
1605 proof: impl IdempotentProof + 'static,
1606 ) -> StreamMapFuncAlgebra<T, B, C, Proved, S> {
1607 StreamMapFuncAlgebra(self.0, Some(Box::new(proof)), PhantomData)
1608 }
1609
1610 /// Registers the expression with the underlying proof mechanisms, and takes the
1611 /// simulator ordering hook attached to the commutativity proof, if any.
1612 pub(crate) fn register_proof(
1613 self,
1614 expr: &syn::Expr,
1615 ) -> Option<OrderingHook<T, B, ExactlyOnce, S>> {
1616 let hook = self.0.and_then(|mut proof| {
1617 proof.register_proof(expr);
1618 proof.take_hook()
1619 });
1620 if let Some(proof) = self.1 {
1621 proof.register_proof(expr);
1622 }
1623 hook
1624 }
1625}
1626
1627impl<T, B: Boundedness, C, I, S> Property for StreamMapFuncAlgebra<T, B, C, I, S> {
1628 type Root = StreamMapFuncAlgebra<T, B, NotProved, NotProved, S>;
1629
1630 fn make_root(_target: &mut Option<Self>) -> Self::Root {
1631 StreamMapFuncAlgebra(None, None, PhantomData)
1632 }
1633}
1634
1635/// Marker trait identifying that the commutativity property is valid for the given stream ordering.
1636///
1637/// **Definition (aggregations, `|acc: &mut A, item: T|`):** for any accumulator value
1638/// and any two items `x`, `y`, applying the closure with `x` then `y` must produce the
1639/// same final accumulator as applying it with `y` then `x`. This makes the final
1640/// aggregate independent of the (non-deterministic) arrival order; note that
1641/// *intermediate* accumulator values may still differ and must not be observed without
1642/// a non-determinism annotation.
1643#[diagnostic::on_unimplemented(
1644 message = "because the input stream has ordering `{O}`, the closure must demonstrate commutativity",
1645 label = "required for this call",
1646 note = "prove it with a `commutative = manual_proof!(/** why the order does not matter */)` annotation on the closure",
1647 note = "to intentionally process the stream by observing a non-deterministic (shuffled) order of elements, use `.assume_ordering`; this introduces non-determinism, so avoid it unless necessary"
1648)]
1649#[sealed::sealed]
1650pub trait ValidCommutativityFor<O: Ordering> {}
1651#[sealed::sealed]
1652impl ValidCommutativityFor<TotalOrder> for NotProved {}
1653#[sealed::sealed]
1654impl<O: Ordering> ValidCommutativityFor<O> for Proved {}
1655
1656/// Marker trait identifying that the idempotence property is valid for the given stream ordering.
1657///
1658/// **Definition (aggregations, `|acc: &mut A, item: T|`):** for any accumulator value
1659/// and any item `x`, applying the closure with `x` twice in a row must produce the same
1660/// final accumulator as applying it once. For `reduce`, whose accumulator is seeded with
1661/// the first element, applying the closure to an accumulator equal to `x` with the item
1662/// `x` must also leave the accumulator equal to `x`. On unordered streams, combined with
1663/// commutativity, this makes the final aggregate independent of duplicates at any
1664/// position. **Definition (`for_each`, `|item: T|`):** processing an item twice in a row
1665/// must leave the mutably-captured state in the same final value as processing it once.
1666#[diagnostic::on_unimplemented(
1667 message = "because the input stream has retries `{R}`, the closure must demonstrate idempotence",
1668 label = "required for this call",
1669 note = "prove it with an `idempotent = manual_proof!(/** why a repeat does not matter */)` annotation on the closure",
1670 note = "to intentionally process the stream by observing non-deterministic (randomly duplicated) retries, use `.assume_retries`; this introduces non-determinism, so avoid it unless necessary"
1671)]
1672#[sealed::sealed]
1673pub trait ValidIdempotenceFor<R: Retries> {}
1674#[sealed::sealed]
1675impl ValidIdempotenceFor<ExactlyOnce> for NotProved {}
1676#[sealed::sealed]
1677impl<R: Retries> ValidIdempotenceFor<R> for Proved {}
1678
1679/// Marker trait identifying that the commutativity property is valid for the given stream ordering.
1680///
1681/// A proof is required when the stream is unordered **and** the closure mutably captures
1682/// state (`WAS_MUT`). **Definition (owned-item closures, `|item: T| -> Out`, e.g. `map`
1683/// / `for_each`):** processing any two items in either order must (1) leave the
1684/// mutably-captured state (e.g. [`Singleton::by_mut`] references) in the same final
1685/// value, *and* (2) produce the same **multiset of return values**. Condition (2) is
1686/// required whenever the return value is observable (as in `map`): a closure that
1687/// emits, say, a running total is **not** commutative even if its state update is. For
1688/// `()`-returning closures (as in `for_each`), condition (2) is trivial.
1689///
1690/// [`Singleton::by_mut`]: crate::live_collections::singleton::Singleton::by_mut
1691#[sealed::sealed]
1692#[diagnostic::on_unimplemented(
1693 message = "because the input stream has ordering `{O}`, the closure must demonstrate commutativity",
1694 label = "required for this call",
1695 note = "prove it with a `commutative = manual_proof!(/** why the order does not matter */)` annotation on the closure",
1696 note = "to intentionally process the stream by observing a non-deterministic (shuffled) order of elements, use `.assume_ordering`; this introduces non-determinism, so avoid it unless necessary"
1697)]
1698pub trait ValidMutCommutativityFor<F: FnMut(In) -> Out, In, Out, O: Ordering, const WAS_MUT: bool> {}
1699#[sealed::sealed]
1700impl<In, Out, F: FnMut(In) -> Out> ValidMutCommutativityFor<F, In, Out, TotalOrder, true>
1701 for NotProved
1702{
1703}
1704#[sealed::sealed]
1705impl<In, Out, F: Fn(In) -> Out, O: Ordering> ValidMutCommutativityFor<F, In, Out, O, false>
1706 for NotProved
1707{
1708}
1709#[sealed::sealed]
1710impl<In, Out, F: FnMut(In) -> Out, O: Ordering> ValidMutCommutativityFor<F, In, Out, O, true>
1711 for Proved
1712{
1713}
1714#[sealed::sealed]
1715impl<In, Out, F: Fn(In) -> Out, O: Ordering> ValidMutCommutativityFor<F, In, Out, O, false>
1716 for Proved
1717{
1718}
1719
1720/// Marker trait identifying that the idempotence property is valid for the given stream ordering.
1721///
1722/// A proof is required when the stream has retries (`AtLeastOnce`) **and** the closure
1723/// mutably captures state (`WAS_MUT`). **Definition (owned-item closures,
1724/// `|item: T| -> Out`, e.g. `map` / `filter_map` / `flat_map_*`):** processing an item
1725/// twice in a row must (1) leave the mutably-captured state (e.g. [`Singleton::by_mut`]
1726/// references) in the same final value as processing it once, *and* (2) return the same
1727/// value both times as when processing it once. Condition (2) is required because the
1728/// output stream inherits the retries: a retried element may re-emit its output, but must
1729/// not emit a *different* one (e.g. a closure that reports whether an element is new is
1730/// **not** idempotent even if its state update is).
1731///
1732/// [`Singleton::by_mut`]: crate::live_collections::singleton::Singleton::by_mut
1733#[diagnostic::on_unimplemented(
1734 message = "because the input stream has retries `{R}`, the closure must demonstrate idempotence",
1735 label = "required for this call",
1736 note = "prove it with an `idempotent = manual_proof!(/** why a repeat does not matter */)` annotation on the closure",
1737 note = "to intentionally process the stream by observing non-deterministic (randomly duplicated) retries, use `.assume_retries`; this introduces non-determinism, so avoid it unless necessary"
1738)]
1739#[sealed::sealed]
1740pub trait ValidMutIdempotenceFor<F: FnMut(In) -> Out, In, Out, R: Retries, const WAS_MUT: bool> {}
1741#[sealed::sealed]
1742impl<In, Out, F: FnMut(In) -> Out> ValidMutIdempotenceFor<F, In, Out, ExactlyOnce, true>
1743 for NotProved
1744{
1745}
1746#[sealed::sealed]
1747impl<In, Out, F: Fn(In) -> Out, R: Retries> ValidMutIdempotenceFor<F, In, Out, R, false>
1748 for NotProved
1749{
1750}
1751#[sealed::sealed]
1752impl<In, Out, F: FnMut(In) -> Out, R: Retries> ValidMutIdempotenceFor<F, In, Out, R, true>
1753 for Proved
1754{
1755}
1756#[sealed::sealed]
1757impl<In, Out, F: Fn(In) -> Out, R: Retries> ValidMutIdempotenceFor<F, In, Out, R, false>
1758 for Proved
1759{
1760}
1761
1762/// Marker trait for commutativity of closures that borrow their input (`FnMut(&In) -> Out`).
1763///
1764/// A proof is required when the stream is unordered **and** the closure mutably captures
1765/// state (`WAS_MUT`). **Definition (borrowing closures, `|item: &T| -> Out`, e.g.
1766/// `filter` / `inspect`):** processing any two items in either order must (1) leave the
1767/// mutably-captured state (e.g. [`Singleton::by_mut`] references) in the same final
1768/// value, *and* (2) keep the operator's observable output identical as a multiset. For
1769/// `filter`, the outputs are the **retained elements**, so the predicate's decisions
1770/// must not depend on the processing order: a stateful predicate like a rate limiter is
1771/// **not** commutative — its budget converges either way, but *which* element passes
1772/// depends on the order. For `inspect`, the elements pass through unchanged, so only
1773/// condition (1) applies.
1774///
1775/// [`Singleton::by_mut`]: crate::live_collections::singleton::Singleton::by_mut
1776#[sealed::sealed]
1777#[diagnostic::on_unimplemented(
1778 message = "because the input stream has ordering `{O}`, the closure must demonstrate commutativity",
1779 label = "required for this call",
1780 note = "prove it with a `commutative = manual_proof!(/** why the order does not matter */)` annotation on the closure",
1781 note = "to intentionally process the stream by observing a non-deterministic (shuffled) order of elements, use `.assume_ordering`; this introduces non-determinism, so avoid it unless necessary"
1782)]
1783pub trait ValidMutBorrowCommutativityFor<
1784 F: FnMut(&In) -> Out,
1785 In: ?Sized,
1786 Out,
1787 O: Ordering,
1788 const WAS_MUT: bool,
1789>
1790{
1791}
1792#[sealed::sealed]
1793impl<In: ?Sized, Out, F: FnMut(&In) -> Out>
1794 ValidMutBorrowCommutativityFor<F, In, Out, TotalOrder, true> for NotProved
1795{
1796}
1797#[sealed::sealed]
1798impl<In: ?Sized, Out, F: Fn(&In) -> Out, O: Ordering>
1799 ValidMutBorrowCommutativityFor<F, In, Out, O, false> for NotProved
1800{
1801}
1802#[sealed::sealed]
1803impl<In: ?Sized, Out, F: FnMut(&In) -> Out, O: Ordering>
1804 ValidMutBorrowCommutativityFor<F, In, Out, O, true> for Proved
1805{
1806}
1807#[sealed::sealed]
1808impl<In: ?Sized, Out, F: Fn(&In) -> Out, O: Ordering>
1809 ValidMutBorrowCommutativityFor<F, In, Out, O, false> for Proved
1810{
1811}
1812
1813/// Marker trait for idempotence of closures that borrow their input (`FnMut(&In) -> Out`).
1814///
1815/// A proof is required when the stream has retries (`AtLeastOnce`) **and** the closure
1816/// mutably captures state (`WAS_MUT`). **Definition (borrowing closures,
1817/// `|item: &T| -> Out`, e.g. `filter` / `partition` / `inspect`):** processing an item
1818/// twice in a row must (1) leave the mutably-captured state (e.g. [`Singleton::by_mut`]
1819/// references) in the same final value as processing it once, *and* (2) keep the
1820/// operator's observable output unchanged. For `filter` and `partition`, the predicate
1821/// must return the same decision both times as when processing the item once (for
1822/// `partition`, an element rejected on retry would appear in the *other* output). For
1823/// `inspect`, the elements pass through unchanged, so only condition (1) applies.
1824///
1825/// [`Singleton::by_mut`]: crate::live_collections::singleton::Singleton::by_mut
1826#[diagnostic::on_unimplemented(
1827 message = "because the input stream has retries `{R}`, the closure must demonstrate idempotence",
1828 label = "required for this call",
1829 note = "prove it with an `idempotent = manual_proof!(/** why a repeat does not matter */)` annotation on the closure",
1830 note = "to intentionally process the stream by observing non-deterministic (randomly duplicated) retries, use `.assume_retries`; this introduces non-determinism, so avoid it unless necessary"
1831)]
1832#[sealed::sealed]
1833pub trait ValidMutBorrowIdempotenceFor<
1834 F: FnMut(&In) -> Out,
1835 In: ?Sized,
1836 Out,
1837 R: Retries,
1838 const WAS_MUT: bool,
1839>
1840{
1841}
1842#[sealed::sealed]
1843impl<In: ?Sized, Out, F: FnMut(&In) -> Out>
1844 ValidMutBorrowIdempotenceFor<F, In, Out, ExactlyOnce, true> for NotProved
1845{
1846}
1847#[sealed::sealed]
1848impl<In: ?Sized, Out, F: Fn(&In) -> Out, R: Retries>
1849 ValidMutBorrowIdempotenceFor<F, In, Out, R, false> for NotProved
1850{
1851}
1852#[sealed::sealed]
1853impl<In: ?Sized, Out, F: FnMut(&In) -> Out, R: Retries>
1854 ValidMutBorrowIdempotenceFor<F, In, Out, R, true> for Proved
1855{
1856}
1857#[sealed::sealed]
1858impl<In: ?Sized, Out, F: Fn(&In) -> Out, R: Retries>
1859 ValidMutBorrowIdempotenceFor<F, In, Out, R, false> for Proved
1860{
1861}
1862
1863/// Marker trait identifying the boundedness of a singleton given a monotonicity property of
1864/// an aggregation on a stream.
1865#[sealed::sealed]
1866pub trait ApplyMonotoneStream<P, B2: SingletonBound> {}
1867
1868#[sealed::sealed]
1869impl<B: Boundedness> ApplyMonotoneStream<NotProved, B> for B {}
1870
1871#[sealed::sealed]
1872impl<B: Boundedness> ApplyMonotoneStream<Proved, B::StreamToMonotone> for B {}
1873
1874/// Marker trait identifying the boundedness of a singleton given a monotonicity property of
1875/// an aggregation on a keyed stream.
1876#[sealed::sealed]
1877pub trait ApplyMonotoneKeyedStream<P, B2: KeyedSingletonBound> {}
1878
1879#[sealed::sealed]
1880impl<B: Boundedness> ApplyMonotoneKeyedStream<NotProved, B::KeyedStreamToNonMonotone> for B {}
1881
1882#[sealed::sealed]
1883impl<B: Boundedness> ApplyMonotoneKeyedStream<Proved, B::KeyedStreamToMonotone> for B {}
1884
1885/// Marker trait identifying the boundedness of a singleton after a map operation,
1886/// given an order-preserving property.
1887#[sealed::sealed]
1888pub trait ApplyOrderPreservingSingleton<P, B2: SingletonBound> {}
1889
1890#[sealed::sealed]
1891impl<B: SingletonBound> ApplyOrderPreservingSingleton<NotProved, B::UnderlyingBound> for B {}
1892
1893#[sealed::sealed]
1894impl<B: SingletonBound> ApplyOrderPreservingSingleton<Proved, B> for B {}