Skip to main content

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 = ...`).
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#[doc(inline)]
172pub use crate::__manual_proof__ as manual_proof;
173
174// Verus only sees items declared inside `verus!`, so under the Verus driver (with the
175// `verus` feature) the function backing `verus_panic!` is declared there.
176// `external_body`: Verus trusts the signature without checking the body (which calls
177// `panic!`, whose vstd specification is `requires false`). The signature has no
178// precondition and the default unwind specification (may unwind), so callers need not
179// prove anything, and since the return type is `!`, nothing after the call is reachable.
180#[cfg(all(verus_keep_ghost, feature = "verus"))]
181vstd::prelude::verus! {
182    #[doc(hidden)]
183    #[verifier::external_body]
184    pub fn __verus_panic() -> ! {
185        panic!("verus_panic! was reached")
186    }
187}
188
189#[doc(inline)]
190pub use crate::__verus_panic__ as verus_panic;
191
192#[cfg(all(verus_keep_ghost, feature = "verus"))]
193#[doc(hidden)]
194#[macro_export]
195macro_rules! __verus_panic__ {
196    // The message arguments are dropped: Verus does not support `format_args!`, and they
197    // would only be evaluated on a path that panics anyway.
198    ($($arg:tt)*) => {
199        $crate::properties::__verus_panic()
200    };
201}
202
203#[cfg(not(all(verus_keep_ghost, feature = "verus")))]
204#[macro_export]
205/// Panics, in a way that **Verus proofs of algebraic properties treat as an acceptable
206/// outcome**. Takes the same arguments as [`panic!`].
207///
208/// A Verus proof (e.g. [`verus_proof_commutative_fold!`]) normally requires the closure body
209/// to be panic-free for every input, which rejects closures such as `*acc += x` (which
210/// panics on overflow) even though they are commutative whenever they complete. Calling
211/// `verus_panic!(...)` instead tells Verus that this path ends the program: the property
212/// only has to hold for executions that do **not** reach it. That matches Hydro's
213/// requirement, since a panicking closure crashes the process rather than producing a
214/// result.
215///
216/// The typical use is to guard an operation that could otherwise panic, so that Verus
217/// knows it cannot fail on the paths that continue:
218///
219/// ```rust,ignore
220/// q!(
221///     |acc, x| {
222///         if *acc > u32::MAX - x {
223///             verus_panic!("sum overflowed");
224///         }
225///         *acc += x; // cannot overflow here, so Verus accepts it
226///     },
227///     commutative = verus_proof_commutative_fold!(acc = u32, item = u32)
228/// )
229/// ```
230///
231/// This is sound because the guard really executes: the closure panics at runtime whenever
232/// the guard condition holds, so a guard that is stricter than necessary is still correct.
233/// A guard that is too weak is caught by Verus, which reports the operation that may still
234/// panic. Unlike `assume(...)` in a proof script, nothing is taken on faith beyond the fact
235/// that `verus_panic!` never returns.
236///
237/// Under normal compilation, this forwards its arguments to [`panic!`]. When verifying
238/// with `cargo verus verify`, the `verus` feature of `hydro_lang` must be enabled; Verus
239/// then sees a call to a function that never returns (the message arguments are not
240/// evaluated by the proof).
241macro_rules! __verus_panic__ {
242    ($($arg:tt)*) => {
243        ::core::panic!($($arg)*)
244    };
245}
246
247#[macro_export]
248/// Fulfills a proof parameter by declaring a human-written justification for why
249/// the algebraic property (e.g. commutativity, idempotence) holds.
250///
251/// The argument must be a doc comment explaining why the property is satisfied.
252///
253/// # Examples
254/// ```rust
255/// # #[cfg(feature = "deploy")] {
256/// # use hydro_lang::prelude::*;
257/// # use hydro_lang::live_collections::stream::NoOrder;
258/// # use futures::StreamExt;
259/// # tokio_test::block_on(hydro_lang::test_util::stream_transform_test(|process| {
260/// # let stream = process.source_iter(q!(vec![1, 2, 3])).weaken_ordering::<NoOrder>();
261/// // stream: [1, 2, 3] (unordered)
262/// stream
263///     .fold(
264///         q!(|| 0),
265///         q!(
266///             |acc, x| *acc += x,
267///             commutative = manual_proof!(/** integer addition is commutative */)
268///         ),
269///     )
270///     .into_stream()
271/// # }, |mut stream| async move {
272/// # assert_eq!(stream.next().await.unwrap(), 6);
273/// # }));
274/// # }
275/// ```
276/// An optional trailing `hook = ...` argument attaches a **simulator ordering hook** to a
277/// commutativity proof (see `hydro_lang::sim::hooks`). The simulator does not trust manual
278/// proofs — it still explores the input ordering — so the hook lets a simulation test
279/// script that exploration:
280///
281/// ```rust,ignore
282/// commutative = manual_proof!(/** set insert is commutative */ hook = my_ordering_hook)
283/// ```
284macro_rules! __manual_proof__ {
285    (
286        $(#[doc = $doc:expr])+hook =
287        $hook:expr,__target =
288        { $($target:tt)* } $(,__captures = [$($captures:ident),*])? $(,)?
289    ) => {
290        $crate::properties::ManualProof::hooked($hook)
291    };
292    ($(#[doc = $doc:expr])+hook = $hook:expr $(,)?) => {
293        $crate::properties::ManualProof::hooked($hook)
294    };
295    (
296        $(#[doc = $doc:expr])+,__target =
297        { $($target:tt)* } $(,__captures = [$($captures:ident),*])? $(,)?
298    ) => {
299        $crate::properties::ManualProof::<()>::unhooked()
300    };
301    ($(#[doc = $doc:expr])+) => {
302        $crate::properties::ManualProof::<()>::unhooked()
303    };
304}
305
306#[doc(inline)]
307pub use crate::__verus_proof_commutative_effect__ as verus_proof_commutative_effect;
308#[doc(inline)]
309pub use crate::__verus_proof_commutative_filter__ as verus_proof_commutative_filter;
310#[doc(inline)]
311pub use crate::__verus_proof_commutative_fold__ as verus_proof_commutative_fold;
312#[doc(inline)]
313pub use crate::__verus_proof_commutative_map__ as verus_proof_commutative_map;
314
315#[macro_export]
316/// Fulfills a `commutative = ...` proof parameter for an **aggregation closure**
317/// (`fold` / `reduce`, shape `|acc, item|` with `acc: &mut A`) with a **Verus-checked
318/// proof of commutativity**.
319///
320/// Users never write the proof obligation (so they cannot get it wrong): this macro
321/// receives the quoted closure itself from `q!` (as a trailing `__target = { ... }`
322/// argument) and generates a Verus function that symbolically executes the *actual
323/// closure body* in both orders — `x` then `y`, and `y` then `x` — starting from equal
324/// accumulators, and asserts that the final accumulators are equal. Verifying this
325/// obligation also proves the closure body panic-free (e.g. no arithmetic overflow).
326/// Users only declare the accumulator and item types:
327///
328/// ```rust,ignore
329/// batch.reduce(q!(
330///     |curr, new| { if new > *curr { *curr = new; } },
331///     commutative = verus_proof_commutative_fold!(acc = u32, item = u32)
332/// ));
333/// ```
334///
335/// If the closure captures (read-only) variables from its environment, re-declare them
336/// (with their runtime types) in a `captures = |...|` clause; the obligation is then
337/// universally quantified over the capture values, which is sound since any particular
338/// execution uses some fixed value. `q!` also passes the closure's capture list (as a
339/// trailing `__captures = [...]` argument), and the macro checks at compile time that
340/// every capture is declared.
341///
342/// An optional `proof = |state, x, y| { ... }` clause supplies a proof *script* to help
343/// the SMT solver, with ghost bindings for the initial accumulator and the two items
344/// (e.g. `proof = |s, x, y| { assert((s | x) | y == (s | y) | x) by (bit_vector); }`).
345/// The script is checked by Verus and cannot weaken the obligation (though, as anywhere
346/// in Verus, `assume` is an explicit soundness escape hatch).
347///
348/// The proof is gated on `cfg(verus_keep_ghost)`, so it only exists when the crate is
349/// compiled by the Verus driver (`cargo verus verify`). Under normal compilation the
350/// macro just produces a [`VerusCommutativeProof`] marker, so the types go through the
351/// ordinary `commutative = ...` mechanism with zero overhead.
352macro_rules! __verus_proof_commutative_fold__ {
353    (
354        acc = $accty:ty, item = $itemty:ty
355        $(, captures = |$($cap:ident : $capty:ty),* $(,)?|)?
356        $(, proof = |$pstate:ident, $px:ident, $py:ident| { $($proof_body:tt)* })?
357        , __target = { $(move)? |$tacc:ident $(: $taccty:ty)?, $titem:ident $(: $titemty:ty)?| $target_body:expr }
358        , __captures = [$($fv:ident),* $(,)?] $(,)?
359    ) => {{
360        /// Checks that every free variable captured by the closure is declared (with its
361        /// type) in the `captures = |...|` clause: within this function, the *only* names
362        /// in scope are the declared captures and the closure parameters, so an
363        /// undeclared capture fails to resolve.
364        #[allow(unused, reason = "only checks name resolution")]
365        fn __hydro_verus_captures_declared($($($cap: $capty),*)?) {
366            $(let $fv = &$fv;)*
367        }
368        #[cfg(verus_keep_ghost)]
369        const _: () = {
370            #[allow(unused_imports)]
371            use ::vstd::prelude::*;
372
373            ::vstd::prelude::verus! {
374                /// Commutativity obligation, generated by `verus_proof_commutative_fold!`:
375                /// applying the closure body with `x` then `y` must yield the same
376                /// accumulator as applying it with `y` then `x`, from any equal starting
377                /// accumulators, for all items and capture values. Verifying this also
378                /// proves the body panic-free.
379                fn __hydro_verus_commutative(
380                    $($($cap: $capty,)*)?
381                    __hydro_acc_xy: $accty,
382                    __hydro_acc_yx: $accty,
383                    __hydro_x_0: $itemty,
384                    __hydro_x_1: $itemty,
385                    __hydro_y_0: $itemty,
386                    __hydro_y_1: $itemty,
387                )
388                    requires
389                        __hydro_acc_xy == __hydro_acc_yx,
390                        __hydro_x_0 == __hydro_x_1,
391                        __hydro_y_0 == __hydro_y_1,
392                {
393                    $(
394                        let ghost $pstate = __hydro_acc_xy;
395                        let ghost $px = __hydro_x_0;
396                        let ghost $py = __hydro_y_0;
397                    )?
398                    let mut __hydro_acc_xy = __hydro_acc_xy;
399                    let mut __hydro_acc_yx = __hydro_acc_yx;
400                    { let $tacc = &mut __hydro_acc_xy; let $titem = __hydro_x_0; let _ = { $target_body }; }
401                    { let $tacc = &mut __hydro_acc_xy; let $titem = __hydro_y_0; let _ = { $target_body }; }
402                    { let $tacc = &mut __hydro_acc_yx; let $titem = __hydro_y_1; let _ = { $target_body }; }
403                    { let $tacc = &mut __hydro_acc_yx; let $titem = __hydro_x_1; let _ = { $target_body }; }
404                    $(proof { $($proof_body)* })?
405                    assert(__hydro_acc_xy == __hydro_acc_yx);
406                }
407            }
408        };
409
410        $crate::properties::VerusCommutativeProof::new()
411    }};
412    (
413        $($rest:tt)*
414    ) => {{
415        ::core::compile_error!(
416            "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)`"
417        );
418        $crate::properties::VerusCommutativeProof::new()
419    }};
420}
421
422#[macro_export]
423/// Fulfills a `commutative = ...` proof parameter for a **map-like closure**
424/// (shape `|item| -> out`, e.g. for `map`) that may mutate a captured singleton
425/// reference (from [`Singleton::by_mut`]), with a **Verus-checked proof of
426/// commutativity**.
427///
428/// **What commutativity means for a map closure:** processing any two items in either
429/// order, starting from the same captured state, must (1) leave the captured state in
430/// the same final value, *and* (2) produce the same **multiset of output values** (the
431/// outputs may be swapped in order, but not changed). Both conditions are required for
432/// downstream determinism: outputs that depend on the processing order (e.g. emitting a
433/// running total) are **not** commutative even if the state update is.
434///
435/// The commuting state is the mutable capture, declared (with the type behind the
436/// reference) in the required `captures_mut = |state: S|` clause; additional read-only
437/// captures can be declared in a `captures = |...|` clause. `q!` also passes the
438/// closure's capture list (as a trailing `__captures = [...]` argument), and the macro
439/// checks at compile time that every capture is declared. The `item = ...` type must be
440/// written exactly as the closure receives it.
441///
442/// Users never write the proof obligation: this macro receives the quoted closure itself
443/// from `q!` (as a trailing `__target = { ... }` argument) and generates a Verus function
444/// that symbolically executes the *actual closure body* in both orders — `x` then `y`,
445/// and `y` then `x` — from equal captured states, then asserts that the final states are
446/// equal and that the two runs' outputs are equal as a multiset (pairwise or crossed).
447/// Verifying this also proves the body panic-free.
448///
449/// An optional `proof = |state, x, y| { ... }` clause supplies a proof *script* to help
450/// the SMT solver, with ghost bindings for the initial captured state and the two items.
451/// The script is checked by Verus and cannot weaken the obligation.
452///
453/// The proof is gated on `cfg(verus_keep_ghost)`, so it only exists when the crate is
454/// compiled by the Verus driver (`cargo verus verify`). Under normal compilation the
455/// macro just produces a [`VerusCommutativeProof`] marker.
456///
457/// # Example
458/// ```rust,ignore
459/// let count_mut = my_count.by_mut();
460/// stream.map(q!(
461///     |x| {
462///         *count_mut = count_mut.wrapping_add(x);
463///         x // outputting `*count_mut` here would NOT be commutative!
464///     },
465///     commutative = verus_proof_commutative_map!(
466///         item = i32,
467///         captures_mut = |count_mut: i32|
468///     )
469/// ));
470/// ```
471///
472/// [`Singleton::by_mut`]: crate::live_collections::singleton::Singleton::by_mut
473macro_rules! __verus_proof_commutative_map__ {
474    (
475        item = $itemty:ty
476        $(, captures = |$($cap:ident : $capty:ty),* $(,)?|)?
477        , captures_mut = |$mcap:ident : $mcapty:ty $(,)?|
478        $(, proof = |$pstate:ident, $px:ident, $py:ident| { $($proof_body:tt)* })?
479        , __target = { $(move)? |$titem:ident $(: $titemty:ty)?| $target_body:expr }
480        , __captures = [$($fv:ident),* $(,)?] $(,)?
481    ) => {{
482        /// Checks that every free variable captured by the closure is declared (with its
483        /// type) in the `captures = |...|` or `captures_mut = |...|` clauses: within this
484        /// function, the *only* names in scope are the declared captures, so an
485        /// undeclared capture fails to resolve.
486        #[allow(unused, reason = "only checks name resolution")]
487        fn __hydro_verus_captures_declared($($($cap: $capty,)*)? $mcap: $mcapty) {
488            $(let $fv = &$fv;)*
489        }
490
491        #[cfg(verus_keep_ghost)]
492        const _: () = {
493            #[allow(unused_imports)]
494            use ::vstd::prelude::*;
495
496            ::vstd::prelude::verus! {
497                /// Commutativity obligation, generated by `verus_proof_commutative_map!`:
498                /// from any equal starting captured states, processing `x` then `y` must
499                /// yield the same final captured state as processing `y` then `x`, and
500                /// the two runs must produce the same multiset of outputs (pairwise or
501                /// crossed equality). Verifying this also proves the body panic-free.
502                fn __hydro_verus_commutative(
503                    $($($cap: $capty,)*)?
504                    __hydro_state_xy: $mcapty,
505                    __hydro_state_yx: $mcapty,
506                    __hydro_x_0: $itemty,
507                    __hydro_x_1: $itemty,
508                    __hydro_y_0: $itemty,
509                    __hydro_y_1: $itemty,
510                )
511                    requires
512                        __hydro_state_xy == __hydro_state_yx,
513                        __hydro_x_0 == __hydro_x_1,
514                        __hydro_y_0 == __hydro_y_1,
515                {
516                    $(
517                        let ghost $pstate = __hydro_state_xy;
518                        let ghost $px = __hydro_x_0;
519                        let ghost $py = __hydro_y_0;
520                    )?
521                    let mut __hydro_state_xy = __hydro_state_xy;
522                    let mut __hydro_state_yx = __hydro_state_yx;
523                    let __hydro_out_xy_x = { let $mcap = &mut __hydro_state_xy; let $titem = __hydro_x_0; $target_body };
524                    let __hydro_out_xy_y = { let $mcap = &mut __hydro_state_xy; let $titem = __hydro_y_0; $target_body };
525                    let __hydro_out_yx_y = { let $mcap = &mut __hydro_state_yx; let $titem = __hydro_y_1; $target_body };
526                    let __hydro_out_yx_x = { let $mcap = &mut __hydro_state_yx; let $titem = __hydro_x_1; $target_body };
527                    $(proof { $($proof_body)* })?
528                    assert(__hydro_state_xy == __hydro_state_yx);
529                    assert(
530                        (__hydro_out_xy_x == __hydro_out_yx_x && __hydro_out_xy_y == __hydro_out_yx_y)
531                        || (__hydro_out_xy_x == __hydro_out_yx_y && __hydro_out_xy_y == __hydro_out_yx_x)
532                    );
533                }
534            }
535        };
536
537        $crate::properties::VerusCommutativeProof::new()
538    }};
539    (
540        $($rest:tt)*
541    ) => {{
542        ::core::compile_error!(
543            "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|)`"
544        );
545        $crate::properties::VerusCommutativeProof::new()
546    }};
547}
548
549#[macro_export]
550/// Fulfills a `commutative = ...` proof parameter for a **filter predicate**
551/// (shape `|item| -> bool` where `item` is received by reference) that may mutate a
552/// captured singleton reference (from [`Singleton::by_mut`]), with a **Verus-checked
553/// proof of commutativity**.
554///
555/// **What commutativity means for a filter predicate:** processing any two items in
556/// either order, starting from the same captured state, must (1) leave the captured
557/// state in the same final value, *and* (2) retain the same **multiset of elements**.
558/// The second condition is essential for soundness: a stateful predicate like a rate
559/// limiter converges to the same state either way, but *which* element passes depends on
560/// the order, which is **not** commutative (unless the elements are equal).
561///
562/// The commuting state is the mutable capture, declared (with the type behind the
563/// reference) in the required `captures_mut = |state: S|` clause; additional read-only
564/// captures can be declared in a `captures = |...|` clause. `q!` also passes the
565/// closure's capture list (as a trailing `__captures = [...]` argument), and the macro
566/// checks at compile time that every capture is declared. The `item = ...` type must be
567/// written exactly as the closure receives it (for `filter`, a reference like `&u32`).
568///
569/// Users never write the proof obligation: this macro receives the quoted closure itself
570/// from `q!` (as a trailing `__target = { ... }` argument) and generates a Verus function
571/// that symbolically executes the *actual predicate body* in both orders — `x` then `y`,
572/// and `y` then `x` — from equal captured states, then asserts that the final states are
573/// equal and that the retained multisets are equal: either the per-item decisions match
574/// across the two orders, or the two items are equal and the number of retained copies
575/// matches. Verifying this also proves the body panic-free.
576///
577/// An optional `proof = |state, x, y| { ... }` clause supplies a proof *script* to help
578/// the SMT solver, with ghost bindings for the initial captured state and the two items.
579/// The script is checked by Verus and cannot weaken the obligation.
580///
581/// The proof is gated on `cfg(verus_keep_ghost)`, so it only exists when the crate is
582/// compiled by the Verus driver (`cargo verus verify`). Under normal compilation the
583/// macro just produces a [`VerusCommutativeProof`] marker.
584///
585/// # Example
586/// ```rust,ignore
587/// let seen_mut = seen_count.by_mut();
588/// stream.filter(q!(
589///     |x| {
590///         *seen_mut = seen_mut.wrapping_add(1);
591///         *x > 1 // the decision must not depend on the mutable state!
592///     },
593///     commutative = verus_proof_commutative_filter!(
594///         item = &u32,
595///         captures_mut = |seen_mut: u32|
596///     )
597/// ));
598/// ```
599///
600/// [`Singleton::by_mut`]: crate::live_collections::singleton::Singleton::by_mut
601macro_rules! __verus_proof_commutative_filter__ {
602    (
603        item = $itemty:ty
604        $(, captures = |$($cap:ident : $capty:ty),* $(,)?|)?
605        , captures_mut = |$mcap:ident : $mcapty:ty $(,)?|
606        $(, proof = |$pstate:ident, $px:ident, $py:ident| { $($proof_body:tt)* })?
607        , __target = { $(move)? |$titem:ident $(: $titemty:ty)?| $target_body:expr }
608        , __captures = [$($fv:ident),* $(,)?] $(,)?
609    ) => {{
610        /// Checks that every free variable captured by the closure is declared (with its
611        /// type) in the `captures = |...|` or `captures_mut = |...|` clauses: within this
612        /// function, the *only* names in scope are the declared captures, so an
613        /// undeclared capture fails to resolve.
614        #[allow(unused, reason = "only checks name resolution")]
615        fn __hydro_verus_captures_declared($($($cap: $capty,)*)? $mcap: $mcapty) {
616            $(let $fv = &$fv;)*
617        }
618
619        #[cfg(verus_keep_ghost)]
620        const _: () = {
621            #[allow(unused_imports)]
622            use ::vstd::prelude::*;
623
624            ::vstd::prelude::verus! {
625                /// Commutativity obligation, generated by
626                /// `verus_proof_commutative_filter!`: from any equal starting captured
627                /// states, processing `x` then `y` must yield the same final captured
628                /// state as processing `y` then `x`, and the retained multisets must be
629                /// equal: either the per-item decisions match across the two orders, or
630                /// the two items are equal and the retained counts match. Verifying this
631                /// also proves the body panic-free.
632                fn __hydro_verus_commutative(
633                    $($($cap: $capty,)*)?
634                    __hydro_state_xy: $mcapty,
635                    __hydro_state_yx: $mcapty,
636                    __hydro_x_0: $itemty,
637                    __hydro_x_1: $itemty,
638                    __hydro_y_0: $itemty,
639                    __hydro_y_1: $itemty,
640                )
641                    requires
642                        __hydro_state_xy == __hydro_state_yx,
643                        __hydro_x_0 == __hydro_x_1,
644                        __hydro_y_0 == __hydro_y_1,
645                {
646                    $(
647                        let ghost $pstate = __hydro_state_xy;
648                        let ghost $px = __hydro_x_0;
649                        let ghost $py = __hydro_y_0;
650                    )?
651                    let mut __hydro_state_xy = __hydro_state_xy;
652                    let mut __hydro_state_yx = __hydro_state_yx;
653                    let __hydro_keep_xy_x: bool = { let $mcap = &mut __hydro_state_xy; let $titem = __hydro_x_0; $target_body };
654                    let __hydro_keep_xy_y: bool = { let $mcap = &mut __hydro_state_xy; let $titem = __hydro_y_0; $target_body };
655                    let __hydro_keep_yx_y: bool = { let $mcap = &mut __hydro_state_yx; let $titem = __hydro_y_1; $target_body };
656                    let __hydro_keep_yx_x: bool = { let $mcap = &mut __hydro_state_yx; let $titem = __hydro_x_1; $target_body };
657                    $(proof { $($proof_body)* })?
658                    assert(__hydro_state_xy == __hydro_state_yx);
659                    assert(
660                        (__hydro_keep_xy_x == __hydro_keep_yx_x && __hydro_keep_xy_y == __hydro_keep_yx_y)
661                        || (__hydro_x_0 == __hydro_y_0
662                            && (__hydro_keep_xy_x as int) + (__hydro_keep_xy_y as int)
663                                == (__hydro_keep_yx_x as int) + (__hydro_keep_yx_y as int))
664                    );
665                }
666            }
667        };
668
669        $crate::properties::VerusCommutativeProof::new()
670    }};
671    (
672        $($rest:tt)*
673    ) => {{
674        ::core::compile_error!(
675            "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|)`"
676        );
677        $crate::properties::VerusCommutativeProof::new()
678    }};
679}
680
681#[macro_export]
682/// Fulfills a `commutative = ...` proof parameter for a **unit-returning, effectful
683/// closure** (shape `|item| -> ()`, e.g. for `for_each` or `inspect`) that mutates a
684/// captured singleton reference (from [`Singleton::by_mut`]), with a **Verus-checked
685/// proof of commutativity** of the captured-state update.
686///
687/// **What commutativity means for an effectful closure:** processing any two items in
688/// either order, starting from the same captured state, must leave the captured state in
689/// the same final value. Because the closure returns `()` (enforced by this macro; use
690/// [`verus_proof_commutative_map!`] or [`verus_proof_commutative_filter!`] for closures
691/// whose return value is observable), the state is the only observable effect.
692///
693/// The commuting state is the mutable capture, declared (with the type behind the
694/// reference) in the required `captures_mut = |state: S|` clause; additional read-only
695/// captures can be declared in a `captures = |...|` clause. `q!` also passes the
696/// closure's capture list (as a trailing `__captures = [...]` argument), and the macro
697/// checks at compile time that every capture is declared. The `item = ...` type must be
698/// written exactly as the closure receives it (e.g. `&u32` for `inspect`).
699///
700/// Users never write the proof obligation: this macro receives the quoted closure itself
701/// from `q!` (as a trailing `__target = { ... }` argument) and generates a Verus function
702/// that symbolically executes the *actual closure body* in both orders — `x` then `y`,
703/// and `y` then `x` — from equal captured states, and asserts that the final states are
704/// equal. Verifying this also proves the body panic-free.
705///
706/// An optional `proof = |state, x, y| { ... }` clause supplies a proof *script* to help
707/// the SMT solver, with ghost bindings for the initial captured state and the two items.
708/// The script is checked by Verus and cannot weaken the obligation.
709///
710/// The proof is gated on `cfg(verus_keep_ghost)`, so it only exists when the crate is
711/// compiled by the Verus driver (`cargo verus verify`). Under normal compilation the
712/// macro just produces a [`VerusCommutativeProof`] marker.
713///
714/// # Example
715/// ```rust,ignore
716/// let flags_mut = flags.by_mut();
717/// stream.for_each(q!(
718///     |x| { *flags_mut |= x; },
719///     commutative = verus_proof_commutative_effect!(
720///         item = u32,
721///         captures_mut = |flags_mut: u32|,
722///         proof = |s, x, y| { assert(((s | x) | y) == ((s | y) | x)) by (bit_vector); }
723///     )
724/// ));
725/// ```
726///
727/// [`Singleton::by_mut`]: crate::live_collections::singleton::Singleton::by_mut
728macro_rules! __verus_proof_commutative_effect__ {
729    (
730        item = $itemty:ty
731        $(, captures = |$($cap:ident : $capty:ty),* $(,)?|)?
732        , captures_mut = |$mcap:ident : $mcapty:ty $(,)?|
733        $(, proof = |$pstate:ident, $px:ident, $py:ident| { $($proof_body:tt)* })?
734        , __target = { $(move)? |$titem:ident $(: $titemty:ty)?| $target_body:expr }
735        , __captures = [$($fv:ident),* $(,)?] $(,)?
736    ) => {{
737        /// Checks that every free variable captured by the closure is declared (with its
738        /// type) in the `captures = |...|` or `captures_mut = |...|` clauses: within this
739        /// function, the *only* names in scope are the declared captures, so an
740        /// undeclared capture fails to resolve.
741        #[allow(unused, reason = "only checks name resolution")]
742        fn __hydro_verus_captures_declared($($($cap: $capty,)*)? $mcap: $mcapty) {
743            $(let $fv = &$fv;)*
744        }
745
746        #[cfg(verus_keep_ghost)]
747        const _: () = {
748            #[allow(unused_imports)]
749            use ::vstd::prelude::*;
750
751            ::vstd::prelude::verus! {
752                /// Commutativity obligation, generated by
753                /// `verus_proof_commutative_effect!`: applying the closure body with `x`
754                /// then `y` must yield the same captured state as applying it with `y`
755                /// then `x`, from any equal starting states. The closure must return
756                /// `()`, so the state is the only observable effect. Verifying this also
757                /// proves the body panic-free.
758                fn __hydro_verus_commutative(
759                    $($($cap: $capty,)*)?
760                    __hydro_state_xy: $mcapty,
761                    __hydro_state_yx: $mcapty,
762                    __hydro_x_0: $itemty,
763                    __hydro_x_1: $itemty,
764                    __hydro_y_0: $itemty,
765                    __hydro_y_1: $itemty,
766                )
767                    requires
768                        __hydro_state_xy == __hydro_state_yx,
769                        __hydro_x_0 == __hydro_x_1,
770                        __hydro_y_0 == __hydro_y_1,
771                {
772                    $(
773                        let ghost $pstate = __hydro_state_xy;
774                        let ghost $px = __hydro_x_0;
775                        let ghost $py = __hydro_y_0;
776                    )?
777                    let mut __hydro_state_xy = __hydro_state_xy;
778                    let mut __hydro_state_yx = __hydro_state_yx;
779                    { let $mcap = &mut __hydro_state_xy; let $titem = __hydro_x_0; let __hydro_out: () = { $target_body }; }
780                    { let $mcap = &mut __hydro_state_xy; let $titem = __hydro_y_0; let __hydro_out: () = { $target_body }; }
781                    { let $mcap = &mut __hydro_state_yx; let $titem = __hydro_y_1; let __hydro_out: () = { $target_body }; }
782                    { let $mcap = &mut __hydro_state_yx; let $titem = __hydro_x_1; let __hydro_out: () = { $target_body }; }
783                    $(proof { $($proof_body)* })?
784                    assert(__hydro_state_xy == __hydro_state_yx);
785                }
786            }
787        };
788
789        $crate::properties::VerusCommutativeProof::new()
790    }};
791    (
792        $($rest:tt)*
793    ) => {{
794        ::core::compile_error!(
795            "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|)`"
796        );
797        $crate::properties::VerusCommutativeProof::new()
798    }};
799}
800
801/// Marks that the property is not proved.
802pub enum NotProved {}
803
804/// Marks that the property is proven.
805pub enum Proved {}
806
807/// Algebraic properties for an aggregation function of type (T, &mut A) -> ().
808///
809/// Commutativity:
810/// ```rust,ignore
811/// let mut state = ???;
812/// f(a, &mut state); f(b, &mut state) // produces same final state as
813/// f(b, &mut state); f(a, &mut state)
814/// ```
815///
816/// Idempotence:
817/// ```rust,ignore
818/// let mut state = ???;
819/// f(a, &mut state);
820/// let state1 = *state;
821/// f(a, &mut state);
822/// // state1 must be equal to state
823/// ```
824pub struct AggFuncAlgebra<
825    T = (),
826    B: Boundedness = crate::live_collections::boundedness::Unbounded,
827    Commutative = NotProved,
828    Idempotent = NotProved,
829    Monotone = NotProved,
830    S = OnProcess,
831>(
832    Option<Box<dyn CommutativeProof<T, B, S>>>,
833    Option<Box<dyn IdempotentProof>>,
834    Option<Box<dyn MonotoneProof>>,
835    PhantomData<(Commutative, Idempotent, Monotone)>,
836);
837
838impl<T, B: Boundedness, C, I, M, S> AggFuncAlgebra<T, B, C, I, M, S> {
839    /// Marks the function as being commutative, with the given proof mechanism.
840    pub fn commutative(
841        self,
842        proof: impl CommutativeProof<T, B, S> + 'static,
843    ) -> AggFuncAlgebra<T, B, Proved, I, M, S> {
844        AggFuncAlgebra(Some(Box::new(proof)), self.1, self.2, PhantomData)
845    }
846
847    /// Marks the function as being idempotent, with the given proof mechanism.
848    pub fn idempotent(
849        self,
850        proof: impl IdempotentProof + 'static,
851    ) -> AggFuncAlgebra<T, B, C, Proved, M, S> {
852        AggFuncAlgebra(self.0, Some(Box::new(proof)), self.2, PhantomData)
853    }
854
855    /// Marks the function as being monotone, with the given proof mechanism.
856    pub fn monotone(
857        self,
858        proof: impl MonotoneProof + 'static,
859    ) -> AggFuncAlgebra<T, B, C, I, Proved, S> {
860        AggFuncAlgebra(self.0, self.1, Some(Box::new(proof)), PhantomData)
861    }
862
863    /// Registers the expression with the underlying proof mechanisms, and takes the
864    /// simulator ordering hook attached to the commutativity proof, if any.
865    pub(crate) fn register_proof(
866        self,
867        expr: &syn::Expr,
868    ) -> Option<OrderingHook<T, B, ExactlyOnce, S>> {
869        let mut hook = None;
870        if let Some(mut comm_proof) = self.0 {
871            comm_proof.register_proof(expr);
872            hook = comm_proof.take_hook();
873        }
874
875        if let Some(idem_proof) = self.1 {
876            idem_proof.register_proof(expr);
877        }
878
879        if let Some(monotone_proof) = self.2 {
880            monotone_proof.register_proof(expr);
881        }
882
883        hook
884    }
885}
886
887impl<T, B: Boundedness, C, I, M, S> Property for AggFuncAlgebra<T, B, C, I, M, S> {
888    type Root = AggFuncAlgebra<T, B, NotProved, NotProved, NotProved, S>;
889
890    fn make_root(_target: &mut Option<Self>) -> Self::Root {
891        AggFuncAlgebra(None, None, None, PhantomData)
892    }
893}
894
895/// Algebraic properties for a singleton map function of type T -> U.
896///
897/// Order-preserving means that if the input grows monotonically, the output also grows monotonically.
898pub struct SingletonMapFuncAlgebra<
899    T = (),
900    B: Boundedness = crate::live_collections::boundedness::Unbounded,
901    OrderPreserving = NotProved,
902    Commutative = NotProved,
903    Idempotent = NotProved,
904    S = OnProcess,
905>(
906    Option<Box<dyn OrderPreservingProof>>,
907    Option<Box<dyn CommutativeProof<T, B, S>>>,
908    Option<Box<dyn IdempotentProof>>,
909    PhantomData<(OrderPreserving, Commutative, Idempotent)>,
910);
911
912impl<T, B: Boundedness, O, C, I, S> SingletonMapFuncAlgebra<T, B, O, C, I, S> {
913    /// Marks the function as being order-preserving, with the given proof mechanism.
914    pub fn order_preserving(
915        self,
916        proof: impl OrderPreservingProof + 'static,
917    ) -> SingletonMapFuncAlgebra<T, B, Proved, C, I, S> {
918        SingletonMapFuncAlgebra(Some(Box::new(proof)), self.1, self.2, PhantomData)
919    }
920
921    /// Marks the function as being commutative, with the given proof mechanism.
922    pub fn commutative(
923        self,
924        proof: impl CommutativeProof<T, B, S> + 'static,
925    ) -> SingletonMapFuncAlgebra<T, B, O, Proved, I, S> {
926        SingletonMapFuncAlgebra(self.0, Some(Box::new(proof)), self.2, PhantomData)
927    }
928
929    /// Marks the function as being idempotent, with the given proof mechanism.
930    pub fn idempotent(
931        self,
932        proof: impl IdempotentProof + 'static,
933    ) -> SingletonMapFuncAlgebra<T, B, O, C, Proved, S> {
934        SingletonMapFuncAlgebra(self.0, self.1, Some(Box::new(proof)), PhantomData)
935    }
936
937    /// Registers the expression with the underlying proof mechanisms, and takes the
938    /// simulator ordering hook attached to the commutativity proof, if any.
939    pub(crate) fn register_proof(
940        self,
941        expr: &syn::Expr,
942    ) -> Option<OrderingHook<T, B, ExactlyOnce, S>> {
943        if let Some(proof) = self.0 {
944            proof.register_proof(expr);
945        }
946        self.1.and_then(|mut proof| {
947            proof.register_proof(expr);
948            proof.take_hook()
949        })
950    }
951}
952
953impl<T, B: Boundedness, O, C, I, S> Property for SingletonMapFuncAlgebra<T, B, O, C, I, S> {
954    type Root = SingletonMapFuncAlgebra<T, B, NotProved, NotProved, NotProved, S>;
955
956    fn make_root(_target: &mut Option<Self>) -> Self::Root {
957        SingletonMapFuncAlgebra(None, None, None, PhantomData)
958    }
959}
960
961/// Algebraic properties for a stream map function of type T -> U.
962pub struct StreamMapFuncAlgebra<
963    T = (),
964    B: Boundedness = crate::live_collections::boundedness::Unbounded,
965    Commutative = NotProved,
966    Idempotent = NotProved,
967    S = OnProcess,
968>(
969    Option<Box<dyn CommutativeProof<T, B, S>>>,
970    Option<Box<dyn IdempotentProof>>,
971    PhantomData<(Commutative, Idempotent)>,
972);
973
974impl<T, B: Boundedness, C, I, S> StreamMapFuncAlgebra<T, B, C, I, S> {
975    /// Marks the function as being commutative, with the given proof mechanism.
976    pub fn commutative(
977        self,
978        proof: impl CommutativeProof<T, B, S> + 'static,
979    ) -> StreamMapFuncAlgebra<T, B, Proved, I, S> {
980        StreamMapFuncAlgebra(Some(Box::new(proof)), self.1, PhantomData)
981    }
982
983    /// Marks the function as being idempotent, with the given proof mechanism.
984    pub fn idempotent(
985        self,
986        proof: impl IdempotentProof + 'static,
987    ) -> StreamMapFuncAlgebra<T, B, C, Proved, S> {
988        StreamMapFuncAlgebra(self.0, Some(Box::new(proof)), PhantomData)
989    }
990
991    /// Registers the expression with the underlying proof mechanisms, and takes the
992    /// simulator ordering hook attached to the commutativity proof, if any.
993    pub(crate) fn register_proof(
994        self,
995        expr: &syn::Expr,
996    ) -> Option<OrderingHook<T, B, ExactlyOnce, S>> {
997        let hook = self.0.and_then(|mut proof| {
998            proof.register_proof(expr);
999            proof.take_hook()
1000        });
1001        if let Some(proof) = self.1 {
1002            proof.register_proof(expr);
1003        }
1004        hook
1005    }
1006}
1007
1008impl<T, B: Boundedness, C, I, S> Property for StreamMapFuncAlgebra<T, B, C, I, S> {
1009    type Root = StreamMapFuncAlgebra<T, B, NotProved, NotProved, S>;
1010
1011    fn make_root(_target: &mut Option<Self>) -> Self::Root {
1012        StreamMapFuncAlgebra(None, None, PhantomData)
1013    }
1014}
1015
1016/// Marker trait identifying that the commutativity property is valid for the given stream ordering.
1017///
1018/// **Definition (aggregations, `|acc: &mut A, item: T|`):** for any accumulator value
1019/// and any two items `x`, `y`, applying the closure with `x` then `y` must produce the
1020/// same final accumulator as applying it with `y` then `x`. This makes the final
1021/// aggregate independent of the (non-deterministic) arrival order; note that
1022/// *intermediate* accumulator values may still differ and must not be observed without
1023/// a non-determinism annotation.
1024#[diagnostic::on_unimplemented(
1025    message = "because the input stream has ordering `{O}`, the closure must demonstrate commutativity",
1026    label = "required for this call",
1027    note = "prove it with a `commutative = manual_proof!(/** why the order does not matter */)` annotation on the closure",
1028    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"
1029)]
1030#[sealed::sealed]
1031pub trait ValidCommutativityFor<O: Ordering> {}
1032#[sealed::sealed]
1033impl ValidCommutativityFor<TotalOrder> for NotProved {}
1034#[sealed::sealed]
1035impl<O: Ordering> ValidCommutativityFor<O> for Proved {}
1036
1037/// Marker trait identifying that the idempotence property is valid for the given stream ordering.
1038#[diagnostic::on_unimplemented(
1039    message = "because the input stream has retries `{R}`, the closure must demonstrate idempotence",
1040    label = "required for this call",
1041    note = "prove it with an `idempotent = manual_proof!(/** why a repeat does not matter */)` annotation on the closure",
1042    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"
1043)]
1044#[sealed::sealed]
1045pub trait ValidIdempotenceFor<R: Retries> {}
1046#[sealed::sealed]
1047impl ValidIdempotenceFor<ExactlyOnce> for NotProved {}
1048#[sealed::sealed]
1049impl<R: Retries> ValidIdempotenceFor<R> for Proved {}
1050
1051/// Marker trait identifying that the commutativity property is valid for the given stream ordering.
1052///
1053/// A proof is required when the stream is unordered **and** the closure mutably captures
1054/// state (`WAS_MUT`). **Definition (owned-item closures, `|item: T| -> Out`, e.g. `map`
1055/// / `for_each`):** processing any two items in either order must (1) leave the
1056/// mutably-captured state (e.g. [`Singleton::by_mut`] references) in the same final
1057/// value, *and* (2) produce the same **multiset of return values**. Condition (2) is
1058/// required whenever the return value is observable (as in `map`): a closure that
1059/// emits, say, a running total is **not** commutative even if its state update is. For
1060/// `()`-returning closures (as in `for_each`), condition (2) is trivial.
1061///
1062/// [`Singleton::by_mut`]: crate::live_collections::singleton::Singleton::by_mut
1063#[sealed::sealed]
1064#[diagnostic::on_unimplemented(
1065    message = "because the input stream has ordering `{O}`, the closure must demonstrate commutativity",
1066    label = "required for this call",
1067    note = "prove it with a `commutative = manual_proof!(/** why the order does not matter */)` annotation on the closure",
1068    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"
1069)]
1070pub trait ValidMutCommutativityFor<F: FnMut(In) -> Out, In, Out, O: Ordering, const WAS_MUT: bool> {}
1071#[sealed::sealed]
1072impl<In, Out, F: FnMut(In) -> Out> ValidMutCommutativityFor<F, In, Out, TotalOrder, true>
1073    for NotProved
1074{
1075}
1076#[sealed::sealed]
1077impl<In, Out, F: Fn(In) -> Out, O: Ordering> ValidMutCommutativityFor<F, In, Out, O, false>
1078    for NotProved
1079{
1080}
1081#[sealed::sealed]
1082impl<In, Out, F: FnMut(In) -> Out, O: Ordering> ValidMutCommutativityFor<F, In, Out, O, true>
1083    for Proved
1084{
1085}
1086#[sealed::sealed]
1087impl<In, Out, F: Fn(In) -> Out, O: Ordering> ValidMutCommutativityFor<F, In, Out, O, false>
1088    for Proved
1089{
1090}
1091
1092/// Marker trait identifying that the idempotence property is valid for the given stream ordering.
1093#[diagnostic::on_unimplemented(
1094    message = "because the input stream has retries `{R}`, the closure must demonstrate idempotence",
1095    label = "required for this call",
1096    note = "prove it with an `idempotent = manual_proof!(/** why a repeat does not matter */)` annotation on the closure",
1097    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"
1098)]
1099#[sealed::sealed]
1100pub trait ValidMutIdempotenceFor<F: FnMut(In) -> Out, In, Out, R: Retries, const WAS_MUT: bool> {}
1101#[sealed::sealed]
1102impl<In, Out, F: FnMut(In) -> Out> ValidMutIdempotenceFor<F, In, Out, ExactlyOnce, true>
1103    for NotProved
1104{
1105}
1106#[sealed::sealed]
1107impl<In, Out, F: Fn(In) -> Out, R: Retries> ValidMutIdempotenceFor<F, In, Out, R, false>
1108    for NotProved
1109{
1110}
1111#[sealed::sealed]
1112impl<In, Out, F: FnMut(In) -> Out, R: Retries> ValidMutIdempotenceFor<F, In, Out, R, true>
1113    for Proved
1114{
1115}
1116#[sealed::sealed]
1117impl<In, Out, F: Fn(In) -> Out, R: Retries> ValidMutIdempotenceFor<F, In, Out, R, false>
1118    for Proved
1119{
1120}
1121
1122/// Marker trait for commutativity of closures that borrow their input (`FnMut(&In) -> Out`).
1123///
1124/// A proof is required when the stream is unordered **and** the closure mutably captures
1125/// state (`WAS_MUT`). **Definition (borrowing closures, `|item: &T| -> Out`, e.g.
1126/// `filter` / `inspect`):** processing any two items in either order must (1) leave the
1127/// mutably-captured state (e.g. [`Singleton::by_mut`] references) in the same final
1128/// value, *and* (2) keep the operator's observable output identical as a multiset. For
1129/// `filter`, the outputs are the **retained elements**, so the predicate's decisions
1130/// must not depend on the processing order: a stateful predicate like a rate limiter is
1131/// **not** commutative — its budget converges either way, but *which* element passes
1132/// depends on the order. For `inspect`, the elements pass through unchanged, so only
1133/// condition (1) applies.
1134///
1135/// [`Singleton::by_mut`]: crate::live_collections::singleton::Singleton::by_mut
1136#[sealed::sealed]
1137#[diagnostic::on_unimplemented(
1138    message = "because the input stream has ordering `{O}`, the closure must demonstrate commutativity",
1139    label = "required for this call",
1140    note = "prove it with a `commutative = manual_proof!(/** why the order does not matter */)` annotation on the closure",
1141    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"
1142)]
1143pub trait ValidMutBorrowCommutativityFor<
1144    F: FnMut(&In) -> Out,
1145    In: ?Sized,
1146    Out,
1147    O: Ordering,
1148    const WAS_MUT: bool,
1149>
1150{
1151}
1152#[sealed::sealed]
1153impl<In: ?Sized, Out, F: FnMut(&In) -> Out>
1154    ValidMutBorrowCommutativityFor<F, In, Out, TotalOrder, true> for NotProved
1155{
1156}
1157#[sealed::sealed]
1158impl<In: ?Sized, Out, F: Fn(&In) -> Out, O: Ordering>
1159    ValidMutBorrowCommutativityFor<F, In, Out, O, false> for NotProved
1160{
1161}
1162#[sealed::sealed]
1163impl<In: ?Sized, Out, F: FnMut(&In) -> Out, O: Ordering>
1164    ValidMutBorrowCommutativityFor<F, In, Out, O, true> for Proved
1165{
1166}
1167#[sealed::sealed]
1168impl<In: ?Sized, Out, F: Fn(&In) -> Out, O: Ordering>
1169    ValidMutBorrowCommutativityFor<F, In, Out, O, false> for Proved
1170{
1171}
1172
1173/// Marker trait for idempotence of closures that borrow their input (`FnMut(&In) -> Out`).
1174#[diagnostic::on_unimplemented(
1175    message = "because the input stream has retries `{R}`, the closure must demonstrate idempotence",
1176    label = "required for this call",
1177    note = "prove it with an `idempotent = manual_proof!(/** why a repeat does not matter */)` annotation on the closure",
1178    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"
1179)]
1180#[sealed::sealed]
1181pub trait ValidMutBorrowIdempotenceFor<
1182    F: FnMut(&In) -> Out,
1183    In: ?Sized,
1184    Out,
1185    R: Retries,
1186    const WAS_MUT: bool,
1187>
1188{
1189}
1190#[sealed::sealed]
1191impl<In: ?Sized, Out, F: FnMut(&In) -> Out>
1192    ValidMutBorrowIdempotenceFor<F, In, Out, ExactlyOnce, true> for NotProved
1193{
1194}
1195#[sealed::sealed]
1196impl<In: ?Sized, Out, F: Fn(&In) -> Out, R: Retries>
1197    ValidMutBorrowIdempotenceFor<F, In, Out, R, false> for NotProved
1198{
1199}
1200#[sealed::sealed]
1201impl<In: ?Sized, Out, F: FnMut(&In) -> Out, R: Retries>
1202    ValidMutBorrowIdempotenceFor<F, In, Out, R, true> for Proved
1203{
1204}
1205#[sealed::sealed]
1206impl<In: ?Sized, Out, F: Fn(&In) -> Out, R: Retries>
1207    ValidMutBorrowIdempotenceFor<F, In, Out, R, false> for Proved
1208{
1209}
1210
1211/// Marker trait identifying the boundedness of a singleton given a monotonicity property of
1212/// an aggregation on a stream.
1213#[sealed::sealed]
1214pub trait ApplyMonotoneStream<P, B2: SingletonBound> {}
1215
1216#[sealed::sealed]
1217impl<B: Boundedness> ApplyMonotoneStream<NotProved, B> for B {}
1218
1219#[sealed::sealed]
1220impl<B: Boundedness> ApplyMonotoneStream<Proved, B::StreamToMonotone> for B {}
1221
1222/// Marker trait identifying the boundedness of a singleton given a monotonicity property of
1223/// an aggregation on a keyed stream.
1224#[sealed::sealed]
1225pub trait ApplyMonotoneKeyedStream<P, B2: KeyedSingletonBound> {}
1226
1227#[sealed::sealed]
1228impl<B: Boundedness> ApplyMonotoneKeyedStream<NotProved, B::KeyedStreamToNonMonotone> for B {}
1229
1230#[sealed::sealed]
1231impl<B: Boundedness> ApplyMonotoneKeyedStream<Proved, B::KeyedStreamToMonotone> for B {}
1232
1233/// Marker trait identifying the boundedness of a singleton after a map operation,
1234/// given an order-preserving property.
1235#[sealed::sealed]
1236pub trait ApplyOrderPreservingSingleton<P, B2: SingletonBound> {}
1237
1238#[sealed::sealed]
1239impl<B: SingletonBound> ApplyOrderPreservingSingleton<NotProved, B::UnderlyingBound> for B {}
1240
1241#[sealed::sealed]
1242impl<B: SingletonBound> ApplyOrderPreservingSingleton<Proved, B> for B {}