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