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