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