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::{
15    BoundedValue, KeyedSingletonBound, KeyedSingletonSnapshotBound,
16};
17use crate::live_collections::optional::OptionalBound;
18use crate::live_collections::singleton::SingletonBound;
19use crate::live_collections::stream::{ExactlyOnce, Ordering, Retries, TotalOrder};
20use crate::location::Location;
21use crate::location::tick::{DeferTick, Tick};
22use crate::nondet::{NonDet, nondet};
23
24/// Default style wrapper that stores a collection and its non-determinism guard.
25///
26/// This is used by the `sliced!` macro when no explicit style is specified. This style is
27/// deprecated; use the explicit [`batch`] or [`snapshot`] styles instead.
28pub struct Default<T> {
29    pub(crate) collection: T,
30    pub(crate) nondet: NonDet,
31}
32
33impl<T> Default<T> {
34    /// Creates a new default-styled wrapper.
35    pub fn new(collection: T, nondet: NonDet) -> Self {
36        Self { collection, nondet }
37    }
38}
39
40/// Helper function for unstyled `use` in `sliced!` macro - wraps the collection in Default style.
41#[doc(hidden)]
42#[deprecated(
43    note = "use `use::batch(...)` for stream-like collections or `use::snapshot(...)` for singleton-like collections instead"
44)]
45pub fn default<T>(t: T, nondet: NonDet) -> Default<T> {
46    Default::new(t, nondet)
47}
48
49/// Batch style wrapper that stores a stream-like collection and its non-determinism guard.
50///
51/// This is used by the `sliced!` macro when `use::batch(...)` is specified.
52pub struct Batch<T, H = ()> {
53    pub(crate) collection: T,
54    pub(crate) nondet: NonDet<H>,
55}
56
57impl<T, H> Batch<T, H> {
58    /// Creates a new batch-styled wrapper.
59    pub fn new(collection: T, nondet: NonDet<H>) -> Self {
60        Self { collection, nondet }
61    }
62}
63
64/// Wraps a stream-like live collection (such as a [`Stream`](crate::live_collections::Stream),
65/// [`KeyedStream`](crate::live_collections::KeyedStream), or a
66/// [`KeyedSingleton`](crate::live_collections::KeyedSingleton) with bounded values) to be
67/// sliced into non-deterministic batches of asynchronously arriving elements.
68pub fn batch<T, H>(t: T, nondet: NonDet<H>) -> Batch<T, H> {
69    Batch::new(t, nondet)
70}
71
72/// Snapshot style wrapper that stores a singleton-like collection and its non-determinism guard.
73///
74/// This is used by the `sliced!` macro when `use::snapshot(...)` is specified.
75pub struct Snapshot<T, H = ()> {
76    pub(crate) collection: T,
77    pub(crate) nondet: NonDet<H>,
78}
79
80impl<T, H> Snapshot<T, H> {
81    /// Creates a new snapshot-styled wrapper.
82    pub fn new(collection: T, nondet: NonDet<H>) -> Self {
83        Self { collection, nondet }
84    }
85}
86
87/// Wraps a singleton-like live collection (such as a
88/// [`Singleton`](crate::live_collections::Singleton),
89/// [`Optional`](crate::live_collections::Optional), or a
90/// [`KeyedSingleton`](crate::live_collections::KeyedSingleton) with asynchronously updated
91/// values) to be sliced into non-deterministic snapshots of its continuously changing value.
92pub fn snapshot<T, H>(t: T, nondet: NonDet<H>) -> Snapshot<T, H> {
93    Snapshot::new(t, nondet)
94}
95
96/// Atomic style wrapper that stores a collection and its non-determinism guard.
97///
98/// This is used by the `sliced!` macro when `use::atomic(...)` is specified.
99pub struct Atomic<T> {
100    pub(crate) collection: T,
101    pub(crate) nondet: NonDet,
102}
103
104impl<T> Atomic<T> {
105    /// Creates a new atomic-styled wrapper.
106    pub fn new(collection: T, nondet: NonDet) -> Self {
107        Self { collection, nondet }
108    }
109}
110
111/// Wraps a live collection to be treated atomically during slicing.
112pub fn atomic<T>(t: T, nondet: NonDet) -> Atomic<T> {
113    Atomic::new(t, nondet)
114}
115
116/// Creates a stateful cycle with an initial value for use in `sliced!`.
117///
118/// The tick (which is the source of truth for lifetimes) is bound first, returning a
119/// [`StateBuilder`] which accepts the user-provided initializer via [`StateBuilder::build`].
120/// This two-step layout ensures that type errors caused by a bad initializer are attributed
121/// to the initializer argument rather than the tick or the entire macro invocation.
122///
123/// The initial value is computed from a closure that receives the location
124/// for the body of the slice.
125///
126/// The initial value is used on the first iteration, and subsequent iterations receive
127/// the value assigned to the mutable binding at the end of the previous iteration.
128#[cfg(stageleft_runtime)]
129pub fn state<'t, S, L>(tick: &'t Tick<L>) -> StateBuilder<'t, S, L> {
130    StateBuilder {
131        tick,
132        _phantom: PhantomData,
133    }
134}
135
136/// Builder returned by [`state`], which accepts the user-provided initializer.
137#[cfg(stageleft_runtime)]
138pub struct StateBuilder<'t, S, L> {
139    tick: &'t Tick<L>,
140    _phantom: PhantomData<S>,
141}
142
143#[cfg(stageleft_runtime)]
144impl<'t, 'a, S, L: Location<'a>> StateBuilder<'t, S, L> {
145    /// Supplies the initializer closure and creates the stateful cycle.
146    ///
147    /// The initializer takes the tick at the builder's `'t` lifetime (rather than a
148    /// higher-ranked `for<'x>` bound), since the builder already stores the tick reference.
149    /// This way, an initializer that requires a specific tick reference lifetime produces a
150    /// borrow error directly on the tick, instead of a confusing "implementation of `Fn` is
151    /// not general enough" error that blames an unrelated variable.
152    #[expect(
153        private_bounds,
154        reason = "only Hydro collections can implement CycleCollectionWithInitial"
155    )]
156    pub fn build(self, initial_fn: impl FnOnce(&'t Tick<L>) -> S) -> (TickCycleHandle<'a, S>, S)
157    where
158        S: CycleCollectionWithInitial<'a, TickCycle, Location = Tick<L::DropConsistency>>,
159    {
160        let initial = initial_fn(self.tick);
161        initial.location().clone().cycle_with_initial(initial)
162    }
163}
164
165/// Creates a stateful cycle without an initial value for use in `sliced!`.
166///
167/// The tick (which is the source of truth for lifetimes) is bound first, returning a
168/// [`StateNullBuilder`] which creates the cycle via [`StateNullBuilder::build`].
169///
170/// On the first iteration, the state will be null/empty. Subsequent iterations receive
171/// the value assigned to the mutable binding at the end of the previous iteration.
172#[cfg(stageleft_runtime)]
173pub fn state_null<'t, S, L>(tick: &'t Tick<L>) -> StateNullBuilder<'t, S, L> {
174    StateNullBuilder {
175        tick,
176        _phantom: PhantomData,
177    }
178}
179
180/// Builder returned by [`state_null`], which creates the cycle.
181#[cfg(stageleft_runtime)]
182pub struct StateNullBuilder<'t, S, L> {
183    tick: &'t Tick<L>,
184    _phantom: PhantomData<S>,
185}
186
187#[cfg(stageleft_runtime)]
188impl<'t, 'a, S, L: Location<'a>> StateNullBuilder<'t, S, L> {
189    /// Creates the stateful cycle, which starts as null/empty on the first iteration.
190    #[expect(
191        private_bounds,
192        reason = "only Hydro collections can implement CycleCollection"
193    )]
194    pub fn build(self) -> (TickCycleHandle<'a, S>, S)
195    where
196        S: CycleCollection<'a, TickCycle, Location = Tick<L::DropConsistency>> + DeferTick,
197    {
198        self.tick.cycle::<S, _>()
199    }
200}
201
202// ============================================================================
203// Default style Slicable implementations
204//
205// All of these drop consistency because they are performing non-deterministic
206// batching / snapshotting.
207// ============================================================================
208
209impl<'a, T, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
210    Slicable<'a, L::DropConsistency> for Default<crate::live_collections::Stream<T, L, B, O, R>>
211{
212    type Slice = crate::live_collections::Stream<T, Tick<L::DropConsistency>, Bounded, O, R>;
213    type Backtrace = crate::compile::ir::backtrace::Backtrace;
214
215    fn get_location(&self) -> L::DropConsistency {
216        self.collection.location().drop_consistency()
217    }
218    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
219        let _ = self.nondet;
220        let out = self.collection.batch(
221            tick,
222            nondet!(/** justified by the guard stored in this style wrapper */),
223        );
224        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
225        out
226    }
227}
228
229impl<'a, T, L: Location<'a>, B: SingletonBound> Slicable<'a, L::DropConsistency>
230    for Default<crate::live_collections::Singleton<T, L, B>>
231{
232    type Slice = crate::live_collections::Singleton<T, Tick<L::DropConsistency>, Bounded>;
233    type Backtrace = crate::compile::ir::backtrace::Backtrace;
234
235    fn get_location(&self) -> L::DropConsistency {
236        self.collection.location().drop_consistency()
237    }
238    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
239        let _ = self.nondet;
240        let out = self.collection.snapshot(
241            tick,
242            nondet!(/** justified by the guard stored in this style wrapper */),
243        );
244        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
245        out
246    }
247}
248
249impl<'a, T, L: Location<'a>, B: OptionalBound> Slicable<'a, L::DropConsistency>
250    for Default<crate::live_collections::Optional<T, L, B>>
251{
252    type Slice = crate::live_collections::Optional<T, Tick<L::DropConsistency>, Bounded>;
253    type Backtrace = crate::compile::ir::backtrace::Backtrace;
254
255    fn get_location(&self) -> L::DropConsistency {
256        self.collection.location().drop_consistency()
257    }
258    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
259        let out = self.collection.snapshot(tick, self.nondet);
260        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
261        out
262    }
263}
264
265impl<'a, K, V, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
266    Slicable<'a, L::DropConsistency>
267    for Default<crate::live_collections::KeyedStream<K, V, L, B, O, R>>
268{
269    type Slice =
270        crate::live_collections::KeyedStream<K, V, Tick<L::DropConsistency>, Bounded, O, R>;
271    type Backtrace = crate::compile::ir::backtrace::Backtrace;
272
273    fn get_location(&self) -> L::DropConsistency {
274        self.collection.location().drop_consistency()
275    }
276    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
277        let _ = self.nondet;
278        let out = self.collection.batch(
279            tick,
280            nondet!(/** justified by the guard stored in this style wrapper */),
281        );
282        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
283        out
284    }
285}
286
287impl<'a, K, V, L: Location<'a>, B: KeyedSingletonSnapshotBound<ValueBound = Unbounded>>
288    Slicable<'a, L::DropConsistency>
289    for Default<crate::live_collections::KeyedSingleton<K, V, L, B>>
290{
291    type Slice = crate::live_collections::KeyedSingleton<K, V, Tick<L::DropConsistency>, Bounded>;
292    type Backtrace = crate::compile::ir::backtrace::Backtrace;
293
294    fn get_location(&self) -> L::DropConsistency {
295        self.collection.location().drop_consistency()
296    }
297    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
298        let _ = self.nondet;
299        let out = self.collection.snapshot(
300            tick,
301            nondet!(/** justified by the guard stored in this style wrapper */),
302        );
303        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
304        out
305    }
306}
307
308impl<'a, K, V, L: Location<'a>> Slicable<'a, L::DropConsistency>
309    for Default<crate::live_collections::KeyedSingleton<K, V, L, BoundedValue>>
310{
311    type Slice = crate::live_collections::KeyedSingleton<K, V, Tick<L::DropConsistency>, Bounded>;
312    type Backtrace = crate::compile::ir::backtrace::Backtrace;
313
314    fn get_location(&self) -> L::DropConsistency {
315        self.collection.location().drop_consistency()
316    }
317    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
318        let _ = self.nondet;
319        let out = self.collection.batch(
320            tick,
321            nondet!(/** justified by the guard stored in this style wrapper */),
322        );
323        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
324        out
325    }
326}
327
328// ============================================================================
329// Batch style Slicable implementations (stream-like collections)
330//
331// All of these drop consistency because they are performing non-deterministic
332// batching.
333// ============================================================================
334
335impl<'a, T, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
336    Slicable<'a, L::DropConsistency>
337    for Batch<
338        crate::live_collections::Stream<T, L, B, O, R>,
339        Option<crate::sim_hooks::BatchHook<T, O, R, L::SimHookScope>>,
340    >
341{
342    type Slice = crate::live_collections::Stream<T, Tick<L::DropConsistency>, Bounded, O, R>;
343    type Backtrace = crate::compile::ir::backtrace::Backtrace;
344
345    fn get_location(&self) -> L::DropConsistency {
346        self.collection.location().drop_consistency()
347    }
348    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
349        let out = self.collection.batch(tick, self.nondet);
350        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
351        out
352    }
353}
354
355impl<'a, K, V, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
356    Slicable<'a, L::DropConsistency>
357    for Batch<
358        crate::live_collections::KeyedStream<K, V, L, B, O, R>,
359        Option<crate::sim_hooks::KeyedBatchHook<K, V, O, R, L::SimHookScope>>,
360    >
361{
362    type Slice =
363        crate::live_collections::KeyedStream<K, V, Tick<L::DropConsistency>, Bounded, O, R>;
364    type Backtrace = crate::compile::ir::backtrace::Backtrace;
365
366    fn get_location(&self) -> L::DropConsistency {
367        self.collection.location().drop_consistency()
368    }
369    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
370        let out = self.collection.batch(tick, self.nondet);
371        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
372        out
373    }
374}
375
376impl<'a, K, V, L: Location<'a>, B: KeyedSingletonBound<ValueBound = Bounded>>
377    Slicable<'a, L::DropConsistency>
378    for Batch<
379        crate::live_collections::KeyedSingleton<K, V, L, B>,
380        Option<crate::sim_hooks::KeyedBatchHook<K, V, TotalOrder, ExactlyOnce, L::SimHookScope>>,
381    >
382{
383    type Slice = crate::live_collections::KeyedSingleton<K, V, Tick<L::DropConsistency>, Bounded>;
384    type Backtrace = crate::compile::ir::backtrace::Backtrace;
385
386    fn get_location(&self) -> L::DropConsistency {
387        self.collection.location().drop_consistency()
388    }
389    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
390        let out = self.collection.batch(tick, self.nondet);
391        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
392        out
393    }
394}
395
396// ============================================================================
397// Snapshot style Slicable implementations (singleton-like collections)
398//
399// All of these drop consistency because they are performing non-deterministic
400// snapshotting.
401// ============================================================================
402
403impl<'a, T, L: Location<'a>, B: SingletonBound> Slicable<'a, L::DropConsistency>
404    for Snapshot<
405        crate::live_collections::Singleton<T, L, B>,
406        Option<crate::sim_hooks::SnapshotHook<T, L::SimHookScope>>,
407    >
408{
409    type Slice = crate::live_collections::Singleton<T, Tick<L::DropConsistency>, Bounded>;
410    type Backtrace = crate::compile::ir::backtrace::Backtrace;
411
412    fn get_location(&self) -> L::DropConsistency {
413        self.collection.location().drop_consistency()
414    }
415    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
416        let out = self.collection.snapshot(tick, self.nondet);
417        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
418        out
419    }
420}
421
422impl<'a, T, L: Location<'a>, B: OptionalBound> Slicable<'a, L::DropConsistency>
423    for Snapshot<crate::live_collections::Optional<T, L, B>>
424{
425    type Slice = crate::live_collections::Optional<T, Tick<L::DropConsistency>, Bounded>;
426    type Backtrace = crate::compile::ir::backtrace::Backtrace;
427
428    fn get_location(&self) -> L::DropConsistency {
429        self.collection.location().drop_consistency()
430    }
431    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
432        let out = self.collection.snapshot(tick, self.nondet);
433        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
434        out
435    }
436}
437
438impl<'a, K, V, L: Location<'a>, B: KeyedSingletonSnapshotBound> Slicable<'a, L::DropConsistency>
439    for Snapshot<
440        crate::live_collections::KeyedSingleton<K, V, L, B>,
441        Option<crate::sim_hooks::KeyedSnapshotHook<K, V, L::SimHookScope>>,
442    >
443{
444    type Slice = crate::live_collections::KeyedSingleton<K, V, Tick<L::DropConsistency>, Bounded>;
445    type Backtrace = crate::compile::ir::backtrace::Backtrace;
446
447    fn get_location(&self) -> L::DropConsistency {
448        self.collection.location().drop_consistency()
449    }
450    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
451        let out = self.collection.snapshot(tick, self.nondet);
452        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
453        out
454    }
455}
456
457// ============================================================================
458// Atomic style Slicable implementations
459// ============================================================================
460
461impl<'a, T, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
462    Slicable<'a, L::DropConsistency>
463    for Atomic<crate::live_collections::Stream<T, crate::location::Atomic<L>, B, O, R>>
464{
465    type Slice = crate::live_collections::Stream<T, Tick<L::DropConsistency>, Bounded, O, R>;
466    type Backtrace = crate::compile::ir::backtrace::Backtrace;
467    fn get_location(&self) -> L::DropConsistency {
468        self.collection.location().tick.l.drop_consistency()
469    }
470
471    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
472        let _ = self.nondet;
473        let out = self.collection.batch_atomic(
474            tick,
475            nondet!(/** justified by the guard stored in this style wrapper */),
476        );
477        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
478        out
479    }
480}
481
482impl<'a, T, L: Location<'a>, B: SingletonBound> Slicable<'a, L::DropConsistency>
483    for Atomic<crate::live_collections::Singleton<T, crate::location::Atomic<L>, B>>
484{
485    type Slice = crate::live_collections::Singleton<T, Tick<L::DropConsistency>, Bounded>;
486    type Backtrace = crate::compile::ir::backtrace::Backtrace;
487    fn get_location(&self) -> L::DropConsistency {
488        self.collection.location().tick.l.drop_consistency()
489    }
490
491    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
492        let _ = self.nondet;
493        let out = self.collection.snapshot_atomic(
494            tick,
495            nondet!(/** justified by the guard stored in this style wrapper */),
496        );
497        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
498        out
499    }
500}
501
502impl<'a, T, L: Location<'a>, B: OptionalBound> Slicable<'a, L::DropConsistency>
503    for Atomic<crate::live_collections::Optional<T, crate::location::Atomic<L>, B>>
504{
505    type Slice = crate::live_collections::Optional<T, Tick<L::DropConsistency>, Bounded>;
506    type Backtrace = crate::compile::ir::backtrace::Backtrace;
507    fn get_location(&self) -> L::DropConsistency {
508        self.collection.location().tick.l.drop_consistency()
509    }
510
511    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
512        let out = self.collection.snapshot_atomic(tick, self.nondet);
513        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
514        out
515    }
516}
517
518impl<'a, K, V, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
519    Slicable<'a, L::DropConsistency>
520    for Atomic<crate::live_collections::KeyedStream<K, V, crate::location::Atomic<L>, B, O, R>>
521{
522    type Slice =
523        crate::live_collections::KeyedStream<K, V, Tick<L::DropConsistency>, Bounded, O, R>;
524    type Backtrace = crate::compile::ir::backtrace::Backtrace;
525    fn get_location(&self) -> L::DropConsistency {
526        self.collection.location().tick.l.drop_consistency()
527    }
528
529    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
530        let out = self.collection.batch_atomic(tick, self.nondet);
531        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
532        out
533    }
534}
535
536impl<'a, K, V, L: Location<'a>, B: KeyedSingletonSnapshotBound<ValueBound = Unbounded>>
537    Slicable<'a, L::DropConsistency>
538    for Atomic<crate::live_collections::KeyedSingleton<K, V, crate::location::Atomic<L>, B>>
539{
540    type Slice = crate::live_collections::KeyedSingleton<K, V, Tick<L::DropConsistency>, Bounded>;
541    type Backtrace = crate::compile::ir::backtrace::Backtrace;
542    fn get_location(&self) -> L::DropConsistency {
543        self.collection.location().tick.l.drop_consistency()
544    }
545
546    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
547        let out = self.collection.snapshot_atomic(tick, self.nondet);
548        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
549        out
550    }
551}
552
553impl<'a, K, V, L: Location<'a>> Slicable<'a, L::DropConsistency>
554    for Atomic<
555        crate::live_collections::KeyedSingleton<K, V, crate::location::Atomic<L>, BoundedValue>,
556    >
557{
558    type Slice = crate::live_collections::KeyedSingleton<K, V, Tick<L::DropConsistency>, Bounded>;
559    type Backtrace = crate::compile::ir::backtrace::Backtrace;
560    fn get_location(&self) -> L::DropConsistency {
561        self.collection.location().tick.l.drop_consistency()
562    }
563
564    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
565        let out = self.collection.batch_atomic(tick, self.nondet);
566        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
567        out
568    }
569}