1pub mod style;
4
5use super::boundedness::{Bounded, Unbounded};
6use super::stream::{Ordering, Retries};
7use crate::location::{Location, Tick};
8
9#[doc(hidden)]
10#[macro_export]
11macro_rules! __sliced_parse_uses__ {
12 (
14 @uses [$($uses:tt)*]
15 @states [$($states:tt)*]
16 let $name:ident = use:: $invocation:expr; $($rest:tt)*
17 ) => {
18 $crate::__sliced_parse_uses__!(
19 @uses [$($uses)* { $name, $invocation, $invocation }]
20 @states [$($states)*]
21 $($rest)*
22 )
23 };
24
25 (
30 @uses [$($uses:tt)*]
31 @states [$($states:tt)*]
32 let $name:ident = use($($args:expr),* $(,)?); $($rest:tt)*
33 ) => {
34 $crate::__sliced_parse_uses__!(
35 @uses [$($uses)* { $name, $crate::macro_support::copy_span::copy_span!($($args,)* default)($($args),*), $($args),* }]
36 @states [$($states)*]
37 $($rest)*
38 )
39 };
40
41 (
43 @uses [$($uses:tt)*]
44 @states [$($states:tt)*]
45 let mut $name:ident = use:: $style:ident $(::<$ty:ty>)? ($($args:expr)?); $($rest:tt)*
46 ) => {
47 $crate::__sliced_parse_uses__!(
48 @uses [$($uses)*]
49 @states [$($states)* { $name, $style, (($($ty)?), ($($args)?)) }]
50 $($rest)*
51 )
52 };
53
54 (
56 @uses []
57 @states [$({ $state_name:ident, $state_style:ident, $state_arg:tt })+]
58 $($body:tt)*
59 ) => {
60 {
61 compile_error!("sliced! requires at least one `let name = use(...)` statement to determine the tick")
63 }
64 };
65
66 (
68 @uses [$({ $use_name:ident, $invocation:expr, $($invocation_spans:expr),* })+]
69 @states [$({ $state_name:ident, $state_style:ident, (($($state_ty:ty)?), ($($state_arg:expr)?)) })*]
70 $($body:tt)*
71 ) => {
72 {
73 use $crate::live_collections::sliced::style::*;
74 let __styled = (
75 $($invocation,)+
76 );
77
78 let __tick = $crate::live_collections::sliced::Slicable::create_tick(&__styled.0);
79 let __backtraces = {
80 use $crate::compile::ir::backtrace::__macro_get_backtrace;
81 (
82 $($crate::macro_support::copy_span::copy_span!($($invocation_spans,)* {
83 __macro_get_backtrace(1)
84 }),)+
85 )
86 };
87 let __sliced = $crate::live_collections::sliced::Slicable::slice(__styled, &__tick, __backtraces);
88 let (
89 $($use_name,)+
90 ) = __sliced;
91
92 let (__handles, __states) = $crate::live_collections::sliced::unzip_cycles((
107 $($crate::macro_support::copy_span::copy_span!($state_style, {
108 $crate::live_collections::sliced::style::$state_style$(::<$state_ty, _>)?(& __tick.clone()).build($($state_arg)?)
109 }),)*
110 ));
111
112 let (
114 $(mut $state_name,)*
115 ) = __states;
116
117 let __body_result = {
119 $($body)*
120 };
121
122 let __final_states = (
124 $($state_name,)*
125 );
126 $crate::live_collections::sliced::complete_cycles(__handles, __final_states);
127
128 $crate::live_collections::sliced::Unslicable::unslice(__body_result)
130 }
131 };
132}
133
134#[macro_export]
135macro_rules! __sliced__ {
188 ($($tt:tt)*) => {
189 $crate::__sliced_parse_uses__!(
190 @uses []
191 @states []
192 $($tt)*
193 )
194 };
195}
196
197pub use crate::__sliced__ as sliced;
198
199pub fn yield_atomic<T>(t: T) -> style::Atomic<T> {
203 style::Atomic {
204 collection: t,
205 nondet: crate::nondet::NonDet::unhooked(),
207 }
208}
209
210pub trait Slicable<'a, L: Location<'a>> {
212 type Slice;
214
215 type Backtrace;
217
218 fn get_location(&self) -> L;
220
221 fn create_tick(&self) -> Tick<L> {
223 self.get_location().try_tick().unwrap()
224 }
225
226 fn slice(self, tick: &Tick<L>, backtrace: Self::Backtrace) -> Self::Slice;
232}
233
234pub trait Unslicable {
236 type Unsliced;
238
239 fn unslice(self) -> Self::Unsliced;
241}
242
243#[doc(hidden)]
245pub trait UnzipCycles {
246 type Handles;
248 type States;
250
251 fn unzip(self) -> (Self::Handles, Self::States);
253}
254
255#[doc(hidden)]
257pub fn unzip_cycles<T: UnzipCycles>(cycles: T) -> (T::Handles, T::States) {
258 cycles.unzip()
259}
260
261#[doc(hidden)]
263pub trait CompleteCycles<States> {
264 fn complete(self, states: States);
266}
267
268#[doc(hidden)]
270pub fn complete_cycles<H: CompleteCycles<S>, S>(handles: H, states: S) {
271 handles.complete(states);
272}
273
274impl<'a, L: Location<'a>> Slicable<'a, L> for () {
275 type Slice = ();
276 type Backtrace = ();
277
278 fn get_location(&self) -> L {
279 unreachable!()
280 }
281
282 fn slice(self, _tick: &Tick<L>, _backtrace: Self::Backtrace) -> Self::Slice {}
283}
284
285impl Unslicable for () {
286 type Unsliced = ();
287
288 fn unslice(self) -> Self::Unsliced {}
289}
290
291macro_rules! impl_slicable_for_tuple {
292 ($($T:ident, $T_bt:ident, $idx:tt),+) => {
293 impl<'a, L: Location<'a>, $($T: Slicable<'a, L>),+> Slicable<'a, L> for ($($T,)+) {
294 type Slice = ($($T::Slice,)+);
295 type Backtrace = ($($T::Backtrace,)+);
296
297 fn get_location(&self) -> L {
298 self.0.get_location()
299 }
300
301 #[expect(non_snake_case, reason = "macro codegen")]
302 fn slice(self, tick: &Tick<L>, backtrace: Self::Backtrace) -> Self::Slice {
303 let ($($T,)+) = self;
304 let ($($T_bt,)+) = backtrace;
305 ($($T.slice(tick, $T_bt),)+)
306 }
307 }
308
309 impl<$($T: Unslicable),+> Unslicable for ($($T,)+) {
310 type Unsliced = ($($T::Unsliced,)+);
311
312 #[expect(non_snake_case, reason = "macro codegen")]
313 fn unslice(self) -> Self::Unsliced {
314 let ($($T,)+) = self;
315 ($($T.unslice(),)+)
316 }
317 }
318 };
319}
320
321#[cfg(stageleft_runtime)]
322impl_slicable_for_tuple!(S1, S1_bt, 0);
323#[cfg(stageleft_runtime)]
324impl_slicable_for_tuple!(S1, S1_bt, 0, S2, S2_bt, 1);
325#[cfg(stageleft_runtime)]
326impl_slicable_for_tuple!(S1, S1_bt, 0, S2, S2_bt, 1, S3, S3_bt, 2);
327#[cfg(stageleft_runtime)]
328impl_slicable_for_tuple!(S1, S1_bt, 0, S2, S2_bt, 1, S3, S3_bt, 2, S4, S4_bt, 3);
329#[cfg(stageleft_runtime)]
330impl_slicable_for_tuple!(
331 S1, S1_bt, 0, S2, S2_bt, 1, S3, S3_bt, 2, S4, S4_bt, 3, S5, S5_bt, 4
332);
333#[cfg(stageleft_runtime)]
334impl_slicable_for_tuple!(
335 S1, S1_bt, 0, S2, S2_bt, 1, S3, S3_bt, 2, S4, S4_bt, 3, S5, S5_bt, 4, S6, S6_bt, 5
336);
337#[cfg(stageleft_runtime)]
338impl_slicable_for_tuple!(
339 S1, S1_bt, 0, S2, S2_bt, 1, S3, S3_bt, 2, S4, S4_bt, 3, S5, S5_bt, 4, S6, S6_bt, 5, S7, S7_bt,
340 6
341);
342#[cfg(stageleft_runtime)]
343impl_slicable_for_tuple!(
344 S1, S1_bt, 0, S2, S2_bt, 1, S3, S3_bt, 2, S4, S4_bt, 3, S5, S5_bt, 4, S6, S6_bt, 5, S7, S7_bt,
345 6, S8, S8_bt, 7
346);
347#[cfg(stageleft_runtime)]
348impl_slicable_for_tuple!(
349 S1, S1_bt, 0, S2, S2_bt, 1, S3, S3_bt, 2, S4, S4_bt, 3, S5, S5_bt, 4, S6, S6_bt, 5, S7, S7_bt,
350 6, S8, S8_bt, 7, S9, S9_bt, 8
351);
352#[cfg(stageleft_runtime)]
353impl_slicable_for_tuple!(
354 S1, S1_bt, 0, S2, S2_bt, 1, S3, S3_bt, 2, S4, S4_bt, 3, S5, S5_bt, 4, S6, S6_bt, 5, S7, S7_bt,
355 6, S8, S8_bt, 7, S9, S9_bt, 8, S10, S10_bt, 9
356);
357#[cfg(stageleft_runtime)]
358impl_slicable_for_tuple!(
359 S1, S1_bt, 0, S2, S2_bt, 1, S3, S3_bt, 2, S4, S4_bt, 3, S5, S5_bt, 4, S6, S6_bt, 5, S7, S7_bt,
360 6, S8, S8_bt, 7, S9, S9_bt, 8, S10, S10_bt, 9, S11, S11_bt, 10
361);
362#[cfg(stageleft_runtime)]
363impl_slicable_for_tuple!(
364 S1, S1_bt, 0, S2, S2_bt, 1, S3, S3_bt, 2, S4, S4_bt, 3, S5, S5_bt, 4, S6, S6_bt, 5, S7, S7_bt,
365 6, S8, S8_bt, 7, S9, S9_bt, 8, S10, S10_bt, 9, S11, S11_bt, 10, S12, S12_bt, 11
366);
367
368macro_rules! impl_cycles_for_tuple {
369 ($($H:ident, $S:ident, $idx:tt),*) => {
370 impl<$($H, $S),*> UnzipCycles for ($(($H, $S),)*) {
371 type Handles = ($($H,)*);
372 type States = ($($S,)*);
373
374 #[expect(clippy::allow_attributes, reason = "macro codegen")]
375 #[allow(non_snake_case, reason = "macro codegen")]
376 fn unzip(self) -> (Self::Handles, Self::States) {
377 let ($($H,)*) = self;
378 (
379 ($($H.0,)*),
380 ($($H.1,)*),
381 )
382 }
383 }
384
385 impl<$($H: crate::forward_handle::CompleteCycle<$S>, $S),*> CompleteCycles<($($S,)*)> for ($($H,)*) {
386 #[expect(clippy::allow_attributes, reason = "macro codegen")]
387 #[allow(non_snake_case, reason = "macro codegen")]
388 fn complete(self, states: ($($S,)*)) {
389 let ($($H,)*) = self;
390 let ($($S,)*) = states;
391 $($H.complete_next_tick($S);)*
392 }
393 }
394 };
395}
396
397#[cfg(stageleft_runtime)]
398impl_cycles_for_tuple!();
399#[cfg(stageleft_runtime)]
400impl_cycles_for_tuple!(H1, S1, 0);
401#[cfg(stageleft_runtime)]
402impl_cycles_for_tuple!(H1, S1, 0, H2, S2, 1);
403#[cfg(stageleft_runtime)]
404impl_cycles_for_tuple!(H1, S1, 0, H2, S2, 1, H3, S3, 2);
405#[cfg(stageleft_runtime)]
406impl_cycles_for_tuple!(H1, S1, 0, H2, S2, 1, H3, S3, 2, H4, S4, 3);
407#[cfg(stageleft_runtime)]
408impl_cycles_for_tuple!(H1, S1, 0, H2, S2, 1, H3, S3, 2, H4, S4, 3, H5, S5, 4);
409#[cfg(stageleft_runtime)]
410impl_cycles_for_tuple!(
411 H1, S1, 0, H2, S2, 1, H3, S3, 2, H4, S4, 3, H5, S5, 4, H6, S6, 5
412);
413#[cfg(stageleft_runtime)]
414impl_cycles_for_tuple!(
415 H1, S1, 0, H2, S2, 1, H3, S3, 2, H4, S4, 3, H5, S5, 4, H6, S6, 5, H7, S7, 6
416);
417#[cfg(stageleft_runtime)]
418impl_cycles_for_tuple!(
419 H1, S1, 0, H2, S2, 1, H3, S3, 2, H4, S4, 3, H5, S5, 4, H6, S6, 5, H7, S7, 6, H8, S8, 7
420);
421#[cfg(stageleft_runtime)]
422impl_cycles_for_tuple!(
423 H1, S1, 0, H2, S2, 1, H3, S3, 2, H4, S4, 3, H5, S5, 4, H6, S6, 5, H7, S7, 6, H8, S8, 7, H9, S9,
424 8
425);
426#[cfg(stageleft_runtime)]
427impl_cycles_for_tuple!(
428 H1, S1, 0, H2, S2, 1, H3, S3, 2, H4, S4, 3, H5, S5, 4, H6, S6, 5, H7, S7, 6, H8, S8, 7, H9, S9,
429 8, H10, S10, 9
430);
431#[cfg(stageleft_runtime)]
432impl_cycles_for_tuple!(
433 H1, S1, 0, H2, S2, 1, H3, S3, 2, H4, S4, 3, H5, S5, 4, H6, S6, 5, H7, S7, 6, H8, S8, 7, H9, S9,
434 8, H10, S10, 9, H11, S11, 10
435);
436#[cfg(stageleft_runtime)]
437impl_cycles_for_tuple!(
438 H1, S1, 0, H2, S2, 1, H3, S3, 2, H4, S4, 3, H5, S5, 4, H6, S6, 5, H7, S7, 6, H8, S8, 7, H9, S9,
439 8, H10, S10, 9, H11, S11, 10, H12, S12, 11
440);
441
442impl<'a, T, L: Location<'a>, O: Ordering, R: Retries> Unslicable
444 for super::Stream<T, Tick<L>, Bounded, O, R>
445{
446 type Unsliced = super::Stream<T, L, Unbounded, O, R>;
447
448 fn unslice(self) -> Self::Unsliced {
449 self.all_ticks()
450 }
451}
452
453impl<'a, T, L: Location<'a>> Unslicable for super::Singleton<T, Tick<L>, Bounded> {
454 type Unsliced = super::Optional<T, L, crate::live_collections::optional::InitNone>;
455
456 fn unslice(self) -> Self::Unsliced {
457 self.latest()
458 }
459}
460
461impl<'a, T, L: Location<'a>> Unslicable for super::Optional<T, Tick<L>, Bounded> {
462 type Unsliced = super::Optional<T, L, Unbounded>;
463
464 fn unslice(self) -> Self::Unsliced {
465 self.latest()
466 }
467}
468
469impl<'a, K, V, L: Location<'a>, O: Ordering, R: Retries> Unslicable
470 for super::KeyedStream<K, V, Tick<L>, Bounded, O, R>
471{
472 type Unsliced = super::KeyedStream<K, V, L, Unbounded, O, R>;
473
474 fn unslice(self) -> Self::Unsliced {
475 self.all_ticks()
476 }
477}
478
479impl<'a, T, L: Location<'a>, O: Ordering, R: Retries> Unslicable
481 for style::Atomic<super::Stream<T, Tick<L>, Bounded, O, R>>
482{
483 type Unsliced = super::Stream<T, crate::location::Atomic<L>, Unbounded, O, R>;
484
485 fn unslice(self) -> Self::Unsliced {
486 self.collection.all_ticks_atomic()
487 }
488}
489
490impl<'a, T, L: Location<'a>> Unslicable for style::Atomic<super::Singleton<T, Tick<L>, Bounded>> {
491 type Unsliced =
492 super::Optional<T, crate::location::Atomic<L>, crate::live_collections::optional::InitNone>;
493
494 fn unslice(self) -> Self::Unsliced {
495 self.collection.latest_atomic()
496 }
497}
498
499impl<'a, T, L: Location<'a>> Unslicable for style::Atomic<super::Optional<T, Tick<L>, Bounded>> {
500 type Unsliced = super::Optional<T, crate::location::Atomic<L>, Unbounded>;
501
502 fn unslice(self) -> Self::Unsliced {
503 self.collection.latest_atomic()
504 }
505}
506
507impl<'a, K, V, L: Location<'a>, O: Ordering, R: Retries> Unslicable
508 for style::Atomic<super::KeyedStream<K, V, Tick<L>, Bounded, O, R>>
509{
510 type Unsliced = super::KeyedStream<K, V, crate::location::Atomic<L>, Unbounded, O, R>;
511
512 fn unslice(self) -> Self::Unsliced {
513 self.collection.all_ticks_atomic()
514 }
515}
516
517#[cfg(feature = "sim")]
518#[cfg(test)]
519mod tests {
520 use stageleft::q;
521
522 use super::sliced;
523 use crate::location::Location;
524 use crate::nondet::nondet;
525 use crate::prelude::FlowBuilder;
526
527 #[test]
530 fn sim_state_counter() {
531 let mut flow = FlowBuilder::new();
532 let node = flow.process::<()>();
533
534 let (input_send, input) = node.sim_input::<i32, _, _>();
535
536 let out_recv = sliced! {
537 let batch = use::batch(input, nondet!());
538 let mut counter = use::state(|l| l.singleton(q!(0)));
539
540 let new_count = counter.clone().zip(batch.count())
541 .map(q!(|(old, add)| old + add));
542 counter = new_count.clone();
543 new_count.into_stream()
544 }
545 .sim_output();
546
547 flow.sim().exhaustive(async || {
548 input_send.send(1);
549 assert_eq!(out_recv.next().await, 1);
550
551 input_send.send(1);
552 assert_eq!(out_recv.next().await, 2);
553
554 input_send.send(1);
555 assert_eq!(out_recv.next().await, 3);
556 });
557 }
558
559 #[cfg(feature = "sim")]
561 #[test]
562 fn sim_state_null_optional() {
563 use crate::live_collections::Optional;
564 use crate::live_collections::boundedness::Bounded;
565 use crate::location::{Location, Tick};
566
567 let mut flow = FlowBuilder::new();
568 let node = flow.process::<()>();
569
570 let (input_send, input) = node.sim_input::<i32, _, _>();
571
572 let out_recv = sliced! {
573 let batch = use::batch(input, nondet!());
574 let mut prev = use::state_null::<Optional<i32, Tick<_>, Bounded>>();
575
576 let output = prev.clone().unwrap_or(prev.location().singleton(q!(-1)));
578 prev = batch.first();
580 output.into_stream()
581 }
582 .sim_output();
583
584 flow.sim().exhaustive(async || {
585 input_send.send(10);
586 assert_eq!(out_recv.next().await, -1);
588
589 input_send.send(20);
590 assert_eq!(out_recv.next().await, 10);
592
593 input_send.send(30);
594 assert_eq!(out_recv.next().await, 20);
596 });
597 }
598
599 #[test]
603 fn sim_state_source_iter() {
604 let mut flow = FlowBuilder::new();
605 let node = flow.process::<()>();
606
607 let (input_send, input) = node.sim_input::<i32, _, _>();
608
609 let out_recv = sliced! {
610 let batch = use::batch(input, nondet!());
611 let mut items = use::state(|l| l.source_iter(q!([10, 20])));
612
613 let output = items.clone();
615 items = batch;
616 output
617 }
618 .sim_output();
619
620 flow.sim().exhaustive(async || {
621 input_send.send(3);
622 let mut results = vec![];
624 results.push(out_recv.next().await);
625 results.push(out_recv.next().await);
626 results.sort();
627 assert_eq!(results, vec![10, 20]);
628
629 input_send.send(4);
630 assert_eq!(out_recv.next().await, 3);
632
633 input_send.send(5);
634 assert_eq!(out_recv.next().await, 4);
636 });
637 }
638
639 #[test]
641 fn sim_sliced_atomic_keyed_stream() {
642 let mut flow = FlowBuilder::new();
643 let node = flow.process::<()>();
644
645 let (input_send, input) = node.sim_input::<(i32, i32), _, _>();
646 let atomic_keyed_input = input.into_keyed().atomic();
647 let accumulated_inputs = atomic_keyed_input
648 .clone()
649 .assume_ordering(nondet!())
650 .fold(
651 q!(|| 0),
652 q!(|curr, new| {
653 *curr += new;
654 }),
655 );
656
657 let out_recv = sliced! {
658 let atomic_keyed_input = use::atomic(atomic_keyed_input, nondet!());
659 let accumulated_inputs = use::atomic(accumulated_inputs, nondet!());
660 accumulated_inputs.join_keyed_stream(atomic_keyed_input)
661 .map(q!(|(sum, _input)| sum))
662 .entries()
663 }
664 .assume_ordering_trusted(nondet!())
665 .sim_output();
666
667 flow.sim().exhaustive(async || {
668 input_send.send((1, 1));
669 assert_eq!(out_recv.next().await, (1, 1));
670
671 input_send.send((1, 2));
672 assert_eq!(out_recv.next().await, (1, 3));
673
674 input_send.send((2, 1));
675 assert_eq!(out_recv.next().await, (2, 1));
676
677 input_send.send((1, 3));
678 assert_eq!(out_recv.next().await, (1, 6));
679 });
680 }
681}