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 {}