Skip to main content

hydro_lang/live_collections/sliced/
style.rs

1//! Styled wrappers for live collections used with the `sliced!` macro.
2//!
3//! This module provides wrapper types that store both a collection and its associated
4//! non-determinism guard, allowing the nondet to be properly passed through during slicing.
5
6#[cfg(stageleft_runtime)]
7use std::marker::PhantomData;
8
9use super::Slicable;
10#[cfg(stageleft_runtime)]
11use crate::forward_handle::{CycleCollection, CycleCollectionWithInitial};
12use crate::forward_handle::{TickCycle, TickCycleHandle};
13use crate::live_collections::boundedness::{Bounded, Boundedness, Unbounded};
14use crate::live_collections::keyed_singleton::{BoundedValue, KeyedSingletonBound};
15use crate::live_collections::singleton::SingletonBound;
16use crate::live_collections::stream::{Ordering, Retries};
17use crate::location::Location;
18use crate::location::tick::{DeferTick, Tick};
19use crate::nondet::NonDet;
20
21/// Default style wrapper that stores a collection and its non-determinism guard.
22///
23/// This is used by the `sliced!` macro when no explicit style is specified.
24pub struct Default<T> {
25    pub(crate) collection: T,
26    pub(crate) nondet: NonDet,
27}
28
29impl<T> Default<T> {
30    /// Creates a new default-styled wrapper.
31    pub fn new(collection: T, nondet: NonDet) -> Self {
32        Self { collection, nondet }
33    }
34}
35
36/// Helper function for unstyled `use` in `sliced!` macro - wraps the collection in Default style.
37#[doc(hidden)]
38pub fn default<T>(t: T, nondet: NonDet) -> Default<T> {
39    Default::new(t, nondet)
40}
41
42/// Atomic style wrapper that stores a collection and its non-determinism guard.
43///
44/// This is used by the `sliced!` macro when `use::atomic(...)` is specified.
45pub struct Atomic<T> {
46    pub(crate) collection: T,
47    pub(crate) nondet: NonDet,
48}
49
50impl<T> Atomic<T> {
51    /// Creates a new atomic-styled wrapper.
52    pub fn new(collection: T, nondet: NonDet) -> Self {
53        Self { collection, nondet }
54    }
55}
56
57/// Wraps a live collection to be treated atomically during slicing.
58pub fn atomic<T>(t: T, nondet: NonDet) -> Atomic<T> {
59    Atomic::new(t, nondet)
60}
61
62/// Creates a stateful cycle with an initial value for use in `sliced!`.
63///
64/// The tick (which is the source of truth for lifetimes) is bound first, returning a
65/// [`StateBuilder`] which accepts the user-provided initializer via [`StateBuilder::build`].
66/// This two-step layout ensures that type errors caused by a bad initializer are attributed
67/// to the initializer argument rather than the tick or the entire macro invocation.
68///
69/// The initial value is computed from a closure that receives the location
70/// for the body of the slice.
71///
72/// The initial value is used on the first iteration, and subsequent iterations receive
73/// the value assigned to the mutable binding at the end of the previous iteration.
74#[cfg(stageleft_runtime)]
75pub fn state<'t, S, L>(tick: &'t Tick<L>) -> StateBuilder<'t, S, L> {
76    StateBuilder {
77        tick,
78        _phantom: PhantomData,
79    }
80}
81
82/// Builder returned by [`state`], which accepts the user-provided initializer.
83#[cfg(stageleft_runtime)]
84pub struct StateBuilder<'t, S, L> {
85    tick: &'t Tick<L>,
86    _phantom: PhantomData<S>,
87}
88
89#[cfg(stageleft_runtime)]
90impl<'t, 'a, S, L: Location<'a>> StateBuilder<'t, S, L> {
91    /// Supplies the initializer closure and creates the stateful cycle.
92    ///
93    /// The initializer takes the tick at the builder's `'t` lifetime (rather than a
94    /// higher-ranked `for<'x>` bound), since the builder already stores the tick reference.
95    /// This way, an initializer that requires a specific tick reference lifetime produces a
96    /// borrow error directly on the tick, instead of a confusing "implementation of `Fn` is
97    /// not general enough" error that blames an unrelated variable.
98    #[expect(
99        private_bounds,
100        reason = "only Hydro collections can implement CycleCollectionWithInitial"
101    )]
102    pub fn build(self, initial_fn: impl FnOnce(&'t Tick<L>) -> S) -> (TickCycleHandle<'a, S>, S)
103    where
104        S: CycleCollectionWithInitial<'a, TickCycle, Location = Tick<L::DropConsistency>>,
105    {
106        let initial = initial_fn(self.tick);
107        initial.location().clone().cycle_with_initial(initial)
108    }
109}
110
111/// Creates a stateful cycle without an initial value for use in `sliced!`.
112///
113/// The tick (which is the source of truth for lifetimes) is bound first, returning a
114/// [`StateNullBuilder`] which creates the cycle via [`StateNullBuilder::build`].
115///
116/// On the first iteration, the state will be null/empty. Subsequent iterations receive
117/// the value assigned to the mutable binding at the end of the previous iteration.
118#[cfg(stageleft_runtime)]
119pub fn state_null<'t, S, L>(tick: &'t Tick<L>) -> StateNullBuilder<'t, S, L> {
120    StateNullBuilder {
121        tick,
122        _phantom: PhantomData,
123    }
124}
125
126/// Builder returned by [`state_null`], which creates the cycle.
127#[cfg(stageleft_runtime)]
128pub struct StateNullBuilder<'t, S, L> {
129    tick: &'t Tick<L>,
130    _phantom: PhantomData<S>,
131}
132
133#[cfg(stageleft_runtime)]
134impl<'t, 'a, S, L: Location<'a>> StateNullBuilder<'t, S, L> {
135    /// Creates the stateful cycle, which starts as null/empty on the first iteration.
136    #[expect(
137        private_bounds,
138        reason = "only Hydro collections can implement CycleCollection"
139    )]
140    pub fn build(self) -> (TickCycleHandle<'a, S>, S)
141    where
142        S: CycleCollection<'a, TickCycle, Location = Tick<L::DropConsistency>> + DeferTick,
143    {
144        self.tick.cycle::<S, _>()
145    }
146}
147
148// ============================================================================
149// Default style Slicable implementations
150//
151// All of these drop consistency because they are performing non-deterministic
152// batching / snapshotting.
153// ============================================================================
154
155impl<'a, T, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
156    Slicable<'a, L::DropConsistency> for Default<crate::live_collections::Stream<T, L, B, O, R>>
157{
158    type Slice = crate::live_collections::Stream<T, Tick<L::DropConsistency>, Bounded, O, R>;
159    type Backtrace = crate::compile::ir::backtrace::Backtrace;
160
161    fn get_location(&self) -> L::DropConsistency {
162        self.collection.location().drop_consistency()
163    }
164    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
165        let out = self.collection.batch(tick, self.nondet);
166        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
167        out
168    }
169}
170
171impl<'a, T, L: Location<'a>, B: SingletonBound> Slicable<'a, L::DropConsistency>
172    for Default<crate::live_collections::Singleton<T, L, B>>
173{
174    type Slice = crate::live_collections::Singleton<T, Tick<L::DropConsistency>, Bounded>;
175    type Backtrace = crate::compile::ir::backtrace::Backtrace;
176
177    fn get_location(&self) -> L::DropConsistency {
178        self.collection.location().drop_consistency()
179    }
180    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
181        let out = self.collection.snapshot(tick, self.nondet);
182        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
183        out
184    }
185}
186
187impl<'a, T, L: Location<'a>, B: Boundedness> Slicable<'a, L::DropConsistency>
188    for Default<crate::live_collections::Optional<T, L, B>>
189{
190    type Slice = crate::live_collections::Optional<T, Tick<L::DropConsistency>, Bounded>;
191    type Backtrace = crate::compile::ir::backtrace::Backtrace;
192
193    fn get_location(&self) -> L::DropConsistency {
194        self.collection.location().drop_consistency()
195    }
196    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
197        let out = self.collection.snapshot(tick, self.nondet);
198        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
199        out
200    }
201}
202
203impl<'a, K, V, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
204    Slicable<'a, L::DropConsistency>
205    for Default<crate::live_collections::KeyedStream<K, V, L, B, O, R>>
206{
207    type Slice =
208        crate::live_collections::KeyedStream<K, V, Tick<L::DropConsistency>, Bounded, O, R>;
209    type Backtrace = crate::compile::ir::backtrace::Backtrace;
210
211    fn get_location(&self) -> L::DropConsistency {
212        self.collection.location().drop_consistency()
213    }
214    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
215        let out = self.collection.batch(tick, self.nondet);
216        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
217        out
218    }
219}
220
221impl<'a, K, V, L: Location<'a>, B: KeyedSingletonBound<ValueBound = Unbounded>>
222    Slicable<'a, L::DropConsistency>
223    for Default<crate::live_collections::KeyedSingleton<K, V, L, B>>
224{
225    type Slice = crate::live_collections::KeyedSingleton<K, V, Tick<L::DropConsistency>, Bounded>;
226    type Backtrace = crate::compile::ir::backtrace::Backtrace;
227
228    fn get_location(&self) -> L::DropConsistency {
229        self.collection.location().drop_consistency()
230    }
231    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
232        let out = self.collection.snapshot(tick, self.nondet);
233        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
234        out
235    }
236}
237
238impl<'a, K, V, L: Location<'a>> Slicable<'a, L::DropConsistency>
239    for Default<crate::live_collections::KeyedSingleton<K, V, L, BoundedValue>>
240{
241    type Slice = crate::live_collections::KeyedSingleton<K, V, Tick<L::DropConsistency>, Bounded>;
242    type Backtrace = crate::compile::ir::backtrace::Backtrace;
243
244    fn get_location(&self) -> L::DropConsistency {
245        self.collection.location().drop_consistency()
246    }
247    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
248        let out = self.collection.batch(tick, self.nondet);
249        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
250        out
251    }
252}
253
254// ============================================================================
255// Atomic style Slicable implementations
256// ============================================================================
257
258impl<'a, T, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
259    Slicable<'a, L::DropConsistency>
260    for Atomic<crate::live_collections::Stream<T, crate::location::Atomic<L>, B, O, R>>
261{
262    type Slice = crate::live_collections::Stream<T, Tick<L::DropConsistency>, Bounded, O, R>;
263    type Backtrace = crate::compile::ir::backtrace::Backtrace;
264    fn get_location(&self) -> L::DropConsistency {
265        self.collection.location().tick.l.drop_consistency()
266    }
267
268    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
269        let out = self.collection.batch_atomic(tick, self.nondet);
270        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
271        out
272    }
273}
274
275impl<'a, T, L: Location<'a>, B: SingletonBound> Slicable<'a, L::DropConsistency>
276    for Atomic<crate::live_collections::Singleton<T, crate::location::Atomic<L>, B>>
277{
278    type Slice = crate::live_collections::Singleton<T, Tick<L::DropConsistency>, Bounded>;
279    type Backtrace = crate::compile::ir::backtrace::Backtrace;
280    fn get_location(&self) -> L::DropConsistency {
281        self.collection.location().tick.l.drop_consistency()
282    }
283
284    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
285        let out = self.collection.snapshot_atomic(tick, self.nondet);
286        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
287        out
288    }
289}
290
291impl<'a, T, L: Location<'a>, B: Boundedness> Slicable<'a, L::DropConsistency>
292    for Atomic<crate::live_collections::Optional<T, crate::location::Atomic<L>, B>>
293{
294    type Slice = crate::live_collections::Optional<T, Tick<L::DropConsistency>, Bounded>;
295    type Backtrace = crate::compile::ir::backtrace::Backtrace;
296    fn get_location(&self) -> L::DropConsistency {
297        self.collection.location().tick.l.drop_consistency()
298    }
299
300    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
301        let out = self.collection.snapshot_atomic(tick, self.nondet);
302        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
303        out
304    }
305}
306
307impl<'a, K, V, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
308    Slicable<'a, L::DropConsistency>
309    for Atomic<crate::live_collections::KeyedStream<K, V, crate::location::Atomic<L>, B, O, R>>
310{
311    type Slice =
312        crate::live_collections::KeyedStream<K, V, Tick<L::DropConsistency>, Bounded, O, R>;
313    type Backtrace = crate::compile::ir::backtrace::Backtrace;
314    fn get_location(&self) -> L::DropConsistency {
315        self.collection.location().tick.l.drop_consistency()
316    }
317
318    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
319        let out = self.collection.batch_atomic(tick, self.nondet);
320        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
321        out
322    }
323}
324
325impl<'a, K, V, L: Location<'a>, B: KeyedSingletonBound<ValueBound = Unbounded>>
326    Slicable<'a, L::DropConsistency>
327    for Atomic<crate::live_collections::KeyedSingleton<K, V, crate::location::Atomic<L>, B>>
328{
329    type Slice = crate::live_collections::KeyedSingleton<K, V, Tick<L::DropConsistency>, Bounded>;
330    type Backtrace = crate::compile::ir::backtrace::Backtrace;
331    fn get_location(&self) -> L::DropConsistency {
332        self.collection.location().tick.l.drop_consistency()
333    }
334
335    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
336        let out = self.collection.snapshot_atomic(tick, self.nondet);
337        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
338        out
339    }
340}
341
342impl<'a, K, V, L: Location<'a>> Slicable<'a, L::DropConsistency>
343    for Atomic<
344        crate::live_collections::KeyedSingleton<K, V, crate::location::Atomic<L>, BoundedValue>,
345    >
346{
347    type Slice = crate::live_collections::KeyedSingleton<K, V, Tick<L::DropConsistency>, Bounded>;
348    type Backtrace = crate::compile::ir::backtrace::Backtrace;
349    fn get_location(&self) -> L::DropConsistency {
350        self.collection.location().tick.l.drop_consistency()
351    }
352
353    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
354        let out = self.collection.batch_atomic(tick, self.nondet);
355        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
356        out
357    }
358}