1use std::cell::RefCell;
16use std::marker::PhantomData;
17use std::rc::Rc;
18
19use proc_macro2::Span;
20use quote::quote;
21use stageleft::runtime_support::{FreeVariableWithContextWithProps, QuoteTokens};
22
23use crate::compile::ir::{AccessCounter, HydroNode, SharedNode};
24use crate::live_collections::OperatorContext;
25use crate::location::Location;
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
29pub enum HandoffRefKind {
30 Singleton,
32 Optional,
34 Vec,
36}
37
38thread_local! {
42 static CAPTURED_REFS: RefCell<Option<Vec<(HydroNode, bool)>>> = const { RefCell::new(None) };
43}
44
45pub(crate) fn handoff_ref_ident(index: usize) -> syn::Ident {
47 syn::Ident::new(
48 &format!("__hydro_singleton_ref_{}", index),
49 Span::call_site(),
50 )
51}
52
53pub fn with_ref_capture(
57 f: impl FnOnce() -> crate::compile::ir::DebugExpr,
58) -> crate::compile::ir::ClosureExpr {
59 CAPTURED_REFS.with(|cell| {
60 let prev = cell.borrow_mut().replace(Vec::new());
61 assert!(
62 prev.is_none(),
63 "nested handoff reference capture scopes are not supported"
64 );
65 });
66 let expr = (f)();
67 let captured_refs = CAPTURED_REFS.with(|cell| cell.borrow_mut().take().unwrap());
68 crate::compile::ir::ClosureExpr::new(expr, captured_refs)
69}
70
71fn register_handoff_ref(
74 ir_node: &RefCell<HydroNode>,
75 is_mut: bool,
76 kind: HandoffRefKind,
77) -> syn::Ident {
78 CAPTURED_REFS.with(|cell| {
79 let mut guard = cell.borrow_mut();
80 let refs = guard.as_mut().expect(
81 "HandoffRef used inside q!() but no reference capture scope is active. \
82 This is a bug — reference capture should be set up by the operator that uses q!().",
83 );
84
85 let index = refs.len();
86 let ident = handoff_ref_ident(index);
87
88 let metadata = ir_node.borrow().metadata().clone();
89
90 if !matches!(&*ir_node.borrow(), HydroNode::Reference { .. }) {
93 let orig = ir_node.replace(HydroNode::Placeholder);
94 *ir_node.borrow_mut() = HydroNode::Reference {
95 inner: SharedNode(Rc::new(RefCell::new(orig))),
96 kind,
97 access_counter: AccessCounter::new(),
98 metadata: metadata.clone(),
99 };
100 }
101
102 let borrow: std::cell::Ref<'_, HydroNode> = ir_node.borrow();
103 let HydroNode::Reference {
104 inner,
105 access_counter,
106 ..
107 } = &*borrow
108 else {
109 unreachable!()
110 };
111
112 let group = access_counter.next_group(is_mut);
114
115 refs.push((
116 HydroNode::Reference {
117 inner: SharedNode(Rc::clone(&inner.0)),
118 kind,
119 access_counter: group,
120 metadata,
121 },
122 is_mut,
123 ));
124
125 ident
126 })
127}
128
129macro_rules! define_handoff_ref {
131 (
132 $(
133 $(#[$meta:meta])*
134 $name:ident, $is_mut:expr, $kind:expr, $output:ty
135 )+
136 ) => {
137 $(
138 $(#[$meta])*
139 pub struct $name<'a, 'slf, T, L, B> {
140 pub(crate) ir_node: &'slf RefCell<HydroNode>,
141 _phantom: PhantomData<(&'a T, L, B)>,
142 }
143
144 impl<'slf, T, L, B> $name<'_, 'slf, T, L, B> {
145 pub(crate) fn new(ir_node: &'slf RefCell<HydroNode>) -> Self {
147 Self {
148 ir_node,
149 _phantom: PhantomData,
150 }
151 }
152 }
153
154 impl<T, L, B> Copy for $name<'_, '_, T, L, B> {}
155 impl<T, L, B> Clone for $name<'_, '_, T, L, B> {
156 fn clone(&self) -> Self {
157 *self
158 }
159 }
160
161 impl<'a, 'slf, T: 'a, L, B> FreeVariableWithContextWithProps<OperatorContext<L, B>, ()>
162 for $name<'a, 'slf, T, L, B>
163 where
164 L: Location<'a>,
165 {
166 type O = $output;
167
168 fn to_tokens(self, _ctx: &OperatorContext<L, B>) -> (QuoteTokens, ()) {
169 let ident = register_handoff_ref(
170 self.ir_node,
171 $is_mut,
172 $kind,
173 );
174 (
175 QuoteTokens {
176 prelude: None,
177 expr: Some(quote!(#ident)),
178 },
179 (),
180 )
181 }
182 }
183 )+
184 };
185}
186
187#[stageleft::export(
188 SingletonRef,
189 SingletonMut,
190 OptionalRef,
191 OptionalMut,
192 StreamRef,
193 StreamMut
194)]
195define_handoff_ref!(
196 SingletonRef, false, HandoffRefKind::Singleton, &'a T
200
201 SingletonMut, true, HandoffRefKind::Singleton, &'a mut T
205
206 OptionalRef, false, HandoffRefKind::Optional, &'a Option<T>
210
211 OptionalMut, true, HandoffRefKind::Optional, &'a mut Option<T>
215
216 StreamRef, false, HandoffRefKind::Vec, &'a Vec<T>
220
221 StreamMut, true, HandoffRefKind::Vec, &'a mut Vec<T>
225);
226
227#[cfg(test)]
228#[cfg(feature = "build")]
229mod tests {
230 use stageleft::q;
231
232 use crate::compile::builder::FlowBuilder;
233 use crate::location::Location;
234
235 struct P1 {}
236
237 #[test]
239 fn singleton_by_ref_compiles() {
240 let mut flow = FlowBuilder::new();
241 let node = flow.process::<P1>();
242
243 let my_count = node
244 .source_iter(q!(0..5i32))
245 .fold(q!(|| 0i32), q!(|acc: &mut i32, x| *acc += x));
246 let count_ref = my_count.by_ref();
247
248 node.source_iter(q!(1..=3i32))
249 .map(q!(|x| x + *count_ref))
250 .for_each(q!(|_| {}));
251
252 my_count.into_stream().for_each(q!(|_| {}));
253 let _built = flow.finalize();
254 }
255
256 #[test]
258 fn singleton_by_ref_non_copy() {
259 let mut flow = FlowBuilder::new();
260 let node = flow.process::<P1>();
261
262 let my_vec = node.source_iter(q!(0..5i32)).fold(
263 q!(|| Vec::<i32>::new()),
264 q!(|acc: &mut Vec<i32>, x| acc.push(x)),
265 );
266 let vec_ref = my_vec.by_ref();
267
268 node.source_iter(q!(1..=3i32))
269 .map(q!(|x| x + vec_ref.len() as i32))
270 .for_each(q!(|_| {}));
271
272 my_vec.into_stream().for_each(q!(|_| {}));
273 let _built = flow.finalize();
274 }
275
276 #[test]
278 fn singleton_by_ref_filter() {
279 let mut flow = FlowBuilder::new();
280 let node = flow.process::<P1>();
281
282 let threshold = node
283 .source_iter(q!(0..5i32))
284 .fold(q!(|| 0i32), q!(|acc: &mut i32, x| *acc += x));
285 let threshold_ref = threshold.by_ref();
286
287 node.source_iter(q!(1..=10i32))
288 .filter(q!(|x| *x > *threshold_ref))
289 .for_each(q!(|_| {}));
290
291 threshold.into_stream().for_each(q!(|_| {}));
292 let _built = flow.finalize();
293 }
294
295 #[test]
297 fn singleton_by_ref_flat_map() {
298 let mut flow = FlowBuilder::new();
299 let node = flow.process::<P1>();
300
301 let count = node
302 .source_iter(q!(0..3i32))
303 .fold(q!(|| 0i32), q!(|acc: &mut i32, _| *acc += 1));
304 let count_ref = count.by_ref();
305
306 node.source_iter(q!(1..=2i32))
307 .flat_map_ordered(q!(|x| (0..*count_ref).map(move |i| x + i)))
308 .for_each(q!(|_| {}));
309
310 count.into_stream().for_each(q!(|_| {}));
311 let _built = flow.finalize();
312 }
313
314 #[test]
316 fn singleton_by_ref_inspect() {
317 let mut flow = FlowBuilder::new();
318 let node = flow.process::<P1>();
319
320 let count = node
321 .source_iter(q!(0..5i32))
322 .fold(q!(|| 0i32), q!(|acc: &mut i32, _| *acc += 1));
323 let count_ref = count.by_ref();
324
325 node.source_iter(q!(1..=3i32))
326 .inspect(q!(|x| println!("count={}, x={}", *count_ref, x)))
327 .for_each(q!(|_| {}));
328
329 count.into_stream().for_each(q!(|_| {}));
330 let _built = flow.finalize();
331 }
332
333 #[test]
335 fn singleton_by_ref_partition() {
336 let mut flow = FlowBuilder::new();
337 let node = flow.process::<P1>();
338
339 let threshold = node
340 .source_iter(q!(0..5i32))
341 .fold(q!(|| 0i32), q!(|acc: &mut i32, x| *acc += x));
342 let threshold_ref = threshold.by_ref();
343
344 let (above, below) = node
345 .source_iter(q!(1..=10i32))
346 .partition(q!(|x| *x > *threshold_ref));
347
348 above.for_each(q!(|_| {}));
349 below.for_each(q!(|_| {}));
350 threshold.into_stream().for_each(q!(|_| {}));
351 let _built = flow.finalize();
352 }
353
354 #[test]
356 fn singleton_by_ref_partition_with_downstream_ops() {
357 let mut flow = FlowBuilder::new();
358 let node = flow.process::<P1>();
359
360 let threshold = node
361 .source_iter(q!(0..5i32))
362 .fold(q!(|| 0i32), q!(|acc: &mut i32, x| *acc += x));
363 let threshold_ref = threshold.by_ref();
364
365 let (above, below) = node
366 .source_iter(q!(1..=10i32))
367 .partition(q!(|x| *x > *threshold_ref));
368
369 above.map(q!(|x| x * 2)).for_each(q!(|_| {}));
370 below.map(q!(|x| x + 100)).for_each(q!(|_| {}));
371 threshold.into_stream().for_each(q!(|_| {}));
372 let _built = flow.finalize();
373 }
374
375 #[test]
377 fn singleton_by_mut_compiles() {
378 let mut flow = FlowBuilder::new();
379 let node = flow.process::<P1>();
380
381 let my_count = node
382 .source_iter(q!(0..5i32))
383 .fold(q!(|| 0i32), q!(|acc: &mut i32, x| *acc += x));
384 let count_mut = my_count.by_mut();
385
386 node.source_iter(q!(1..=3i32))
387 .map(q!(|x| {
388 *count_mut += x;
389 x
390 }))
391 .for_each(q!(|_| {}));
392
393 my_count.into_stream().for_each(q!(|_| {}));
394 let _built = flow.finalize();
395 }
396
397 #[test]
399 fn optional_by_ref_compiles() {
400 let mut flow = FlowBuilder::new();
401 let node = flow.process::<P1>();
402
403 let my_opt = node.source_iter(q!(0..5i32)).reduce(q!(|a, b| *a += b));
404 let opt_ref = my_opt.by_ref();
405
406 node.source_iter(q!(1..=3i32))
407 .map(q!(|x| x + opt_ref.unwrap_or(0)))
408 .for_each(q!(|_| {}));
409
410 my_opt.into_stream().for_each(q!(|_| {}));
411 let _built = flow.finalize();
412 }
413
414 #[test]
416 fn stream_by_ref_compiles() {
417 let mut flow = FlowBuilder::new();
418 let node = flow.process::<P1>();
419
420 let my_stream = node.source_iter(q!(0..5i32));
421 let stream_ref = my_stream.by_ref();
422
423 node.source_iter(q!(1..=3i32))
424 .map(q!(|x| x + stream_ref.len() as i32))
425 .for_each(q!(|_| {}));
426
427 my_stream.for_each(q!(|_| {}));
428 let _built = flow.finalize();
429 }
430
431 #[test]
433 fn singleton_by_mut_filter() {
434 let mut flow = FlowBuilder::new();
435 let node = flow.process::<P1>();
436
437 let my_count = node
438 .source_iter(q!(0..5i32))
439 .fold(q!(|| 0i32), q!(|acc: &mut i32, x| *acc += x));
440 let count_mut = my_count.by_mut();
441
442 node.source_iter(q!(1..=3i32))
443 .filter(q!(|x| {
444 *count_mut += *x;
445 *count_mut > 0
446 }))
447 .for_each(q!(|_| {}));
448
449 my_count.into_stream().for_each(q!(|_| {}));
450 let _built = flow.finalize();
451 }
452
453 #[test]
455 fn singleton_by_mut_flat_map() {
456 let mut flow = FlowBuilder::new();
457 let node = flow.process::<P1>();
458
459 let my_count = node
460 .source_iter(q!(0..5i32))
461 .fold(q!(|| 0i32), q!(|acc: &mut i32, x| *acc += x));
462 let count_mut = my_count.by_mut();
463
464 node.source_iter(q!(1..=3i32))
465 .flat_map_ordered(q!(|x| {
466 *count_mut += x;
467 vec![*count_mut]
468 }))
469 .for_each(q!(|_| {}));
470
471 my_count.into_stream().for_each(q!(|_| {}));
472 let _built = flow.finalize();
473 }
474
475 #[test]
477 fn singleton_by_mut_filter_map() {
478 let mut flow = FlowBuilder::new();
479 let node = flow.process::<P1>();
480
481 let my_count = node
482 .source_iter(q!(0..5i32))
483 .fold(q!(|| 0i32), q!(|acc: &mut i32, x| *acc += x));
484 let count_mut = my_count.by_mut();
485
486 node.source_iter(q!(1..=3i32))
487 .filter_map(q!(|x| {
488 *count_mut += x;
489 Some(*count_mut)
490 }))
491 .for_each(q!(|_| {}));
492
493 my_count.into_stream().for_each(q!(|_| {}));
494 let _built = flow.finalize();
495 }
496
497 #[test]
499 fn singleton_by_mut_inspect() {
500 let mut flow = FlowBuilder::new();
501 let node = flow.process::<P1>();
502
503 let my_count = node
504 .source_iter(q!(0..5i32))
505 .fold(q!(|| 0i32), q!(|acc: &mut i32, x| *acc += x));
506 let count_mut = my_count.by_mut();
507
508 node.source_iter(q!(1..=3i32))
509 .inspect(q!(|x| {
510 *count_mut += *x;
511 }))
512 .for_each(q!(|_| {}));
513
514 my_count.into_stream().for_each(q!(|_| {}));
515 let _built = flow.finalize();
516 }
517
518 #[test]
520 fn singleton_by_ref_for_each() {
521 let mut flow = FlowBuilder::new();
522 let node = flow.process::<P1>();
523
524 let my_count = node
525 .source_iter(q!(0..5i32))
526 .fold(q!(|| 0i32), q!(|acc: &mut i32, x| *acc += x));
527 let count_ref = my_count.by_ref();
528
529 node.source_iter(q!(1..=3i32))
530 .for_each(q!(|x| println!("{}", x + *count_ref)));
531
532 my_count.into_stream().for_each(q!(|_| {}));
533 let _built = flow.finalize();
534 }
535
536 #[test]
538 fn singleton_by_mut_for_each() {
539 let mut flow = FlowBuilder::new();
540 let node = flow.process::<P1>();
541
542 let my_count = node
543 .source_iter(q!(0..5i32))
544 .fold(q!(|| 0i32), q!(|acc: &mut i32, x| *acc += x));
545 let count_mut = my_count.by_mut();
546
547 node.source_iter(q!(1..=3i32)).for_each(q!(|x| {
548 *count_mut += x;
549 }));
550
551 my_count.into_stream().for_each(q!(|_| {}));
552 let _built = flow.finalize();
553 }
554
555 #[cfg(feature = "deploy")]
564 #[test]
565 fn singleton_by_ref_for_each_sole_consumer_emits() {
566 use crate::live_collections::sliced::sliced;
567 use crate::nondet::nondet;
568
569 let mut flow = FlowBuilder::new();
570 let node = flow.process::<P1>();
571
572 let items = node.source_iter(q!(1..=3i32));
573
574 sliced! {
575 let items = use::batch(items, nondet!());
576 let my_count = items
577 .location()
578 .source_iter(q!(0..5i32))
579 .fold(q!(|| 0i32), q!(|acc: &mut i32, x| *acc += x));
580 let count_ref = my_count.by_ref();
581
582 items.for_each(q!(|x| println!("{}", x + *count_ref)));
585 };
586
587 let _ = flow
588 .finalize()
589 .with_default_optimize::<crate::deploy::HydroDeploy>()
590 .preview_compile();
591 }
592
593 #[cfg(feature = "deploy")]
597 #[test]
598 fn singleton_by_mut_for_each_sole_consumer_emits() {
599 use crate::live_collections::sliced::sliced;
600 use crate::nondet::nondet;
601
602 let mut flow = FlowBuilder::new();
603 let node = flow.process::<P1>();
604
605 let items = node.source_iter(q!(1..=3i32));
606
607 sliced! {
608 let items = use::batch(items, nondet!());
609 let my_count = items
610 .location()
611 .source_iter(q!(0..5i32))
612 .fold(q!(|| 0i32), q!(|acc: &mut i32, x| *acc += x));
613 let count_mut = my_count.by_mut();
614
615 items.for_each(q!(|x| {
617 *count_mut += x;
618 }));
619 };
620
621 let _ = flow
622 .finalize()
623 .with_default_optimize::<crate::deploy::HydroDeploy>()
624 .preview_compile();
625 }
626
627 #[test]
629 fn singleton_by_ref_scan() {
630 let mut flow = FlowBuilder::new();
631 let node = flow.process::<P1>();
632
633 let offset = node
634 .source_iter(q!(0..5i32))
635 .fold(q!(|| 0i32), q!(|acc: &mut i32, x| *acc += x));
636 let offset_ref = offset.by_ref();
637
638 node.source_iter(q!(1..=3i32))
639 .scan(
640 q!(move || *offset_ref),
641 q!(move |acc: &mut i32, x| {
642 *acc += x + *offset_ref;
643 Some(*acc)
644 }),
645 )
646 .for_each(q!(|_| {}));
647
648 offset.into_stream().for_each(q!(|_| {}));
649 let _built = flow.finalize();
650 }
651
652 #[test]
654 fn singleton_by_ref_scan_async_blocking() {
655 let mut flow = FlowBuilder::new();
656 let node = flow.process::<P1>();
657
658 let offset = node
659 .source_iter(q!(0..5i32))
660 .fold(q!(|| 0i32), q!(|acc: &mut i32, x| *acc += x));
661 let offset_ref = offset.by_ref();
662
663 node.source_iter(q!(1..=3i32))
664 .scan_async_blocking(
665 q!(|| 0i32),
666 q!(move |acc: &mut i32, x| {
667 *acc += x + *offset_ref;
668 let val = *acc;
669 async move { Some(val) }
670 }),
671 )
672 .for_each(q!(|_| {}));
673
674 offset.into_stream().for_each(q!(|_| {}));
675 let _built = flow.finalize();
676 }
677
678 #[test]
680 fn singleton_by_ref_generator() {
681 use crate::live_collections::keyed_stream::Generate;
682
683 let mut flow = FlowBuilder::new();
684 let node = flow.process::<P1>();
685
686 let threshold = node
687 .source_iter(q!(0..5i32))
688 .fold(q!(|| 0i32), q!(|acc: &mut i32, x| *acc += x));
689 let threshold_ref = threshold.by_ref();
690
691 node.source_iter(q!(1..=3i32))
692 .generator(
693 q!(|| 0i32),
694 q!(move |acc: &mut i32, x| {
695 *acc += x;
696 if *acc > *threshold_ref {
697 Generate::Return(*acc)
698 } else {
699 Generate::Yield(*acc)
700 }
701 }),
702 )
703 .for_each(q!(|_| {}));
704
705 threshold.into_stream().for_each(q!(|_| {}));
706 let _built = flow.finalize();
707 }
708
709 #[test]
711 fn singleton_by_ref_keyed_scan() {
712 let mut flow = FlowBuilder::new();
713 let node = flow.process::<P1>();
714
715 let offset = node
716 .source_iter(q!(0..5i32))
717 .fold(q!(|| 0i32), q!(|acc: &mut i32, x| *acc += x));
718 let offset_ref = offset.by_ref();
719
720 node.source_iter(q!(vec![(0, 1i32), (1, 2i32)]))
721 .into_keyed()
722 .scan(
723 q!(|| 0i32),
724 q!(move |acc: &mut i32, x| {
725 *acc += x + *offset_ref;
726 Some(*acc)
727 }),
728 )
729 .entries()
730 .assume_ordering::<crate::live_collections::stream::TotalOrder>(
731 crate::nondet::nondet!(),
732 )
733 .for_each(q!(|_| {}));
734
735 offset.into_stream().for_each(q!(|_| {}));
736 let _built = flow.finalize();
737 }
738
739 #[test]
741 fn singleton_by_ref_keyed_generator() {
742 use crate::live_collections::keyed_stream::Generate;
743
744 let mut flow = FlowBuilder::new();
745 let node = flow.process::<P1>();
746
747 let threshold = node
748 .source_iter(q!(0..5i32))
749 .fold(q!(|| 0i32), q!(|acc: &mut i32, x| *acc += x));
750 let threshold_ref = threshold.by_ref();
751
752 node.source_iter(q!(vec![(0, 1i32), (1, 2i32)]))
753 .into_keyed()
754 .generator(
755 q!(|| 0i32),
756 q!(move |acc: &mut i32, x| {
757 *acc += x;
758 if *acc > *threshold_ref {
759 Generate::Return(*acc)
760 } else {
761 Generate::Yield(*acc)
762 }
763 }),
764 )
765 .entries()
766 .assume_ordering::<crate::live_collections::stream::TotalOrder>(
767 crate::nondet::nondet!(),
768 )
769 .for_each(q!(|_| {}));
770
771 threshold.into_stream().for_each(q!(|_| {}));
772 let _built = flow.finalize();
773 }
774}