1use core::panic;
2use std::cell::{Cell, RefCell};
3use std::collections::HashMap;
4#[cfg(feature = "build")]
5use std::collections::HashSet;
6use std::fmt::{Debug, Display};
7use std::hash::{Hash, Hasher};
8use std::ops::Deref;
9use std::rc::Rc;
10
11#[cfg(feature = "build")]
12use dfir_lang::graph::FlatGraphBuilder;
13#[cfg(feature = "build")]
14use proc_macro2::Span;
15use proc_macro2::TokenStream;
16use quote::ToTokens;
17#[cfg(feature = "build")]
18use quote::quote;
19#[cfg(feature = "build")]
20use slotmap::{SecondaryMap, SparseSecondaryMap};
21#[cfg(feature = "build")]
22use syn::parse_quote;
23
24#[cfg(feature = "build")]
25use crate::compile::builder::ClockId;
26#[cfg(feature = "build")]
27use crate::compile::builder::StmtId;
28use crate::compile::builder::{CycleId, ExternalPortId};
29#[cfg(feature = "build")]
30use crate::compile::deploy_provider::{Deploy, Node, RegisterPort};
31#[cfg(feature = "build")]
32use crate::handoff_ref::handoff_ref_ident;
33use crate::location::dynamic::{ClusterConsistency, LocationId};
34use crate::location::{LocationKey, NetworkHint};
35
36pub mod backtrace;
37use backtrace::Backtrace;
38
39pub struct ClosureExpr {
45 pub(crate) expr: DebugExpr,
46 pub(crate) singleton_refs: Vec<(HydroNode, bool)>,
50}
51
52impl Clone for ClosureExpr {
53 fn clone(&self) -> Self {
54 Self {
55 expr: self.expr.clone(),
56 singleton_refs: self
57 .singleton_refs
58 .iter()
59 .map(|(node, is_mut)| {
60 let HydroNode::Reference {
61 inner,
62 kind,
63 access_counter,
64 metadata,
65 } = node
66 else {
67 panic!("singleton_refs should only contain HydroNode::Reference");
68 };
69 (
70 HydroNode::Reference {
71 inner: SharedNode(Rc::clone(&inner.0)),
72 kind: *kind,
73 access_counter: access_counter.freeze(),
74 metadata: metadata.clone(),
75 },
76 *is_mut,
77 )
78 })
79 .collect(),
80 }
81 }
82}
83
84impl Hash for ClosureExpr {
85 fn hash<H: Hasher>(&self, state: &mut H) {
86 self.expr.hash(state);
87 }
91}
92
93impl serde::Serialize for ClosureExpr {
94 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
95 use serde::ser::SerializeStruct;
96 let mut s = serializer.serialize_struct("ClosureExpr", 2)?;
97 s.serialize_field("expr", &self.expr)?;
98 s.serialize_field(
99 "singleton_refs",
100 &SerializableSingletonRefs(&self.singleton_refs),
101 )?;
102 s.end()
103 }
104}
105
106struct SerializableSingletonRefs<'a>(&'a [(HydroNode, bool)]);
107
108impl serde::Serialize for SerializableSingletonRefs<'_> {
109 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
110 use serde::ser::SerializeSeq;
111 let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
112 for (node, is_mut) in self.0.iter() {
113 seq.serialize_element(&(node, is_mut))?;
114 }
115 seq.end()
116 }
117}
118
119impl Debug for ClosureExpr {
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121 Debug::fmt(&self.expr, f)
122 }
123}
124
125impl Display for ClosureExpr {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 Display::fmt(&self.expr, f)
128 }
129}
130
131impl From<syn::Expr> for ClosureExpr {
132 fn from(expr: syn::Expr) -> Self {
133 Self {
134 expr: DebugExpr(Box::new(expr)),
135 singleton_refs: Vec::new(),
136 }
137 }
138}
139
140impl From<DebugExpr> for ClosureExpr {
141 fn from(expr: DebugExpr) -> Self {
142 Self {
143 expr,
144 singleton_refs: Vec::new(),
145 }
146 }
147}
148
149impl ClosureExpr {
150 pub fn new(expr: DebugExpr, singleton_refs: Vec<(HydroNode, bool)>) -> Self {
151 Self {
152 expr,
153 singleton_refs,
154 }
155 }
156
157 pub fn has_mut_ref(&self) -> bool {
158 self.singleton_refs.iter().any(|(_, is_mut)| *is_mut)
159 }
160
161 pub fn deep_clone(&self, seen_tees: &mut SeenSharedNodes) -> Self {
162 Self {
163 expr: self.expr.clone(),
164 singleton_refs: self
165 .singleton_refs
166 .iter()
167 .map(|(node, is_mut)| (node.deep_clone(seen_tees), *is_mut))
168 .collect(),
169 }
170 }
171
172 pub fn transform_children(
173 &mut self,
174 transform: &mut impl FnMut(&mut HydroNode, &mut SeenSharedNodes),
175 seen_tees: &mut SeenSharedNodes,
176 ) {
177 for (ref_node, _is_mut) in self.singleton_refs.iter_mut() {
178 transform(ref_node, seen_tees);
179 }
180 }
181
182 #[cfg(feature = "build")]
185 pub fn emit_tokens(&self, ident_stack: &mut Vec<syn::Ident>) -> TokenStream {
186 if self.singleton_refs.is_empty() {
187 self.expr.0.to_token_stream()
188 } else {
189 assert!(
190 ident_stack.len() >= self.singleton_refs.len(),
191 "ident_stack has {} entries but expected at least {} for singleton_refs",
192 ident_stack.len(),
193 self.singleton_refs.len()
194 );
195 let ref_idents = ident_stack.drain(ident_stack.len() - self.singleton_refs.len()..);
196
197 let mut let_bindings = Vec::new();
198 for ((i, (ref_node, is_mut)), ref_ident) in
199 self.singleton_refs.iter().enumerate().zip(ref_idents)
200 {
201 let HydroNode::Reference { access_counter, .. } = ref_node else {
202 panic!("ClosureExpression expected references to `HydroNode::Reference`");
203 };
204 let group = access_counter.frozen_group();
205 let local_ident = handoff_ref_ident(i);
207 let hash = proc_macro2::Punct::new('#', proc_macro2::Spacing::Alone);
208 let group_lit = proc_macro2::Literal::u32_unsuffixed(group);
209 let mut_token = is_mut.then(|| quote!(mut));
210 let binding = quote! {
211 let #local_ident = #hash {#group_lit} #mut_token #ref_ident;
212 };
213 let_bindings.push(binding);
214 }
215
216 let expr = &self.expr.0;
217 quote! {
218 {
219 #( #let_bindings )*
220 #expr
221 }
222 }
223 }
224 }
225}
226
227#[derive(Clone, Hash)]
231pub struct DebugExpr(pub Box<syn::Expr>);
232
233impl serde::Serialize for DebugExpr {
234 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
235 serializer.serialize_str(&self.to_string())
236 }
237}
238
239impl From<syn::Expr> for DebugExpr {
240 fn from(expr: syn::Expr) -> Self {
241 Self(Box::new(expr))
242 }
243}
244
245impl Deref for DebugExpr {
246 type Target = syn::Expr;
247
248 fn deref(&self) -> &Self::Target {
249 &self.0
250 }
251}
252
253impl ToTokens for DebugExpr {
254 fn to_tokens(&self, tokens: &mut TokenStream) {
255 self.0.to_tokens(tokens);
256 }
257}
258
259impl Debug for DebugExpr {
260 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
261 write!(f, "{}", self.0.to_token_stream())
262 }
263}
264
265impl Display for DebugExpr {
266 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
267 let original = self.0.as_ref().clone();
268 let simplified = simplify_q_macro(original);
269
270 write!(f, "q!({})", quote::quote!(#simplified))
273 }
274}
275
276fn simplify_q_macro(expr: syn::Expr) -> syn::Expr {
278 if let syn::Expr::Call(ref call) = expr && let syn::Expr::Path(path_expr) = call.func.as_ref()
279 && is_stageleft_runtime_support_call(&path_expr.path)
281 && let syn::Expr::Block(b) = &call.args[0]
282 && b.block.stmts.len() == 3
283 && let Some(syn::Stmt::Expr(e, _)) = b.block.stmts.get(2)
284 {
286 let mut e = e.clone();
287 while let syn::Expr::Block(ref mut block) = e
288 && block.block.stmts.len() == 1
289 && let syn::Stmt::Expr(inner_e, _) = block.block.stmts.remove(0)
290 {
291 e = inner_e;
292 }
293
294 e
295 } else {
296 expr
297 }
298}
299
300fn is_stageleft_runtime_support_call(path: &syn::Path) -> bool {
301 if let Some(last_segment) = path.segments.last() {
303 let fn_name = last_segment.ident.to_string();
304 path.segments.len() > 2
305 && path.segments[0].ident == "stageleft"
306 && path.segments[1].ident == "runtime_support"
307 && fn_name.contains("_type_hint")
308 } else {
309 false
310 }
311}
312
313#[derive(Clone, PartialEq, Eq, Hash)]
317pub struct DebugType(pub Box<syn::Type>);
318
319impl From<syn::Type> for DebugType {
320 fn from(t: syn::Type) -> Self {
321 Self(Box::new(t))
322 }
323}
324
325impl Deref for DebugType {
326 type Target = syn::Type;
327
328 fn deref(&self) -> &Self::Target {
329 &self.0
330 }
331}
332
333impl ToTokens for DebugType {
334 fn to_tokens(&self, tokens: &mut TokenStream) {
335 self.0.to_tokens(tokens);
336 }
337}
338
339impl Debug for DebugType {
340 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
341 write!(f, "{}", self.0.to_token_stream())
342 }
343}
344
345impl serde::Serialize for DebugType {
346 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
347 serializer.serialize_str(&format!("{}", self.0.to_token_stream()))
348 }
349}
350
351fn serialize_backtrace_as_span<S: serde::Serializer>(
352 backtrace: &Backtrace,
353 serializer: S,
354) -> Result<S::Ok, S::Error> {
355 match backtrace.format_span() {
356 Some(span) => serializer.serialize_some(&span),
357 None => serializer.serialize_none(),
358 }
359}
360
361fn serialize_ident<S: serde::Serializer>(
362 ident: &syn::Ident,
363 serializer: S,
364) -> Result<S::Ok, S::Error> {
365 serializer.serialize_str(&ident.to_string())
366}
367
368pub enum DebugInstantiate {
369 Building,
370 Finalized(Box<DebugInstantiateFinalized>),
371}
372
373impl serde::Serialize for DebugInstantiate {
374 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
375 match self {
376 DebugInstantiate::Building => {
377 serializer.serialize_unit_variant("DebugInstantiate", 0, "Building")
378 }
379 DebugInstantiate::Finalized(_) => {
380 panic!(
381 "cannot serialize DebugInstantiate::Finalized: contains non-serializable runtime state (closures)"
382 )
383 }
384 }
385 }
386}
387
388#[cfg_attr(
389 not(feature = "build"),
390 expect(
391 dead_code,
392 reason = "sink, source unused without `feature = \"build\"`."
393 )
394)]
395pub struct DebugInstantiateFinalized {
396 sink: syn::Expr,
397 source: syn::Expr,
398 connect_fn: Option<Box<dyn FnOnce()>>,
399}
400
401impl From<DebugInstantiateFinalized> for DebugInstantiate {
402 fn from(f: DebugInstantiateFinalized) -> Self {
403 Self::Finalized(Box::new(f))
404 }
405}
406
407impl Debug for DebugInstantiate {
408 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
409 write!(f, "<network instantiate>")
410 }
411}
412
413impl Hash for DebugInstantiate {
414 fn hash<H: Hasher>(&self, _state: &mut H) {
415 }
417}
418
419impl Clone for DebugInstantiate {
420 fn clone(&self) -> Self {
421 match self {
422 DebugInstantiate::Building => DebugInstantiate::Building,
423 DebugInstantiate::Finalized(_) => {
424 panic!("DebugInstantiate::Finalized should not be cloned")
425 }
426 }
427 }
428}
429
430#[derive(Debug, Hash, Clone, serde::Serialize)]
439pub enum ClusterMembersState {
440 Uninit,
442 Stream(DebugExpr),
445 Tee(LocationId, LocationId),
449}
450
451#[derive(Debug, Hash, Clone, serde::Serialize)]
453pub enum HydroSource {
454 Stream(DebugExpr),
455 ExternalNetwork(),
456 Iter(DebugExpr),
457 Spin(),
458 ClusterMembers(LocationId, ClusterMembersState),
459 Embedded(#[serde(serialize_with = "serialize_ident")] syn::Ident),
460 EmbeddedSingleton(#[serde(serialize_with = "serialize_ident")] syn::Ident),
461}
462
463#[cfg(feature = "build")]
464pub trait DfirBuilder {
470 fn singleton_intermediates(&self) -> bool;
472
473 fn get_dfir_mut(&mut self, location: &LocationId) -> &mut FlatGraphBuilder;
475
476 #[expect(clippy::too_many_arguments, reason = "TODO")]
477 fn batch(
478 &mut self,
479 in_ident: syn::Ident,
480 in_location: &LocationId,
481 in_kind: &CollectionKind,
482 out_ident: &syn::Ident,
483 out_location: &LocationId,
484 op_meta: &HydroIrOpMetadata,
485 fold_hooked_idents: &HashSet<String>,
486 );
487 fn yield_from_tick(
488 &mut self,
489 in_ident: syn::Ident,
490 in_location: &LocationId,
491 in_kind: &CollectionKind,
492 out_ident: &syn::Ident,
493 out_location: &LocationId,
494 );
495
496 fn begin_atomic(
497 &mut self,
498 in_ident: syn::Ident,
499 in_location: &LocationId,
500 in_kind: &CollectionKind,
501 out_ident: &syn::Ident,
502 out_location: &LocationId,
503 op_meta: &HydroIrOpMetadata,
504 );
505 fn end_atomic(
506 &mut self,
507 in_ident: syn::Ident,
508 in_location: &LocationId,
509 in_kind: &CollectionKind,
510 out_ident: &syn::Ident,
511 );
512
513 #[expect(clippy::too_many_arguments, reason = "TODO // internal")]
514 fn observe_nondet(
515 &mut self,
516 trusted: bool,
517 location: &LocationId,
518 in_ident: syn::Ident,
519 in_kind: &CollectionKind,
520 out_ident: &syn::Ident,
521 out_kind: &CollectionKind,
522 op_meta: &HydroIrOpMetadata,
523 );
524
525 #[expect(clippy::too_many_arguments, reason = "TODO")]
526 fn merge_ordered(
527 &mut self,
528 location: &LocationId,
529 first_ident: syn::Ident,
530 second_ident: syn::Ident,
531 out_ident: &syn::Ident,
532 in_kind: &CollectionKind,
533 op_meta: &HydroIrOpMetadata,
534 operator_tag: Option<&str>,
535 );
536
537 #[expect(clippy::too_many_arguments, reason = "TODO")]
538 fn create_network(
539 &mut self,
540 from: &LocationId,
541 to: &LocationId,
542 input_ident: syn::Ident,
543 out_ident: &syn::Ident,
544 serialize: Option<&DebugExpr>,
545 sink: syn::Expr,
546 source: syn::Expr,
547 deserialize: Option<&DebugExpr>,
548 tag_id: StmtId,
549 networking_info: &crate::networking::NetworkingInfo,
550 );
551
552 fn create_external_source(
553 &mut self,
554 on: &LocationId,
555 source_expr: syn::Expr,
556 out_ident: &syn::Ident,
557 deserialize: Option<&DebugExpr>,
558 tag_id: StmtId,
559 );
560
561 fn create_external_output(
562 &mut self,
563 on: &LocationId,
564 sink_expr: syn::Expr,
565 input_ident: &syn::Ident,
566 serialize: Option<&DebugExpr>,
567 tag_id: StmtId,
568 );
569
570 fn emit_fold_hook(
573 &mut self,
574 location: &LocationId,
575 in_ident: &syn::Ident,
576 in_kind: &CollectionKind,
577 op_meta: &HydroIrOpMetadata,
578 ) -> Option<syn::Ident>;
579
580 fn assert_is_consistent(
584 &mut self,
585 trusted: bool,
586 location: &LocationId,
587 in_ident: syn::Ident,
588 out_ident: &syn::Ident,
589 );
590
591 fn observe_for_mut(
595 &mut self,
596 location: &LocationId,
597 in_ident: syn::Ident,
598 in_kind: &CollectionKind,
599 out_ident: &syn::Ident,
600 op_meta: &HydroIrOpMetadata,
601 );
602
603 fn create_versioned_network_fork(
604 &mut self,
605 channel_id: u32,
606 dest: &LocationId,
607 senders: Vec<(LocationId, syn::Ident, Option<DebugExpr>)>,
608 tag_id: StmtId,
609 );
610
611 fn create_versioned_network(
612 &mut self,
613 channel_id: u32,
614 source: &LocationId,
615 dest: &LocationId,
616 out_ident: &syn::Ident,
617 deserialize: Option<&DebugExpr>,
618 tag_id: StmtId,
619 );
620}
621
622#[cfg(feature = "build")]
623impl DfirBuilder for SecondaryMap<LocationKey, FlatGraphBuilder> {
624 fn singleton_intermediates(&self) -> bool {
625 false
626 }
627
628 fn get_dfir_mut(&mut self, location: &LocationId) -> &mut FlatGraphBuilder {
629 self.entry(location.root().key())
630 .expect("location was removed")
631 .or_default()
632 }
633
634 fn batch(
635 &mut self,
636 in_ident: syn::Ident,
637 in_location: &LocationId,
638 in_kind: &CollectionKind,
639 out_ident: &syn::Ident,
640 _out_location: &LocationId,
641 _op_meta: &HydroIrOpMetadata,
642 _fold_hooked_idents: &HashSet<String>,
643 ) {
644 let builder = self.get_dfir_mut(in_location.root());
645 if in_kind.is_bounded()
646 && matches!(
647 in_kind,
648 CollectionKind::Singleton { .. }
649 | CollectionKind::Optional { .. }
650 | CollectionKind::KeyedSingleton { .. }
651 )
652 {
653 assert!(in_location.is_top_level());
654 builder.add_dfir(
655 parse_quote! {
656 #out_ident = #in_ident -> persist::<'static>();
657 },
658 None,
659 None,
660 );
661 } else {
662 builder.add_dfir(
663 parse_quote! {
664 #out_ident = #in_ident;
665 },
666 None,
667 None,
668 );
669 }
670 }
671
672 fn yield_from_tick(
673 &mut self,
674 in_ident: syn::Ident,
675 in_location: &LocationId,
676 _in_kind: &CollectionKind,
677 out_ident: &syn::Ident,
678 _out_location: &LocationId,
679 ) {
680 let builder = self.get_dfir_mut(in_location.root());
681 builder.add_dfir(
682 parse_quote! {
683 #out_ident = #in_ident;
684 },
685 None,
686 None,
687 );
688 }
689
690 fn begin_atomic(
691 &mut self,
692 in_ident: syn::Ident,
693 in_location: &LocationId,
694 _in_kind: &CollectionKind,
695 out_ident: &syn::Ident,
696 _out_location: &LocationId,
697 _op_meta: &HydroIrOpMetadata,
698 ) {
699 let builder = self.get_dfir_mut(in_location.root());
700 builder.add_dfir(
701 parse_quote! {
702 #out_ident = #in_ident;
703 },
704 None,
705 None,
706 );
707 }
708
709 fn end_atomic(
710 &mut self,
711 in_ident: syn::Ident,
712 in_location: &LocationId,
713 _in_kind: &CollectionKind,
714 out_ident: &syn::Ident,
715 ) {
716 let builder = self.get_dfir_mut(in_location.root());
717 builder.add_dfir(
718 parse_quote! {
719 #out_ident = #in_ident;
720 },
721 None,
722 None,
723 );
724 }
725
726 fn observe_nondet(
727 &mut self,
728 _trusted: bool,
729 location: &LocationId,
730 in_ident: syn::Ident,
731 _in_kind: &CollectionKind,
732 out_ident: &syn::Ident,
733 _out_kind: &CollectionKind,
734 _op_meta: &HydroIrOpMetadata,
735 ) {
736 let builder = self.get_dfir_mut(location);
737 builder.add_dfir(
738 parse_quote! {
739 #out_ident = #in_ident;
740 },
741 None,
742 None,
743 );
744 }
745
746 fn merge_ordered(
747 &mut self,
748 location: &LocationId,
749 first_ident: syn::Ident,
750 second_ident: syn::Ident,
751 out_ident: &syn::Ident,
752 _in_kind: &CollectionKind,
753 _op_meta: &HydroIrOpMetadata,
754 operator_tag: Option<&str>,
755 ) {
756 let builder = self.get_dfir_mut(location);
757 builder.add_dfir(
758 parse_quote! {
759 #out_ident = union();
760 #first_ident -> [0]#out_ident;
761 #second_ident -> [1]#out_ident;
762 },
763 None,
764 operator_tag,
765 );
766 }
767
768 fn create_network(
769 &mut self,
770 from: &LocationId,
771 to: &LocationId,
772 input_ident: syn::Ident,
773 out_ident: &syn::Ident,
774 serialize: Option<&DebugExpr>,
775 sink: syn::Expr,
776 source: syn::Expr,
777 deserialize: Option<&DebugExpr>,
778 tag_id: StmtId,
779 _networking_info: &crate::networking::NetworkingInfo,
780 ) {
781 let sender_builder = self.get_dfir_mut(from);
782 if let Some(serialize_pipeline) = serialize {
783 sender_builder.add_dfir(
784 parse_quote! {
785 #input_ident -> map(#serialize_pipeline) -> dest_sink(#sink);
786 },
787 None,
788 Some(&format!("send{}", tag_id)),
790 );
791 } else {
792 sender_builder.add_dfir(
793 parse_quote! {
794 #input_ident -> dest_sink(#sink);
795 },
796 None,
797 Some(&format!("send{}", tag_id)),
798 );
799 }
800
801 let receiver_builder = self.get_dfir_mut(to);
802 if let Some(deserialize_pipeline) = deserialize {
803 receiver_builder.add_dfir(
804 parse_quote! {
805 #out_ident = source_stream(#source) -> map(#deserialize_pipeline);
806 },
807 None,
808 Some(&format!("recv{}", tag_id)),
809 );
810 } else {
811 receiver_builder.add_dfir(
812 parse_quote! {
813 #out_ident = source_stream(#source);
814 },
815 None,
816 Some(&format!("recv{}", tag_id)),
817 );
818 }
819 }
820
821 fn create_external_source(
822 &mut self,
823 on: &LocationId,
824 source_expr: syn::Expr,
825 out_ident: &syn::Ident,
826 deserialize: Option<&DebugExpr>,
827 tag_id: StmtId,
828 ) {
829 let receiver_builder = self.get_dfir_mut(on);
830 if let Some(deserialize_pipeline) = deserialize {
831 receiver_builder.add_dfir(
832 parse_quote! {
833 #out_ident = source_stream(#source_expr) -> map(#deserialize_pipeline);
834 },
835 None,
836 Some(&format!("recv{}", tag_id)),
837 );
838 } else {
839 receiver_builder.add_dfir(
840 parse_quote! {
841 #out_ident = source_stream(#source_expr);
842 },
843 None,
844 Some(&format!("recv{}", tag_id)),
845 );
846 }
847 }
848
849 fn create_external_output(
850 &mut self,
851 on: &LocationId,
852 sink_expr: syn::Expr,
853 input_ident: &syn::Ident,
854 serialize: Option<&DebugExpr>,
855 tag_id: StmtId,
856 ) {
857 let sender_builder = self.get_dfir_mut(on);
858 if let Some(serialize_fn) = serialize {
859 sender_builder.add_dfir(
860 parse_quote! {
861 #input_ident -> map(#serialize_fn) -> dest_sink(#sink_expr);
862 },
863 None,
864 Some(&format!("send{}", tag_id)),
866 );
867 } else {
868 sender_builder.add_dfir(
869 parse_quote! {
870 #input_ident -> dest_sink(#sink_expr);
871 },
872 None,
873 Some(&format!("send{}", tag_id)),
874 );
875 }
876 }
877
878 fn emit_fold_hook(
879 &mut self,
880 _location: &LocationId,
881 _in_ident: &syn::Ident,
882 _in_kind: &CollectionKind,
883 _op_meta: &HydroIrOpMetadata,
884 ) -> Option<syn::Ident> {
885 None
886 }
887
888 fn assert_is_consistent(
889 &mut self,
890 _trusted: bool,
891 location: &LocationId,
892 in_ident: syn::Ident,
893 out_ident: &syn::Ident,
894 ) {
895 let builder = self.get_dfir_mut(location);
896 builder.add_dfir(
897 parse_quote! {
898 #out_ident = #in_ident;
899 },
900 None,
901 None,
902 );
903 }
904
905 fn observe_for_mut(
906 &mut self,
907 location: &LocationId,
908 in_ident: syn::Ident,
909 _in_kind: &CollectionKind,
910 out_ident: &syn::Ident,
911 _op_meta: &HydroIrOpMetadata,
912 ) {
913 let builder = self.get_dfir_mut(location);
914 builder.add_dfir(
915 parse_quote! {
916 #out_ident = #in_ident;
917 },
918 None,
919 None,
920 );
921 }
922
923 fn create_versioned_network_fork(
924 &mut self,
925 _channel_id: u32,
926 _dest: &LocationId,
927 _senders: Vec<(LocationId, syn::Ident, Option<DebugExpr>)>,
928 _tag_id: StmtId,
929 ) {
930 unreachable!(
931 "HydroNode::VersionedNetworkFork is only produced by the multi-version simulator merge \
932 pass and cannot be emitted by the non-simulation builder"
933 );
934 }
935
936 fn create_versioned_network(
937 &mut self,
938 _channel_id: u32,
939 _source: &LocationId,
940 _dest: &LocationId,
941 _out_ident: &syn::Ident,
942 _deserialize: Option<&DebugExpr>,
943 _tag_id: StmtId,
944 ) {
945 unreachable!(
946 "HydroNode::VersionedNetwork is only produced by the multi-version simulator merge \
947 pass and cannot be emitted by the non-simulation builder"
948 );
949 }
950}
951
952#[cfg(feature = "build")]
953pub enum BuildersOrCallback<'a, L, N>
954where
955 L: FnMut(&mut HydroRoot, &mut crate::Counter<StmtId>),
956 N: FnMut(&mut HydroNode, &mut crate::Counter<StmtId>),
957{
958 Builders(&'a mut dyn DfirBuilder),
959 Callback(L, N),
960}
961
962#[derive(Debug, Hash, serde::Serialize)]
966pub enum HydroRoot {
967 ForEach {
968 f: ClosureExpr,
969 input: Box<HydroNode>,
970 op_metadata: HydroIrOpMetadata,
971 },
972 SendExternal {
973 to_external_key: LocationKey,
974 to_port_id: ExternalPortId,
975 to_many: bool,
976 unpaired: bool,
977 serialize_fn: Option<DebugExpr>,
978 instantiate_fn: DebugInstantiate,
979 input: Box<HydroNode>,
980 op_metadata: HydroIrOpMetadata,
981 },
982 DestSink {
983 sink: DebugExpr,
984 input: Box<HydroNode>,
985 op_metadata: HydroIrOpMetadata,
986 },
987 CycleSink {
988 cycle_id: CycleId,
989 input: Box<HydroNode>,
990 op_metadata: HydroIrOpMetadata,
991 },
992 EmbeddedOutput {
993 #[serde(serialize_with = "serialize_ident")]
994 ident: syn::Ident,
995 input: Box<HydroNode>,
996 op_metadata: HydroIrOpMetadata,
997 },
998 Null {
999 input: Box<HydroNode>,
1000 op_metadata: HydroIrOpMetadata,
1001 },
1002}
1003
1004impl HydroRoot {
1005 #[cfg(feature = "build")]
1006 #[expect(clippy::too_many_arguments, reason = "TODO(internal)")]
1007 pub fn compile_network<'a, D>(
1008 &mut self,
1009 extra_stmts: &mut SparseSecondaryMap<LocationKey, Vec<syn::Stmt>>,
1010 seen_tees: &mut SeenSharedNodes,
1011 seen_cluster_members: &mut HashSet<(LocationId, LocationKey)>,
1012 processes: &SparseSecondaryMap<LocationKey, D::Process>,
1013 clusters: &SparseSecondaryMap<LocationKey, D::Cluster>,
1014 externals: &SparseSecondaryMap<LocationKey, D::External>,
1015 env: &mut D::InstantiateEnv,
1016 ) where
1017 D: Deploy<'a>,
1018 {
1019 let refcell_extra_stmts = RefCell::new(extra_stmts);
1020 let refcell_env = RefCell::new(env);
1021 let refcell_seen_cluster_members = RefCell::new(seen_cluster_members);
1022 self.transform_bottom_up(
1023 &mut |l| {
1024 if let HydroRoot::SendExternal {
1025 #[cfg(feature = "tokio")]
1026 input,
1027 #[cfg(feature = "tokio")]
1028 to_external_key,
1029 #[cfg(feature = "tokio")]
1030 to_port_id,
1031 #[cfg(feature = "tokio")]
1032 to_many,
1033 #[cfg(feature = "tokio")]
1034 unpaired,
1035 #[cfg(feature = "tokio")]
1036 instantiate_fn,
1037 ..
1038 } = l
1039 {
1040 #[cfg(feature = "tokio")]
1041 let ((sink_expr, source_expr), connect_fn) = match instantiate_fn {
1042 DebugInstantiate::Building => {
1043 let to_node = externals
1044 .get(*to_external_key)
1045 .unwrap_or_else(|| {
1046 panic!("A external used in the graph was not instantiated: {}", to_external_key)
1047 })
1048 .clone();
1049
1050 match input.metadata().location_id.root() {
1051 &LocationId::Process(process_key) => {
1052 if *to_many {
1053 (
1054 (
1055 D::e2o_many_sink(format!("{}_{}", *to_external_key, *to_port_id)),
1056 parse_quote!(DUMMY),
1057 ),
1058 Box::new(|| {}) as Box<dyn FnOnce()>,
1059 )
1060 } else {
1061 let from_node = processes
1062 .get(process_key)
1063 .unwrap_or_else(|| {
1064 panic!("A process used in the graph was not instantiated: {}", process_key)
1065 })
1066 .clone();
1067
1068 let sink_port = from_node.next_port();
1069 let source_port = to_node.next_port();
1070
1071 if *unpaired {
1072 use stageleft::quote_type;
1073 use tokio_util::codec::LengthDelimitedCodec;
1074
1075 to_node.register(*to_port_id, source_port.clone());
1076
1077 let _ = D::e2o_source(
1078 refcell_extra_stmts.borrow_mut().entry(process_key).expect("location was removed").or_default(),
1079 &to_node, &source_port,
1080 &from_node, &sink_port,
1081 "e_type::<LengthDelimitedCodec>(),
1082 format!("{}_{}", *to_external_key, *to_port_id)
1083 );
1084 }
1085
1086 (
1087 (
1088 D::o2e_sink(
1089 &from_node,
1090 &sink_port,
1091 &to_node,
1092 &source_port,
1093 format!("{}_{}", *to_external_key, *to_port_id)
1094 ),
1095 parse_quote!(DUMMY),
1096 ),
1097 if *unpaired {
1098 D::e2o_connect(
1099 &to_node,
1100 &source_port,
1101 &from_node,
1102 &sink_port,
1103 *to_many,
1104 NetworkHint::Auto,
1105 )
1106 } else {
1107 Box::new(|| {}) as Box<dyn FnOnce()>
1108 },
1109 )
1110 }
1111 }
1112 LocationId::Cluster(cluster_key) => {
1113 let from_node = clusters
1114 .get(*cluster_key)
1115 .unwrap_or_else(|| {
1116 panic!("A cluster used in the graph was not instantiated: {}", cluster_key)
1117 })
1118 .clone();
1119
1120 let sink_port = from_node.next_port();
1121 let source_port = to_node.next_port();
1122
1123 if *unpaired {
1124 to_node.register(*to_port_id, source_port.clone());
1125 }
1126
1127 (
1128 (
1129 D::m2e_sink(
1130 &from_node,
1131 &sink_port,
1132 &to_node,
1133 &source_port,
1134 format!("{}_{}", *to_external_key, *to_port_id)
1135 ),
1136 parse_quote!(DUMMY),
1137 ),
1138 Box::new(|| {}) as Box<dyn FnOnce()>,
1139 )
1140 }
1141 _ => panic!()
1142 }
1143 },
1144
1145 DebugInstantiate::Finalized(_) => panic!("network already finalized"),
1146 };
1147
1148 #[cfg(not(feature = "tokio"))]
1149 {
1150 panic!("Cannot instantiate external inputs without tokio");
1151 };
1152
1153 #[cfg(feature = "tokio")]
1154 {
1155 *instantiate_fn = DebugInstantiateFinalized {
1156 sink: sink_expr,
1157 source: source_expr,
1158 connect_fn: Some(connect_fn),
1159 }
1160 .into();
1161 };
1162 } else if let HydroRoot::EmbeddedOutput { ident, input, .. } = l {
1163 let element_type = match &input.metadata().collection_kind {
1164 CollectionKind::Stream { element_type, .. } => element_type.0.as_ref().clone(),
1165 _ => panic!("Embedded output must have Stream collection kind"),
1166 };
1167 let location_key = match input.metadata().location_id.root() {
1168 LocationId::Process(key) | LocationId::Cluster(key) => *key,
1169 _ => panic!("Embedded output must be on a process or cluster"),
1170 };
1171 D::register_embedded_output(
1172 &mut refcell_env.borrow_mut(),
1173 location_key,
1174 ident,
1175 &element_type,
1176 );
1177 }
1178 },
1179 &mut |n| {
1180 if let HydroNode::Network {
1181 name,
1182 networking_info,
1183 input,
1184 instantiate_fn,
1185 metadata,
1186 ..
1187 } = n
1188 {
1189 let (sink_expr, source_expr, connect_fn) = match instantiate_fn {
1190 DebugInstantiate::Building => instantiate_network::<D>(
1191 &mut refcell_env.borrow_mut(),
1192 input.metadata().location_id.root(),
1193 metadata.location_id.root(),
1194 processes,
1195 clusters,
1196 name.as_deref(),
1197 networking_info,
1198 ),
1199
1200 DebugInstantiate::Finalized(_) => panic!("network already finalized"),
1201 };
1202
1203 *instantiate_fn = DebugInstantiateFinalized {
1204 sink: sink_expr,
1205 source: source_expr,
1206 connect_fn: Some(connect_fn),
1207 }
1208 .into();
1209 } else if let HydroNode::ExternalInput {
1210 from_external_key,
1211 from_port_id,
1212 from_many,
1213 codec_type,
1214 port_hint,
1215 instantiate_fn,
1216 metadata,
1217 ..
1218 } = n
1219 {
1220 let ((sink_expr, source_expr), connect_fn) = match instantiate_fn {
1221 DebugInstantiate::Building => {
1222 let from_node = externals
1223 .get(*from_external_key)
1224 .unwrap_or_else(|| {
1225 panic!(
1226 "A external used in the graph was not instantiated: {}",
1227 from_external_key,
1228 )
1229 })
1230 .clone();
1231
1232 match metadata.location_id.root() {
1233 &LocationId::Process(process_key) => {
1234 let to_node = processes
1235 .get(process_key)
1236 .unwrap_or_else(|| {
1237 panic!("A process used in the graph was not instantiated: {}", process_key)
1238 })
1239 .clone();
1240
1241 let sink_port = from_node.next_port();
1242 let source_port = to_node.next_port();
1243
1244 from_node.register(*from_port_id, sink_port.clone());
1245
1246 (
1247 (
1248 parse_quote!(DUMMY),
1249 if *from_many {
1250 D::e2o_many_source(
1251 refcell_extra_stmts.borrow_mut().entry(process_key).expect("location was removed").or_default(),
1252 &to_node, &source_port,
1253 codec_type.0.as_ref(),
1254 format!("{}_{}", *from_external_key, *from_port_id)
1255 )
1256 } else {
1257 D::e2o_source(
1258 refcell_extra_stmts.borrow_mut().entry(process_key).expect("location was removed").or_default(),
1259 &from_node, &sink_port,
1260 &to_node, &source_port,
1261 codec_type.0.as_ref(),
1262 format!("{}_{}", *from_external_key, *from_port_id)
1263 )
1264 },
1265 ),
1266 D::e2o_connect(&from_node, &sink_port, &to_node, &source_port, *from_many, *port_hint),
1267 )
1268 }
1269 LocationId::Cluster(cluster_key) => {
1270 let to_node = clusters
1271 .get(*cluster_key)
1272 .unwrap_or_else(|| {
1273 panic!("A cluster used in the graph was not instantiated: {}", cluster_key)
1274 })
1275 .clone();
1276
1277 let sink_port = from_node.next_port();
1278 let source_port = to_node.next_port();
1279
1280 from_node.register(*from_port_id, sink_port.clone());
1281
1282 (
1283 (
1284 parse_quote!(DUMMY),
1285 D::e2m_source(
1286 refcell_extra_stmts.borrow_mut().entry(*cluster_key).expect("location was removed").or_default(),
1287 &from_node, &sink_port,
1288 &to_node, &source_port,
1289 codec_type.0.as_ref(),
1290 format!("{}_{}", *from_external_key, *from_port_id)
1291 ),
1292 ),
1293 D::e2m_connect(&from_node, &sink_port, &to_node, &source_port, *port_hint),
1294 )
1295 }
1296 _ => panic!()
1297 }
1298 },
1299
1300 DebugInstantiate::Finalized(_) => panic!("network already finalized"),
1301 };
1302
1303 *instantiate_fn = DebugInstantiateFinalized {
1304 sink: sink_expr,
1305 source: source_expr,
1306 connect_fn: Some(connect_fn),
1307 }
1308 .into();
1309 } else if let HydroNode::Source { source: HydroSource::Embedded(ident), metadata } = n {
1310 let element_type = match &metadata.collection_kind {
1311 CollectionKind::Stream { element_type, .. } => element_type.0.as_ref().clone(),
1312 _ => panic!("Embedded source must have Stream collection kind"),
1313 };
1314 let location_key = match metadata.location_id.root() {
1315 LocationId::Process(key) | LocationId::Cluster(key) => *key,
1316 _ => panic!("Embedded source must be on a process or cluster"),
1317 };
1318 D::register_embedded_stream_input(
1319 &mut refcell_env.borrow_mut(),
1320 location_key,
1321 ident,
1322 &element_type,
1323 );
1324 } else if let HydroNode::Source { source: HydroSource::EmbeddedSingleton(ident), metadata } = n {
1325 let element_type = match &metadata.collection_kind {
1326 CollectionKind::Singleton { element_type, .. } => element_type.0.as_ref().clone(),
1327 _ => panic!("EmbeddedSingleton source must have Singleton collection kind"),
1328 };
1329 let location_key = match metadata.location_id.root() {
1330 LocationId::Process(key) | LocationId::Cluster(key) => *key,
1331 _ => panic!("EmbeddedSingleton source must be on a process or cluster"),
1332 };
1333 D::register_embedded_singleton_input(
1334 &mut refcell_env.borrow_mut(),
1335 location_key,
1336 ident,
1337 &element_type,
1338 );
1339 } else if let HydroNode::Source { source: HydroSource::ClusterMembers(location_id, state), metadata } = n {
1340 match state {
1341 ClusterMembersState::Uninit => {
1342 let at_location = metadata.location_id.root().clone();
1343 let key = (at_location.clone(), location_id.key());
1344 if refcell_seen_cluster_members.borrow_mut().insert(key) {
1345 let expr = stageleft::QuotedWithContext::splice_untyped_ctx(
1347 D::cluster_membership_stream(&mut refcell_env.borrow_mut(), &at_location, location_id),
1348 &(),
1349 );
1350 *state = ClusterMembersState::Stream(expr.into());
1351 } else {
1352 *state = ClusterMembersState::Tee(at_location, location_id.clone());
1354 }
1355 }
1356 ClusterMembersState::Stream(_) | ClusterMembersState::Tee(..) => {
1357 panic!("cluster members already finalized");
1358 }
1359 }
1360 }
1361 },
1362 seen_tees,
1363 false,
1364 );
1365 }
1366
1367 pub fn connect_network(&mut self, seen_tees: &mut SeenSharedNodes) {
1368 self.transform_bottom_up(
1369 &mut |l| {
1370 if let HydroRoot::SendExternal { instantiate_fn, .. } = l {
1371 match instantiate_fn {
1372 DebugInstantiate::Building => panic!("network not built"),
1373
1374 DebugInstantiate::Finalized(finalized) => {
1375 (finalized.connect_fn.take().unwrap())();
1376 }
1377 }
1378 }
1379 },
1380 &mut |n| {
1381 if let HydroNode::Network { instantiate_fn, .. }
1382 | HydroNode::ExternalInput { instantiate_fn, .. } = n
1383 {
1384 match instantiate_fn {
1385 DebugInstantiate::Building => panic!("network not built"),
1386
1387 DebugInstantiate::Finalized(finalized) => {
1388 (finalized.connect_fn.take().unwrap())();
1389 }
1390 }
1391 }
1392 },
1393 seen_tees,
1394 false,
1395 );
1396 }
1397
1398 pub fn transform_bottom_up(
1399 &mut self,
1400 transform_root: &mut impl FnMut(&mut HydroRoot),
1401 transform_node: &mut impl FnMut(&mut HydroNode),
1402 seen_tees: &mut SeenSharedNodes,
1403 check_well_formed: bool,
1404 ) {
1405 self.transform_children(
1406 |n, s| n.transform_bottom_up(transform_node, s, check_well_formed),
1407 seen_tees,
1408 );
1409
1410 transform_root(self);
1411 }
1412
1413 pub fn transform_children(
1414 &mut self,
1415 mut transform: impl FnMut(&mut HydroNode, &mut SeenSharedNodes),
1416 seen_tees: &mut SeenSharedNodes,
1417 ) {
1418 match self {
1419 HydroRoot::ForEach { f, input, .. } => {
1420 f.transform_children(&mut transform, seen_tees);
1421 transform(input, seen_tees);
1422 }
1423 HydroRoot::SendExternal { input, .. }
1424 | HydroRoot::DestSink { input, .. }
1425 | HydroRoot::CycleSink { input, .. }
1426 | HydroRoot::EmbeddedOutput { input, .. }
1427 | HydroRoot::Null { input, .. } => {
1428 transform(input, seen_tees);
1429 }
1430 }
1431 }
1432
1433 pub fn deep_clone(&self, seen_tees: &mut SeenSharedNodes) -> HydroRoot {
1434 match self {
1435 HydroRoot::ForEach {
1436 f,
1437 input,
1438 op_metadata,
1439 } => HydroRoot::ForEach {
1440 f: f.deep_clone(seen_tees),
1441 input: Box::new(input.deep_clone(seen_tees)),
1442 op_metadata: op_metadata.clone(),
1443 },
1444 HydroRoot::SendExternal {
1445 to_external_key,
1446 to_port_id,
1447 to_many,
1448 unpaired,
1449 serialize_fn,
1450 instantiate_fn,
1451 input,
1452 op_metadata,
1453 } => HydroRoot::SendExternal {
1454 to_external_key: *to_external_key,
1455 to_port_id: *to_port_id,
1456 to_many: *to_many,
1457 unpaired: *unpaired,
1458 serialize_fn: serialize_fn.clone(),
1459 instantiate_fn: instantiate_fn.clone(),
1460 input: Box::new(input.deep_clone(seen_tees)),
1461 op_metadata: op_metadata.clone(),
1462 },
1463 HydroRoot::DestSink {
1464 sink,
1465 input,
1466 op_metadata,
1467 } => HydroRoot::DestSink {
1468 sink: sink.clone(),
1469 input: Box::new(input.deep_clone(seen_tees)),
1470 op_metadata: op_metadata.clone(),
1471 },
1472 HydroRoot::CycleSink {
1473 cycle_id,
1474 input,
1475 op_metadata,
1476 } => HydroRoot::CycleSink {
1477 cycle_id: *cycle_id,
1478 input: Box::new(input.deep_clone(seen_tees)),
1479 op_metadata: op_metadata.clone(),
1480 },
1481 HydroRoot::EmbeddedOutput {
1482 ident,
1483 input,
1484 op_metadata,
1485 } => HydroRoot::EmbeddedOutput {
1486 ident: ident.clone(),
1487 input: Box::new(input.deep_clone(seen_tees)),
1488 op_metadata: op_metadata.clone(),
1489 },
1490 HydroRoot::Null { input, op_metadata } => HydroRoot::Null {
1491 input: Box::new(input.deep_clone(seen_tees)),
1492 op_metadata: op_metadata.clone(),
1493 },
1494 }
1495 }
1496
1497 #[cfg(feature = "build")]
1498 pub fn emit(
1499 &mut self,
1500 graph_builders: &mut dyn DfirBuilder,
1501 seen_tees: &mut SeenSharedNodes,
1502 built_tees: &mut HashMap<*const RefCell<HydroNode>, Vec<syn::Ident>>,
1503 next_stmt_id: &mut crate::Counter<StmtId>,
1504 fold_hooked_idents: &mut HashSet<String>,
1505 ) {
1506 self.emit_core(
1507 &mut BuildersOrCallback::<
1508 fn(&mut HydroRoot, &mut crate::Counter<StmtId>),
1509 fn(&mut HydroNode, &mut crate::Counter<StmtId>),
1510 >::Builders(graph_builders),
1511 seen_tees,
1512 built_tees,
1513 next_stmt_id,
1514 fold_hooked_idents,
1515 );
1516 }
1517
1518 #[cfg(feature = "build")]
1519 pub fn emit_core(
1520 &mut self,
1521 builders_or_callback: &mut BuildersOrCallback<
1522 impl FnMut(&mut HydroRoot, &mut crate::Counter<StmtId>),
1523 impl FnMut(&mut HydroNode, &mut crate::Counter<StmtId>),
1524 >,
1525 seen_tees: &mut SeenSharedNodes,
1526 built_tees: &mut HashMap<*const RefCell<HydroNode>, Vec<syn::Ident>>,
1527 next_stmt_id: &mut crate::Counter<StmtId>,
1528 fold_hooked_idents: &mut HashSet<String>,
1529 ) {
1530 match self {
1531 HydroRoot::ForEach { f, input, .. } => {
1532 let input_ident = input.emit_core(
1533 builders_or_callback,
1534 seen_tees,
1535 built_tees,
1536 next_stmt_id,
1537 fold_hooked_idents,
1538 );
1539
1540 let input_ident = if !input.metadata().collection_kind.is_strict() {
1544 let observe_stmt_id = next_stmt_id.get_and_increment();
1545 let observe_ident =
1546 syn::Ident::new(&format!("stream_{}", observe_stmt_id), Span::call_site());
1547 if let BuildersOrCallback::Builders(graph_builders) = builders_or_callback {
1548 graph_builders.observe_for_mut(
1549 &input.metadata().location_id,
1550 input_ident,
1551 &input.metadata().collection_kind,
1552 &observe_ident,
1553 &input.metadata().op,
1554 );
1555 }
1556 observe_ident
1557 } else {
1558 input_ident
1559 };
1560
1561 let stmt_id = next_stmt_id.get_and_increment();
1562
1563 match builders_or_callback {
1564 BuildersOrCallback::Builders(graph_builders) => {
1565 let mut ident_stack: Vec<syn::Ident> = Vec::new();
1566
1567 for (ref_node, _is_mut) in f.singleton_refs.iter() {
1569 let HydroNode::Reference { inner, .. } = ref_node else {
1570 panic!("singleton_refs should only contain HydroNode::Reference");
1571 };
1572 let ptr = inner.0.as_ref() as *const RefCell<HydroNode>;
1573 let idents = built_tees.get(&ptr).expect(
1574 "ForEach singleton ref not found in built_tees — ref node was not emitted",
1575 );
1576 ident_stack.push(idents[0].clone());
1577 }
1578
1579 let f_tokens = f.emit_tokens(&mut ident_stack);
1580
1581 graph_builders
1582 .get_dfir_mut(&input.metadata().location_id)
1583 .add_dfir(
1584 parse_quote! {
1585 #input_ident -> for_each(#f_tokens);
1586 },
1587 None,
1588 Some(&stmt_id.to_string()),
1589 );
1590 }
1591 BuildersOrCallback::Callback(leaf_callback, _) => {
1592 leaf_callback(self, next_stmt_id);
1593 }
1594 }
1595 }
1596
1597 HydroRoot::SendExternal {
1598 serialize_fn,
1599 instantiate_fn,
1600 input,
1601 ..
1602 } => {
1603 let input_ident = input.emit_core(
1604 builders_or_callback,
1605 seen_tees,
1606 built_tees,
1607 next_stmt_id,
1608 fold_hooked_idents,
1609 );
1610
1611 let stmt_id = next_stmt_id.get_and_increment();
1612
1613 match builders_or_callback {
1614 BuildersOrCallback::Builders(graph_builders) => {
1615 let (sink_expr, _) = match instantiate_fn {
1616 DebugInstantiate::Building => (
1617 syn::parse_quote!(DUMMY_SINK),
1618 syn::parse_quote!(DUMMY_SOURCE),
1619 ),
1620
1621 DebugInstantiate::Finalized(finalized) => {
1622 (finalized.sink.clone(), finalized.source.clone())
1623 }
1624 };
1625
1626 graph_builders.create_external_output(
1627 &input.metadata().location_id,
1628 sink_expr,
1629 &input_ident,
1630 serialize_fn.as_ref(),
1631 stmt_id,
1632 );
1633 }
1634 BuildersOrCallback::Callback(leaf_callback, _) => {
1635 leaf_callback(self, next_stmt_id);
1636 }
1637 }
1638 }
1639
1640 HydroRoot::DestSink { sink, input, .. } => {
1641 let input_ident = input.emit_core(
1642 builders_or_callback,
1643 seen_tees,
1644 built_tees,
1645 next_stmt_id,
1646 fold_hooked_idents,
1647 );
1648
1649 let stmt_id = next_stmt_id.get_and_increment();
1650
1651 match builders_or_callback {
1652 BuildersOrCallback::Builders(graph_builders) => {
1653 graph_builders
1654 .get_dfir_mut(&input.metadata().location_id)
1655 .add_dfir(
1656 parse_quote! {
1657 #input_ident -> dest_sink(#sink);
1658 },
1659 None,
1660 Some(&stmt_id.to_string()),
1661 );
1662 }
1663 BuildersOrCallback::Callback(leaf_callback, _) => {
1664 leaf_callback(self, next_stmt_id);
1665 }
1666 }
1667 }
1668
1669 HydroRoot::CycleSink {
1670 cycle_id, input, ..
1671 } => {
1672 let input_ident = input.emit_core(
1673 builders_or_callback,
1674 seen_tees,
1675 built_tees,
1676 next_stmt_id,
1677 fold_hooked_idents,
1678 );
1679
1680 match builders_or_callback {
1681 BuildersOrCallback::Builders(graph_builders) => {
1682 let elem_type: syn::Type = match &input.metadata().collection_kind {
1683 CollectionKind::KeyedSingleton {
1684 key_type,
1685 value_type,
1686 ..
1687 }
1688 | CollectionKind::KeyedStream {
1689 key_type,
1690 value_type,
1691 ..
1692 } => {
1693 parse_quote!((#key_type, #value_type))
1694 }
1695 CollectionKind::Stream { element_type, .. }
1696 | CollectionKind::Singleton { element_type, .. }
1697 | CollectionKind::Optional { element_type, .. } => {
1698 parse_quote!(#element_type)
1699 }
1700 };
1701
1702 let cycle_id_ident = cycle_id.as_ident();
1703 graph_builders
1704 .get_dfir_mut(&input.metadata().location_id)
1705 .add_dfir(
1706 parse_quote! {
1707 #cycle_id_ident = #input_ident -> identity::<#elem_type>();
1708 },
1709 None,
1710 None,
1711 );
1712 }
1713 BuildersOrCallback::Callback(_, _) => {}
1715 }
1716 }
1717
1718 HydroRoot::EmbeddedOutput { ident, input, .. } => {
1719 let input_ident = input.emit_core(
1720 builders_or_callback,
1721 seen_tees,
1722 built_tees,
1723 next_stmt_id,
1724 fold_hooked_idents,
1725 );
1726
1727 let stmt_id = next_stmt_id.get_and_increment();
1728
1729 match builders_or_callback {
1730 BuildersOrCallback::Builders(graph_builders) => {
1731 graph_builders
1732 .get_dfir_mut(&input.metadata().location_id)
1733 .add_dfir(
1734 parse_quote! {
1735 #input_ident -> for_each(&mut #ident);
1736 },
1737 None,
1738 Some(&stmt_id.to_string()),
1739 );
1740 }
1741 BuildersOrCallback::Callback(leaf_callback, _) => {
1742 leaf_callback(self, next_stmt_id);
1743 }
1744 }
1745 }
1746
1747 HydroRoot::Null { input, .. } => {
1748 let input_ident = input.emit_core(
1749 builders_or_callback,
1750 seen_tees,
1751 built_tees,
1752 next_stmt_id,
1753 fold_hooked_idents,
1754 );
1755
1756 let stmt_id = next_stmt_id.get_and_increment();
1757
1758 match builders_or_callback {
1759 BuildersOrCallback::Builders(graph_builders) => {
1760 graph_builders
1761 .get_dfir_mut(&input.metadata().location_id)
1762 .add_dfir(
1763 parse_quote! {
1764 #input_ident -> for_each(|_| {});
1765 },
1766 None,
1767 Some(&stmt_id.to_string()),
1768 );
1769 }
1770 BuildersOrCallback::Callback(leaf_callback, _) => {
1771 leaf_callback(self, next_stmt_id);
1772 }
1773 }
1774 }
1775 }
1776 }
1777
1778 pub fn op_metadata(&self) -> &HydroIrOpMetadata {
1779 match self {
1780 HydroRoot::ForEach { op_metadata, .. }
1781 | HydroRoot::SendExternal { op_metadata, .. }
1782 | HydroRoot::DestSink { op_metadata, .. }
1783 | HydroRoot::CycleSink { op_metadata, .. }
1784 | HydroRoot::EmbeddedOutput { op_metadata, .. }
1785 | HydroRoot::Null { op_metadata, .. } => op_metadata,
1786 }
1787 }
1788
1789 pub fn op_metadata_mut(&mut self) -> &mut HydroIrOpMetadata {
1790 match self {
1791 HydroRoot::ForEach { op_metadata, .. }
1792 | HydroRoot::SendExternal { op_metadata, .. }
1793 | HydroRoot::DestSink { op_metadata, .. }
1794 | HydroRoot::CycleSink { op_metadata, .. }
1795 | HydroRoot::EmbeddedOutput { op_metadata, .. }
1796 | HydroRoot::Null { op_metadata, .. } => op_metadata,
1797 }
1798 }
1799
1800 pub fn input(&self) -> &HydroNode {
1801 match self {
1802 HydroRoot::ForEach { input, .. }
1803 | HydroRoot::SendExternal { input, .. }
1804 | HydroRoot::DestSink { input, .. }
1805 | HydroRoot::CycleSink { input, .. }
1806 | HydroRoot::EmbeddedOutput { input, .. }
1807 | HydroRoot::Null { input, .. } => input,
1808 }
1809 }
1810
1811 pub fn input_metadata(&self) -> &HydroIrMetadata {
1812 self.input().metadata()
1813 }
1814
1815 pub fn print_root(&self) -> String {
1816 match self {
1817 HydroRoot::ForEach { f, .. } => format!("ForEach({:?})", f),
1818 HydroRoot::SendExternal { .. } => "SendExternal".to_owned(),
1819 HydroRoot::DestSink { sink, .. } => format!("DestSink({:?})", sink),
1820 HydroRoot::CycleSink { cycle_id, .. } => format!("CycleSink({})", cycle_id),
1821 HydroRoot::EmbeddedOutput { ident, .. } => {
1822 format!("EmbeddedOutput({})", ident)
1823 }
1824 HydroRoot::Null { .. } => "Null".to_owned(),
1825 }
1826 }
1827
1828 pub fn visit_debug_expr(&mut self, mut transform: impl FnMut(&mut DebugExpr)) {
1829 match self {
1830 HydroRoot::ForEach { f, .. } => {
1831 transform(&mut f.expr);
1832 }
1833 HydroRoot::DestSink { sink, .. } => {
1834 transform(sink);
1835 }
1836 HydroRoot::SendExternal { .. }
1837 | HydroRoot::CycleSink { .. }
1838 | HydroRoot::EmbeddedOutput { .. }
1839 | HydroRoot::Null { .. } => {}
1840 }
1841 }
1842}
1843
1844#[cfg(feature = "build")]
1845fn tick_of(loc: &LocationId) -> Option<ClockId> {
1846 match loc {
1847 LocationId::Tick(id, _) => Some(*id),
1848 LocationId::Atomic(inner) => tick_of(inner),
1849 _ => None,
1850 }
1851}
1852
1853#[cfg(feature = "build")]
1854fn remap_location(loc: &mut LocationId, uf: &mut HashMap<ClockId, ClockId>) {
1855 match loc {
1856 LocationId::Tick(id, inner) => {
1857 *id = uf_find(uf, *id);
1858 remap_location(inner, uf);
1859 }
1860 LocationId::Atomic(inner) => {
1861 remap_location(inner, uf);
1862 }
1863 LocationId::Process(_) | LocationId::Cluster(_) => {}
1864 }
1865}
1866
1867#[cfg(feature = "build")]
1868fn uf_find(parent: &mut HashMap<ClockId, ClockId>, x: ClockId) -> ClockId {
1869 let p = *parent.get(&x).unwrap_or(&x);
1870 if p == x {
1871 return x;
1872 }
1873 let root = uf_find(parent, p);
1874 parent.insert(x, root);
1875 root
1876}
1877
1878#[cfg(feature = "build")]
1879fn uf_union(parent: &mut HashMap<ClockId, ClockId>, a: ClockId, b: ClockId) {
1880 let ra = uf_find(parent, a);
1881 let rb = uf_find(parent, b);
1882 if ra != rb {
1883 parent.insert(ra, rb);
1884 }
1885}
1886
1887#[cfg(feature = "build")]
1891pub fn unify_atomic_ticks(ir: &mut [HydroRoot]) {
1892 let mut uf: HashMap<ClockId, ClockId> = HashMap::new();
1893
1894 transform_bottom_up(
1896 ir,
1897 &mut |_| {},
1898 &mut |node: &mut HydroNode| match node {
1899 HydroNode::Batch { inner, metadata } | HydroNode::YieldConcat { inner, metadata } => {
1900 if let (Some(a), Some(b)) = (
1901 tick_of(&inner.metadata().location_id),
1902 tick_of(&metadata.location_id),
1903 ) {
1904 uf_union(&mut uf, a, b);
1905 }
1906 }
1907 HydroNode::Chain {
1908 first,
1909 second,
1910 metadata,
1911 }
1912 | HydroNode::ChainFirst {
1913 first,
1914 second,
1915 metadata,
1916 }
1917 | HydroNode::MergeOrdered {
1918 first,
1919 second,
1920 metadata,
1921 } => {
1922 if let (Some(a), Some(b)) = (
1923 tick_of(&first.metadata().location_id),
1924 tick_of(&metadata.location_id),
1925 ) {
1926 uf_union(&mut uf, a, b);
1927 }
1928 if let (Some(a), Some(b)) = (
1929 tick_of(&second.metadata().location_id),
1930 tick_of(&metadata.location_id),
1931 ) {
1932 uf_union(&mut uf, a, b);
1933 }
1934 }
1935 _ => {}
1936 },
1937 false,
1938 );
1939
1940 transform_bottom_up(
1942 ir,
1943 &mut |_| {},
1944 &mut |node: &mut HydroNode| {
1945 remap_location(&mut node.metadata_mut().location_id, &mut uf);
1946 },
1947 false,
1948 );
1949}
1950
1951#[cfg(feature = "build")]
1952pub fn emit(ir: &mut Vec<HydroRoot>) -> SecondaryMap<LocationKey, FlatGraphBuilder> {
1953 let mut builders = SecondaryMap::new();
1954 let mut seen_tees = HashMap::new();
1955 let mut built_tees = HashMap::new();
1956 let mut next_stmt_id = crate::Counter::<StmtId>::default();
1957 let mut fold_hooked_idents = HashSet::new();
1958 for leaf in ir {
1959 leaf.emit(
1960 &mut builders,
1961 &mut seen_tees,
1962 &mut built_tees,
1963 &mut next_stmt_id,
1964 &mut fold_hooked_idents,
1965 );
1966 }
1967 builders
1968}
1969
1970#[cfg(feature = "build")]
1971pub fn traverse_dfir(
1972 ir: &mut [HydroRoot],
1973 transform_root: impl FnMut(&mut HydroRoot, &mut crate::Counter<StmtId>),
1974 transform_node: impl FnMut(&mut HydroNode, &mut crate::Counter<StmtId>),
1975) {
1976 let mut seen_tees = HashMap::new();
1977 let mut built_tees = HashMap::new();
1978 let mut next_stmt_id = crate::Counter::<StmtId>::default();
1979 let mut fold_hooked_idents = HashSet::new();
1980 let mut callback = BuildersOrCallback::Callback(transform_root, transform_node);
1981 ir.iter_mut().for_each(|leaf| {
1982 leaf.emit_core(
1983 &mut callback,
1984 &mut seen_tees,
1985 &mut built_tees,
1986 &mut next_stmt_id,
1987 &mut fold_hooked_idents,
1988 );
1989 });
1990}
1991
1992pub fn transform_bottom_up(
1993 ir: &mut [HydroRoot],
1994 transform_root: &mut impl FnMut(&mut HydroRoot),
1995 transform_node: &mut impl FnMut(&mut HydroNode),
1996 check_well_formed: bool,
1997) {
1998 let mut seen_tees = HashMap::new();
1999 ir.iter_mut().for_each(|leaf| {
2000 leaf.transform_bottom_up(
2001 transform_root,
2002 transform_node,
2003 &mut seen_tees,
2004 check_well_formed,
2005 );
2006 });
2007}
2008
2009pub fn deep_clone(ir: &[HydroRoot]) -> Vec<HydroRoot> {
2010 let mut seen_tees = HashMap::new();
2011 ir.iter()
2012 .map(|leaf| leaf.deep_clone(&mut seen_tees))
2013 .collect()
2014}
2015
2016type PrintedTees = RefCell<Option<(usize, HashMap<*const RefCell<HydroNode>, usize>)>>;
2017thread_local! {
2018 static PRINTED_TEES: PrintedTees = const { RefCell::new(None) };
2019 static SERIALIZED_SHARED: PrintedTees
2023 = const { RefCell::new(None) };
2024}
2025
2026pub fn dbg_dedup_tee<T>(f: impl FnOnce() -> T) -> T {
2027 PRINTED_TEES.with(|printed_tees| {
2028 let mut printed_tees_mut = printed_tees.borrow_mut();
2029 *printed_tees_mut = Some((0, HashMap::new()));
2030 drop(printed_tees_mut);
2031
2032 let ret = f();
2033
2034 let mut printed_tees_mut = printed_tees.borrow_mut();
2035 *printed_tees_mut = None;
2036
2037 ret
2038 })
2039}
2040
2041pub fn serialize_dedup_shared<T>(f: impl FnOnce() -> T) -> T {
2046 let _guard = SerializedSharedGuard::enter();
2047 f()
2048}
2049
2050struct SerializedSharedGuard {
2053 previous: Option<(usize, HashMap<*const RefCell<HydroNode>, usize>)>,
2054}
2055
2056impl SerializedSharedGuard {
2057 fn enter() -> Self {
2058 let previous = SERIALIZED_SHARED.with(|cell| {
2059 let mut guard = cell.borrow_mut();
2060 guard.replace((0, HashMap::new()))
2061 });
2062 Self { previous }
2063 }
2064}
2065
2066impl Drop for SerializedSharedGuard {
2067 fn drop(&mut self) {
2068 SERIALIZED_SHARED.with(|cell| {
2069 *cell.borrow_mut() = self.previous.take();
2070 });
2071 }
2072}
2073
2074pub struct SharedNode(pub Rc<RefCell<HydroNode>>);
2075
2076impl serde::Serialize for SharedNode {
2077 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
2088 SERIALIZED_SHARED.with(|cell| {
2089 let mut guard = cell.borrow_mut();
2090 let state = guard.as_mut().ok_or_else(|| {
2092 serde::ser::Error::custom(
2093 "SharedNode serialization requires an active serialize_dedup_shared scope",
2094 )
2095 })?;
2096 let ptr = self.0.as_ptr() as *const RefCell<HydroNode>;
2097
2098 if let Some(&id) = state.1.get(&ptr) {
2099 drop(guard);
2100 use serde::ser::SerializeMap;
2101 let mut map = serializer.serialize_map(Some(1))?;
2102 map.serialize_entry("$shared_ref", &id)?;
2103 map.end()
2104 } else {
2105 let id = state.0;
2106 state.0 += 1;
2107 state.1.insert(ptr, id);
2108 drop(guard);
2109
2110 use serde::ser::SerializeMap;
2111 let mut map = serializer.serialize_map(Some(2))?;
2112 map.serialize_entry("$shared", &id)?;
2113 map.serialize_entry("node", &*self.0.borrow())?;
2114 map.end()
2115 }
2116 })
2117 }
2118}
2119
2120impl SharedNode {
2121 pub fn as_ptr(&self) -> *const RefCell<HydroNode> {
2122 Rc::as_ptr(&self.0)
2123 }
2124}
2125
2126impl Debug for SharedNode {
2127 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2128 PRINTED_TEES.with(|printed_tees| {
2129 let mut printed_tees_mut_borrow = printed_tees.borrow_mut();
2130 let printed_tees_mut = printed_tees_mut_borrow.as_mut();
2131
2132 if let Some(printed_tees_mut) = printed_tees_mut {
2133 if let Some(existing) = printed_tees_mut
2134 .1
2135 .get(&(self.0.as_ref() as *const RefCell<HydroNode>))
2136 {
2137 write!(f, "<shared {}>", existing)
2138 } else {
2139 let next_id = printed_tees_mut.0;
2140 printed_tees_mut.0 += 1;
2141 printed_tees_mut
2142 .1
2143 .insert(self.0.as_ref() as *const RefCell<HydroNode>, next_id);
2144 drop(printed_tees_mut_borrow);
2145 write!(f, "<shared {}>: ", next_id)?;
2146 Debug::fmt(&self.0.borrow(), f)
2147 }
2148 } else {
2149 drop(printed_tees_mut_borrow);
2150 write!(f, "<shared>: ")?;
2151 Debug::fmt(&self.0.borrow(), f)
2152 }
2153 })
2154 }
2155}
2156
2157impl Hash for SharedNode {
2158 fn hash<H: Hasher>(&self, state: &mut H) {
2159 self.0.borrow_mut().hash(state);
2160 }
2161}
2162
2163#[derive(Debug)]
2168pub enum AccessCounter {
2169 Counting(Cell<u32>),
2170 Frozen(u32),
2171}
2172
2173impl AccessCounter {
2174 pub fn new() -> Self {
2175 Self::Counting(Cell::new(0))
2176 }
2177
2178 pub fn next_group(&self, is_mut: bool) -> Self {
2182 let AccessCounter::Counting(count) = self else {
2183 panic!("Cannot count on `AccessCounter::Frozen`");
2184 };
2185 let c = if is_mut {
2186 let c = count.get() + 1;
2187 count.set(c + 1);
2188 c
2189 } else {
2190 count.get()
2191 };
2192 Self::Frozen(c)
2193 }
2194
2195 pub fn freeze(&self) -> Self {
2197 Self::Frozen(match self {
2198 Self::Counting(count) => count.get(),
2199 Self::Frozen(count) => *count,
2200 })
2201 }
2202
2203 pub fn frozen_group(&self) -> u32 {
2204 let Self::Frozen(count) = self else {
2205 panic!("`AccessCounter` not frozen");
2206 };
2207 *count
2208 }
2209}
2210
2211impl Default for AccessCounter {
2212 fn default() -> Self {
2213 Self::new()
2214 }
2215}
2216
2217impl Hash for AccessCounter {
2218 fn hash<H: Hasher>(&self, _state: &mut H) {
2219 }
2221}
2222
2223impl serde::Serialize for AccessCounter {
2224 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
2225 let count = match self {
2226 AccessCounter::Counting(count) => count.get(),
2227 AccessCounter::Frozen(count) => *count,
2228 };
2229 count.serialize(serializer)
2230 }
2231}
2232
2233#[derive(serde::Serialize, Clone, PartialEq, Eq, Debug)]
2234pub enum BoundKind {
2235 Unbounded,
2236 Bounded,
2237}
2238
2239#[derive(serde::Serialize, Clone, PartialEq, Eq, Debug)]
2240pub enum StreamOrder {
2241 NoOrder,
2242 TotalOrder,
2243}
2244
2245#[derive(serde::Serialize, Clone, PartialEq, Eq, Debug)]
2246pub enum StreamRetry {
2247 AtLeastOnce,
2248 ExactlyOnce,
2249}
2250
2251#[derive(serde::Serialize, Clone, PartialEq, Eq, Debug)]
2252pub enum KeyedSingletonBoundKind {
2253 Unbounded,
2254 MonotonicKeys,
2255 MonotonicValue,
2256 BoundedValue,
2257 Bounded,
2258}
2259
2260#[derive(serde::Serialize, Clone, PartialEq, Eq, Debug)]
2261pub enum SingletonBoundKind {
2262 Unbounded,
2263 Monotonic,
2264 Bounded,
2265}
2266
2267#[derive(Clone, PartialEq, Eq, Debug, serde::Serialize)]
2268pub enum CollectionKind {
2269 Stream {
2270 bound: BoundKind,
2271 order: StreamOrder,
2272 retry: StreamRetry,
2273 element_type: DebugType,
2274 },
2275 Singleton {
2276 bound: SingletonBoundKind,
2277 element_type: DebugType,
2278 },
2279 Optional {
2280 bound: BoundKind,
2281 element_type: DebugType,
2282 },
2283 KeyedStream {
2284 bound: BoundKind,
2285 value_order: StreamOrder,
2286 value_retry: StreamRetry,
2287 key_type: DebugType,
2288 value_type: DebugType,
2289 },
2290 KeyedSingleton {
2291 bound: KeyedSingletonBoundKind,
2292 key_type: DebugType,
2293 value_type: DebugType,
2294 },
2295}
2296
2297impl CollectionKind {
2298 pub fn is_bounded(&self) -> bool {
2299 matches!(
2300 self,
2301 CollectionKind::Stream {
2302 bound: BoundKind::Bounded,
2303 ..
2304 } | CollectionKind::Singleton {
2305 bound: SingletonBoundKind::Bounded,
2306 ..
2307 } | CollectionKind::Optional {
2308 bound: BoundKind::Bounded,
2309 ..
2310 } | CollectionKind::KeyedStream {
2311 bound: BoundKind::Bounded,
2312 ..
2313 } | CollectionKind::KeyedSingleton {
2314 bound: KeyedSingletonBoundKind::Bounded,
2315 ..
2316 }
2317 )
2318 }
2319
2320 pub fn is_strict(&self) -> bool {
2323 match self {
2324 CollectionKind::Stream { order, retry, .. } => {
2325 *order == StreamOrder::TotalOrder && *retry == StreamRetry::ExactlyOnce
2326 }
2327 CollectionKind::KeyedStream {
2328 value_order,
2329 value_retry,
2330 ..
2331 } => {
2332 *value_order == StreamOrder::TotalOrder && *value_retry == StreamRetry::ExactlyOnce
2333 }
2334 CollectionKind::Singleton { .. }
2337 | CollectionKind::Optional { .. }
2338 | CollectionKind::KeyedSingleton { .. } => true,
2339 }
2340 }
2341
2342 pub fn strict_kind(&self) -> CollectionKind {
2344 match self {
2345 CollectionKind::Stream {
2346 bound,
2347 element_type,
2348 ..
2349 } => CollectionKind::Stream {
2350 bound: bound.clone(),
2351 order: StreamOrder::TotalOrder,
2352 retry: StreamRetry::ExactlyOnce,
2353 element_type: element_type.clone(),
2354 },
2355 CollectionKind::KeyedStream {
2356 bound,
2357 key_type,
2358 value_type,
2359 ..
2360 } => CollectionKind::KeyedStream {
2361 bound: bound.clone(),
2362 value_order: StreamOrder::TotalOrder,
2363 value_retry: StreamRetry::ExactlyOnce,
2364 key_type: key_type.clone(),
2365 value_type: value_type.clone(),
2366 },
2367 other => other.clone(),
2368 }
2369 }
2370}
2371
2372#[derive(Clone, serde::Serialize)]
2373pub struct HydroIrMetadata {
2374 pub location_id: LocationId,
2375 pub collection_kind: CollectionKind,
2376 pub consistency: Option<ClusterConsistency>,
2377 pub cardinality: Option<usize>,
2378 pub tag: Option<String>,
2379 pub op: HydroIrOpMetadata,
2380}
2381
2382impl Hash for HydroIrMetadata {
2384 fn hash<H: Hasher>(&self, _: &mut H) {}
2385}
2386
2387impl PartialEq for HydroIrMetadata {
2388 fn eq(&self, _: &Self) -> bool {
2389 true
2390 }
2391}
2392
2393impl Eq for HydroIrMetadata {}
2394
2395impl Debug for HydroIrMetadata {
2396 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2397 f.debug_struct("HydroIrMetadata")
2398 .field("location_id", &self.location_id)
2399 .field("collection_kind", &self.collection_kind)
2400 .finish()
2401 }
2402}
2403
2404#[derive(Clone, serde::Serialize)]
2407pub struct HydroIrOpMetadata {
2408 #[serde(rename = "span", serialize_with = "serialize_backtrace_as_span")]
2409 pub backtrace: Backtrace,
2410 pub cpu_usage: Option<f64>,
2411 pub network_recv_cpu_usage: Option<f64>,
2412 pub id: Option<usize>,
2413}
2414
2415impl HydroIrOpMetadata {
2416 #[expect(
2417 clippy::new_without_default,
2418 reason = "explicit calls to new ensure correct backtrace bounds"
2419 )]
2420 pub fn new() -> HydroIrOpMetadata {
2421 Self::new_with_skip(1)
2422 }
2423
2424 fn new_with_skip(skip_count: usize) -> HydroIrOpMetadata {
2425 HydroIrOpMetadata {
2426 backtrace: Backtrace::get_backtrace(2 + skip_count),
2427 cpu_usage: None,
2428 network_recv_cpu_usage: None,
2429 id: None,
2430 }
2431 }
2432}
2433
2434impl Debug for HydroIrOpMetadata {
2435 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2436 f.debug_struct("HydroIrOpMetadata").finish()
2437 }
2438}
2439
2440impl Hash for HydroIrOpMetadata {
2441 fn hash<H: Hasher>(&self, _: &mut H) {}
2442}
2443
2444#[derive(Debug, Hash, serde::Serialize)]
2447pub enum HydroNode {
2448 Placeholder,
2449
2450 Cast {
2458 inner: Box<HydroNode>,
2459 metadata: HydroIrMetadata,
2460 },
2461
2462 ObserveNonDet {
2468 inner: Box<HydroNode>,
2469 trusted: bool, metadata: HydroIrMetadata,
2471 },
2472
2473 Source {
2474 source: HydroSource,
2475 metadata: HydroIrMetadata,
2476 },
2477
2478 SingletonSource {
2479 value: DebugExpr,
2480 first_tick_only: bool,
2481 metadata: HydroIrMetadata,
2482 },
2483
2484 CycleSource {
2485 cycle_id: CycleId,
2486 metadata: HydroIrMetadata,
2487 },
2488
2489 Tee {
2490 inner: SharedNode,
2491 metadata: HydroIrMetadata,
2492 },
2493
2494 Reference {
2503 inner: SharedNode,
2504 kind: crate::handoff_ref::HandoffRefKind,
2505 access_counter: AccessCounter,
2506 metadata: HydroIrMetadata,
2507 },
2508
2509 Partition {
2510 inner: SharedNode,
2511 f: ClosureExpr,
2512 is_true: bool,
2513 metadata: HydroIrMetadata,
2514 },
2515
2516 BeginAtomic {
2517 inner: Box<HydroNode>,
2518 metadata: HydroIrMetadata,
2519 },
2520
2521 EndAtomic {
2522 inner: Box<HydroNode>,
2523 metadata: HydroIrMetadata,
2524 },
2525
2526 Batch {
2527 inner: Box<HydroNode>,
2528 metadata: HydroIrMetadata,
2529 },
2530
2531 YieldConcat {
2532 inner: Box<HydroNode>,
2533 metadata: HydroIrMetadata,
2534 },
2535
2536 Chain {
2537 first: Box<HydroNode>,
2538 second: Box<HydroNode>,
2539 metadata: HydroIrMetadata,
2540 },
2541
2542 MergeOrdered {
2543 first: Box<HydroNode>,
2544 second: Box<HydroNode>,
2545 metadata: HydroIrMetadata,
2546 },
2547
2548 ChainFirst {
2549 first: Box<HydroNode>,
2550 second: Box<HydroNode>,
2551 metadata: HydroIrMetadata,
2552 },
2553
2554 CrossProduct {
2555 left: Box<HydroNode>,
2556 right: Box<HydroNode>,
2557 metadata: HydroIrMetadata,
2558 },
2559
2560 CrossSingleton {
2561 left: Box<HydroNode>,
2562 right: Box<HydroNode>,
2563 metadata: HydroIrMetadata,
2564 },
2565
2566 Join {
2567 left: Box<HydroNode>,
2568 right: Box<HydroNode>,
2569 metadata: HydroIrMetadata,
2570 },
2571
2572 JoinHalf {
2576 left: Box<HydroNode>,
2577 right: Box<HydroNode>,
2578 metadata: HydroIrMetadata,
2579 },
2580
2581 Difference {
2582 pos: Box<HydroNode>,
2583 neg: Box<HydroNode>,
2584 metadata: HydroIrMetadata,
2585 },
2586
2587 AntiJoin {
2588 pos: Box<HydroNode>,
2589 neg: Box<HydroNode>,
2590 metadata: HydroIrMetadata,
2591 },
2592
2593 ResolveFutures {
2594 input: Box<HydroNode>,
2595 metadata: HydroIrMetadata,
2596 },
2597 ResolveFuturesBlocking {
2598 input: Box<HydroNode>,
2599 metadata: HydroIrMetadata,
2600 },
2601 ResolveFuturesOrdered {
2602 input: Box<HydroNode>,
2603 metadata: HydroIrMetadata,
2604 },
2605
2606 Map {
2607 f: ClosureExpr,
2608 input: Box<HydroNode>,
2609 metadata: HydroIrMetadata,
2610 },
2611 FlatMap {
2612 f: ClosureExpr,
2613 input: Box<HydroNode>,
2614 metadata: HydroIrMetadata,
2615 },
2616 FlatMapStreamBlocking {
2617 f: ClosureExpr,
2618 input: Box<HydroNode>,
2619 metadata: HydroIrMetadata,
2620 },
2621 Filter {
2622 f: ClosureExpr,
2623 input: Box<HydroNode>,
2624 metadata: HydroIrMetadata,
2625 },
2626 FilterMap {
2627 f: ClosureExpr,
2628 input: Box<HydroNode>,
2629 metadata: HydroIrMetadata,
2630 },
2631
2632 DeferTick {
2633 input: Box<HydroNode>,
2634 metadata: HydroIrMetadata,
2635 },
2636 Enumerate {
2637 input: Box<HydroNode>,
2638 metadata: HydroIrMetadata,
2639 },
2640 Inspect {
2641 f: ClosureExpr,
2642 input: Box<HydroNode>,
2643 metadata: HydroIrMetadata,
2644 },
2645
2646 Unique {
2647 input: Box<HydroNode>,
2648 metadata: HydroIrMetadata,
2649 },
2650
2651 Sort {
2652 input: Box<HydroNode>,
2653 metadata: HydroIrMetadata,
2654 },
2655 Fold {
2656 init: ClosureExpr,
2657 acc: ClosureExpr,
2658 input: Box<HydroNode>,
2659 metadata: HydroIrMetadata,
2660 },
2661
2662 Scan {
2663 init: ClosureExpr,
2664 acc: ClosureExpr,
2665 input: Box<HydroNode>,
2666 metadata: HydroIrMetadata,
2667 },
2668 ScanAsyncBlocking {
2669 init: ClosureExpr,
2670 acc: ClosureExpr,
2671 input: Box<HydroNode>,
2672 metadata: HydroIrMetadata,
2673 },
2674 FoldKeyed {
2675 init: ClosureExpr,
2676 acc: ClosureExpr,
2677 input: Box<HydroNode>,
2678 metadata: HydroIrMetadata,
2679 },
2680
2681 Reduce {
2682 f: ClosureExpr,
2683 input: Box<HydroNode>,
2684 metadata: HydroIrMetadata,
2685 },
2686 ReduceKeyed {
2687 f: ClosureExpr,
2688 input: Box<HydroNode>,
2689 metadata: HydroIrMetadata,
2690 },
2691 ReduceKeyedWatermark {
2692 f: ClosureExpr,
2693 input: Box<HydroNode>,
2694 watermark: Box<HydroNode>,
2695 metadata: HydroIrMetadata,
2696 },
2697
2698 Network {
2699 name: Option<String>,
2700 networking_info: crate::networking::NetworkingInfo,
2701 serialize_fn: Option<DebugExpr>,
2702 instantiate_fn: DebugInstantiate,
2703 deserialize_fn: Option<DebugExpr>,
2704 input: Box<HydroNode>,
2705 metadata: HydroIrMetadata,
2706 },
2707
2708 VersionedNetworkFork {
2709 channel_id: u32,
2710 channel_name: String,
2711 senders: Vec<(u32, Box<HydroNode>, Option<DebugExpr>)>,
2712 metadata: HydroIrMetadata,
2713 },
2714
2715 VersionedNetwork {
2716 fork: SharedNode,
2717 version: u32,
2718 deserialize_fn: Option<DebugExpr>,
2719 metadata: HydroIrMetadata,
2720 },
2721
2722 ExternalInput {
2723 from_external_key: LocationKey,
2724 from_port_id: ExternalPortId,
2725 from_many: bool,
2726 codec_type: DebugType,
2727 #[serde(skip)]
2728 port_hint: NetworkHint,
2729 instantiate_fn: DebugInstantiate,
2730 deserialize_fn: Option<DebugExpr>,
2731 metadata: HydroIrMetadata,
2732 },
2733
2734 Counter {
2735 tag: String,
2736 duration: DebugExpr,
2737 prefix: String,
2738 input: Box<HydroNode>,
2739 metadata: HydroIrMetadata,
2740 },
2741
2742 AssertIsConsistent {
2743 inner: Box<HydroNode>,
2744 trusted: bool,
2745 metadata: HydroIrMetadata,
2746 },
2747
2748 UnboundSingleton {
2749 inner: Box<HydroNode>,
2750 metadata: HydroIrMetadata,
2751 },
2752}
2753
2754pub type SeenSharedNodes = HashMap<*const RefCell<HydroNode>, Rc<RefCell<HydroNode>>>;
2755pub type SeenSharedNodeLocations = HashMap<*const RefCell<HydroNode>, LocationId>;
2756
2757#[cfg(feature = "build")]
2761fn maybe_observe_for_mut(
2762 f: &ClosureExpr,
2763 in_ident: syn::Ident,
2764 in_location: &LocationId,
2765 in_kind: &CollectionKind,
2766 op_meta: &HydroIrOpMetadata,
2767 builders_or_callback: &mut BuildersOrCallback<
2768 impl FnMut(&mut HydroRoot, &mut crate::Counter<StmtId>),
2769 impl FnMut(&mut HydroNode, &mut crate::Counter<StmtId>),
2770 >,
2771 next_stmt_id: &mut crate::Counter<StmtId>,
2772) -> syn::Ident {
2773 if f.has_mut_ref() && !in_kind.is_strict() {
2774 let observe_stmt_id = next_stmt_id.get_and_increment();
2775 let observe_ident =
2776 syn::Ident::new(&format!("stream_{}", observe_stmt_id), Span::call_site());
2777 if let BuildersOrCallback::Builders(graph_builders) = builders_or_callback {
2778 graph_builders.observe_for_mut(in_location, in_ident, in_kind, &observe_ident, op_meta);
2779 }
2780 observe_ident
2781 } else {
2782 in_ident
2783 }
2784}
2785
2786impl HydroNode {
2787 pub fn transform_bottom_up(
2788 &mut self,
2789 transform: &mut impl FnMut(&mut HydroNode),
2790 seen_tees: &mut SeenSharedNodes,
2791 check_well_formed: bool,
2792 ) {
2793 self.transform_children(
2794 |n, s| n.transform_bottom_up(transform, s, check_well_formed),
2795 seen_tees,
2796 );
2797
2798 transform(self);
2799
2800 let self_location = self.metadata().location_id.root();
2801
2802 if check_well_formed {
2803 match &*self {
2804 HydroNode::Network { .. } => {}
2805 _ => {
2806 self.input_metadata().iter().for_each(|i| {
2807 if i.location_id.root() != self_location {
2808 panic!(
2809 "Mismatching IR locations, child: {:?} ({:?}) of: {:?} ({:?})",
2810 i,
2811 i.location_id.root(),
2812 self,
2813 self_location
2814 )
2815 }
2816 });
2817 }
2818 }
2819 }
2820 }
2821
2822 #[inline(always)]
2823 pub fn transform_children(
2824 &mut self,
2825 mut transform: impl FnMut(&mut HydroNode, &mut SeenSharedNodes),
2826 seen_tees: &mut SeenSharedNodes,
2827 ) {
2828 match self {
2829 HydroNode::Placeholder => {
2830 panic!();
2831 }
2832
2833 HydroNode::Source { .. }
2834 | HydroNode::SingletonSource { .. }
2835 | HydroNode::CycleSource { .. }
2836 | HydroNode::ExternalInput { .. } => {}
2837
2838 HydroNode::Tee { inner, .. } | HydroNode::Reference { inner, .. } => {
2839 if let Some(transformed) = seen_tees.get(&inner.as_ptr()) {
2840 *inner = SharedNode(transformed.clone());
2841 } else {
2842 let transformed_cell = Rc::new(RefCell::new(HydroNode::Placeholder));
2843 seen_tees.insert(inner.as_ptr(), transformed_cell.clone());
2844 let mut orig = inner.0.replace(HydroNode::Placeholder);
2845 transform(&mut orig, seen_tees);
2846 *transformed_cell.borrow_mut() = orig;
2847 *inner = SharedNode(transformed_cell);
2848 }
2849 }
2850
2851 HydroNode::Partition { inner, f, .. } => {
2852 if let Some(transformed) = seen_tees.get(&inner.as_ptr()) {
2853 *inner = SharedNode(transformed.clone());
2854 } else {
2855 f.transform_children(&mut transform, seen_tees);
2856 let transformed_cell = Rc::new(RefCell::new(HydroNode::Placeholder));
2857 seen_tees.insert(inner.as_ptr(), transformed_cell.clone());
2858 let mut orig = inner.0.replace(HydroNode::Placeholder);
2859 transform(&mut orig, seen_tees);
2860 *transformed_cell.borrow_mut() = orig;
2861 *inner = SharedNode(transformed_cell);
2862 }
2863 }
2864
2865 HydroNode::Cast { inner, .. }
2866 | HydroNode::ObserveNonDet { inner, .. }
2867 | HydroNode::BeginAtomic { inner, .. }
2868 | HydroNode::EndAtomic { inner, .. }
2869 | HydroNode::Batch { inner, .. }
2870 | HydroNode::YieldConcat { inner, .. }
2871 | HydroNode::UnboundSingleton { inner, .. }
2872 | HydroNode::AssertIsConsistent { inner, .. } => {
2873 transform(inner.as_mut(), seen_tees);
2874 }
2875
2876 HydroNode::Chain { first, second, .. } => {
2877 transform(first.as_mut(), seen_tees);
2878 transform(second.as_mut(), seen_tees);
2879 }
2880
2881 HydroNode::MergeOrdered { first, second, .. } => {
2882 transform(first.as_mut(), seen_tees);
2883 transform(second.as_mut(), seen_tees);
2884 }
2885
2886 HydroNode::ChainFirst { first, second, .. } => {
2887 transform(first.as_mut(), seen_tees);
2888 transform(second.as_mut(), seen_tees);
2889 }
2890
2891 HydroNode::CrossSingleton { left, right, .. }
2892 | HydroNode::CrossProduct { left, right, .. }
2893 | HydroNode::Join { left, right, .. }
2894 | HydroNode::JoinHalf { left, right, .. } => {
2895 transform(left.as_mut(), seen_tees);
2896 transform(right.as_mut(), seen_tees);
2897 }
2898
2899 HydroNode::Difference { pos, neg, .. } | HydroNode::AntiJoin { pos, neg, .. } => {
2900 transform(pos.as_mut(), seen_tees);
2901 transform(neg.as_mut(), seen_tees);
2902 }
2903
2904 HydroNode::Map { f, input, .. } => {
2905 f.transform_children(&mut transform, seen_tees);
2906 transform(input.as_mut(), seen_tees);
2907 }
2908 HydroNode::FlatMap { f, input, .. }
2909 | HydroNode::FlatMapStreamBlocking { f, input, .. }
2910 | HydroNode::Filter { f, input, .. }
2911 | HydroNode::FilterMap { f, input, .. }
2912 | HydroNode::Inspect { f, input, .. }
2913 | HydroNode::Reduce { f, input, .. }
2914 | HydroNode::ReduceKeyed { f, input, .. } => {
2915 f.transform_children(&mut transform, seen_tees);
2916 transform(input.as_mut(), seen_tees);
2917 }
2918 HydroNode::ReduceKeyedWatermark {
2919 f,
2920 input,
2921 watermark,
2922 ..
2923 } => {
2924 f.transform_children(&mut transform, seen_tees);
2925 transform(input.as_mut(), seen_tees);
2926 transform(watermark.as_mut(), seen_tees);
2927 }
2928 HydroNode::Fold {
2929 init, acc, input, ..
2930 }
2931 | HydroNode::Scan {
2932 init, acc, input, ..
2933 }
2934 | HydroNode::ScanAsyncBlocking {
2935 init, acc, input, ..
2936 }
2937 | HydroNode::FoldKeyed {
2938 init, acc, input, ..
2939 } => {
2940 init.transform_children(&mut transform, seen_tees);
2941 acc.transform_children(&mut transform, seen_tees);
2942 transform(input.as_mut(), seen_tees);
2943 }
2944 HydroNode::ResolveFutures { input, .. }
2945 | HydroNode::ResolveFuturesBlocking { input, .. }
2946 | HydroNode::ResolveFuturesOrdered { input, .. }
2947 | HydroNode::Sort { input, .. }
2948 | HydroNode::DeferTick { input, .. }
2949 | HydroNode::Enumerate { input, .. }
2950 | HydroNode::Unique { input, .. }
2951 | HydroNode::Network { input, .. }
2952 | HydroNode::Counter { input, .. } => {
2953 transform(input.as_mut(), seen_tees);
2954 }
2955
2956 HydroNode::VersionedNetworkFork { senders, .. } => {
2957 for (_version, sender, _serialize) in senders.iter_mut() {
2958 transform(sender.as_mut(), seen_tees);
2959 }
2960 }
2961
2962 HydroNode::VersionedNetwork { fork, .. } => {
2963 if let Some(transformed) = seen_tees.get(&fork.as_ptr()) {
2964 *fork = SharedNode(transformed.clone());
2965 } else {
2966 let transformed_cell = Rc::new(RefCell::new(HydroNode::Placeholder));
2967 seen_tees.insert(fork.as_ptr(), transformed_cell.clone());
2968 let mut orig = fork.0.replace(HydroNode::Placeholder);
2969 transform(&mut orig, seen_tees);
2970 *transformed_cell.borrow_mut() = orig;
2971 *fork = SharedNode(transformed_cell);
2972 }
2973 }
2974 }
2975 }
2976
2977 pub fn deep_clone(&self, seen_tees: &mut SeenSharedNodes) -> HydroNode {
2978 match self {
2979 HydroNode::Placeholder => HydroNode::Placeholder,
2980 HydroNode::Cast { inner, metadata } => HydroNode::Cast {
2981 inner: Box::new(inner.deep_clone(seen_tees)),
2982 metadata: metadata.clone(),
2983 },
2984 HydroNode::UnboundSingleton { inner, metadata } => HydroNode::UnboundSingleton {
2985 inner: Box::new(inner.deep_clone(seen_tees)),
2986 metadata: metadata.clone(),
2987 },
2988 HydroNode::ObserveNonDet {
2989 inner,
2990 trusted,
2991 metadata,
2992 } => HydroNode::ObserveNonDet {
2993 inner: Box::new(inner.deep_clone(seen_tees)),
2994 trusted: *trusted,
2995 metadata: metadata.clone(),
2996 },
2997 HydroNode::AssertIsConsistent {
2998 inner,
2999 trusted,
3000 metadata,
3001 } => HydroNode::AssertIsConsistent {
3002 inner: Box::new(inner.deep_clone(seen_tees)),
3003 trusted: *trusted,
3004 metadata: metadata.clone(),
3005 },
3006 HydroNode::Source { source, metadata } => HydroNode::Source {
3007 source: source.clone(),
3008 metadata: metadata.clone(),
3009 },
3010 HydroNode::SingletonSource {
3011 value,
3012 first_tick_only,
3013 metadata,
3014 } => HydroNode::SingletonSource {
3015 value: value.clone(),
3016 first_tick_only: *first_tick_only,
3017 metadata: metadata.clone(),
3018 },
3019 HydroNode::CycleSource { cycle_id, metadata } => HydroNode::CycleSource {
3020 cycle_id: *cycle_id,
3021 metadata: metadata.clone(),
3022 },
3023 HydroNode::Tee { inner, metadata }
3024 | HydroNode::Reference {
3025 inner, metadata, ..
3026 } => {
3027 let cloned_inner = if let Some(transformed) = seen_tees.get(&inner.as_ptr()) {
3028 SharedNode(transformed.clone())
3029 } else {
3030 let new_rc = Rc::new(RefCell::new(HydroNode::Placeholder));
3031 seen_tees.insert(inner.as_ptr(), new_rc.clone());
3032 let cloned = inner.0.borrow().deep_clone(seen_tees);
3033 *new_rc.borrow_mut() = cloned;
3034 SharedNode(new_rc)
3035 };
3036 if let HydroNode::Reference {
3037 kind,
3038 access_counter,
3039 ..
3040 } = self
3041 {
3042 HydroNode::Reference {
3043 inner: cloned_inner,
3044 kind: *kind,
3045 access_counter: access_counter.freeze(),
3046 metadata: metadata.clone(),
3047 }
3048 } else {
3049 HydroNode::Tee {
3050 inner: cloned_inner,
3051 metadata: metadata.clone(),
3052 }
3053 }
3054 }
3055 HydroNode::Partition {
3056 inner,
3057 f,
3058 is_true,
3059 metadata,
3060 } => {
3061 if let Some(transformed) = seen_tees.get(&inner.as_ptr()) {
3062 HydroNode::Partition {
3063 inner: SharedNode(transformed.clone()),
3064 f: f.deep_clone(seen_tees),
3065 is_true: *is_true,
3066 metadata: metadata.clone(),
3067 }
3068 } else {
3069 let new_rc = Rc::new(RefCell::new(HydroNode::Placeholder));
3070 seen_tees.insert(inner.as_ptr(), new_rc.clone());
3071 let cloned = inner.0.borrow().deep_clone(seen_tees);
3072 *new_rc.borrow_mut() = cloned;
3073 HydroNode::Partition {
3074 inner: SharedNode(new_rc),
3075 f: f.deep_clone(seen_tees),
3076 is_true: *is_true,
3077 metadata: metadata.clone(),
3078 }
3079 }
3080 }
3081 HydroNode::YieldConcat { inner, metadata } => HydroNode::YieldConcat {
3082 inner: Box::new(inner.deep_clone(seen_tees)),
3083 metadata: metadata.clone(),
3084 },
3085 HydroNode::BeginAtomic { inner, metadata } => HydroNode::BeginAtomic {
3086 inner: Box::new(inner.deep_clone(seen_tees)),
3087 metadata: metadata.clone(),
3088 },
3089 HydroNode::EndAtomic { inner, metadata } => HydroNode::EndAtomic {
3090 inner: Box::new(inner.deep_clone(seen_tees)),
3091 metadata: metadata.clone(),
3092 },
3093 HydroNode::Batch { inner, metadata } => HydroNode::Batch {
3094 inner: Box::new(inner.deep_clone(seen_tees)),
3095 metadata: metadata.clone(),
3096 },
3097 HydroNode::Chain {
3098 first,
3099 second,
3100 metadata,
3101 } => HydroNode::Chain {
3102 first: Box::new(first.deep_clone(seen_tees)),
3103 second: Box::new(second.deep_clone(seen_tees)),
3104 metadata: metadata.clone(),
3105 },
3106 HydroNode::MergeOrdered {
3107 first,
3108 second,
3109 metadata,
3110 } => HydroNode::MergeOrdered {
3111 first: Box::new(first.deep_clone(seen_tees)),
3112 second: Box::new(second.deep_clone(seen_tees)),
3113 metadata: metadata.clone(),
3114 },
3115 HydroNode::ChainFirst {
3116 first,
3117 second,
3118 metadata,
3119 } => HydroNode::ChainFirst {
3120 first: Box::new(first.deep_clone(seen_tees)),
3121 second: Box::new(second.deep_clone(seen_tees)),
3122 metadata: metadata.clone(),
3123 },
3124 HydroNode::CrossProduct {
3125 left,
3126 right,
3127 metadata,
3128 } => HydroNode::CrossProduct {
3129 left: Box::new(left.deep_clone(seen_tees)),
3130 right: Box::new(right.deep_clone(seen_tees)),
3131 metadata: metadata.clone(),
3132 },
3133 HydroNode::CrossSingleton {
3134 left,
3135 right,
3136 metadata,
3137 } => HydroNode::CrossSingleton {
3138 left: Box::new(left.deep_clone(seen_tees)),
3139 right: Box::new(right.deep_clone(seen_tees)),
3140 metadata: metadata.clone(),
3141 },
3142 HydroNode::Join {
3143 left,
3144 right,
3145 metadata,
3146 } => HydroNode::Join {
3147 left: Box::new(left.deep_clone(seen_tees)),
3148 right: Box::new(right.deep_clone(seen_tees)),
3149 metadata: metadata.clone(),
3150 },
3151 HydroNode::JoinHalf {
3152 left,
3153 right,
3154 metadata,
3155 } => HydroNode::JoinHalf {
3156 left: Box::new(left.deep_clone(seen_tees)),
3157 right: Box::new(right.deep_clone(seen_tees)),
3158 metadata: metadata.clone(),
3159 },
3160 HydroNode::Difference { pos, neg, metadata } => HydroNode::Difference {
3161 pos: Box::new(pos.deep_clone(seen_tees)),
3162 neg: Box::new(neg.deep_clone(seen_tees)),
3163 metadata: metadata.clone(),
3164 },
3165 HydroNode::AntiJoin { pos, neg, metadata } => HydroNode::AntiJoin {
3166 pos: Box::new(pos.deep_clone(seen_tees)),
3167 neg: Box::new(neg.deep_clone(seen_tees)),
3168 metadata: metadata.clone(),
3169 },
3170 HydroNode::ResolveFutures { input, metadata } => HydroNode::ResolveFutures {
3171 input: Box::new(input.deep_clone(seen_tees)),
3172 metadata: metadata.clone(),
3173 },
3174 HydroNode::ResolveFuturesBlocking { input, metadata } => {
3175 HydroNode::ResolveFuturesBlocking {
3176 input: Box::new(input.deep_clone(seen_tees)),
3177 metadata: metadata.clone(),
3178 }
3179 }
3180 HydroNode::ResolveFuturesOrdered { input, metadata } => {
3181 HydroNode::ResolveFuturesOrdered {
3182 input: Box::new(input.deep_clone(seen_tees)),
3183 metadata: metadata.clone(),
3184 }
3185 }
3186 HydroNode::Map { f, input, metadata } => HydroNode::Map {
3187 f: f.deep_clone(seen_tees),
3188 input: Box::new(input.deep_clone(seen_tees)),
3189 metadata: metadata.clone(),
3190 },
3191 HydroNode::FlatMap { f, input, metadata } => HydroNode::FlatMap {
3192 f: f.deep_clone(seen_tees),
3193 input: Box::new(input.deep_clone(seen_tees)),
3194 metadata: metadata.clone(),
3195 },
3196 HydroNode::FlatMapStreamBlocking { f, input, metadata } => {
3197 HydroNode::FlatMapStreamBlocking {
3198 f: f.deep_clone(seen_tees),
3199 input: Box::new(input.deep_clone(seen_tees)),
3200 metadata: metadata.clone(),
3201 }
3202 }
3203 HydroNode::Filter { f, input, metadata } => HydroNode::Filter {
3204 f: f.deep_clone(seen_tees),
3205 input: Box::new(input.deep_clone(seen_tees)),
3206 metadata: metadata.clone(),
3207 },
3208 HydroNode::FilterMap { f, input, metadata } => HydroNode::FilterMap {
3209 f: f.deep_clone(seen_tees),
3210 input: Box::new(input.deep_clone(seen_tees)),
3211 metadata: metadata.clone(),
3212 },
3213 HydroNode::DeferTick { input, metadata } => HydroNode::DeferTick {
3214 input: Box::new(input.deep_clone(seen_tees)),
3215 metadata: metadata.clone(),
3216 },
3217 HydroNode::Enumerate { input, metadata } => HydroNode::Enumerate {
3218 input: Box::new(input.deep_clone(seen_tees)),
3219 metadata: metadata.clone(),
3220 },
3221 HydroNode::Inspect { f, input, metadata } => HydroNode::Inspect {
3222 f: f.deep_clone(seen_tees),
3223 input: Box::new(input.deep_clone(seen_tees)),
3224 metadata: metadata.clone(),
3225 },
3226 HydroNode::Unique { input, metadata } => HydroNode::Unique {
3227 input: Box::new(input.deep_clone(seen_tees)),
3228 metadata: metadata.clone(),
3229 },
3230 HydroNode::Sort { input, metadata } => HydroNode::Sort {
3231 input: Box::new(input.deep_clone(seen_tees)),
3232 metadata: metadata.clone(),
3233 },
3234 HydroNode::Fold {
3235 init,
3236 acc,
3237 input,
3238 metadata,
3239 } => HydroNode::Fold {
3240 init: init.deep_clone(seen_tees),
3241 acc: acc.deep_clone(seen_tees),
3242 input: Box::new(input.deep_clone(seen_tees)),
3243 metadata: metadata.clone(),
3244 },
3245 HydroNode::Scan {
3246 init,
3247 acc,
3248 input,
3249 metadata,
3250 } => HydroNode::Scan {
3251 init: init.deep_clone(seen_tees),
3252 acc: acc.deep_clone(seen_tees),
3253 input: Box::new(input.deep_clone(seen_tees)),
3254 metadata: metadata.clone(),
3255 },
3256 HydroNode::ScanAsyncBlocking {
3257 init,
3258 acc,
3259 input,
3260 metadata,
3261 } => HydroNode::ScanAsyncBlocking {
3262 init: init.deep_clone(seen_tees),
3263 acc: acc.deep_clone(seen_tees),
3264 input: Box::new(input.deep_clone(seen_tees)),
3265 metadata: metadata.clone(),
3266 },
3267 HydroNode::FoldKeyed {
3268 init,
3269 acc,
3270 input,
3271 metadata,
3272 } => HydroNode::FoldKeyed {
3273 init: init.deep_clone(seen_tees),
3274 acc: acc.deep_clone(seen_tees),
3275 input: Box::new(input.deep_clone(seen_tees)),
3276 metadata: metadata.clone(),
3277 },
3278 HydroNode::ReduceKeyedWatermark {
3279 f,
3280 input,
3281 watermark,
3282 metadata,
3283 } => HydroNode::ReduceKeyedWatermark {
3284 f: f.deep_clone(seen_tees),
3285 input: Box::new(input.deep_clone(seen_tees)),
3286 watermark: Box::new(watermark.deep_clone(seen_tees)),
3287 metadata: metadata.clone(),
3288 },
3289 HydroNode::Reduce { f, input, metadata } => HydroNode::Reduce {
3290 f: f.deep_clone(seen_tees),
3291 input: Box::new(input.deep_clone(seen_tees)),
3292 metadata: metadata.clone(),
3293 },
3294 HydroNode::ReduceKeyed { f, input, metadata } => HydroNode::ReduceKeyed {
3295 f: f.deep_clone(seen_tees),
3296 input: Box::new(input.deep_clone(seen_tees)),
3297 metadata: metadata.clone(),
3298 },
3299 HydroNode::Network {
3300 name,
3301 networking_info,
3302 serialize_fn,
3303 instantiate_fn,
3304 deserialize_fn,
3305 input,
3306 metadata,
3307 } => HydroNode::Network {
3308 name: name.clone(),
3309 networking_info: networking_info.clone(),
3310 serialize_fn: serialize_fn.clone(),
3311 instantiate_fn: instantiate_fn.clone(),
3312 deserialize_fn: deserialize_fn.clone(),
3313 input: Box::new(input.deep_clone(seen_tees)),
3314 metadata: metadata.clone(),
3315 },
3316 HydroNode::ExternalInput {
3317 from_external_key,
3318 from_port_id,
3319 from_many,
3320 codec_type,
3321 port_hint,
3322 instantiate_fn,
3323 deserialize_fn,
3324 metadata,
3325 } => HydroNode::ExternalInput {
3326 from_external_key: *from_external_key,
3327 from_port_id: *from_port_id,
3328 from_many: *from_many,
3329 codec_type: codec_type.clone(),
3330 port_hint: *port_hint,
3331 instantiate_fn: instantiate_fn.clone(),
3332 deserialize_fn: deserialize_fn.clone(),
3333 metadata: metadata.clone(),
3334 },
3335 HydroNode::Counter {
3336 tag,
3337 duration,
3338 prefix,
3339 input,
3340 metadata,
3341 } => HydroNode::Counter {
3342 tag: tag.clone(),
3343 duration: duration.clone(),
3344 prefix: prefix.clone(),
3345 input: Box::new(input.deep_clone(seen_tees)),
3346 metadata: metadata.clone(),
3347 },
3348 HydroNode::VersionedNetworkFork {
3349 channel_id,
3350 channel_name,
3351 senders,
3352 metadata,
3353 } => HydroNode::VersionedNetworkFork {
3354 channel_id: *channel_id,
3355 channel_name: channel_name.clone(),
3356 senders: senders
3357 .iter()
3358 .map(|(version, sender, serialize)| {
3359 (
3360 *version,
3361 Box::new(sender.deep_clone(seen_tees)),
3362 serialize.clone(),
3363 )
3364 })
3365 .collect(),
3366 metadata: metadata.clone(),
3367 },
3368 HydroNode::VersionedNetwork {
3369 fork,
3370 version,
3371 deserialize_fn,
3372 metadata,
3373 } => {
3374 let cloned_fork = if let Some(transformed) = seen_tees.get(&fork.as_ptr()) {
3375 SharedNode(transformed.clone())
3376 } else {
3377 let new_rc = Rc::new(RefCell::new(HydroNode::Placeholder));
3378 seen_tees.insert(fork.as_ptr(), new_rc.clone());
3379 let cloned = fork.0.borrow().deep_clone(seen_tees);
3380 *new_rc.borrow_mut() = cloned;
3381 SharedNode(new_rc)
3382 };
3383 HydroNode::VersionedNetwork {
3384 fork: cloned_fork,
3385 version: *version,
3386 deserialize_fn: deserialize_fn.clone(),
3387 metadata: metadata.clone(),
3388 }
3389 }
3390 }
3391 }
3392
3393 #[cfg(feature = "build")]
3394 pub fn emit_core(
3395 &mut self,
3396 builders_or_callback: &mut BuildersOrCallback<
3397 impl FnMut(&mut HydroRoot, &mut crate::Counter<StmtId>),
3398 impl FnMut(&mut HydroNode, &mut crate::Counter<StmtId>),
3399 >,
3400 seen_tees: &mut SeenSharedNodes,
3401 built_tees: &mut HashMap<*const RefCell<HydroNode>, Vec<syn::Ident>>,
3402 next_stmt_id: &mut crate::Counter<StmtId>,
3403 fold_hooked_idents: &mut HashSet<String>,
3404 ) -> syn::Ident {
3405 let mut ident_stack: Vec<syn::Ident> = Vec::new();
3406
3407 self.transform_bottom_up(
3408 &mut |node: &mut HydroNode| {
3409 let out_location = node.metadata().location_id.clone();
3410 match node {
3411 HydroNode::Placeholder => {
3412 panic!()
3413 }
3414
3415 HydroNode::Cast { .. } => {
3416 let _ = next_stmt_id.get_and_increment();
3419 match builders_or_callback {
3420 BuildersOrCallback::Builders(_) => {}
3421 BuildersOrCallback::Callback(_, node_callback) => {
3422 node_callback(node, next_stmt_id);
3423 }
3424 }
3425 }
3427
3428 HydroNode::UnboundSingleton { .. } => {
3429 let inner_ident = ident_stack.pop().unwrap();
3430
3431 let stmt_id = next_stmt_id.get_and_increment();
3432 let out_ident =
3433 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3434
3435 match builders_or_callback {
3436 BuildersOrCallback::Builders(graph_builders) => {
3437 if graph_builders.singleton_intermediates() {
3438 let builder = graph_builders.get_dfir_mut(&out_location);
3439 builder.add_dfir(
3440 parse_quote! {
3441 #out_ident = #inner_ident;
3442 },
3443 None,
3444 None,
3445 );
3446 } else {
3447 let builder = graph_builders.get_dfir_mut(&out_location);
3448 builder.add_dfir(
3449 parse_quote! {
3450 #out_ident = #inner_ident -> persist::<'static>();
3451 },
3452 None,
3453 None,
3454 );
3455 }
3456 }
3457 BuildersOrCallback::Callback(_, node_callback) => {
3458 node_callback(node, next_stmt_id);
3459 }
3460 }
3461
3462 ident_stack.push(out_ident);
3463 }
3464
3465 HydroNode::AssertIsConsistent { inner, trusted, .. } => {
3466 let inner_ident = ident_stack.pop().unwrap();
3467
3468 let stmt_id = next_stmt_id.get_and_increment();
3469 let out_ident =
3470 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3471
3472 match builders_or_callback {
3473 BuildersOrCallback::Builders(graph_builders) => {
3474 graph_builders.assert_is_consistent(
3475 *trusted,
3476 &inner.metadata().location_id,
3477 inner_ident,
3478 &out_ident,
3479 );
3480 }
3481 BuildersOrCallback::Callback(_, node_callback) => {
3482 node_callback(node, next_stmt_id);
3483 }
3484 }
3485
3486 ident_stack.push(out_ident);
3487 }
3488
3489 HydroNode::ObserveNonDet {
3490 inner,
3491 trusted,
3492 metadata,
3493 ..
3494 } => {
3495 let inner_ident = ident_stack.pop().unwrap();
3496
3497 let stmt_id = next_stmt_id.get_and_increment();
3498 let observe_ident =
3499 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3500
3501 match builders_or_callback {
3502 BuildersOrCallback::Builders(graph_builders) => {
3503 graph_builders.observe_nondet(
3504 *trusted,
3505 &inner.metadata().location_id,
3506 inner_ident,
3507 &inner.metadata().collection_kind,
3508 &observe_ident,
3509 &metadata.collection_kind,
3510 &metadata.op,
3511 );
3512 }
3513 BuildersOrCallback::Callback(_, node_callback) => {
3514 node_callback(node, next_stmt_id);
3515 }
3516 }
3517
3518 ident_stack.push(observe_ident);
3519 }
3520
3521 HydroNode::Batch {
3522 inner, metadata, ..
3523 } => {
3524 let inner_ident = ident_stack.pop().unwrap();
3525
3526 let stmt_id = next_stmt_id.get_and_increment();
3527 let batch_ident =
3528 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3529
3530 match builders_or_callback {
3531 BuildersOrCallback::Builders(graph_builders) => {
3532 graph_builders.batch(
3533 inner_ident,
3534 &inner.metadata().location_id,
3535 &inner.metadata().collection_kind,
3536 &batch_ident,
3537 &out_location,
3538 &metadata.op,
3539 fold_hooked_idents,
3540 );
3541 }
3542 BuildersOrCallback::Callback(_, node_callback) => {
3543 node_callback(node, next_stmt_id);
3544 }
3545 }
3546
3547 ident_stack.push(batch_ident);
3548 }
3549
3550 HydroNode::YieldConcat { inner, .. } => {
3551 let inner_ident = ident_stack.pop().unwrap();
3552
3553 let stmt_id = next_stmt_id.get_and_increment();
3554 let yield_ident =
3555 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3556
3557 match builders_or_callback {
3558 BuildersOrCallback::Builders(graph_builders) => {
3559 graph_builders.yield_from_tick(
3560 inner_ident,
3561 &inner.metadata().location_id,
3562 &inner.metadata().collection_kind,
3563 &yield_ident,
3564 &out_location,
3565 );
3566 }
3567 BuildersOrCallback::Callback(_, node_callback) => {
3568 node_callback(node, next_stmt_id);
3569 }
3570 }
3571
3572 ident_stack.push(yield_ident);
3573 }
3574
3575 HydroNode::BeginAtomic { inner, metadata } => {
3576 let inner_ident = ident_stack.pop().unwrap();
3577
3578 let stmt_id = next_stmt_id.get_and_increment();
3579 let begin_ident =
3580 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3581
3582 match builders_or_callback {
3583 BuildersOrCallback::Builders(graph_builders) => {
3584 graph_builders.begin_atomic(
3585 inner_ident,
3586 &inner.metadata().location_id,
3587 &inner.metadata().collection_kind,
3588 &begin_ident,
3589 &out_location,
3590 &metadata.op,
3591 );
3592 }
3593 BuildersOrCallback::Callback(_, node_callback) => {
3594 node_callback(node, next_stmt_id);
3595 }
3596 }
3597
3598 ident_stack.push(begin_ident);
3599 }
3600
3601 HydroNode::EndAtomic { inner, .. } => {
3602 let inner_ident = ident_stack.pop().unwrap();
3603
3604 let stmt_id = next_stmt_id.get_and_increment();
3605 let end_ident =
3606 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3607
3608 match builders_or_callback {
3609 BuildersOrCallback::Builders(graph_builders) => {
3610 graph_builders.end_atomic(
3611 inner_ident,
3612 &inner.metadata().location_id,
3613 &inner.metadata().collection_kind,
3614 &end_ident,
3615 );
3616 }
3617 BuildersOrCallback::Callback(_, node_callback) => {
3618 node_callback(node, next_stmt_id);
3619 }
3620 }
3621
3622 ident_stack.push(end_ident);
3623 }
3624
3625 HydroNode::Source {
3626 source, metadata, ..
3627 } => {
3628 if let HydroSource::ExternalNetwork() = source {
3629 ident_stack.push(syn::Ident::new("DUMMY", Span::call_site()));
3630 } else {
3631 let stmt_id = next_stmt_id.get_and_increment();
3632 let source_ident =
3633 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3634
3635 let source_stmt = match source {
3636 HydroSource::Stream(expr) => {
3637 debug_assert!(metadata.location_id.is_top_level());
3638 parse_quote! {
3639 #source_ident = source_stream(#expr);
3640 }
3641 }
3642
3643 HydroSource::ExternalNetwork() => {
3644 unreachable!()
3645 }
3646
3647 HydroSource::Iter(expr) => {
3648 if metadata.location_id.is_top_level() {
3649 parse_quote! {
3650 #source_ident = source_iter(#expr);
3651 }
3652 } else {
3653 parse_quote! {
3655 #source_ident = source_iter(#expr) -> persist::<'static>();
3656 }
3657 }
3658 }
3659
3660 HydroSource::Spin() => {
3661 debug_assert!(metadata.location_id.is_top_level());
3662 parse_quote! {
3663 #source_ident = spin();
3664 }
3665 }
3666
3667 HydroSource::ClusterMembers(target_loc, state) => {
3668 debug_assert!(metadata.location_id.is_top_level());
3669
3670 let members_tee_ident = syn::Ident::new(
3671 &format!(
3672 "__cluster_members_tee_{}_{}",
3673 metadata.location_id.root().key(),
3674 target_loc.key(),
3675 ),
3676 Span::call_site(),
3677 );
3678
3679 match state {
3680 ClusterMembersState::Stream(d) => {
3681 parse_quote! {
3682 #members_tee_ident = source_stream(#d) -> tee();
3683 #source_ident = #members_tee_ident;
3684 }
3685 },
3686 ClusterMembersState::Uninit => syn::parse_quote! {
3687 #source_ident = source_stream(DUMMY);
3688 },
3689 ClusterMembersState::Tee(..) => parse_quote! {
3690 #source_ident = #members_tee_ident;
3691 },
3692 }
3693 }
3694
3695 HydroSource::Embedded(ident) => {
3696 parse_quote! {
3697 #source_ident = source_stream(#ident);
3698 }
3699 }
3700
3701 HydroSource::EmbeddedSingleton(ident) => {
3702 parse_quote! {
3703 #source_ident = source_iter([#ident]);
3704 }
3705 }
3706 };
3707
3708 match builders_or_callback {
3709 BuildersOrCallback::Builders(graph_builders) => {
3710 let builder = graph_builders.get_dfir_mut(&out_location);
3711 builder.add_dfir(source_stmt, None, Some(&stmt_id.to_string()));
3712 }
3713 BuildersOrCallback::Callback(_, node_callback) => {
3714 node_callback(node, next_stmt_id);
3715 }
3716 }
3717
3718 ident_stack.push(source_ident);
3719 }
3720 }
3721
3722 HydroNode::SingletonSource { value, first_tick_only, metadata } => {
3723 let stmt_id = next_stmt_id.get_and_increment();
3724 let source_ident =
3725 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3726
3727 match builders_or_callback {
3728 BuildersOrCallback::Builders(graph_builders) => {
3729 let builder = graph_builders.get_dfir_mut(&out_location);
3730
3731 if *first_tick_only {
3732 assert!(
3733 !metadata.location_id.is_top_level(),
3734 "first_tick_only SingletonSource must be inside a tick"
3735 );
3736 }
3737
3738 if *first_tick_only
3739 || (metadata.location_id.is_top_level()
3740 && metadata.collection_kind.is_bounded())
3741 {
3742 builder.add_dfir(
3743 parse_quote! {
3744 #source_ident = source_iter([#value]);
3745 },
3746 None,
3747 Some(&stmt_id.to_string()),
3748 );
3749 } else {
3750 builder.add_dfir(
3751 parse_quote! {
3752 #source_ident = source_iter([#value]) -> persist::<'static>();
3753 },
3754 None,
3755 Some(&stmt_id.to_string()),
3756 );
3757 }
3758 }
3759 BuildersOrCallback::Callback(_, node_callback) => {
3760 node_callback(node, next_stmt_id);
3761 }
3762 }
3763
3764 ident_stack.push(source_ident);
3765 }
3766
3767 HydroNode::CycleSource { cycle_id, .. } => {
3768 let ident = cycle_id.as_ident();
3769
3770 let _ = next_stmt_id.get_and_increment();
3772
3773 match builders_or_callback {
3774 BuildersOrCallback::Builders(_) => {}
3775 BuildersOrCallback::Callback(_, node_callback) => {
3776 node_callback(node, next_stmt_id);
3777 }
3778 }
3779
3780 ident_stack.push(ident);
3781 }
3782
3783 HydroNode::Tee { inner, .. } => {
3784 let stmt_id = next_stmt_id.get_and_increment();
3787
3788 let ret_ident = if let Some(built_idents) =
3789 built_tees.get(&(inner.0.as_ref() as *const RefCell<HydroNode>))
3790 {
3791 match builders_or_callback {
3792 BuildersOrCallback::Builders(_) => {}
3793 BuildersOrCallback::Callback(_, node_callback) => {
3794 node_callback(node, next_stmt_id);
3795 }
3796 }
3797
3798 built_idents[0].clone()
3799 } else {
3800 let inner_ident = ident_stack.pop().unwrap();
3803
3804 let tee_ident =
3805 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3806
3807 built_tees.insert(
3808 inner.0.as_ref() as *const RefCell<HydroNode>,
3809 vec![tee_ident.clone()],
3810 );
3811
3812 match builders_or_callback {
3813 BuildersOrCallback::Builders(graph_builders) => {
3814 if fold_hooked_idents.contains(&inner_ident.to_string()) {
3826 fold_hooked_idents.insert(tee_ident.to_string());
3827 }
3828 let builder = graph_builders.get_dfir_mut(&out_location);
3829 builder.add_dfir(
3830 parse_quote! {
3831 #tee_ident = #inner_ident -> tee();
3832 },
3833 None,
3834 Some(&stmt_id.to_string()),
3835 );
3836 }
3837 BuildersOrCallback::Callback(_, node_callback) => {
3838 node_callback(node, next_stmt_id);
3839 }
3840 }
3841
3842 tee_ident
3843 };
3844
3845 ident_stack.push(ret_ident);
3846 }
3847
3848 HydroNode::Reference { inner, kind, .. } => {
3849 let stmt_id = next_stmt_id.get_and_increment();
3852
3853 let ret_ident = if let Some(built_idents) =
3854 built_tees.get(&(inner.0.as_ref() as *const RefCell<HydroNode>))
3855 {
3856 built_idents[0].clone()
3857 } else {
3858 let inner_ident = ident_stack.pop().unwrap();
3859
3860 let ref_ident =
3861 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3862
3863 built_tees.insert(
3864 inner.0.as_ref() as *const RefCell<HydroNode>,
3865 vec![ref_ident.clone()],
3866 );
3867
3868 match builders_or_callback {
3869 BuildersOrCallback::Builders(graph_builders) => {
3870 let builder = graph_builders.get_dfir_mut(&out_location);
3871 let op_ident = syn::Ident::new(
3872 match kind {
3873 crate::handoff_ref::HandoffRefKind::Singleton => "singleton",
3874 crate::handoff_ref::HandoffRefKind::Optional => "optional",
3875 crate::handoff_ref::HandoffRefKind::Vec => "handoff",
3876 },
3877 Span::call_site(),
3878 );
3879 builder.add_dfir(
3880 parse_quote! {
3881 #ref_ident = #inner_ident -> #op_ident();
3882 },
3883 None,
3884 Some(&stmt_id.to_string()),
3885 );
3886 }
3887 BuildersOrCallback::Callback(_, node_callback) => {
3888 node_callback(node, next_stmt_id);
3889 }
3890 }
3891
3892 ref_ident
3893 };
3894
3895 ident_stack.push(ret_ident);
3896 }
3897
3898 HydroNode::Partition {
3899 inner, f, is_true, metadata,
3900 } => {
3901 let is_true = *is_true; let ptr = inner.0.as_ref() as *const RefCell<HydroNode>;
3903 let stmt_id = next_stmt_id.get_and_increment();
3904
3905 let ret_ident = if let Some(built_idents) = built_tees.get(&ptr) {
3906 match builders_or_callback {
3907 BuildersOrCallback::Builders(_) => {}
3908 BuildersOrCallback::Callback(_, node_callback) => {
3909 node_callback(node, next_stmt_id);
3910 }
3911 }
3912
3913 let idx = if is_true { 0 } else { 1 };
3914 built_idents[idx].clone()
3915 } else {
3916 let inner_ident = ident_stack.pop().unwrap();
3919 let f_tokens = f.emit_tokens(&mut ident_stack);
3920
3921 let inner_ident = {
3922 let inner_borrow = inner.0.borrow();
3923 maybe_observe_for_mut(
3924 f, inner_ident,
3925 &inner_borrow.metadata().location_id,
3926 &inner_borrow.metadata().collection_kind,
3927 &metadata.op,
3928 builders_or_callback, next_stmt_id,
3929 )
3930 };
3931
3932 let partition_ident = syn::Ident::new(
3933 &format!("stream_{}_partition", stmt_id),
3934 Span::call_site(),
3935 );
3936 let true_ident = syn::Ident::new(
3937 &format!("stream_{}_true", stmt_id),
3938 Span::call_site(),
3939 );
3940 let false_ident = syn::Ident::new(
3941 &format!("stream_{}_false", stmt_id),
3942 Span::call_site(),
3943 );
3944
3945 built_tees.insert(
3946 ptr,
3947 vec![true_ident.clone(), false_ident.clone()],
3948 );
3949
3950 let stmt_id = next_stmt_id.get_and_increment();
3951 match builders_or_callback {
3952 BuildersOrCallback::Builders(graph_builders) => {
3953 let builder = graph_builders.get_dfir_mut(&out_location);
3954 builder.add_dfir(
3955 parse_quote! {
3956 #partition_ident = #inner_ident -> partition(|__item, __num_outputs| if (#f_tokens)(__item) { 0_usize } else { 1_usize });
3957 #true_ident = #partition_ident[0];
3958 #false_ident = #partition_ident[1];
3959 },
3960 None,
3961 Some(&stmt_id.to_string()),
3962 );
3963 }
3964 BuildersOrCallback::Callback(_, node_callback) => {
3965 node_callback(node, next_stmt_id);
3966 }
3967 }
3968
3969 if is_true { true_ident } else { false_ident }
3970 };
3971
3972 ident_stack.push(ret_ident);
3973 }
3974
3975 HydroNode::Chain { .. } => {
3976 let second_ident = ident_stack.pop().unwrap();
3978 let first_ident = ident_stack.pop().unwrap();
3979
3980 let stmt_id = next_stmt_id.get_and_increment();
3981 let chain_ident =
3982 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3983
3984 match builders_or_callback {
3985 BuildersOrCallback::Builders(graph_builders) => {
3986 let builder = graph_builders.get_dfir_mut(&out_location);
3987 builder.add_dfir(
3988 parse_quote! {
3989 #chain_ident = chain();
3990 #first_ident -> [0]#chain_ident;
3991 #second_ident -> [1]#chain_ident;
3992 },
3993 None,
3994 Some(&stmt_id.to_string()),
3995 );
3996 }
3997 BuildersOrCallback::Callback(_, node_callback) => {
3998 node_callback(node, next_stmt_id);
3999 }
4000 }
4001
4002 ident_stack.push(chain_ident);
4003 }
4004
4005 HydroNode::MergeOrdered { first, metadata, .. } => {
4006 let second_ident = ident_stack.pop().unwrap();
4007 let first_ident = ident_stack.pop().unwrap();
4008
4009 let stmt_id = next_stmt_id.get_and_increment();
4010 let merge_ident =
4011 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4012
4013 match builders_or_callback {
4014 BuildersOrCallback::Builders(graph_builders) => {
4015 graph_builders.merge_ordered(
4016 &first.metadata().location_id,
4017 first_ident,
4018 second_ident,
4019 &merge_ident,
4020 &first.metadata().collection_kind,
4021 &metadata.op,
4022 Some(&stmt_id.to_string()),
4023 );
4024 }
4025 BuildersOrCallback::Callback(_, node_callback) => {
4026 node_callback(node, next_stmt_id);
4027 }
4028 }
4029
4030 ident_stack.push(merge_ident);
4031 }
4032
4033 HydroNode::ChainFirst { .. } => {
4034 let second_ident = ident_stack.pop().unwrap();
4035 let first_ident = ident_stack.pop().unwrap();
4036
4037 let stmt_id = next_stmt_id.get_and_increment();
4038 let chain_ident =
4039 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4040
4041 match builders_or_callback {
4042 BuildersOrCallback::Builders(graph_builders) => {
4043 let builder = graph_builders.get_dfir_mut(&out_location);
4044 builder.add_dfir(
4045 parse_quote! {
4046 #chain_ident = chain_first_n(1);
4047 #first_ident -> [0]#chain_ident;
4048 #second_ident -> [1]#chain_ident;
4049 },
4050 None,
4051 Some(&stmt_id.to_string()),
4052 );
4053 }
4054 BuildersOrCallback::Callback(_, node_callback) => {
4055 node_callback(node, next_stmt_id);
4056 }
4057 }
4058
4059 ident_stack.push(chain_ident);
4060 }
4061
4062 HydroNode::CrossSingleton { right, .. } => {
4063 let right_ident = ident_stack.pop().unwrap();
4064 let left_ident = ident_stack.pop().unwrap();
4065
4066 let stmt_id = next_stmt_id.get_and_increment();
4067 let cross_ident =
4068 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4069
4070 match builders_or_callback {
4071 BuildersOrCallback::Builders(graph_builders) => {
4072 let builder = graph_builders.get_dfir_mut(&out_location);
4073
4074 if right.metadata().location_id.is_top_level()
4075 && right.metadata().collection_kind.is_bounded()
4076 {
4077 builder.add_dfir(
4078 parse_quote! {
4079 #cross_ident = cross_singleton::<'static>();
4080 #left_ident -> [input]#cross_ident;
4081 #right_ident -> [single]#cross_ident;
4082 },
4083 None,
4084 Some(&stmt_id.to_string()),
4085 );
4086 } else {
4087 builder.add_dfir(
4088 parse_quote! {
4089 #cross_ident = cross_singleton();
4090 #left_ident -> [input]#cross_ident;
4091 #right_ident -> [single]#cross_ident;
4092 },
4093 None,
4094 Some(&stmt_id.to_string()),
4095 );
4096 }
4097 }
4098 BuildersOrCallback::Callback(_, node_callback) => {
4099 node_callback(node, next_stmt_id);
4100 }
4101 }
4102
4103 ident_stack.push(cross_ident);
4104 }
4105
4106 HydroNode::CrossProduct { .. } | HydroNode::Join { .. } => {
4107 let operator: syn::Ident = if matches!(node, HydroNode::CrossProduct { .. }) {
4108 parse_quote!(cross_join_multiset)
4109 } else {
4110 parse_quote!(join_multiset)
4111 };
4112
4113 let (HydroNode::CrossProduct { left, right, .. }
4114 | HydroNode::Join { left, right, .. }) = node
4115 else {
4116 unreachable!()
4117 };
4118
4119 let is_top_level = left.metadata().location_id.is_top_level()
4120 && right.metadata().location_id.is_top_level();
4121 let left_lifetime = if left.metadata().location_id.is_top_level() {
4122 quote!('static)
4123 } else {
4124 quote!('tick)
4125 };
4126
4127 let right_lifetime = if right.metadata().location_id.is_top_level() {
4128 quote!('static)
4129 } else {
4130 quote!('tick)
4131 };
4132
4133 let right_ident = ident_stack.pop().unwrap();
4134 let left_ident = ident_stack.pop().unwrap();
4135
4136 let stmt_id = next_stmt_id.get_and_increment();
4137 let stream_ident =
4138 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4139
4140 match builders_or_callback {
4141 BuildersOrCallback::Builders(graph_builders) => {
4142 let builder = graph_builders.get_dfir_mut(&out_location);
4143 builder.add_dfir(
4144 if is_top_level {
4145 parse_quote! {
4148 #stream_ident = #operator::<#left_lifetime, #right_lifetime>() -> multiset_delta();
4149 #left_ident -> [0]#stream_ident;
4150 #right_ident -> [1]#stream_ident;
4151 }
4152 } else {
4153 parse_quote! {
4154 #stream_ident = #operator::<#left_lifetime, #right_lifetime>();
4155 #left_ident -> [0]#stream_ident;
4156 #right_ident -> [1]#stream_ident;
4157 }
4158 }
4159 ,
4160 None,
4161 Some(&stmt_id.to_string()),
4162 );
4163 }
4164 BuildersOrCallback::Callback(_, node_callback) => {
4165 node_callback(node, next_stmt_id);
4166 }
4167 }
4168
4169 ident_stack.push(stream_ident);
4170 }
4171
4172 HydroNode::Difference { .. } | HydroNode::AntiJoin { .. } => {
4173 let operator: syn::Ident = if matches!(node, HydroNode::Difference { .. }) {
4174 parse_quote!(difference)
4175 } else {
4176 parse_quote!(anti_join)
4177 };
4178
4179 let (HydroNode::Difference { neg, .. } | HydroNode::AntiJoin { neg, .. }) =
4180 node
4181 else {
4182 unreachable!()
4183 };
4184
4185 let neg_lifetime = if neg.metadata().location_id.is_top_level() {
4186 quote!('static)
4187 } else {
4188 quote!('tick)
4189 };
4190
4191 let neg_ident = ident_stack.pop().unwrap();
4192 let pos_ident = ident_stack.pop().unwrap();
4193
4194 let stmt_id = next_stmt_id.get_and_increment();
4195 let stream_ident =
4196 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4197
4198 match builders_or_callback {
4199 BuildersOrCallback::Builders(graph_builders) => {
4200 let builder = graph_builders.get_dfir_mut(&out_location);
4201 builder.add_dfir(
4202 parse_quote! {
4203 #stream_ident = #operator::<'tick, #neg_lifetime>();
4204 #pos_ident -> [pos]#stream_ident;
4205 #neg_ident -> [neg]#stream_ident;
4206 },
4207 None,
4208 Some(&stmt_id.to_string()),
4209 );
4210 }
4211 BuildersOrCallback::Callback(_, node_callback) => {
4212 node_callback(node, next_stmt_id);
4213 }
4214 }
4215
4216 ident_stack.push(stream_ident);
4217 }
4218
4219 HydroNode::JoinHalf { .. } => {
4220 let HydroNode::JoinHalf { right, .. } = node else {
4221 unreachable!()
4222 };
4223
4224 assert!(
4225 right.metadata().collection_kind.is_bounded(),
4226 "JoinHalf requires the right (build) side to be Bounded, got {:?}",
4227 right.metadata().collection_kind
4228 );
4229
4230 let build_lifetime = if right.metadata().location_id.is_top_level() {
4231 quote!('static)
4232 } else {
4233 quote!('tick)
4234 };
4235
4236 let build_ident = ident_stack.pop().unwrap();
4237 let probe_ident = ident_stack.pop().unwrap();
4238
4239 let stmt_id = next_stmt_id.get_and_increment();
4240 let stream_ident =
4241 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4242
4243 match builders_or_callback {
4244 BuildersOrCallback::Builders(graph_builders) => {
4245 let builder = graph_builders.get_dfir_mut(&out_location);
4246 builder.add_dfir(
4247 parse_quote! {
4248 #stream_ident = join_multiset_half::<#build_lifetime, 'tick>();
4249 #probe_ident -> [probe]#stream_ident;
4250 #build_ident -> [build]#stream_ident;
4251 },
4252 None,
4253 Some(&stmt_id.to_string()),
4254 );
4255 }
4256 BuildersOrCallback::Callback(_, node_callback) => {
4257 node_callback(node, next_stmt_id);
4258 }
4259 }
4260
4261 ident_stack.push(stream_ident);
4262 }
4263
4264 HydroNode::ResolveFutures { .. } => {
4265 let input_ident = ident_stack.pop().unwrap();
4266
4267 let stmt_id = next_stmt_id.get_and_increment();
4268 let futures_ident =
4269 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4270
4271 match builders_or_callback {
4272 BuildersOrCallback::Builders(graph_builders) => {
4273 let builder = graph_builders.get_dfir_mut(&out_location);
4274 builder.add_dfir(
4275 parse_quote! {
4276 #futures_ident = #input_ident -> resolve_futures();
4277 },
4278 None,
4279 Some(&stmt_id.to_string()),
4280 );
4281 }
4282 BuildersOrCallback::Callback(_, node_callback) => {
4283 node_callback(node, next_stmt_id);
4284 }
4285 }
4286
4287 ident_stack.push(futures_ident);
4288 }
4289
4290 HydroNode::ResolveFuturesBlocking { .. } => {
4291 let input_ident = ident_stack.pop().unwrap();
4292
4293 let stmt_id = next_stmt_id.get_and_increment();
4294 let futures_ident =
4295 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4296
4297 match builders_or_callback {
4298 BuildersOrCallback::Builders(graph_builders) => {
4299 let builder = graph_builders.get_dfir_mut(&out_location);
4300 builder.add_dfir(
4301 parse_quote! {
4302 #futures_ident = #input_ident -> resolve_futures_blocking();
4303 },
4304 None,
4305 Some(&stmt_id.to_string()),
4306 );
4307 }
4308 BuildersOrCallback::Callback(_, node_callback) => {
4309 node_callback(node, next_stmt_id);
4310 }
4311 }
4312
4313 ident_stack.push(futures_ident);
4314 }
4315
4316 HydroNode::ResolveFuturesOrdered { .. } => {
4317 let input_ident = ident_stack.pop().unwrap();
4318
4319 let stmt_id = next_stmt_id.get_and_increment();
4320 let futures_ident =
4321 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4322
4323 match builders_or_callback {
4324 BuildersOrCallback::Builders(graph_builders) => {
4325 let builder = graph_builders.get_dfir_mut(&out_location);
4326 builder.add_dfir(
4327 parse_quote! {
4328 #futures_ident = #input_ident -> resolve_futures_ordered();
4329 },
4330 None,
4331 Some(&stmt_id.to_string()),
4332 );
4333 }
4334 BuildersOrCallback::Callback(_, node_callback) => {
4335 node_callback(node, next_stmt_id);
4336 }
4337 }
4338
4339 ident_stack.push(futures_ident);
4340 }
4341
4342 HydroNode::Map {
4343 f,
4344 input,
4345 metadata,
4346 } => {
4347 let input_ident = ident_stack.pop().unwrap();
4349 let f_tokens = f.emit_tokens(&mut ident_stack);
4350
4351 let input_ident = maybe_observe_for_mut(
4352 f,
4353 input_ident,
4354 &input.metadata().location_id,
4355 &input.metadata().collection_kind,
4356 &metadata.op,
4357 builders_or_callback,
4358 next_stmt_id,
4359 );
4360
4361 let stmt_id = next_stmt_id.get_and_increment();
4362 let map_ident =
4363 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4364
4365 match builders_or_callback {
4366 BuildersOrCallback::Builders(graph_builders) => {
4367 let builder = graph_builders.get_dfir_mut(&out_location);
4368 builder.add_dfir(
4369 parse_quote! {
4370 #map_ident = #input_ident -> map(#f_tokens);
4371 },
4372 None,
4373 Some(&stmt_id.to_string()),
4374 );
4375 }
4376 BuildersOrCallback::Callback(_, node_callback) => {
4377 node_callback(node, next_stmt_id);
4378 }
4379 }
4380
4381 ident_stack.push(map_ident);
4382 }
4383
4384 HydroNode::FlatMap { f, input, metadata } => {
4385 let input_ident = ident_stack.pop().unwrap();
4386 let f_tokens = f.emit_tokens(&mut ident_stack);
4387
4388 let input_ident = maybe_observe_for_mut(
4389 f, input_ident,
4390 &input.metadata().location_id,
4391 &input.metadata().collection_kind,
4392 &metadata.op,
4393 builders_or_callback, next_stmt_id,
4394 );
4395
4396 let stmt_id = next_stmt_id.get_and_increment();
4397 let flat_map_ident =
4398 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4399
4400 match builders_or_callback {
4401 BuildersOrCallback::Builders(graph_builders) => {
4402 let builder = graph_builders.get_dfir_mut(&out_location);
4403 builder.add_dfir(
4404 parse_quote! {
4405 #flat_map_ident = #input_ident -> flat_map(#f_tokens);
4406 },
4407 None,
4408 Some(&stmt_id.to_string()),
4409 );
4410 }
4411 BuildersOrCallback::Callback(_, node_callback) => {
4412 node_callback(node, next_stmt_id);
4413 }
4414 }
4415
4416 ident_stack.push(flat_map_ident);
4417 }
4418
4419 HydroNode::FlatMapStreamBlocking { f, input, metadata } => {
4420 let input_ident = ident_stack.pop().unwrap();
4421 let f_tokens = f.emit_tokens(&mut ident_stack);
4422
4423 let input_ident = maybe_observe_for_mut(
4424 f, input_ident,
4425 &input.metadata().location_id,
4426 &input.metadata().collection_kind,
4427 &metadata.op,
4428 builders_or_callback, next_stmt_id,
4429 );
4430
4431 let stmt_id = next_stmt_id.get_and_increment();
4432 let flat_map_stream_blocking_ident =
4433 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4434
4435 match builders_or_callback {
4436 BuildersOrCallback::Builders(graph_builders) => {
4437 let builder = graph_builders.get_dfir_mut(&out_location);
4438 builder.add_dfir(
4439 parse_quote! {
4440 #flat_map_stream_blocking_ident = #input_ident -> flat_map_stream_blocking(#f_tokens);
4441 },
4442 None,
4443 Some(&stmt_id.to_string()),
4444 );
4445 }
4446 BuildersOrCallback::Callback(_, node_callback) => {
4447 node_callback(node, next_stmt_id);
4448 }
4449 }
4450
4451 ident_stack.push(flat_map_stream_blocking_ident);
4452 }
4453
4454 HydroNode::Filter { f, input, metadata } => {
4455 let input_ident = ident_stack.pop().unwrap();
4456 let f_tokens = f.emit_tokens(&mut ident_stack);
4457
4458 let input_ident = maybe_observe_for_mut(
4459 f, input_ident,
4460 &input.metadata().location_id,
4461 &input.metadata().collection_kind,
4462 &metadata.op,
4463 builders_or_callback, next_stmt_id,
4464 );
4465
4466 let stmt_id = next_stmt_id.get_and_increment();
4467 let filter_ident =
4468 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4469
4470 match builders_or_callback {
4471 BuildersOrCallback::Builders(graph_builders) => {
4472 let builder = graph_builders.get_dfir_mut(&out_location);
4473 builder.add_dfir(
4474 parse_quote! {
4475 #filter_ident = #input_ident -> filter(#f_tokens);
4476 },
4477 None,
4478 Some(&stmt_id.to_string()),
4479 );
4480 }
4481 BuildersOrCallback::Callback(_, node_callback) => {
4482 node_callback(node, next_stmt_id);
4483 }
4484 }
4485
4486 ident_stack.push(filter_ident);
4487 }
4488
4489 HydroNode::FilterMap { f, input, metadata } => {
4490 let input_ident = ident_stack.pop().unwrap();
4491 let f_tokens = f.emit_tokens(&mut ident_stack);
4492
4493 let input_ident = maybe_observe_for_mut(
4494 f, input_ident,
4495 &input.metadata().location_id,
4496 &input.metadata().collection_kind,
4497 &metadata.op,
4498 builders_or_callback, next_stmt_id,
4499 );
4500
4501 let stmt_id = next_stmt_id.get_and_increment();
4502 let filter_map_ident =
4503 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4504
4505 match builders_or_callback {
4506 BuildersOrCallback::Builders(graph_builders) => {
4507 let builder = graph_builders.get_dfir_mut(&out_location);
4508 builder.add_dfir(
4509 parse_quote! {
4510 #filter_map_ident = #input_ident -> filter_map(#f_tokens);
4511 },
4512 None,
4513 Some(&stmt_id.to_string()),
4514 );
4515 }
4516 BuildersOrCallback::Callback(_, node_callback) => {
4517 node_callback(node, next_stmt_id);
4518 }
4519 }
4520
4521 ident_stack.push(filter_map_ident);
4522 }
4523
4524 HydroNode::Sort { .. } => {
4525 let input_ident = ident_stack.pop().unwrap();
4526
4527 let stmt_id = next_stmt_id.get_and_increment();
4528 let sort_ident =
4529 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4530
4531 match builders_or_callback {
4532 BuildersOrCallback::Builders(graph_builders) => {
4533 let builder = graph_builders.get_dfir_mut(&out_location);
4534 builder.add_dfir(
4535 parse_quote! {
4536 #sort_ident = #input_ident -> sort();
4537 },
4538 None,
4539 Some(&stmt_id.to_string()),
4540 );
4541 }
4542 BuildersOrCallback::Callback(_, node_callback) => {
4543 node_callback(node, next_stmt_id);
4544 }
4545 }
4546
4547 ident_stack.push(sort_ident);
4548 }
4549
4550 HydroNode::DeferTick { .. } => {
4551 let input_ident = ident_stack.pop().unwrap();
4552
4553 let stmt_id = next_stmt_id.get_and_increment();
4554 let defer_tick_ident =
4555 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4556
4557 match builders_or_callback {
4558 BuildersOrCallback::Builders(graph_builders) => {
4559 let builder = graph_builders.get_dfir_mut(&out_location);
4560 builder.add_dfir(
4561 parse_quote! {
4562 #defer_tick_ident = #input_ident -> defer_tick_lazy();
4563 },
4564 None,
4565 Some(&stmt_id.to_string()),
4566 );
4567 }
4568 BuildersOrCallback::Callback(_, node_callback) => {
4569 node_callback(node, next_stmt_id);
4570 }
4571 }
4572
4573 ident_stack.push(defer_tick_ident);
4574 }
4575
4576 HydroNode::Enumerate { input, .. } => {
4577 let input_ident = ident_stack.pop().unwrap();
4578
4579 let stmt_id = next_stmt_id.get_and_increment();
4580 let enumerate_ident =
4581 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4582
4583 match builders_or_callback {
4584 BuildersOrCallback::Builders(graph_builders) => {
4585 let builder = graph_builders.get_dfir_mut(&out_location);
4586 let lifetime = if input.metadata().location_id.is_top_level() {
4587 quote!('static)
4588 } else {
4589 quote!('tick)
4590 };
4591 builder.add_dfir(
4592 parse_quote! {
4593 #enumerate_ident = #input_ident -> enumerate::<#lifetime>();
4594 },
4595 None,
4596 Some(&stmt_id.to_string()),
4597 );
4598 }
4599 BuildersOrCallback::Callback(_, node_callback) => {
4600 node_callback(node, next_stmt_id);
4601 }
4602 }
4603
4604 ident_stack.push(enumerate_ident);
4605 }
4606
4607 HydroNode::Inspect { f, input, metadata } => {
4608 let input_ident = ident_stack.pop().unwrap();
4609 let f_tokens = f.emit_tokens(&mut ident_stack);
4610
4611 let input_ident = maybe_observe_for_mut(
4612 f, input_ident,
4613 &input.metadata().location_id,
4614 &input.metadata().collection_kind,
4615 &metadata.op,
4616 builders_or_callback, next_stmt_id,
4617 );
4618
4619 let stmt_id = next_stmt_id.get_and_increment();
4620 let inspect_ident =
4621 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4622
4623 match builders_or_callback {
4624 BuildersOrCallback::Builders(graph_builders) => {
4625 let builder = graph_builders.get_dfir_mut(&out_location);
4626 builder.add_dfir(
4627 parse_quote! {
4628 #inspect_ident = #input_ident -> inspect(#f_tokens);
4629 },
4630 None,
4631 Some(&stmt_id.to_string()),
4632 );
4633 }
4634 BuildersOrCallback::Callback(_, node_callback) => {
4635 node_callback(node, next_stmt_id);
4636 }
4637 }
4638
4639 ident_stack.push(inspect_ident);
4640 }
4641
4642 HydroNode::Unique { input, .. } => {
4643 let input_ident = ident_stack.pop().unwrap();
4644
4645 let stmt_id = next_stmt_id.get_and_increment();
4646 let unique_ident =
4647 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4648
4649 match builders_or_callback {
4650 BuildersOrCallback::Builders(graph_builders) => {
4651 let builder = graph_builders.get_dfir_mut(&out_location);
4652 let lifetime = if input.metadata().location_id.is_top_level() {
4653 quote!('static)
4654 } else {
4655 quote!('tick)
4656 };
4657
4658 builder.add_dfir(
4659 parse_quote! {
4660 #unique_ident = #input_ident -> unique::<#lifetime>();
4661 },
4662 None,
4663 Some(&stmt_id.to_string()),
4664 );
4665 }
4666 BuildersOrCallback::Callback(_, node_callback) => {
4667 node_callback(node, next_stmt_id);
4668 }
4669 }
4670
4671 ident_stack.push(unique_ident);
4672 }
4673
4674 HydroNode::Fold { .. } | HydroNode::FoldKeyed { .. } | HydroNode::Scan { .. } | HydroNode::ScanAsyncBlocking { .. } => {
4675 let operator: syn::Ident = if let HydroNode::Fold { input, .. } = node {
4676 if input.metadata().location_id.is_top_level()
4677 && input.metadata().collection_kind.is_bounded()
4678 {
4679 parse_quote!(fold_no_replay)
4680 } else {
4681 parse_quote!(fold)
4682 }
4683 } else if matches!(node, HydroNode::Scan { .. }) {
4684 parse_quote!(scan)
4685 } else if matches!(node, HydroNode::ScanAsyncBlocking { .. }) {
4686 parse_quote!(scan_async_blocking)
4687 } else if let HydroNode::FoldKeyed { input, .. } = node {
4688 if input.metadata().location_id.is_top_level()
4689 && input.metadata().collection_kind.is_bounded()
4690 {
4691 todo!("Fold keyed on a top-level bounded collection is not yet supported")
4692 } else {
4693 parse_quote!(fold_keyed)
4694 }
4695 } else {
4696 unreachable!()
4697 };
4698
4699 let (HydroNode::Fold { input, .. }
4700 | HydroNode::FoldKeyed { input, .. }
4701 | HydroNode::Scan { input, .. }
4702 | HydroNode::ScanAsyncBlocking { input, .. }) = node
4703 else {
4704 unreachable!()
4705 };
4706
4707 let lifetime = if input.metadata().location_id.is_top_level() {
4708 quote!('static)
4709 } else {
4710 quote!('tick)
4711 };
4712
4713 let input_ident = ident_stack.pop().unwrap();
4714
4715 let (HydroNode::Fold { init, acc, .. }
4716 | HydroNode::FoldKeyed { init, acc, .. }
4717 | HydroNode::Scan { init, acc, .. }
4718 | HydroNode::ScanAsyncBlocking { init, acc, .. }) = &*node
4719 else {
4720 unreachable!()
4721 };
4722
4723 let acc_tokens = acc.emit_tokens(&mut ident_stack);
4724 let init_tokens = init.emit_tokens(&mut ident_stack);
4725
4726 let stmt_id = next_stmt_id.get_and_increment();
4727 let fold_ident =
4728 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4729
4730 match builders_or_callback {
4731 BuildersOrCallback::Builders(graph_builders) => {
4732 if matches!(node, HydroNode::Fold { .. })
4733 && node.metadata().location_id.is_top_level()
4734 && !(matches!(node.metadata().location_id, LocationId::Atomic(_)))
4735 && graph_builders.singleton_intermediates()
4736 && !node.metadata().collection_kind.is_bounded()
4737 {
4738 let HydroNode::Fold { input, .. } = &*node else { unreachable!() };
4739 let hooked_input_ident = graph_builders.emit_fold_hook(
4740 &input.metadata().location_id,
4741 &input_ident,
4742 &input.metadata().collection_kind,
4743 &node.metadata().op,
4744 );
4745
4746 let (effective_input, wrapped_acc) = if let Some(ref hooked) = hooked_input_ident {
4747 let acc: syn::Expr = parse_quote!({
4748 let mut __inner = #acc_tokens;
4749 move |__state, __batch: Vec<_>| {
4750 if __batch.is_empty() {
4751 return None;
4752 }
4753 for __value in __batch {
4754 __inner(__state, __value);
4755 }
4756 Some(__state.clone())
4757 }
4758 });
4759 (hooked, acc)
4760 } else {
4761 let acc: syn::Expr = parse_quote!({
4762 let mut __inner = #acc_tokens;
4763 move |__state, __value| {
4764 __inner(__state, __value);
4765 Some(__state.clone())
4766 }
4767 });
4768 (&input_ident, acc)
4769 };
4770
4771 let builder = graph_builders.get_dfir_mut(&out_location);
4772 builder.add_dfir(
4773 parse_quote! {
4774 source_iter([(#init_tokens)()]) -> [0]#fold_ident;
4775 #effective_input -> scan::<#lifetime>(#init_tokens, #wrapped_acc) -> [1]#fold_ident;
4776 #fold_ident = chain();
4777 },
4778 None,
4779 Some(&stmt_id.to_string()),
4780 );
4781
4782 if hooked_input_ident.is_some() {
4783 fold_hooked_idents.insert(fold_ident.to_string());
4784 }
4785 } else if matches!(node, HydroNode::FoldKeyed { .. })
4786 && node.metadata().location_id.is_top_level()
4787 && !(matches!(node.metadata().location_id, LocationId::Atomic(_)))
4788 && graph_builders.singleton_intermediates()
4789 && !node.metadata().collection_kind.is_bounded()
4790 {
4791 let HydroNode::FoldKeyed { input, .. } = &*node else { unreachable!() };
4792 let hooked_input_ident = graph_builders.emit_fold_hook(
4793 &input.metadata().location_id,
4794 &input_ident,
4795 &input.metadata().collection_kind,
4796 &node.metadata().op,
4797 );
4798 let builder = graph_builders.get_dfir_mut(&out_location);
4799
4800 let wrapped_acc: syn::Expr = parse_quote!({
4801 let mut __init = #init_tokens;
4802 let mut __inner = #acc_tokens;
4803 move |__state, __kv: (_, _)| {
4804 let __state = __state
4806 .entry(::std::clone::Clone::clone(&__kv.0))
4807 .or_insert_with(|| (__init)());
4808 __inner(__state, __kv.1);
4809 Some((__kv.0, ::std::clone::Clone::clone(&*__state)))
4810 }
4811 });
4812
4813 if let Some(hooked_input_ident) = hooked_input_ident {
4814 builder.add_dfir(
4815 parse_quote! {
4816 #fold_ident = #hooked_input_ident -> flatten() -> scan::<#lifetime>(|| ::std::collections::HashMap::new(), #wrapped_acc);
4817 },
4818 None,
4819 Some(&stmt_id.to_string()),
4820 );
4821
4822 fold_hooked_idents.insert(fold_ident.to_string());
4823 } else {
4824 builder.add_dfir(
4825 parse_quote! {
4826 #fold_ident = #input_ident -> scan::<#lifetime>(|| ::std::collections::HashMap::new(), #wrapped_acc);
4827 },
4828 None,
4829 Some(&stmt_id.to_string()),
4830 );
4831 }
4832 } else if (matches!(node, HydroNode::Fold { .. })
4833 || matches!(node, HydroNode::FoldKeyed { .. }))
4834 && !node.metadata().location_id.is_top_level()
4835 && graph_builders.singleton_intermediates()
4836 {
4837 let input_ref = match &*node {
4838 HydroNode::Fold { input, .. } => input,
4839 HydroNode::FoldKeyed { input, .. } => input,
4840 _ => unreachable!(),
4841 };
4842 let hooked_input_ident = graph_builders.emit_fold_hook(
4843 &input_ref.metadata().location_id,
4844 &input_ident,
4845 &input_ref.metadata().collection_kind,
4846 &node.metadata().op,
4847 );
4848
4849 let actual_input = hooked_input_ident.as_ref().unwrap_or(&input_ident);
4850 let builder = graph_builders.get_dfir_mut(&out_location);
4851 builder.add_dfir(
4852 parse_quote! {
4853 #fold_ident = #actual_input -> #operator::<#lifetime>(#init_tokens, #acc_tokens);
4854 },
4855 None,
4856 Some(&stmt_id.to_string()),
4857 );
4858 } else {
4859 let builder = graph_builders.get_dfir_mut(&out_location);
4860 builder.add_dfir(
4861 parse_quote! {
4862 #fold_ident = #input_ident -> #operator::<#lifetime>(#init_tokens, #acc_tokens);
4863 },
4864 None,
4865 Some(&stmt_id.to_string()),
4866 );
4867 }
4868 }
4869 BuildersOrCallback::Callback(_, node_callback) => {
4870 node_callback(node, next_stmt_id);
4871 }
4872 }
4873
4874 ident_stack.push(fold_ident);
4875 }
4876
4877 HydroNode::Reduce { .. } | HydroNode::ReduceKeyed { .. } => {
4878 let operator: syn::Ident = if let HydroNode::Reduce { input, .. } = node {
4879 if input.metadata().location_id.is_top_level()
4880 && input.metadata().collection_kind.is_bounded()
4881 {
4882 parse_quote!(reduce_no_replay)
4883 } else {
4884 parse_quote!(reduce)
4885 }
4886 } else if let HydroNode::ReduceKeyed { input, .. } = node {
4887 if input.metadata().location_id.is_top_level()
4888 && input.metadata().collection_kind.is_bounded()
4889 {
4890 todo!(
4891 "Calling keyed reduce on a top-level bounded collection is not supported"
4892 )
4893 } else {
4894 parse_quote!(reduce_keyed)
4895 }
4896 } else {
4897 unreachable!()
4898 };
4899
4900 let (HydroNode::Reduce { input, .. } | HydroNode::ReduceKeyed { input, .. }) = node
4901 else {
4902 unreachable!()
4903 };
4904
4905 let lifetime = if input.metadata().location_id.is_top_level() {
4906 quote!('static)
4907 } else {
4908 quote!('tick)
4909 };
4910
4911 let input_ident = ident_stack.pop().unwrap();
4912
4913 let (HydroNode::Reduce { f, .. } | HydroNode::ReduceKeyed { f, .. }) = &*node
4914 else {
4915 unreachable!()
4916 };
4917
4918 let f_tokens = f.emit_tokens(&mut ident_stack);
4919
4920 let stmt_id = next_stmt_id.get_and_increment();
4921 let reduce_ident =
4922 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4923
4924 match builders_or_callback {
4925 BuildersOrCallback::Builders(graph_builders) => {
4926 if matches!(node, HydroNode::Reduce { .. })
4927 && node.metadata().location_id.is_top_level()
4928 && !(matches!(node.metadata().location_id, LocationId::Atomic(_)))
4929 && graph_builders.singleton_intermediates()
4930 && !node.metadata().collection_kind.is_bounded()
4931 {
4932 todo!(
4933 "Reduce with optional intermediates is not yet supported in simulator"
4934 );
4935 } else if matches!(node, HydroNode::ReduceKeyed { .. })
4936 && node.metadata().location_id.is_top_level()
4937 && !(matches!(node.metadata().location_id, LocationId::Atomic(_)))
4938 && graph_builders.singleton_intermediates()
4939 && !node.metadata().collection_kind.is_bounded()
4940 {
4941 todo!(
4942 "Reduce keyed with optional intermediates is not yet supported in simulator"
4943 );
4944 } else {
4945 let builder = graph_builders.get_dfir_mut(&out_location);
4946 builder.add_dfir(
4947 parse_quote! {
4948 #reduce_ident = #input_ident -> #operator::<#lifetime>(#f_tokens);
4949 },
4950 None,
4951 Some(&stmt_id.to_string()),
4952 );
4953 }
4954 }
4955 BuildersOrCallback::Callback(_, node_callback) => {
4956 node_callback(node, next_stmt_id);
4957 }
4958 }
4959
4960 ident_stack.push(reduce_ident);
4961 }
4962
4963 HydroNode::ReduceKeyedWatermark {
4964 f,
4965 input,
4966 metadata,
4967 ..
4968 } => {
4969 let lifetime = if input.metadata().location_id.is_top_level() {
4970 quote!('static)
4971 } else {
4972 quote!('tick)
4973 };
4974
4975 let watermark_ident = ident_stack.pop().unwrap();
4977 let input_ident = ident_stack.pop().unwrap();
4978 let f_tokens = f.emit_tokens(&mut ident_stack);
4979
4980 let stmt_id = next_stmt_id.get_and_increment();
4981 let chain_ident = syn::Ident::new(
4982 &format!("reduce_keyed_watermark_chain_{}", stmt_id),
4983 Span::call_site(),
4984 );
4985
4986 let fold_ident =
4987 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4988
4989 let agg_operator: syn::Ident = if input.metadata().location_id.is_top_level()
4990 && input.metadata().collection_kind.is_bounded()
4991 {
4992 parse_quote!(fold_no_replay)
4993 } else {
4994 parse_quote!(fold)
4995 };
4996
4997 match builders_or_callback {
4998 BuildersOrCallback::Builders(graph_builders) => {
4999 if metadata.location_id.is_top_level()
5000 && !(matches!(metadata.location_id, LocationId::Atomic(_)))
5001 && graph_builders.singleton_intermediates()
5002 && !metadata.collection_kind.is_bounded()
5003 {
5004 todo!(
5005 "Reduce keyed watermarked on a top-level bounded collection is not yet supported"
5006 )
5007 } else {
5008 let builder = graph_builders.get_dfir_mut(&out_location);
5009 builder.add_dfir(
5010 parse_quote! {
5011 #chain_ident = chain();
5012 #input_ident
5013 -> map(|x| (Some(x), None))
5014 -> [0]#chain_ident;
5015 #watermark_ident
5016 -> map(|watermark| (None, Some(watermark)))
5017 -> [1]#chain_ident;
5018
5019 #fold_ident = #chain_ident
5020 -> #agg_operator::<#lifetime>(|| (::std::collections::HashMap::new(), None), {
5021 let __reduce_keyed_fn = #f_tokens;
5022 move |(map, opt_curr_watermark), (opt_payload, opt_watermark)| {
5023 if let Some((k, v)) = opt_payload {
5024 if let Some(curr_watermark) = *opt_curr_watermark {
5025 if k < curr_watermark {
5026 return;
5027 }
5028 }
5029 match map.entry(k) {
5030 ::std::collections::hash_map::Entry::Vacant(e) => {
5031 e.insert(v);
5032 }
5033 ::std::collections::hash_map::Entry::Occupied(mut e) => {
5034 __reduce_keyed_fn(e.get_mut(), v);
5035 }
5036 }
5037 } else {
5038 let watermark = opt_watermark.unwrap();
5039 if let Some(curr_watermark) = *opt_curr_watermark {
5040 if watermark <= curr_watermark {
5041 return;
5042 }
5043 }
5044 map.retain(|k, _| *k >= watermark);
5045 *opt_curr_watermark = Some(watermark);
5046 }
5047 }
5048 })
5049 -> flat_map(|(map, _curr_watermark)| map);
5050 },
5051 None,
5052 Some(&stmt_id.to_string()),
5053 );
5054 }
5055 }
5056 BuildersOrCallback::Callback(_, node_callback) => {
5057 node_callback(node, next_stmt_id);
5058 }
5059 }
5060
5061 ident_stack.push(fold_ident);
5062 }
5063
5064 HydroNode::Network {
5065 networking_info,
5066 serialize_fn: serialize_pipeline,
5067 instantiate_fn,
5068 deserialize_fn: deserialize_pipeline,
5069 input,
5070 ..
5071 } => {
5072 let input_ident = ident_stack.pop().unwrap();
5073
5074 let stmt_id = next_stmt_id.get_and_increment();
5075 let receiver_stream_ident =
5076 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5077
5078 match builders_or_callback {
5079 BuildersOrCallback::Builders(graph_builders) => {
5080 let (sink_expr, source_expr) = match instantiate_fn {
5081 DebugInstantiate::Building => (
5082 syn::parse_quote!(DUMMY_SINK),
5083 syn::parse_quote!(DUMMY_SOURCE),
5084 ),
5085
5086 DebugInstantiate::Finalized(finalized) => {
5087 (finalized.sink.clone(), finalized.source.clone())
5088 }
5089 };
5090
5091 graph_builders.create_network(
5092 &input.metadata().location_id,
5093 &out_location,
5094 input_ident,
5095 &receiver_stream_ident,
5096 serialize_pipeline.as_ref(),
5097 sink_expr,
5098 source_expr,
5099 deserialize_pipeline.as_ref(),
5100 stmt_id,
5101 networking_info,
5102 );
5103 }
5104 BuildersOrCallback::Callback(_, node_callback) => {
5105 node_callback(node, next_stmt_id);
5106 }
5107 }
5108
5109 ident_stack.push(receiver_stream_ident);
5110 }
5111
5112 HydroNode::ExternalInput {
5113 instantiate_fn,
5114 deserialize_fn: deserialize_pipeline,
5115 ..
5116 } => {
5117 let stmt_id = next_stmt_id.get_and_increment();
5118 let receiver_stream_ident =
5119 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5120
5121 match builders_or_callback {
5122 BuildersOrCallback::Builders(graph_builders) => {
5123 let (_, source_expr) = match instantiate_fn {
5124 DebugInstantiate::Building => (
5125 syn::parse_quote!(DUMMY_SINK),
5126 syn::parse_quote!(DUMMY_SOURCE),
5127 ),
5128
5129 DebugInstantiate::Finalized(finalized) => {
5130 (finalized.sink.clone(), finalized.source.clone())
5131 }
5132 };
5133
5134 graph_builders.create_external_source(
5135 &out_location,
5136 source_expr,
5137 &receiver_stream_ident,
5138 deserialize_pipeline.as_ref(),
5139 stmt_id,
5140 );
5141 }
5142 BuildersOrCallback::Callback(_, node_callback) => {
5143 node_callback(node, next_stmt_id);
5144 }
5145 }
5146
5147 ident_stack.push(receiver_stream_ident);
5148 }
5149
5150 HydroNode::Counter {
5151 tag,
5152 duration,
5153 prefix,
5154 ..
5155 } => {
5156 let input_ident = ident_stack.pop().unwrap();
5157
5158 let stmt_id = next_stmt_id.get_and_increment();
5159 let counter_ident =
5160 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5161
5162 match builders_or_callback {
5163 BuildersOrCallback::Builders(graph_builders) => {
5164 let arg = format!("{}({})", prefix, tag);
5165 let builder = graph_builders.get_dfir_mut(&out_location);
5166 builder.add_dfir(
5167 parse_quote! {
5168 #counter_ident = #input_ident -> _counter(#arg, #duration);
5169 },
5170 None,
5171 Some(&stmt_id.to_string()),
5172 );
5173 }
5174 BuildersOrCallback::Callback(_, node_callback) => {
5175 node_callback(node, next_stmt_id);
5176 }
5177 }
5178
5179 ident_stack.push(counter_ident);
5180 }
5181
5182 HydroNode::VersionedNetworkFork {
5183 channel_id,
5184 senders,
5185 metadata,
5186 ..
5187 } => {
5188 let split_at = ident_stack.len() - senders.len();
5190 let sender_idents = ident_stack.split_off(split_at);
5191
5192 let stmt_id = next_stmt_id.get_and_increment();
5193
5194 match builders_or_callback {
5195 BuildersOrCallback::Builders(graph_builders) => {
5196 let sender_args: Vec<(LocationId, syn::Ident, Option<DebugExpr>)> =
5197 senders
5198 .iter()
5199 .zip(sender_idents)
5200 .map(|((_version, sender, serialize), ident)| {
5201 (
5202 sender.metadata().location_id.clone(),
5203 ident,
5204 serialize.clone(),
5205 )
5206 })
5207 .collect();
5208 graph_builders.create_versioned_network_fork(
5209 *channel_id,
5210 &metadata.location_id,
5211 sender_args,
5212 stmt_id,
5213 );
5214 }
5215 BuildersOrCallback::Callback(_, node_callback) => {
5216 node_callback(node, next_stmt_id);
5217 }
5218 }
5219 }
5220
5221 HydroNode::VersionedNetwork {
5222 fork,
5223 deserialize_fn,
5224 metadata,
5225 ..
5226 } => {
5227 let stmt_id = next_stmt_id.get_and_increment();
5228 let receiver_stream_ident =
5229 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5230
5231 let (channel_id, source_loc) = {
5234 let fork_ref = fork.0.borrow();
5235 let HydroNode::VersionedNetworkFork {
5236 channel_id,
5237 senders,
5238 ..
5239 } = &*fork_ref
5240 else {
5241 unreachable!("VersionedNetwork.fork must be a VersionedNetworkFork");
5242 };
5243 let source_loc = senders
5244 .first()
5245 .map(|(_v, sender, _s)| sender.metadata().location_id.clone())
5246 .expect("a VersionedNetworkFork always has at least one sender");
5247 (*channel_id, source_loc)
5248 };
5249
5250 match builders_or_callback {
5251 BuildersOrCallback::Builders(graph_builders) => {
5252 graph_builders.create_versioned_network(
5253 channel_id,
5254 &source_loc,
5255 &metadata.location_id,
5256 &receiver_stream_ident,
5257 deserialize_fn.as_ref(),
5258 stmt_id,
5259 );
5260 }
5261 BuildersOrCallback::Callback(_, node_callback) => {
5262 node_callback(node, next_stmt_id);
5263 }
5264 }
5265
5266 ident_stack.push(receiver_stream_ident);
5267 }
5268 }
5269 },
5270 seen_tees,
5271 false,
5272 );
5273
5274 let ret = ident_stack
5275 .pop()
5276 .expect("ident_stack should have exactly one element after traversal");
5277 assert!(
5278 ident_stack.is_empty(),
5279 "ident_stack should be empty after popping the final ident, but has {} remaining element(s). \
5280 This indicates a bug in the code gen: some node pushed idents that were never consumed.",
5281 ident_stack.len()
5282 );
5283 ret
5284 }
5285
5286 pub fn visit_debug_expr(&mut self, mut transform: impl FnMut(&mut DebugExpr)) {
5287 match self {
5288 HydroNode::Placeholder => {
5289 panic!()
5290 }
5291 HydroNode::Cast { .. }
5292 | HydroNode::ObserveNonDet { .. }
5293 | HydroNode::UnboundSingleton { .. }
5294 | HydroNode::AssertIsConsistent { .. } => {}
5295 HydroNode::Source { source, .. } => match source {
5296 HydroSource::Stream(expr) | HydroSource::Iter(expr) => transform(expr),
5297 HydroSource::ExternalNetwork()
5298 | HydroSource::Spin()
5299 | HydroSource::ClusterMembers(_, _)
5300 | HydroSource::Embedded(_)
5301 | HydroSource::EmbeddedSingleton(_) => {} },
5303 HydroNode::SingletonSource { value, .. } => {
5304 transform(value);
5305 }
5306 HydroNode::CycleSource { .. }
5307 | HydroNode::Tee { .. }
5308 | HydroNode::Reference { .. }
5309 | HydroNode::YieldConcat { .. }
5310 | HydroNode::BeginAtomic { .. }
5311 | HydroNode::EndAtomic { .. }
5312 | HydroNode::Batch { .. }
5313 | HydroNode::Chain { .. }
5314 | HydroNode::MergeOrdered { .. }
5315 | HydroNode::ChainFirst { .. }
5316 | HydroNode::CrossProduct { .. }
5317 | HydroNode::CrossSingleton { .. }
5318 | HydroNode::ResolveFutures { .. }
5319 | HydroNode::ResolveFuturesBlocking { .. }
5320 | HydroNode::ResolveFuturesOrdered { .. }
5321 | HydroNode::Join { .. }
5322 | HydroNode::JoinHalf { .. }
5323 | HydroNode::Difference { .. }
5324 | HydroNode::AntiJoin { .. }
5325 | HydroNode::DeferTick { .. }
5326 | HydroNode::Enumerate { .. }
5327 | HydroNode::Unique { .. }
5328 | HydroNode::Sort { .. }
5329 | HydroNode::VersionedNetworkFork { .. }
5330 | HydroNode::VersionedNetwork { .. } => {}
5331 HydroNode::Map { f, .. }
5332 | HydroNode::FlatMap { f, .. }
5333 | HydroNode::FlatMapStreamBlocking { f, .. }
5334 | HydroNode::Filter { f, .. }
5335 | HydroNode::FilterMap { f, .. }
5336 | HydroNode::Inspect { f, .. }
5337 | HydroNode::Partition { f, .. }
5338 | HydroNode::Reduce { f, .. }
5339 | HydroNode::ReduceKeyed { f, .. }
5340 | HydroNode::ReduceKeyedWatermark { f, .. } => {
5341 transform(&mut f.expr);
5342 }
5343 HydroNode::Fold { init, acc, .. }
5344 | HydroNode::Scan { init, acc, .. }
5345 | HydroNode::ScanAsyncBlocking { init, acc, .. }
5346 | HydroNode::FoldKeyed { init, acc, .. } => {
5347 transform(&mut init.expr);
5348 transform(&mut acc.expr);
5349 }
5350 HydroNode::Network {
5351 serialize_fn,
5352 deserialize_fn,
5353 ..
5354 } => {
5355 if let Some(serialize_fn) = serialize_fn {
5356 transform(serialize_fn);
5357 }
5358 if let Some(deserialize_fn) = deserialize_fn {
5359 transform(deserialize_fn);
5360 }
5361 }
5362 HydroNode::ExternalInput { deserialize_fn, .. } => {
5363 if let Some(deserialize_fn) = deserialize_fn {
5364 transform(deserialize_fn);
5365 }
5366 }
5367 HydroNode::Counter { duration, .. } => {
5368 transform(duration);
5369 }
5370 }
5371 }
5372
5373 pub fn op_metadata(&self) -> &HydroIrOpMetadata {
5374 &self.metadata().op
5375 }
5376
5377 pub fn metadata(&self) -> &HydroIrMetadata {
5378 match self {
5379 HydroNode::Placeholder => {
5380 panic!()
5381 }
5382 HydroNode::VersionedNetworkFork { metadata, .. }
5383 | HydroNode::VersionedNetwork { metadata, .. } => metadata,
5384 HydroNode::Cast { metadata, .. }
5385 | HydroNode::ObserveNonDet { metadata, .. }
5386 | HydroNode::AssertIsConsistent { metadata, .. }
5387 | HydroNode::UnboundSingleton { metadata, .. }
5388 | HydroNode::Source { metadata, .. }
5389 | HydroNode::SingletonSource { metadata, .. }
5390 | HydroNode::CycleSource { metadata, .. }
5391 | HydroNode::Tee { metadata, .. }
5392 | HydroNode::Reference { metadata, .. }
5393 | HydroNode::Partition { metadata, .. }
5394 | HydroNode::YieldConcat { metadata, .. }
5395 | HydroNode::BeginAtomic { metadata, .. }
5396 | HydroNode::EndAtomic { metadata, .. }
5397 | HydroNode::Batch { metadata, .. }
5398 | HydroNode::Chain { metadata, .. }
5399 | HydroNode::MergeOrdered { metadata, .. }
5400 | HydroNode::ChainFirst { metadata, .. }
5401 | HydroNode::CrossProduct { metadata, .. }
5402 | HydroNode::CrossSingleton { metadata, .. }
5403 | HydroNode::Join { metadata, .. }
5404 | HydroNode::JoinHalf { metadata, .. }
5405 | HydroNode::Difference { metadata, .. }
5406 | HydroNode::AntiJoin { metadata, .. }
5407 | HydroNode::ResolveFutures { metadata, .. }
5408 | HydroNode::ResolveFuturesBlocking { metadata, .. }
5409 | HydroNode::ResolveFuturesOrdered { metadata, .. }
5410 | HydroNode::Map { metadata, .. }
5411 | HydroNode::FlatMap { metadata, .. }
5412 | HydroNode::FlatMapStreamBlocking { metadata, .. }
5413 | HydroNode::Filter { metadata, .. }
5414 | HydroNode::FilterMap { metadata, .. }
5415 | HydroNode::DeferTick { metadata, .. }
5416 | HydroNode::Enumerate { metadata, .. }
5417 | HydroNode::Inspect { metadata, .. }
5418 | HydroNode::Unique { metadata, .. }
5419 | HydroNode::Sort { metadata, .. }
5420 | HydroNode::Scan { metadata, .. }
5421 | HydroNode::ScanAsyncBlocking { metadata, .. }
5422 | HydroNode::Fold { metadata, .. }
5423 | HydroNode::FoldKeyed { metadata, .. }
5424 | HydroNode::Reduce { metadata, .. }
5425 | HydroNode::ReduceKeyed { metadata, .. }
5426 | HydroNode::ReduceKeyedWatermark { metadata, .. }
5427 | HydroNode::ExternalInput { metadata, .. }
5428 | HydroNode::Network { metadata, .. }
5429 | HydroNode::Counter { metadata, .. } => metadata,
5430 }
5431 }
5432
5433 pub fn op_metadata_mut(&mut self) -> &mut HydroIrOpMetadata {
5434 &mut self.metadata_mut().op
5435 }
5436
5437 pub fn metadata_mut(&mut self) -> &mut HydroIrMetadata {
5438 match self {
5439 HydroNode::Placeholder => {
5440 panic!()
5441 }
5442 HydroNode::VersionedNetworkFork { metadata, .. }
5443 | HydroNode::VersionedNetwork { metadata, .. } => metadata,
5444 HydroNode::Cast { metadata, .. }
5445 | HydroNode::ObserveNonDet { metadata, .. }
5446 | HydroNode::AssertIsConsistent { metadata, .. }
5447 | HydroNode::UnboundSingleton { metadata, .. }
5448 | HydroNode::Source { metadata, .. }
5449 | HydroNode::SingletonSource { metadata, .. }
5450 | HydroNode::CycleSource { metadata, .. }
5451 | HydroNode::Tee { metadata, .. }
5452 | HydroNode::Reference { metadata, .. }
5453 | HydroNode::Partition { metadata, .. }
5454 | HydroNode::YieldConcat { metadata, .. }
5455 | HydroNode::BeginAtomic { metadata, .. }
5456 | HydroNode::EndAtomic { metadata, .. }
5457 | HydroNode::Batch { metadata, .. }
5458 | HydroNode::Chain { metadata, .. }
5459 | HydroNode::MergeOrdered { metadata, .. }
5460 | HydroNode::ChainFirst { metadata, .. }
5461 | HydroNode::CrossProduct { metadata, .. }
5462 | HydroNode::CrossSingleton { metadata, .. }
5463 | HydroNode::Join { metadata, .. }
5464 | HydroNode::JoinHalf { metadata, .. }
5465 | HydroNode::Difference { metadata, .. }
5466 | HydroNode::AntiJoin { metadata, .. }
5467 | HydroNode::ResolveFutures { metadata, .. }
5468 | HydroNode::ResolveFuturesBlocking { metadata, .. }
5469 | HydroNode::ResolveFuturesOrdered { metadata, .. }
5470 | HydroNode::Map { metadata, .. }
5471 | HydroNode::FlatMap { metadata, .. }
5472 | HydroNode::FlatMapStreamBlocking { metadata, .. }
5473 | HydroNode::Filter { metadata, .. }
5474 | HydroNode::FilterMap { metadata, .. }
5475 | HydroNode::DeferTick { metadata, .. }
5476 | HydroNode::Enumerate { metadata, .. }
5477 | HydroNode::Inspect { metadata, .. }
5478 | HydroNode::Unique { metadata, .. }
5479 | HydroNode::Sort { metadata, .. }
5480 | HydroNode::Scan { metadata, .. }
5481 | HydroNode::ScanAsyncBlocking { metadata, .. }
5482 | HydroNode::Fold { metadata, .. }
5483 | HydroNode::FoldKeyed { metadata, .. }
5484 | HydroNode::Reduce { metadata, .. }
5485 | HydroNode::ReduceKeyed { metadata, .. }
5486 | HydroNode::ReduceKeyedWatermark { metadata, .. }
5487 | HydroNode::ExternalInput { metadata, .. }
5488 | HydroNode::Network { metadata, .. }
5489 | HydroNode::Counter { metadata, .. } => metadata,
5490 }
5491 }
5492
5493 pub fn input(&self) -> Vec<&HydroNode> {
5494 match self {
5495 HydroNode::Placeholder => {
5496 panic!()
5497 }
5498 HydroNode::Source { .. }
5499 | HydroNode::SingletonSource { .. }
5500 | HydroNode::ExternalInput { .. }
5501 | HydroNode::CycleSource { .. }
5502 | HydroNode::Tee { .. }
5503 | HydroNode::Reference { .. }
5504 | HydroNode::Partition { .. }
5505 | HydroNode::VersionedNetwork { .. } => {
5506 vec![]
5508 }
5509 HydroNode::Cast { inner, .. }
5510 | HydroNode::ObserveNonDet { inner, .. }
5511 | HydroNode::YieldConcat { inner, .. }
5512 | HydroNode::BeginAtomic { inner, .. }
5513 | HydroNode::EndAtomic { inner, .. }
5514 | HydroNode::Batch { inner, .. }
5515 | HydroNode::UnboundSingleton { inner, .. }
5516 | HydroNode::AssertIsConsistent { inner, .. } => {
5517 vec![inner]
5518 }
5519 HydroNode::Chain { first, second, .. } => {
5520 vec![first, second]
5521 }
5522 HydroNode::MergeOrdered { first, second, .. } => {
5523 vec![first, second]
5524 }
5525 HydroNode::ChainFirst { first, second, .. } => {
5526 vec![first, second]
5527 }
5528 HydroNode::CrossProduct { left, right, .. }
5529 | HydroNode::CrossSingleton { left, right, .. }
5530 | HydroNode::Join { left, right, .. }
5531 | HydroNode::JoinHalf { left, right, .. } => {
5532 vec![left, right]
5533 }
5534 HydroNode::Difference { pos, neg, .. } | HydroNode::AntiJoin { pos, neg, .. } => {
5535 vec![pos, neg]
5536 }
5537 HydroNode::Map { input, .. }
5538 | HydroNode::FlatMap { input, .. }
5539 | HydroNode::FlatMapStreamBlocking { input, .. }
5540 | HydroNode::Filter { input, .. }
5541 | HydroNode::FilterMap { input, .. }
5542 | HydroNode::Sort { input, .. }
5543 | HydroNode::DeferTick { input, .. }
5544 | HydroNode::Enumerate { input, .. }
5545 | HydroNode::Inspect { input, .. }
5546 | HydroNode::Unique { input, .. }
5547 | HydroNode::Network { input, .. }
5548 | HydroNode::Counter { input, .. }
5549 | HydroNode::ResolveFutures { input, .. }
5550 | HydroNode::ResolveFuturesBlocking { input, .. }
5551 | HydroNode::ResolveFuturesOrdered { input, .. }
5552 | HydroNode::Fold { input, .. }
5553 | HydroNode::FoldKeyed { input, .. }
5554 | HydroNode::Reduce { input, .. }
5555 | HydroNode::ReduceKeyed { input, .. }
5556 | HydroNode::Scan { input, .. }
5557 | HydroNode::ScanAsyncBlocking { input, .. } => {
5558 vec![input]
5559 }
5560 HydroNode::ReduceKeyedWatermark {
5561 input, watermark, ..
5562 } => {
5563 vec![input, watermark]
5564 }
5565 HydroNode::VersionedNetworkFork { senders, .. } => senders
5566 .iter()
5567 .map(|(_version, sender, _serialize)| sender.as_ref())
5568 .collect(),
5569 }
5570 }
5571
5572 pub fn input_metadata(&self) -> Vec<&HydroIrMetadata> {
5573 self.input()
5574 .iter()
5575 .map(|input_node| input_node.metadata())
5576 .collect()
5577 }
5578
5579 pub fn is_shared_with_others(&self) -> bool {
5583 match self {
5584 HydroNode::Tee { inner, .. } | HydroNode::Partition { inner, .. } => {
5585 Rc::strong_count(&inner.0) > 1
5586 }
5587 HydroNode::Reference { .. } => false,
5590 _ => false,
5591 }
5592 }
5593
5594 pub fn print_root(&self) -> String {
5595 match self {
5596 HydroNode::Placeholder => {
5597 panic!()
5598 }
5599 HydroNode::Cast { .. } => "Cast()".to_owned(),
5600 HydroNode::UnboundSingleton { .. } => "UnboundSingleton()".to_owned(),
5601 HydroNode::ObserveNonDet { .. } => "ObserveNonDet()".to_owned(),
5602 HydroNode::AssertIsConsistent { .. } => "AssertIsConsistent()".to_owned(),
5603 HydroNode::Source { source, .. } => format!("Source({:?})", source),
5604 HydroNode::SingletonSource {
5605 value,
5606 first_tick_only,
5607 ..
5608 } => format!(
5609 "SingletonSource({:?}, first_tick_only={})",
5610 value, first_tick_only
5611 ),
5612 HydroNode::CycleSource { cycle_id, .. } => format!("CycleSource({})", cycle_id),
5613 HydroNode::Tee { inner, .. } => {
5614 format!("Tee({})", inner.0.borrow().print_root())
5615 }
5616 HydroNode::Reference { inner, kind, .. } => {
5617 format!("Reference({:?}, {})", kind, inner.0.borrow().print_root())
5618 }
5619 HydroNode::Partition { f, is_true, .. } => {
5620 format!("Partition({:?}, is_true={})", f, is_true)
5621 }
5622 HydroNode::YieldConcat { .. } => "YieldConcat()".to_owned(),
5623 HydroNode::BeginAtomic { .. } => "BeginAtomic()".to_owned(),
5624 HydroNode::EndAtomic { .. } => "EndAtomic()".to_owned(),
5625 HydroNode::Batch { .. } => "Batch()".to_owned(),
5626 HydroNode::Chain { first, second, .. } => {
5627 format!("Chain({}, {})", first.print_root(), second.print_root())
5628 }
5629 HydroNode::MergeOrdered { first, second, .. } => {
5630 format!(
5631 "MergeOrdered({}, {})",
5632 first.print_root(),
5633 second.print_root()
5634 )
5635 }
5636 HydroNode::ChainFirst { first, second, .. } => {
5637 format!(
5638 "ChainFirst({}, {})",
5639 first.print_root(),
5640 second.print_root()
5641 )
5642 }
5643 HydroNode::CrossProduct { left, right, .. } => {
5644 format!(
5645 "CrossProduct({}, {})",
5646 left.print_root(),
5647 right.print_root()
5648 )
5649 }
5650 HydroNode::CrossSingleton { left, right, .. } => {
5651 format!(
5652 "CrossSingleton({}, {})",
5653 left.print_root(),
5654 right.print_root()
5655 )
5656 }
5657 HydroNode::Join { left, right, .. } => {
5658 format!("Join({}, {})", left.print_root(), right.print_root())
5659 }
5660 HydroNode::JoinHalf { left, right, .. } => {
5661 format!("JoinHalf({}, {})", left.print_root(), right.print_root())
5662 }
5663 HydroNode::Difference { pos, neg, .. } => {
5664 format!("Difference({}, {})", pos.print_root(), neg.print_root())
5665 }
5666 HydroNode::AntiJoin { pos, neg, .. } => {
5667 format!("AntiJoin({}, {})", pos.print_root(), neg.print_root())
5668 }
5669 HydroNode::ResolveFutures { .. } => "ResolveFutures()".to_owned(),
5670 HydroNode::ResolveFuturesBlocking { .. } => "ResolveFuturesBlocking()".to_owned(),
5671 HydroNode::ResolveFuturesOrdered { .. } => "ResolveFuturesOrdered()".to_owned(),
5672 HydroNode::Map { f, .. } => format!("Map({:?})", f),
5673 HydroNode::FlatMap { f, .. } => format!("FlatMap({:?})", f),
5674 HydroNode::FlatMapStreamBlocking { f, .. } => format!("FlatMapStreamBlocking({:?})", f),
5675 HydroNode::Filter { f, .. } => format!("Filter({:?})", f),
5676 HydroNode::FilterMap { f, .. } => format!("FilterMap({:?})", f),
5677 HydroNode::DeferTick { .. } => "DeferTick()".to_owned(),
5678 HydroNode::Enumerate { .. } => "Enumerate()".to_owned(),
5679 HydroNode::Inspect { f, .. } => format!("Inspect({:?})", f),
5680 HydroNode::Unique { .. } => "Unique()".to_owned(),
5681 HydroNode::Sort { .. } => "Sort()".to_owned(),
5682 HydroNode::Fold { init, acc, .. } => format!("Fold({:?}, {:?})", init, acc),
5683 HydroNode::Scan { init, acc, .. } => format!("Scan({:?}, {:?})", init, acc),
5684 HydroNode::ScanAsyncBlocking { init, acc, .. } => {
5685 format!("ScanAsyncBlocking({:?}, {:?})", init, acc)
5686 }
5687 HydroNode::FoldKeyed { init, acc, .. } => format!("FoldKeyed({:?}, {:?})", init, acc),
5688 HydroNode::Reduce { f, .. } => format!("Reduce({:?})", f),
5689 HydroNode::ReduceKeyed { f, .. } => format!("ReduceKeyed({:?})", f),
5690 HydroNode::ReduceKeyedWatermark { f, .. } => format!("ReduceKeyedWatermark({:?})", f),
5691 HydroNode::Network { .. } => "Network()".to_owned(),
5692 HydroNode::ExternalInput { .. } => "ExternalInput()".to_owned(),
5693 HydroNode::Counter { tag, duration, .. } => {
5694 format!("Counter({:?}, {:?})", tag, duration)
5695 }
5696 HydroNode::VersionedNetworkFork {
5697 channel_name,
5698 senders,
5699 ..
5700 } => {
5701 let versions: Vec<u32> = senders.iter().map(|(v, _, _)| *v).collect();
5702 format!(
5703 "VersionedNetworkFork({}, senders={:?})",
5704 channel_name, versions
5705 )
5706 }
5707 HydroNode::VersionedNetwork { version, .. } => {
5708 format!("VersionedNetwork(v{})", version)
5709 }
5710 }
5711 }
5712}
5713
5714#[cfg(feature = "build")]
5715fn instantiate_network<'a, D>(
5716 env: &mut D::InstantiateEnv,
5717 from_location: &LocationId,
5718 to_location: &LocationId,
5719 processes: &SparseSecondaryMap<LocationKey, D::Process>,
5720 clusters: &SparseSecondaryMap<LocationKey, D::Cluster>,
5721 name: Option<&str>,
5722 networking_info: &crate::networking::NetworkingInfo,
5723) -> (syn::Expr, syn::Expr, Box<dyn FnOnce()>)
5724where
5725 D: Deploy<'a>,
5726{
5727 let ((sink, source), connect_fn) = match (from_location, to_location) {
5728 (&LocationId::Process(from), &LocationId::Process(to)) => {
5729 let from_node = processes
5730 .get(from)
5731 .unwrap_or_else(|| {
5732 panic!("A process used in the graph was not instantiated: {}", from)
5733 })
5734 .clone();
5735 let to_node = processes
5736 .get(to)
5737 .unwrap_or_else(|| {
5738 panic!("A process used in the graph was not instantiated: {}", to)
5739 })
5740 .clone();
5741
5742 let sink_port = from_node.next_port();
5743 let source_port = to_node.next_port();
5744
5745 (
5746 D::o2o_sink_source(
5747 env,
5748 &from_node,
5749 &sink_port,
5750 &to_node,
5751 &source_port,
5752 name,
5753 networking_info,
5754 ),
5755 D::o2o_connect(&from_node, &sink_port, &to_node, &source_port),
5756 )
5757 }
5758 (&LocationId::Process(from), &LocationId::Cluster(to)) => {
5759 let from_node = processes
5760 .get(from)
5761 .unwrap_or_else(|| {
5762 panic!("A process used in the graph was not instantiated: {}", from)
5763 })
5764 .clone();
5765 let to_node = clusters
5766 .get(to)
5767 .unwrap_or_else(|| {
5768 panic!("A cluster used in the graph was not instantiated: {}", to)
5769 })
5770 .clone();
5771
5772 let sink_port = from_node.next_port();
5773 let source_port = to_node.next_port();
5774
5775 (
5776 D::o2m_sink_source(
5777 env,
5778 &from_node,
5779 &sink_port,
5780 &to_node,
5781 &source_port,
5782 name,
5783 networking_info,
5784 ),
5785 D::o2m_connect(&from_node, &sink_port, &to_node, &source_port),
5786 )
5787 }
5788 (&LocationId::Cluster(from), &LocationId::Process(to)) => {
5789 let from_node = clusters
5790 .get(from)
5791 .unwrap_or_else(|| {
5792 panic!("A cluster used in the graph was not instantiated: {}", from)
5793 })
5794 .clone();
5795 let to_node = processes
5796 .get(to)
5797 .unwrap_or_else(|| {
5798 panic!("A process used in the graph was not instantiated: {}", to)
5799 })
5800 .clone();
5801
5802 let sink_port = from_node.next_port();
5803 let source_port = to_node.next_port();
5804
5805 (
5806 D::m2o_sink_source(
5807 env,
5808 &from_node,
5809 &sink_port,
5810 &to_node,
5811 &source_port,
5812 name,
5813 networking_info,
5814 ),
5815 D::m2o_connect(&from_node, &sink_port, &to_node, &source_port),
5816 )
5817 }
5818 (&LocationId::Cluster(from), &LocationId::Cluster(to)) => {
5819 let from_node = clusters
5820 .get(from)
5821 .unwrap_or_else(|| {
5822 panic!("A cluster used in the graph was not instantiated: {}", from)
5823 })
5824 .clone();
5825 let to_node = clusters
5826 .get(to)
5827 .unwrap_or_else(|| {
5828 panic!("A cluster used in the graph was not instantiated: {}", to)
5829 })
5830 .clone();
5831
5832 let sink_port = from_node.next_port();
5833 let source_port = to_node.next_port();
5834
5835 (
5836 D::m2m_sink_source(
5837 env,
5838 &from_node,
5839 &sink_port,
5840 &to_node,
5841 &source_port,
5842 name,
5843 networking_info,
5844 ),
5845 D::m2m_connect(&from_node, &sink_port, &to_node, &source_port),
5846 )
5847 }
5848 (LocationId::Tick(_, _), _) => panic!(),
5849 (_, LocationId::Tick(_, _)) => panic!(),
5850 (LocationId::Atomic(_), _) => panic!(),
5851 (_, LocationId::Atomic(_)) => panic!(),
5852 };
5853 (sink, source, connect_fn)
5854}
5855
5856#[cfg(test)]
5857mod serde_test;
5858
5859#[cfg(test)]
5860mod test {
5861 use std::mem::size_of;
5862
5863 use stageleft::{QuotedWithContext, q};
5864
5865 use super::*;
5866
5867 #[test]
5868 #[cfg_attr(
5869 not(feature = "build"),
5870 ignore = "expects inclusion of feature-gated fields"
5871 )]
5872 fn hydro_node_size() {
5873 assert_eq!(size_of::<HydroNode>(), 264);
5874 }
5875
5876 #[test]
5877 #[cfg_attr(
5878 not(feature = "build"),
5879 ignore = "expects inclusion of feature-gated fields"
5880 )]
5881 fn hydro_root_size() {
5882 assert_eq!(size_of::<HydroRoot>(), 136);
5883 }
5884
5885 #[test]
5886 fn test_simplify_q_macro_basic() {
5887 let simple_expr: syn::Expr = syn::parse_str("x + y").unwrap();
5889 let result = simplify_q_macro(simple_expr.clone());
5890 assert_eq!(result, simple_expr);
5891 }
5892
5893 #[test]
5894 fn test_simplify_q_macro_actual_stageleft_call() {
5895 let stageleft_call = q!(|x: usize| x + 1).splice_fn1_ctx(&());
5897 let result = simplify_q_macro(stageleft_call);
5898 hydro_build_utils::assert_snapshot!(result.to_token_stream().to_string());
5901 }
5902
5903 #[test]
5904 fn test_closure_no_pipe_at_start() {
5905 let stageleft_call = q!({
5907 let foo = 123;
5908 move |b: usize| b + foo
5909 })
5910 .splice_fn1_ctx(&());
5911 let result = simplify_q_macro(stageleft_call);
5912 hydro_build_utils::assert_snapshot!(result.to_token_stream().to_string());
5913 }
5914}