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 add_dfir_at(
480 &mut self,
481 location: &LocationId,
482 dfir: dfir_lang::parse::DfirCode,
483 operator_tag: Option<&str>,
484 );
485
486 fn add_tick_source(
498 &mut self,
499 location: &LocationId,
500 source_rhs: TokenStream,
501 out_ident: &syn::Ident,
502 replay_each_tick: bool,
503 operator_tag: Option<&str>,
504 );
505
506 fn tick_state_lifetime(&self, _op_location: &LocationId) -> TokenStream {
512 quote!('tick)
513 }
514
515 fn cross_tick_state_lifetime(&self, _op_location: &LocationId) -> TokenStream {
522 quote!('static)
523 }
524
525 #[expect(clippy::too_many_arguments, reason = "TODO")]
526 fn batch(
527 &mut self,
528 in_ident: syn::Ident,
529 in_location: &LocationId,
530 in_kind: &CollectionKind,
531 out_ident: &syn::Ident,
532 out_location: &LocationId,
533 op_meta: &HydroIrOpMetadata,
534 fold_hooked_idents: &HashSet<String>,
535 );
536
537 fn yield_from_tick(
538 &mut self,
539 in_ident: syn::Ident,
540 in_location: &LocationId,
541 in_kind: &CollectionKind,
542 out_ident: &syn::Ident,
543 out_location: &LocationId,
544 );
545
546 fn unwindow_for_consume(
554 &mut self,
555 in_ident: syn::Ident,
556 in_location: &LocationId,
557 out_location: &LocationId,
558 ) -> syn::Ident;
559
560 fn begin_atomic(
561 &mut self,
562 in_ident: syn::Ident,
563 in_location: &LocationId,
564 in_kind: &CollectionKind,
565 out_ident: &syn::Ident,
566 out_location: &LocationId,
567 op_meta: &HydroIrOpMetadata,
568 );
569 fn end_atomic(
570 &mut self,
571 in_ident: syn::Ident,
572 in_location: &LocationId,
573 in_kind: &CollectionKind,
574 out_ident: &syn::Ident,
575 );
576
577 #[expect(clippy::too_many_arguments, reason = "TODO // internal")]
578 fn observe_nondet(
579 &mut self,
580 trusted: bool,
581 location: &LocationId,
582 in_ident: syn::Ident,
583 in_kind: &CollectionKind,
584 out_ident: &syn::Ident,
585 out_kind: &CollectionKind,
586 op_meta: &HydroIrOpMetadata,
587 );
588
589 #[expect(clippy::too_many_arguments, reason = "TODO")]
590 fn merge_ordered(
591 &mut self,
592 location: &LocationId,
593 first_ident: syn::Ident,
594 second_ident: syn::Ident,
595 out_ident: &syn::Ident,
596 in_kind: &CollectionKind,
597 op_meta: &HydroIrOpMetadata,
598 operator_tag: Option<&str>,
599 );
600
601 #[expect(clippy::too_many_arguments, reason = "TODO")]
602 fn create_network(
603 &mut self,
604 from: &LocationId,
605 to: &LocationId,
606 input_ident: syn::Ident,
607 out_ident: &syn::Ident,
608 serialize: Option<&DebugExpr>,
609 sink: syn::Expr,
610 source: syn::Expr,
611 deserialize: Option<&DebugExpr>,
612 external_element_type: Option<&syn::Type>,
613 tag_id: StmtId,
614 networking_info: &crate::networking::NetworkingInfo,
615 );
616
617 fn create_external_source(
618 &mut self,
619 on: &LocationId,
620 source_expr: syn::Expr,
621 out_ident: &syn::Ident,
622 deserialize: Option<&DebugExpr>,
623 tag_id: StmtId,
624 );
625
626 fn create_external_output(
627 &mut self,
628 on: &LocationId,
629 sink_expr: syn::Expr,
630 input_ident: &syn::Ident,
631 serialize: Option<&DebugExpr>,
632 tag_id: StmtId,
633 );
634
635 fn emit_fold_hook(
638 &mut self,
639 location: &LocationId,
640 in_ident: &syn::Ident,
641 in_kind: &CollectionKind,
642 op_meta: &HydroIrOpMetadata,
643 ) -> Option<syn::Ident>;
644
645 fn assert_is_consistent(
649 &mut self,
650 trusted: bool,
651 location: &LocationId,
652 in_ident: syn::Ident,
653 out_ident: &syn::Ident,
654 );
655
656 fn observe_for_mut(
660 &mut self,
661 location: &LocationId,
662 in_ident: syn::Ident,
663 in_kind: &CollectionKind,
664 out_ident: &syn::Ident,
665 op_meta: &HydroIrOpMetadata,
666 );
667
668 fn create_versioned_network_fork(
669 &mut self,
670 channel_id: u32,
671 dest: &LocationId,
672 senders: Vec<(LocationId, syn::Ident, Option<DebugExpr>)>,
673 external_element_type: Option<&syn::Type>,
674 tag_id: StmtId,
675 );
676
677 #[expect(clippy::too_many_arguments, reason = "networking codegen")]
678 fn create_versioned_network(
679 &mut self,
680 channel_id: u32,
681 source: &LocationId,
682 dest: &LocationId,
683 out_ident: &syn::Ident,
684 deserialize: Option<&DebugExpr>,
685 external_element_type: Option<&syn::Type>,
686 tag_id: StmtId,
687 );
688}
689
690#[cfg(feature = "build")]
697#[derive(Default)]
698pub struct ProdDfirBuilder {
699 pub graphs: SecondaryMap<LocationKey, FlatGraphBuilder>,
701 tick_loops: HashMap<LocationId, dfir_lang::graph::GraphLoopId>,
705 next_intermediate_id: usize,
707}
708
709#[cfg(feature = "build")]
710impl ProdDfirBuilder {
711 fn graph_mut(&mut self, location: &LocationId) -> &mut FlatGraphBuilder {
713 self.graphs
714 .entry(location.root().key())
715 .expect("location was removed")
716 .or_default()
717 }
718
719 fn tick_of(location: &LocationId) -> Option<&LocationId> {
726 match location {
727 LocationId::Tick {
728 tick: Some(_),
729 parent_location: _,
730 } => Some(location),
731 LocationId::Tick {
733 tick: None,
734 parent_location,
735 } => Self::tick_of(parent_location),
736 LocationId::Atomic(tick) => Self::tick_of(tick),
737 LocationId::Process(_) | LocationId::Cluster(_) => None,
738 }
739 }
740
741 fn loop_context(&mut self, location: &LocationId) -> Option<dfir_lang::graph::GraphLoopId> {
744 let tick_location = Self::tick_of(location)?.clone();
745 if let Some(&loop_id) = self.tick_loops.get(&tick_location) {
746 return Some(loop_id);
747 }
748 let loop_id = self.graph_mut(location).insert_loop(None);
749 self.tick_loops.insert(tick_location, loop_id);
750 Some(loop_id)
751 }
752
753 fn add_dfir_in(
756 &mut self,
757 location: &LocationId,
758 dfir: dfir_lang::parse::DfirCode,
759 operator_tag: Option<&str>,
760 ) {
761 let loop_context = self.loop_context(location);
762 self.graph_mut(location)
763 .add_dfir(dfir, loop_context, operator_tag);
764 }
765
766 fn intermediate_ident(&mut self) -> syn::Ident {
768 let id = self.next_intermediate_id;
769 self.next_intermediate_id += 1;
770 syn::Ident::new(&format!("__loop_boundary_{}", id), Span::call_site())
771 }
772}
773
774#[cfg(feature = "build")]
775impl DfirBuilder for ProdDfirBuilder {
776 fn singleton_intermediates(&self) -> bool {
777 false
778 }
779
780 fn add_dfir_at(
781 &mut self,
782 location: &LocationId,
783 dfir: dfir_lang::parse::DfirCode,
784 operator_tag: Option<&str>,
785 ) {
786 self.add_dfir_in(location, dfir, operator_tag);
789 }
790
791 fn add_tick_source(
794 &mut self,
795 location: &LocationId,
796 source_rhs: TokenStream,
797 out_ident: &syn::Ident,
798 replay_each_tick: bool,
799 operator_tag: Option<&str>,
800 ) {
801 let source_ident = self.intermediate_ident();
805 let source_stmt = if replay_each_tick {
806 parse_quote! {
807 #source_ident = #source_rhs -> persist::<'static>();
808 }
809 } else {
810 parse_quote! {
811 #source_ident = #source_rhs;
812 }
813 };
814 self.graph_mut(location)
815 .add_dfir(source_stmt, None, operator_tag);
816 self.add_dfir_in(
817 location,
818 parse_quote! {
819 #out_ident = #source_ident -> batch_eager();
820 },
821 operator_tag,
822 );
823 }
824
825 fn batch(
826 &mut self,
827 in_ident: syn::Ident,
828 in_location: &LocationId,
829 in_kind: &CollectionKind,
830 out_ident: &syn::Ident,
831 out_location: &LocationId,
832 _op_meta: &HydroIrOpMetadata,
833 _fold_hooked_idents: &HashSet<String>,
834 ) {
835 let is_singleton_like = matches!(
836 in_kind,
837 CollectionKind::Singleton { .. }
838 | CollectionKind::Optional { .. }
839 | CollectionKind::KeyedSingleton { .. }
840 );
841
842 match (Self::tick_of(in_location), Self::tick_of(out_location)) {
843 (Some(in_tick), Some(out_tick)) => {
844 assert_eq!(
847 in_tick, out_tick,
848 "batch between distinct ticks should have been unified"
849 );
850 self.add_dfir_in(
855 out_location,
856 parse_quote! {
857 #out_ident = #in_ident;
858 },
859 None,
860 );
861 }
862 (None, Some(_)) => {
863 let in_ident = if is_singleton_like && in_kind.is_bounded() {
867 let persisted_ident = self.intermediate_ident();
871 self.add_dfir_in(
872 in_location,
873 parse_quote! {
874 #persisted_ident = #in_ident -> persist::<'static>();
875 },
876 None,
877 );
878 persisted_ident
879 } else {
880 in_ident
881 };
882 self.add_dfir_in(
883 out_location,
884 parse_quote! {
885 #out_ident = #in_ident -> batch_eager();
886 },
887 None,
888 );
889 }
890 (Some(_), None) | (None, None) => {
891 unreachable!("batch must target a tick location");
892 }
893 }
894 }
895
896 fn yield_from_tick(
897 &mut self,
898 in_ident: syn::Ident,
899 in_location: &LocationId,
900 _in_kind: &CollectionKind,
901 out_ident: &syn::Ident,
902 out_location: &LocationId,
903 ) {
904 match (Self::tick_of(in_location), Self::tick_of(out_location)) {
914 (Some(in_tick), Some(out_tick)) => {
915 assert_eq!(
916 in_tick, out_tick,
917 "atomic yield to a different tick should have been unified"
918 );
919 self.add_dfir_in(
920 out_location,
921 parse_quote! {
922 #out_ident = #in_ident;
923 },
924 None,
925 );
926 }
927 _ => {
928 self.graph_mut(in_location).add_dfir(
931 parse_quote! {
932 #out_ident = #in_ident -> all_iterations();
933 },
934 None,
935 None,
936 );
937 }
938 }
939 }
940
941 fn unwindow_for_consume(
942 &mut self,
943 in_ident: syn::Ident,
944 in_location: &LocationId,
945 out_location: &LocationId,
946 ) -> syn::Ident {
947 let in_loop = self.loop_context(in_location);
952 let out_loop = self.loop_context(out_location);
953 if in_loop.is_some() && in_loop != out_loop {
954 let out_ident = self.intermediate_ident();
955 self.graph_mut(in_location).add_dfir(
956 parse_quote! {
957 #out_ident = #in_ident -> all_iterations();
958 },
959 None,
960 None,
961 );
962 out_ident
963 } else {
964 in_ident
965 }
966 }
967
968 fn begin_atomic(
969 &mut self,
970 in_ident: syn::Ident,
971 in_location: &LocationId,
972 _in_kind: &CollectionKind,
973 out_ident: &syn::Ident,
974 out_location: &LocationId,
975 _op_meta: &HydroIrOpMetadata,
976 ) {
977 match (Self::tick_of(in_location), Self::tick_of(out_location)) {
980 (None, Some(_)) => {
981 self.add_dfir_in(
982 out_location,
983 parse_quote! {
984 #out_ident = #in_ident -> batch_eager();
985 },
986 None,
987 );
988 }
989 _ => {
990 self.add_dfir_in(
991 out_location,
992 parse_quote! {
993 #out_ident = #in_ident;
994 },
995 None,
996 );
997 }
998 }
999 }
1000
1001 fn end_atomic(
1002 &mut self,
1003 in_ident: syn::Ident,
1004 in_location: &LocationId,
1005 _in_kind: &CollectionKind,
1006 out_ident: &syn::Ident,
1007 ) {
1008 self.graph_mut(in_location).add_dfir(
1010 parse_quote! {
1011 #out_ident = #in_ident -> all_iterations();
1012 },
1013 None,
1014 None,
1015 );
1016 }
1017
1018 fn observe_nondet(
1019 &mut self,
1020 _trusted: bool,
1021 location: &LocationId,
1022 in_ident: syn::Ident,
1023 _in_kind: &CollectionKind,
1024 out_ident: &syn::Ident,
1025 _out_kind: &CollectionKind,
1026 _op_meta: &HydroIrOpMetadata,
1027 ) {
1028 let builder = self.graph_mut(location);
1029 builder.add_dfir(
1030 parse_quote! {
1031 #out_ident = #in_ident;
1032 },
1033 None,
1034 None,
1035 );
1036 }
1037
1038 fn merge_ordered(
1039 &mut self,
1040 location: &LocationId,
1041 first_ident: syn::Ident,
1042 second_ident: syn::Ident,
1043 out_ident: &syn::Ident,
1044 _in_kind: &CollectionKind,
1045 _op_meta: &HydroIrOpMetadata,
1046 operator_tag: Option<&str>,
1047 ) {
1048 let builder = self.graph_mut(location);
1049 builder.add_dfir(
1050 parse_quote! {
1051 #out_ident = union();
1052 #first_ident -> [0]#out_ident;
1053 #second_ident -> [1]#out_ident;
1054 },
1055 None,
1056 operator_tag,
1057 );
1058 }
1059
1060 fn create_network(
1061 &mut self,
1062 from: &LocationId,
1063 to: &LocationId,
1064 input_ident: syn::Ident,
1065 out_ident: &syn::Ident,
1066 serialize: Option<&DebugExpr>,
1067 sink: syn::Expr,
1068 source: syn::Expr,
1069 deserialize: Option<&DebugExpr>,
1070 _external_element_type: Option<&syn::Type>,
1071 tag_id: StmtId,
1072 _networking_info: &crate::networking::NetworkingInfo,
1073 ) {
1074 let sender_builder = self.graph_mut(from);
1075 if let Some(serialize_pipeline) = serialize {
1076 sender_builder.add_dfir(
1077 parse_quote! {
1078 #input_ident -> map(#serialize_pipeline) -> dest_sink(#sink);
1079 },
1080 None,
1081 Some(&format!("send{}", tag_id)),
1083 );
1084 } else {
1085 sender_builder.add_dfir(
1086 parse_quote! {
1087 #input_ident -> dest_sink(#sink);
1088 },
1089 None,
1090 Some(&format!("send{}", tag_id)),
1091 );
1092 }
1093
1094 let receiver_builder = self.graph_mut(to);
1095 if let Some(deserialize_pipeline) = deserialize {
1096 receiver_builder.add_dfir(
1097 parse_quote! {
1098 #out_ident = source_stream(#source) -> map(#deserialize_pipeline);
1099 },
1100 None,
1101 Some(&format!("recv{}", tag_id)),
1102 );
1103 } else {
1104 receiver_builder.add_dfir(
1105 parse_quote! {
1106 #out_ident = source_stream(#source);
1107 },
1108 None,
1109 Some(&format!("recv{}", tag_id)),
1110 );
1111 }
1112 }
1113
1114 fn create_external_source(
1115 &mut self,
1116 on: &LocationId,
1117 source_expr: syn::Expr,
1118 out_ident: &syn::Ident,
1119 deserialize: Option<&DebugExpr>,
1120 tag_id: StmtId,
1121 ) {
1122 let receiver_builder = self.graph_mut(on);
1123 if let Some(deserialize_pipeline) = deserialize {
1124 receiver_builder.add_dfir(
1125 parse_quote! {
1126 #out_ident = source_stream(#source_expr) -> map(#deserialize_pipeline);
1127 },
1128 None,
1129 Some(&format!("recv{}", tag_id)),
1130 );
1131 } else {
1132 receiver_builder.add_dfir(
1133 parse_quote! {
1134 #out_ident = source_stream(#source_expr);
1135 },
1136 None,
1137 Some(&format!("recv{}", tag_id)),
1138 );
1139 }
1140 }
1141
1142 fn create_external_output(
1143 &mut self,
1144 on: &LocationId,
1145 sink_expr: syn::Expr,
1146 input_ident: &syn::Ident,
1147 serialize: Option<&DebugExpr>,
1148 tag_id: StmtId,
1149 ) {
1150 let sender_builder = self.graph_mut(on);
1151 if let Some(serialize_fn) = serialize {
1152 sender_builder.add_dfir(
1153 parse_quote! {
1154 #input_ident -> map(#serialize_fn) -> dest_sink(#sink_expr);
1155 },
1156 None,
1157 Some(&format!("send{}", tag_id)),
1159 );
1160 } else {
1161 sender_builder.add_dfir(
1162 parse_quote! {
1163 #input_ident -> dest_sink(#sink_expr);
1164 },
1165 None,
1166 Some(&format!("send{}", tag_id)),
1167 );
1168 }
1169 }
1170
1171 fn emit_fold_hook(
1172 &mut self,
1173 _location: &LocationId,
1174 _in_ident: &syn::Ident,
1175 _in_kind: &CollectionKind,
1176 _op_meta: &HydroIrOpMetadata,
1177 ) -> Option<syn::Ident> {
1178 None
1179 }
1180
1181 fn assert_is_consistent(
1182 &mut self,
1183 _trusted: bool,
1184 location: &LocationId,
1185 in_ident: syn::Ident,
1186 out_ident: &syn::Ident,
1187 ) {
1188 let builder = self.graph_mut(location);
1189 builder.add_dfir(
1190 parse_quote! {
1191 #out_ident = #in_ident;
1192 },
1193 None,
1194 None,
1195 );
1196 }
1197
1198 fn observe_for_mut(
1199 &mut self,
1200 location: &LocationId,
1201 in_ident: syn::Ident,
1202 _in_kind: &CollectionKind,
1203 out_ident: &syn::Ident,
1204 _op_meta: &HydroIrOpMetadata,
1205 ) {
1206 let builder = self.graph_mut(location);
1207 builder.add_dfir(
1208 parse_quote! {
1209 #out_ident = #in_ident;
1210 },
1211 None,
1212 None,
1213 );
1214 }
1215
1216 fn create_versioned_network_fork(
1217 &mut self,
1218 _channel_id: u32,
1219 _dest: &LocationId,
1220 _senders: Vec<(LocationId, syn::Ident, Option<DebugExpr>)>,
1221 _external_element_type: Option<&syn::Type>,
1222 _tag_id: StmtId,
1223 ) {
1224 unreachable!(
1225 "HydroNode::VersionedNetworkFork is only produced by the multi-version simulator merge \
1226 pass and cannot be emitted by the non-simulation builder"
1227 );
1228 }
1229
1230 fn create_versioned_network(
1231 &mut self,
1232 _channel_id: u32,
1233 _source: &LocationId,
1234 _dest: &LocationId,
1235 _out_ident: &syn::Ident,
1236 _deserialize: Option<&DebugExpr>,
1237 _external_element_type: Option<&syn::Type>,
1238 _tag_id: StmtId,
1239 ) {
1240 unreachable!(
1241 "HydroNode::VersionedNetwork is only produced by the multi-version simulator merge \
1242 pass and cannot be emitted by the non-simulation builder"
1243 );
1244 }
1245}
1246
1247#[cfg(feature = "build")]
1248pub enum BuildersOrCallback<'a, L, N>
1249where
1250 L: FnMut(&mut HydroRoot, &mut crate::Counter<StmtId>),
1251 N: FnMut(&mut HydroNode, &mut crate::Counter<StmtId>),
1252{
1253 Builders(&'a mut dyn DfirBuilder),
1254 Callback(L, N),
1255}
1256
1257#[derive(Debug, Hash, serde::Serialize)]
1261pub enum HydroRoot {
1262 ForEach {
1263 f: ClosureExpr,
1264 input: Box<HydroNode>,
1265 op_metadata: HydroIrOpMetadata,
1266 },
1267 SendExternal {
1268 to_external_key: LocationKey,
1269 to_port_id: ExternalPortId,
1270 to_many: bool,
1271 unpaired: bool,
1272 serialize_fn: Option<DebugExpr>,
1273 instantiate_fn: DebugInstantiate,
1274 input: Box<HydroNode>,
1275 op_metadata: HydroIrOpMetadata,
1276 },
1277 DestSink {
1278 sink: DebugExpr,
1279 input: Box<HydroNode>,
1280 op_metadata: HydroIrOpMetadata,
1281 },
1282 CycleSink {
1283 cycle_id: CycleId,
1284 input: Box<HydroNode>,
1285 op_metadata: HydroIrOpMetadata,
1286 },
1287 EmbeddedOutput {
1288 #[serde(serialize_with = "serialize_ident")]
1289 ident: syn::Ident,
1290 input: Box<HydroNode>,
1291 op_metadata: HydroIrOpMetadata,
1292 },
1293 Null {
1294 input: Box<HydroNode>,
1295 op_metadata: HydroIrOpMetadata,
1296 },
1297}
1298
1299impl HydroRoot {
1300 #[cfg(feature = "build")]
1301 #[expect(clippy::too_many_arguments, reason = "TODO(internal)")]
1302 pub fn compile_network<'a, D>(
1303 &mut self,
1304 extra_stmts: &mut SparseSecondaryMap<LocationKey, Vec<syn::Stmt>>,
1305 seen_tees: &mut SeenSharedNodes,
1306 seen_cluster_members: &mut HashSet<(LocationId, LocationKey)>,
1307 processes: &SparseSecondaryMap<LocationKey, D::Process>,
1308 clusters: &SparseSecondaryMap<LocationKey, D::Cluster>,
1309 externals: &SparseSecondaryMap<LocationKey, D::External>,
1310 env: &mut D::InstantiateEnv,
1311 ) where
1312 D: Deploy<'a>,
1313 {
1314 let refcell_extra_stmts = RefCell::new(extra_stmts);
1315 let refcell_env = RefCell::new(env);
1316 let refcell_seen_cluster_members = RefCell::new(seen_cluster_members);
1317 self.transform_bottom_up(
1318 &mut |l| {
1319 if let HydroRoot::SendExternal {
1320 #[cfg(feature = "tokio")]
1321 input,
1322 #[cfg(feature = "tokio")]
1323 to_external_key,
1324 #[cfg(feature = "tokio")]
1325 to_port_id,
1326 #[cfg(feature = "tokio")]
1327 to_many,
1328 #[cfg(feature = "tokio")]
1329 unpaired,
1330 #[cfg(feature = "tokio")]
1331 instantiate_fn,
1332 ..
1333 } = l
1334 {
1335 #[cfg(feature = "tokio")]
1336 let ((sink_expr, source_expr), connect_fn) = match instantiate_fn {
1337 DebugInstantiate::Building => {
1338 let to_node = externals
1339 .get(*to_external_key)
1340 .unwrap_or_else(|| {
1341 panic!("A external used in the graph was not instantiated: {}", to_external_key)
1342 })
1343 .clone();
1344
1345 match input.metadata().location_id.root() {
1346 &LocationId::Process(process_key) => {
1347 if *to_many {
1348 (
1349 (
1350 D::e2o_many_sink(format!("{}_{}", *to_external_key, *to_port_id)),
1351 parse_quote!(DUMMY),
1352 ),
1353 Box::new(|| {}) as Box<dyn FnOnce()>,
1354 )
1355 } else {
1356 let from_node = processes
1357 .get(process_key)
1358 .unwrap_or_else(|| {
1359 panic!("A process used in the graph was not instantiated: {}", process_key)
1360 })
1361 .clone();
1362
1363 let sink_port = from_node.next_port();
1364 let source_port = to_node.next_port();
1365
1366 if *unpaired {
1367 use stageleft::quote_type;
1368 use tokio_util::codec::LengthDelimitedCodec;
1369
1370 to_node.register(*to_port_id, source_port.clone());
1371
1372 let _ = D::e2o_source(
1373 refcell_extra_stmts.borrow_mut().entry(process_key).expect("location was removed").or_default(),
1374 &to_node, &source_port,
1375 &from_node, &sink_port,
1376 "e_type::<LengthDelimitedCodec>(),
1377 format!("{}_{}", *to_external_key, *to_port_id)
1378 );
1379 }
1380
1381 (
1382 (
1383 D::o2e_sink(
1384 &from_node,
1385 &sink_port,
1386 &to_node,
1387 &source_port,
1388 format!("{}_{}", *to_external_key, *to_port_id)
1389 ),
1390 parse_quote!(DUMMY),
1391 ),
1392 if *unpaired {
1393 D::e2o_connect(
1394 &to_node,
1395 &source_port,
1396 &from_node,
1397 &sink_port,
1398 *to_many,
1399 NetworkHint::Auto,
1400 )
1401 } else {
1402 Box::new(|| {}) as Box<dyn FnOnce()>
1403 },
1404 )
1405 }
1406 }
1407 LocationId::Cluster(cluster_key) => {
1408 let from_node = clusters
1409 .get(*cluster_key)
1410 .unwrap_or_else(|| {
1411 panic!("A cluster used in the graph was not instantiated: {}", cluster_key)
1412 })
1413 .clone();
1414
1415 let sink_port = from_node.next_port();
1416 let source_port = to_node.next_port();
1417
1418 if *unpaired {
1419 to_node.register(*to_port_id, source_port.clone());
1420 }
1421
1422 (
1423 (
1424 D::m2e_sink(
1425 &from_node,
1426 &sink_port,
1427 &to_node,
1428 &source_port,
1429 format!("{}_{}", *to_external_key, *to_port_id)
1430 ),
1431 parse_quote!(DUMMY),
1432 ),
1433 Box::new(|| {}) as Box<dyn FnOnce()>,
1434 )
1435 }
1436 _ => panic!()
1437 }
1438 },
1439
1440 DebugInstantiate::Finalized(_) => panic!("network already finalized"),
1441 };
1442
1443 #[cfg(not(feature = "tokio"))]
1444 {
1445 panic!("Cannot instantiate external inputs without tokio");
1446 };
1447
1448 #[cfg(feature = "tokio")]
1449 {
1450 *instantiate_fn = DebugInstantiateFinalized {
1451 sink: sink_expr,
1452 source: source_expr,
1453 connect_fn: Some(connect_fn),
1454 }
1455 .into();
1456 };
1457 } else if let HydroRoot::EmbeddedOutput { ident, input, .. } = l {
1458 let element_type = match &input.metadata().collection_kind {
1459 CollectionKind::Stream { element_type, .. } => element_type.0.as_ref().clone(),
1460 _ => panic!("Embedded output must have Stream collection kind"),
1461 };
1462 let location_key = match input.metadata().location_id.root() {
1463 LocationId::Process(key) | LocationId::Cluster(key) => *key,
1464 _ => panic!("Embedded output must be on a process or cluster"),
1465 };
1466 D::register_embedded_output(
1467 &mut refcell_env.borrow_mut(),
1468 location_key,
1469 ident,
1470 &element_type,
1471 );
1472 }
1473 },
1474 &mut |n| {
1475 if let HydroNode::Network {
1476 name,
1477 networking_info,
1478 input,
1479 instantiate_fn,
1480 serialize,
1481 deserialize,
1482 metadata,
1483 ..
1484 } = n
1485 {
1486 let external_types = match (
1487 serialize.external_element_type(),
1488 deserialize.external_element_type(),
1489 ) {
1490 (Some(input_type), Some(output_type)) => Some((input_type, output_type)),
1491 _ => None,
1492 };
1493 let (sink_expr, source_expr, connect_fn) = match instantiate_fn {
1494 DebugInstantiate::Building => instantiate_network::<D>(
1495 &mut refcell_env.borrow_mut(),
1496 input.metadata().location_id.root(),
1497 metadata.location_id.root(),
1498 processes,
1499 clusters,
1500 name.as_deref(),
1501 networking_info,
1502 external_types,
1503 ),
1504
1505 DebugInstantiate::Finalized(_) => panic!("network already finalized"),
1506 };
1507
1508 *instantiate_fn = DebugInstantiateFinalized {
1509 sink: sink_expr,
1510 source: source_expr,
1511 connect_fn: Some(connect_fn),
1512 }
1513 .into();
1514 } else if let HydroNode::ExternalInput {
1515 from_external_key,
1516 from_port_id,
1517 from_many,
1518 codec_type,
1519 port_hint,
1520 instantiate_fn,
1521 metadata,
1522 ..
1523 } = n
1524 {
1525 let ((sink_expr, source_expr), connect_fn) = match instantiate_fn {
1526 DebugInstantiate::Building => {
1527 let from_node = externals
1528 .get(*from_external_key)
1529 .unwrap_or_else(|| {
1530 panic!(
1531 "A external used in the graph was not instantiated: {}",
1532 from_external_key,
1533 )
1534 })
1535 .clone();
1536
1537 match metadata.location_id.root() {
1538 &LocationId::Process(process_key) => {
1539 let to_node = processes
1540 .get(process_key)
1541 .unwrap_or_else(|| {
1542 panic!("A process used in the graph was not instantiated: {}", process_key)
1543 })
1544 .clone();
1545
1546 let sink_port = from_node.next_port();
1547 let source_port = to_node.next_port();
1548
1549 from_node.register(*from_port_id, sink_port.clone());
1550
1551 (
1552 (
1553 parse_quote!(DUMMY),
1554 if *from_many {
1555 D::e2o_many_source(
1556 refcell_extra_stmts.borrow_mut().entry(process_key).expect("location was removed").or_default(),
1557 &to_node, &source_port,
1558 codec_type.0.as_ref(),
1559 format!("{}_{}", *from_external_key, *from_port_id)
1560 )
1561 } else {
1562 D::e2o_source(
1563 refcell_extra_stmts.borrow_mut().entry(process_key).expect("location was removed").or_default(),
1564 &from_node, &sink_port,
1565 &to_node, &source_port,
1566 codec_type.0.as_ref(),
1567 format!("{}_{}", *from_external_key, *from_port_id)
1568 )
1569 },
1570 ),
1571 D::e2o_connect(&from_node, &sink_port, &to_node, &source_port, *from_many, *port_hint),
1572 )
1573 }
1574 LocationId::Cluster(cluster_key) => {
1575 let to_node = clusters
1576 .get(*cluster_key)
1577 .unwrap_or_else(|| {
1578 panic!("A cluster used in the graph was not instantiated: {}", cluster_key)
1579 })
1580 .clone();
1581
1582 let sink_port = from_node.next_port();
1583 let source_port = to_node.next_port();
1584
1585 from_node.register(*from_port_id, sink_port.clone());
1586
1587 (
1588 (
1589 parse_quote!(DUMMY),
1590 D::e2m_source(
1591 refcell_extra_stmts.borrow_mut().entry(*cluster_key).expect("location was removed").or_default(),
1592 &from_node, &sink_port,
1593 &to_node, &source_port,
1594 codec_type.0.as_ref(),
1595 format!("{}_{}", *from_external_key, *from_port_id)
1596 ),
1597 ),
1598 D::e2m_connect(&from_node, &sink_port, &to_node, &source_port, *port_hint),
1599 )
1600 }
1601 _ => panic!()
1602 }
1603 },
1604
1605 DebugInstantiate::Finalized(_) => panic!("network already finalized"),
1606 };
1607
1608 *instantiate_fn = DebugInstantiateFinalized {
1609 sink: sink_expr,
1610 source: source_expr,
1611 connect_fn: Some(connect_fn),
1612 }
1613 .into();
1614 } else if let HydroNode::Source { source: HydroSource::Embedded(ident), metadata } = n {
1615 let element_type = match &metadata.collection_kind {
1616 CollectionKind::Stream { element_type, .. } => element_type.0.as_ref().clone(),
1617 _ => panic!("Embedded source must have Stream collection kind"),
1618 };
1619 let location_key = match metadata.location_id.root() {
1620 LocationId::Process(key) | LocationId::Cluster(key) => *key,
1621 _ => panic!("Embedded source must be on a process or cluster"),
1622 };
1623 D::register_embedded_stream_input(
1624 &mut refcell_env.borrow_mut(),
1625 location_key,
1626 ident,
1627 &element_type,
1628 );
1629 } else if let HydroNode::Source { source: HydroSource::EmbeddedSingleton(ident), metadata } = n {
1630 let element_type = match &metadata.collection_kind {
1631 CollectionKind::Singleton { element_type, .. } => element_type.0.as_ref().clone(),
1632 _ => panic!("EmbeddedSingleton source must have Singleton collection kind"),
1633 };
1634 let location_key = match metadata.location_id.root() {
1635 LocationId::Process(key) | LocationId::Cluster(key) => *key,
1636 _ => panic!("EmbeddedSingleton source must be on a process or cluster"),
1637 };
1638 D::register_embedded_singleton_input(
1639 &mut refcell_env.borrow_mut(),
1640 location_key,
1641 ident,
1642 &element_type,
1643 );
1644 } else if let HydroNode::Source { source: HydroSource::ClusterMembers(location_id, state), metadata } = n {
1645 match state {
1646 ClusterMembersState::Uninit => {
1647 let at_location = metadata.location_id.root().clone();
1648 let key = (at_location.clone(), location_id.key());
1649 if refcell_seen_cluster_members.borrow_mut().insert(key) {
1650 let expr = stageleft::QuotedWithContext::splice_untyped_ctx(
1652 D::cluster_membership_stream(&mut refcell_env.borrow_mut(), &at_location, location_id),
1653 &(),
1654 );
1655 *state = ClusterMembersState::Stream(expr.into());
1656 } else {
1657 *state = ClusterMembersState::Tee(at_location, location_id.clone());
1659 }
1660 }
1661 ClusterMembersState::Stream(_) | ClusterMembersState::Tee(..) => {
1662 panic!("cluster members already finalized");
1663 }
1664 }
1665 }
1666 },
1667 seen_tees,
1668 false,
1669 );
1670 }
1671
1672 pub fn connect_network(&mut self, seen_tees: &mut SeenSharedNodes) {
1673 self.transform_bottom_up(
1674 &mut |l| {
1675 if let HydroRoot::SendExternal { instantiate_fn, .. } = l {
1676 match instantiate_fn {
1677 DebugInstantiate::Building => panic!("network not built"),
1678
1679 DebugInstantiate::Finalized(finalized) => {
1680 (finalized.connect_fn.take().unwrap())();
1681 }
1682 }
1683 }
1684 },
1685 &mut |n| {
1686 if let HydroNode::Network { instantiate_fn, .. }
1687 | HydroNode::ExternalInput { instantiate_fn, .. } = n
1688 {
1689 match instantiate_fn {
1690 DebugInstantiate::Building => panic!("network not built"),
1691
1692 DebugInstantiate::Finalized(finalized) => {
1693 (finalized.connect_fn.take().unwrap())();
1694 }
1695 }
1696 }
1697 },
1698 seen_tees,
1699 false,
1700 );
1701 }
1702
1703 pub fn transform_bottom_up(
1704 &mut self,
1705 transform_root: &mut impl FnMut(&mut HydroRoot),
1706 transform_node: &mut impl FnMut(&mut HydroNode),
1707 seen_tees: &mut SeenSharedNodes,
1708 check_well_formed: bool,
1709 ) {
1710 self.transform_children(
1711 |n, s| n.transform_bottom_up(transform_node, s, check_well_formed),
1712 seen_tees,
1713 );
1714
1715 transform_root(self);
1716 }
1717
1718 pub fn transform_children(
1719 &mut self,
1720 mut transform: impl FnMut(&mut HydroNode, &mut SeenSharedNodes),
1721 seen_tees: &mut SeenSharedNodes,
1722 ) {
1723 match self {
1724 HydroRoot::ForEach { f, input, .. } => {
1725 f.transform_children(&mut transform, seen_tees);
1726 transform(input, seen_tees);
1727 }
1728 HydroRoot::SendExternal { input, .. }
1729 | HydroRoot::DestSink { input, .. }
1730 | HydroRoot::CycleSink { input, .. }
1731 | HydroRoot::EmbeddedOutput { input, .. }
1732 | HydroRoot::Null { input, .. } => {
1733 transform(input, seen_tees);
1734 }
1735 }
1736 }
1737
1738 pub fn deep_clone(&self, seen_tees: &mut SeenSharedNodes) -> HydroRoot {
1739 match self {
1740 HydroRoot::ForEach {
1741 f,
1742 input,
1743 op_metadata,
1744 } => HydroRoot::ForEach {
1745 f: f.deep_clone(seen_tees),
1746 input: Box::new(input.deep_clone(seen_tees)),
1747 op_metadata: op_metadata.clone(),
1748 },
1749 HydroRoot::SendExternal {
1750 to_external_key,
1751 to_port_id,
1752 to_many,
1753 unpaired,
1754 serialize_fn,
1755 instantiate_fn,
1756 input,
1757 op_metadata,
1758 } => HydroRoot::SendExternal {
1759 to_external_key: *to_external_key,
1760 to_port_id: *to_port_id,
1761 to_many: *to_many,
1762 unpaired: *unpaired,
1763 serialize_fn: serialize_fn.clone(),
1764 instantiate_fn: instantiate_fn.clone(),
1765 input: Box::new(input.deep_clone(seen_tees)),
1766 op_metadata: op_metadata.clone(),
1767 },
1768 HydroRoot::DestSink {
1769 sink,
1770 input,
1771 op_metadata,
1772 } => HydroRoot::DestSink {
1773 sink: sink.clone(),
1774 input: Box::new(input.deep_clone(seen_tees)),
1775 op_metadata: op_metadata.clone(),
1776 },
1777 HydroRoot::CycleSink {
1778 cycle_id,
1779 input,
1780 op_metadata,
1781 } => HydroRoot::CycleSink {
1782 cycle_id: *cycle_id,
1783 input: Box::new(input.deep_clone(seen_tees)),
1784 op_metadata: op_metadata.clone(),
1785 },
1786 HydroRoot::EmbeddedOutput {
1787 ident,
1788 input,
1789 op_metadata,
1790 } => HydroRoot::EmbeddedOutput {
1791 ident: ident.clone(),
1792 input: Box::new(input.deep_clone(seen_tees)),
1793 op_metadata: op_metadata.clone(),
1794 },
1795 HydroRoot::Null { input, op_metadata } => HydroRoot::Null {
1796 input: Box::new(input.deep_clone(seen_tees)),
1797 op_metadata: op_metadata.clone(),
1798 },
1799 }
1800 }
1801
1802 #[cfg(feature = "build")]
1803 pub fn emit(
1804 &mut self,
1805 graph_builders: &mut dyn DfirBuilder,
1806 seen_tees: &mut SeenSharedNodes,
1807 built_tees: &mut HashMap<*const RefCell<HydroNode>, Vec<syn::Ident>>,
1808 next_stmt_id: &mut crate::Counter<StmtId>,
1809 fold_hooked_idents: &mut HashSet<String>,
1810 ) {
1811 self.emit_core(
1812 &mut BuildersOrCallback::<
1813 fn(&mut HydroRoot, &mut crate::Counter<StmtId>),
1814 fn(&mut HydroNode, &mut crate::Counter<StmtId>),
1815 >::Builders(graph_builders),
1816 seen_tees,
1817 built_tees,
1818 next_stmt_id,
1819 fold_hooked_idents,
1820 );
1821 }
1822
1823 #[cfg(feature = "build")]
1824 pub fn emit_core(
1825 &mut self,
1826 builders_or_callback: &mut BuildersOrCallback<
1827 '_,
1828 impl FnMut(&mut HydroRoot, &mut crate::Counter<StmtId>),
1829 impl FnMut(&mut HydroNode, &mut crate::Counter<StmtId>),
1830 >,
1831 seen_tees: &mut SeenSharedNodes,
1832 built_tees: &mut HashMap<*const RefCell<HydroNode>, Vec<syn::Ident>>,
1833 next_stmt_id: &mut crate::Counter<StmtId>,
1834 fold_hooked_idents: &mut HashSet<String>,
1835 ) {
1836 match self {
1837 HydroRoot::ForEach { f, input, .. } => {
1838 let input_ident = input.emit_core(
1839 builders_or_callback,
1840 seen_tees,
1841 built_tees,
1842 next_stmt_id,
1843 fold_hooked_idents,
1844 );
1845
1846 let input_ident = if !input.metadata().collection_kind.is_strict() {
1850 let observe_stmt_id = next_stmt_id.get_and_increment();
1851 let observe_ident =
1852 syn::Ident::new(&format!("stream_{}", observe_stmt_id), Span::call_site());
1853 if let BuildersOrCallback::Builders(graph_builders) = builders_or_callback {
1854 graph_builders.observe_for_mut(
1855 &input.metadata().location_id,
1856 input_ident,
1857 &input.metadata().collection_kind,
1858 &observe_ident,
1859 &input.metadata().op,
1860 );
1861 }
1862 observe_ident
1863 } else {
1864 input_ident
1865 };
1866
1867 let mut ref_idents = Vec::new();
1874 for (ref_node, _is_mut) in f.singleton_refs.iter_mut() {
1875 assert!(
1876 matches!(ref_node, HydroNode::Reference { .. }),
1877 "singleton_refs should only contain HydroNode::Reference"
1878 );
1879 ref_idents.push(ref_node.emit_core(
1880 builders_or_callback,
1881 seen_tees,
1882 built_tees,
1883 next_stmt_id,
1884 fold_hooked_idents,
1885 ));
1886 }
1887
1888 let stmt_id = next_stmt_id.get_and_increment();
1893
1894 match builders_or_callback {
1895 BuildersOrCallback::Builders(graph_builders) => {
1896 let mut ident_stack: Vec<syn::Ident> = ref_idents;
1899
1900 let f_tokens = f.emit_tokens(&mut ident_stack);
1901
1902 graph_builders.add_dfir_at(
1903 &input.metadata().location_id,
1904 parse_quote! {
1905 #input_ident -> for_each(#f_tokens);
1906 },
1907 Some(&stmt_id.to_string()),
1908 );
1909 }
1910 BuildersOrCallback::Callback(leaf_callback, _) => {
1911 leaf_callback(self, next_stmt_id);
1912 }
1913 }
1914 }
1915
1916 HydroRoot::SendExternal {
1917 serialize_fn,
1918 instantiate_fn,
1919 input,
1920 ..
1921 } => {
1922 let input_ident = input.emit_core(
1923 builders_or_callback,
1924 seen_tees,
1925 built_tees,
1926 next_stmt_id,
1927 fold_hooked_idents,
1928 );
1929
1930 let stmt_id = next_stmt_id.get_and_increment();
1931
1932 match builders_or_callback {
1933 BuildersOrCallback::Builders(graph_builders) => {
1934 let (sink_expr, _) = match instantiate_fn {
1935 DebugInstantiate::Building => (
1936 syn::parse_quote!(DUMMY_SINK),
1937 syn::parse_quote!(DUMMY_SOURCE),
1938 ),
1939
1940 DebugInstantiate::Finalized(finalized) => {
1941 (finalized.sink.clone(), finalized.source.clone())
1942 }
1943 };
1944
1945 graph_builders.create_external_output(
1946 &input.metadata().location_id,
1947 sink_expr,
1948 &input_ident,
1949 serialize_fn.as_ref(),
1950 stmt_id,
1951 );
1952 }
1953 BuildersOrCallback::Callback(leaf_callback, _) => {
1954 leaf_callback(self, next_stmt_id);
1955 }
1956 }
1957 }
1958
1959 HydroRoot::DestSink { sink, input, .. } => {
1960 let input_ident = input.emit_core(
1961 builders_or_callback,
1962 seen_tees,
1963 built_tees,
1964 next_stmt_id,
1965 fold_hooked_idents,
1966 );
1967
1968 let stmt_id = next_stmt_id.get_and_increment();
1969
1970 match builders_or_callback {
1971 BuildersOrCallback::Builders(graph_builders) => {
1972 graph_builders.add_dfir_at(
1973 &input.metadata().location_id,
1974 parse_quote! {
1975 #input_ident -> dest_sink(#sink);
1976 },
1977 Some(&stmt_id.to_string()),
1978 );
1979 }
1980 BuildersOrCallback::Callback(leaf_callback, _) => {
1981 leaf_callback(self, next_stmt_id);
1982 }
1983 }
1984 }
1985
1986 HydroRoot::CycleSink {
1987 cycle_id, input, ..
1988 } => {
1989 let input_ident = input.emit_core(
1990 builders_or_callback,
1991 seen_tees,
1992 built_tees,
1993 next_stmt_id,
1994 fold_hooked_idents,
1995 );
1996
1997 match builders_or_callback {
1998 BuildersOrCallback::Builders(graph_builders) => {
1999 let elem_type: syn::Type = match &input.metadata().collection_kind {
2000 CollectionKind::KeyedSingleton {
2001 key_type,
2002 value_type,
2003 ..
2004 }
2005 | CollectionKind::KeyedStream {
2006 key_type,
2007 value_type,
2008 ..
2009 } => {
2010 parse_quote!((#key_type, #value_type))
2011 }
2012 CollectionKind::Stream { element_type, .. }
2013 | CollectionKind::Singleton { element_type, .. }
2014 | CollectionKind::Optional { element_type, .. } => {
2015 parse_quote!(#element_type)
2016 }
2017 };
2018
2019 let cycle_id_ident = cycle_id.as_ident();
2020 graph_builders.add_dfir_at(
2021 &input.metadata().location_id,
2022 parse_quote! {
2023 #cycle_id_ident = #input_ident -> identity::<#elem_type>();
2024 },
2025 None,
2026 );
2027 }
2028 BuildersOrCallback::Callback(_, _) => {}
2030 }
2031 }
2032
2033 HydroRoot::EmbeddedOutput { ident, input, .. } => {
2034 let input_ident = input.emit_core(
2035 builders_or_callback,
2036 seen_tees,
2037 built_tees,
2038 next_stmt_id,
2039 fold_hooked_idents,
2040 );
2041
2042 let stmt_id = next_stmt_id.get_and_increment();
2043
2044 match builders_or_callback {
2045 BuildersOrCallback::Builders(graph_builders) => {
2046 graph_builders.add_dfir_at(
2047 &input.metadata().location_id,
2048 parse_quote! {
2049 #input_ident -> for_each(&mut #ident);
2050 },
2051 Some(&stmt_id.to_string()),
2052 );
2053 }
2054 BuildersOrCallback::Callback(leaf_callback, _) => {
2055 leaf_callback(self, next_stmt_id);
2056 }
2057 }
2058 }
2059
2060 HydroRoot::Null { input, .. } => {
2061 let input_ident = input.emit_core(
2062 builders_or_callback,
2063 seen_tees,
2064 built_tees,
2065 next_stmt_id,
2066 fold_hooked_idents,
2067 );
2068
2069 let stmt_id = next_stmt_id.get_and_increment();
2070
2071 match builders_or_callback {
2072 BuildersOrCallback::Builders(graph_builders) => {
2073 graph_builders.add_dfir_at(
2074 &input.metadata().location_id,
2075 parse_quote! {
2076 #input_ident -> for_each(|_| {});
2077 },
2078 Some(&stmt_id.to_string()),
2079 );
2080 }
2081 BuildersOrCallback::Callback(leaf_callback, _) => {
2082 leaf_callback(self, next_stmt_id);
2083 }
2084 }
2085 }
2086 }
2087 }
2088
2089 pub fn op_metadata(&self) -> &HydroIrOpMetadata {
2090 match self {
2091 HydroRoot::ForEach { op_metadata, .. }
2092 | HydroRoot::SendExternal { op_metadata, .. }
2093 | HydroRoot::DestSink { op_metadata, .. }
2094 | HydroRoot::CycleSink { op_metadata, .. }
2095 | HydroRoot::EmbeddedOutput { op_metadata, .. }
2096 | HydroRoot::Null { op_metadata, .. } => op_metadata,
2097 }
2098 }
2099
2100 pub fn op_metadata_mut(&mut self) -> &mut HydroIrOpMetadata {
2101 match self {
2102 HydroRoot::ForEach { op_metadata, .. }
2103 | HydroRoot::SendExternal { op_metadata, .. }
2104 | HydroRoot::DestSink { op_metadata, .. }
2105 | HydroRoot::CycleSink { op_metadata, .. }
2106 | HydroRoot::EmbeddedOutput { op_metadata, .. }
2107 | HydroRoot::Null { op_metadata, .. } => op_metadata,
2108 }
2109 }
2110
2111 pub fn input(&self) -> &HydroNode {
2112 match self {
2113 HydroRoot::ForEach { input, .. }
2114 | HydroRoot::SendExternal { input, .. }
2115 | HydroRoot::DestSink { input, .. }
2116 | HydroRoot::CycleSink { input, .. }
2117 | HydroRoot::EmbeddedOutput { input, .. }
2118 | HydroRoot::Null { input, .. } => input,
2119 }
2120 }
2121
2122 pub fn input_metadata(&self) -> &HydroIrMetadata {
2123 self.input().metadata()
2124 }
2125
2126 pub fn print_root(&self) -> String {
2127 match self {
2128 HydroRoot::ForEach { f, .. } => format!("ForEach({:?})", f),
2129 HydroRoot::SendExternal { .. } => "SendExternal".to_owned(),
2130 HydroRoot::DestSink { sink, .. } => format!("DestSink({:?})", sink),
2131 HydroRoot::CycleSink { cycle_id, .. } => format!("CycleSink({})", cycle_id),
2132 HydroRoot::EmbeddedOutput { ident, .. } => {
2133 format!("EmbeddedOutput({})", ident)
2134 }
2135 HydroRoot::Null { .. } => "Null".to_owned(),
2136 }
2137 }
2138
2139 pub fn visit_debug_expr(&mut self, mut transform: impl FnMut(&mut DebugExpr)) {
2140 match self {
2141 HydroRoot::ForEach { f, .. } => {
2142 transform(&mut f.expr);
2143 }
2144 HydroRoot::DestSink { sink, .. } => {
2145 transform(sink);
2146 }
2147 HydroRoot::SendExternal { .. }
2148 | HydroRoot::CycleSink { .. }
2149 | HydroRoot::EmbeddedOutput { .. }
2150 | HydroRoot::Null { .. } => {}
2151 }
2152 }
2153}
2154
2155#[cfg(feature = "build")]
2156fn tick_of(loc: &LocationId) -> Option<ClockId> {
2157 match loc {
2158 &LocationId::Tick {
2160 tick: Some(tick),
2161 parent_location: _,
2162 } => Some(tick),
2163 LocationId::Tick {
2165 tick: None,
2166 parent_location,
2167 } => Some(
2168 tick_of(parent_location)
2169 .expect("Tick should have either own clock ID or clock ID within parent_location."),
2170 ),
2171 LocationId::Atomic(inner) => tick_of(inner),
2172 _ => None,
2173 }
2174}
2175
2176#[cfg(feature = "build")]
2177fn remap_location(loc: &mut LocationId, uf: &mut HashMap<ClockId, ClockId>) {
2178 match loc {
2179 LocationId::Tick {
2180 tick,
2181 parent_location,
2182 } => {
2183 if let Some(tick) = tick {
2184 *tick = uf_find(uf, *tick);
2185 }
2186 remap_location(parent_location, uf);
2187 }
2188 LocationId::Atomic(inner) => {
2189 remap_location(inner, uf);
2190 }
2191 LocationId::Process(_) | LocationId::Cluster(_) => {}
2192 }
2193}
2194
2195#[cfg(feature = "build")]
2196fn uf_find(parent: &mut HashMap<ClockId, ClockId>, x: ClockId) -> ClockId {
2197 let p = *parent.get(&x).unwrap_or(&x);
2198 if p == x {
2199 return x;
2200 }
2201 let root = uf_find(parent, p);
2202 parent.insert(x, root);
2203 root
2204}
2205
2206#[cfg(feature = "build")]
2207fn uf_union(parent: &mut HashMap<ClockId, ClockId>, a: ClockId, b: ClockId) {
2208 let ra = uf_find(parent, a);
2209 let rb = uf_find(parent, b);
2210 if ra != rb {
2211 parent.insert(ra, rb);
2212 }
2213}
2214
2215#[cfg(feature = "build")]
2219pub fn unify_atomic_ticks(ir: &mut [HydroRoot]) {
2220 let mut uf: HashMap<ClockId, ClockId> = HashMap::new();
2221
2222 transform_bottom_up(
2224 ir,
2225 &mut |_| {},
2226 &mut |node: &mut HydroNode| match node {
2227 HydroNode::Batch { inner, metadata } | HydroNode::YieldConcat { inner, metadata } => {
2228 if let (Some(a), Some(b)) = (
2229 tick_of(&inner.metadata().location_id),
2230 tick_of(&metadata.location_id),
2231 ) {
2232 uf_union(&mut uf, a, b);
2233 }
2234 }
2235 HydroNode::Chain {
2236 first,
2237 second,
2238 metadata,
2239 }
2240 | HydroNode::ChainFirst {
2241 first,
2242 second,
2243 metadata,
2244 }
2245 | HydroNode::MergeOrdered {
2246 first,
2247 second,
2248 metadata,
2249 } => {
2250 if let (Some(a), Some(b)) = (
2251 tick_of(&first.metadata().location_id),
2252 tick_of(&metadata.location_id),
2253 ) {
2254 uf_union(&mut uf, a, b);
2255 }
2256 if let (Some(a), Some(b)) = (
2257 tick_of(&second.metadata().location_id),
2258 tick_of(&metadata.location_id),
2259 ) {
2260 uf_union(&mut uf, a, b);
2261 }
2262 }
2263 _ => {}
2264 },
2265 false,
2266 );
2267
2268 transform_bottom_up(
2270 ir,
2271 &mut |_| {},
2272 &mut |node: &mut HydroNode| {
2273 remap_location(&mut node.metadata_mut().location_id, &mut uf);
2274 },
2275 false,
2276 );
2277}
2278
2279#[cfg(feature = "build")]
2280pub fn emit(ir: &mut Vec<HydroRoot>) -> SecondaryMap<LocationKey, FlatGraphBuilder> {
2281 let mut builders = ProdDfirBuilder::default();
2282 let mut seen_tees = HashMap::new();
2283 let mut built_tees = HashMap::new();
2284 let mut next_stmt_id = crate::Counter::<StmtId>::default();
2285 let mut fold_hooked_idents = HashSet::new();
2286 for leaf in ir {
2287 leaf.emit(
2288 &mut builders,
2289 &mut seen_tees,
2290 &mut built_tees,
2291 &mut next_stmt_id,
2292 &mut fold_hooked_idents,
2293 );
2294 }
2295 builders.graphs
2296}
2297
2298#[cfg(feature = "build")]
2299pub fn traverse_dfir(
2300 ir: &mut [HydroRoot],
2301 transform_root: impl FnMut(&mut HydroRoot, &mut crate::Counter<StmtId>),
2302 transform_node: impl FnMut(&mut HydroNode, &mut crate::Counter<StmtId>),
2303) {
2304 let mut seen_tees = HashMap::new();
2305 let mut built_tees = HashMap::new();
2306 let mut next_stmt_id = crate::Counter::<StmtId>::default();
2307 let mut fold_hooked_idents = HashSet::new();
2308 let mut callback = BuildersOrCallback::Callback(transform_root, transform_node);
2309 ir.iter_mut().for_each(|leaf| {
2310 leaf.emit_core(
2311 &mut callback,
2312 &mut seen_tees,
2313 &mut built_tees,
2314 &mut next_stmt_id,
2315 &mut fold_hooked_idents,
2316 );
2317 });
2318}
2319
2320pub fn transform_bottom_up(
2321 ir: &mut [HydroRoot],
2322 transform_root: &mut impl FnMut(&mut HydroRoot),
2323 transform_node: &mut impl FnMut(&mut HydroNode),
2324 check_well_formed: bool,
2325) {
2326 let mut seen_tees = HashMap::new();
2327 ir.iter_mut().for_each(|leaf| {
2328 leaf.transform_bottom_up(
2329 transform_root,
2330 transform_node,
2331 &mut seen_tees,
2332 check_well_formed,
2333 );
2334 });
2335}
2336
2337pub fn deep_clone(ir: &[HydroRoot]) -> Vec<HydroRoot> {
2338 let mut seen_tees = HashMap::new();
2339 ir.iter()
2340 .map(|leaf| leaf.deep_clone(&mut seen_tees))
2341 .collect()
2342}
2343
2344type PrintedTees = RefCell<Option<(usize, HashMap<*const RefCell<HydroNode>, usize>)>>;
2345thread_local! {
2346 static PRINTED_TEES: PrintedTees = const { RefCell::new(None) };
2347 static SERIALIZED_SHARED: PrintedTees
2351 = const { RefCell::new(None) };
2352}
2353
2354pub fn dbg_dedup_tee<T>(f: impl FnOnce() -> T) -> T {
2355 PRINTED_TEES.with(|printed_tees| {
2356 let mut printed_tees_mut = printed_tees.borrow_mut();
2357 *printed_tees_mut = Some((0, HashMap::new()));
2358 drop(printed_tees_mut);
2359
2360 let ret = f();
2361
2362 let mut printed_tees_mut = printed_tees.borrow_mut();
2363 *printed_tees_mut = None;
2364
2365 ret
2366 })
2367}
2368
2369pub fn serialize_dedup_shared<T>(f: impl FnOnce() -> T) -> T {
2374 let _guard = SerializedSharedGuard::enter();
2375 f()
2376}
2377
2378struct SerializedSharedGuard {
2381 previous: Option<(usize, HashMap<*const RefCell<HydroNode>, usize>)>,
2382}
2383
2384impl SerializedSharedGuard {
2385 fn enter() -> Self {
2386 let previous = SERIALIZED_SHARED.with(|cell| {
2387 let mut guard = cell.borrow_mut();
2388 guard.replace((0, HashMap::new()))
2389 });
2390 Self { previous }
2391 }
2392}
2393
2394impl Drop for SerializedSharedGuard {
2395 fn drop(&mut self) {
2396 SERIALIZED_SHARED.with(|cell| {
2397 *cell.borrow_mut() = self.previous.take();
2398 });
2399 }
2400}
2401
2402pub struct SharedNode(pub Rc<RefCell<HydroNode>>);
2403
2404impl serde::Serialize for SharedNode {
2405 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
2416 SERIALIZED_SHARED.with(|cell| {
2417 let mut guard = cell.borrow_mut();
2418 let state = guard.as_mut().ok_or_else(|| {
2420 serde::ser::Error::custom(
2421 "SharedNode serialization requires an active serialize_dedup_shared scope",
2422 )
2423 })?;
2424 let ptr = self.0.as_ptr() as *const RefCell<HydroNode>;
2425
2426 if let Some(&id) = state.1.get(&ptr) {
2427 drop(guard);
2428 use serde::ser::SerializeMap;
2429 let mut map = serializer.serialize_map(Some(1))?;
2430 map.serialize_entry("$shared_ref", &id)?;
2431 map.end()
2432 } else {
2433 let id = state.0;
2434 state.0 += 1;
2435 state.1.insert(ptr, id);
2436 drop(guard);
2437
2438 use serde::ser::SerializeMap;
2439 let mut map = serializer.serialize_map(Some(2))?;
2440 map.serialize_entry("$shared", &id)?;
2441 map.serialize_entry("node", &*self.0.borrow())?;
2442 map.end()
2443 }
2444 })
2445 }
2446}
2447
2448impl SharedNode {
2449 pub fn as_ptr(&self) -> *const RefCell<HydroNode> {
2450 Rc::as_ptr(&self.0)
2451 }
2452}
2453
2454impl Debug for SharedNode {
2455 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2456 PRINTED_TEES.with(|printed_tees| {
2457 let mut printed_tees_mut_borrow = printed_tees.borrow_mut();
2458 let printed_tees_mut = printed_tees_mut_borrow.as_mut();
2459
2460 if let Some(printed_tees_mut) = printed_tees_mut {
2461 if let Some(existing) = printed_tees_mut
2462 .1
2463 .get(&(std::ptr::from_ref(self.0.as_ref())))
2464 {
2465 write!(f, "<shared {}>", existing)
2466 } else {
2467 let next_id = printed_tees_mut.0;
2468 printed_tees_mut.0 += 1;
2469 printed_tees_mut
2470 .1
2471 .insert(std::ptr::from_ref(self.0.as_ref()), next_id);
2472 drop(printed_tees_mut_borrow);
2473 write!(f, "<shared {}>: ", next_id)?;
2474 Debug::fmt(&self.0.borrow(), f)
2475 }
2476 } else {
2477 drop(printed_tees_mut_borrow);
2478 write!(f, "<shared>: ")?;
2479 Debug::fmt(&self.0.borrow(), f)
2480 }
2481 })
2482 }
2483}
2484
2485impl Hash for SharedNode {
2486 fn hash<H: Hasher>(&self, state: &mut H) {
2487 self.0.borrow_mut().hash(state);
2488 }
2489}
2490
2491#[derive(Debug)]
2496pub enum AccessCounter {
2497 Counting(Cell<u32>),
2498 Frozen(u32),
2499}
2500
2501impl AccessCounter {
2502 pub fn new() -> Self {
2503 Self::Counting(Cell::new(0))
2504 }
2505
2506 pub fn next_group(&self, is_mut: bool) -> Self {
2510 let AccessCounter::Counting(count) = self else {
2511 panic!("Cannot count on `AccessCounter::Frozen`");
2512 };
2513 let c = if is_mut {
2514 let c = count.get() + 1;
2515 count.set(c + 1);
2516 c
2517 } else {
2518 count.get()
2519 };
2520 Self::Frozen(c)
2521 }
2522
2523 pub fn freeze(&self) -> Self {
2525 Self::Frozen(match self {
2526 Self::Counting(count) => count.get(),
2527 Self::Frozen(count) => *count,
2528 })
2529 }
2530
2531 pub fn frozen_group(&self) -> u32 {
2532 let Self::Frozen(count) = self else {
2533 panic!("`AccessCounter` not frozen");
2534 };
2535 *count
2536 }
2537}
2538
2539impl Default for AccessCounter {
2540 fn default() -> Self {
2541 Self::new()
2542 }
2543}
2544
2545impl Hash for AccessCounter {
2546 fn hash<H: Hasher>(&self, _state: &mut H) {
2547 }
2549}
2550
2551impl serde::Serialize for AccessCounter {
2552 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
2553 let count = match self {
2554 AccessCounter::Counting(count) => count.get(),
2555 AccessCounter::Frozen(count) => *count,
2556 };
2557 count.serialize(serializer)
2558 }
2559}
2560
2561#[derive(serde::Serialize, Clone, PartialEq, Eq, Debug)]
2562pub enum BoundKind {
2563 Unbounded,
2564 Bounded,
2565}
2566
2567#[derive(serde::Serialize, Clone, PartialEq, Eq, Debug)]
2568pub enum OptionalBoundKind {
2569 Unbounded,
2570 InitNone,
2573 Bounded,
2574}
2575
2576#[derive(serde::Serialize, Clone, PartialEq, Eq, Debug)]
2577pub enum StreamOrder {
2578 NoOrder,
2579 TotalOrder,
2580}
2581
2582#[derive(serde::Serialize, Clone, PartialEq, Eq, Debug)]
2583pub enum StreamRetry {
2584 AtLeastOnce,
2585 ExactlyOnce,
2586}
2587
2588#[derive(serde::Serialize, Clone, PartialEq, Eq, Debug)]
2589pub enum KeyedSingletonBoundKind {
2590 Unbounded,
2591 MonotonicKeys,
2592 MonotonicValue,
2593 BoundedValue,
2594 Bounded,
2595}
2596
2597#[derive(serde::Serialize, Clone, PartialEq, Eq, Debug)]
2598pub enum SingletonBoundKind {
2599 Unbounded,
2600 Monotonic,
2601 Bounded,
2602}
2603
2604#[derive(Clone, PartialEq, Eq, Debug, serde::Serialize)]
2605pub enum CollectionKind {
2606 Stream {
2607 bound: BoundKind,
2608 order: StreamOrder,
2609 retry: StreamRetry,
2610 element_type: DebugType,
2611 },
2612 Singleton {
2613 bound: SingletonBoundKind,
2614 element_type: DebugType,
2615 },
2616 Optional {
2617 bound: OptionalBoundKind,
2618 element_type: DebugType,
2619 },
2620 KeyedStream {
2621 bound: BoundKind,
2622 value_order: StreamOrder,
2623 value_retry: StreamRetry,
2624 key_type: DebugType,
2625 value_type: DebugType,
2626 },
2627 KeyedSingleton {
2628 bound: KeyedSingletonBoundKind,
2629 key_type: DebugType,
2630 value_type: DebugType,
2631 },
2632}
2633
2634impl CollectionKind {
2635 pub fn is_bounded(&self) -> bool {
2636 matches!(
2637 self,
2638 CollectionKind::Stream {
2639 bound: BoundKind::Bounded,
2640 ..
2641 } | CollectionKind::Singleton {
2642 bound: SingletonBoundKind::Bounded,
2643 ..
2644 } | CollectionKind::Optional {
2645 bound: OptionalBoundKind::Bounded,
2646 ..
2647 } | CollectionKind::KeyedStream {
2648 bound: BoundKind::Bounded,
2649 ..
2650 } | CollectionKind::KeyedSingleton {
2651 bound: KeyedSingletonBoundKind::Bounded,
2652 ..
2653 }
2654 )
2655 }
2656
2657 pub fn is_strict(&self) -> bool {
2660 match self {
2661 CollectionKind::Stream { order, retry, .. } => {
2662 *order == StreamOrder::TotalOrder && *retry == StreamRetry::ExactlyOnce
2663 }
2664 CollectionKind::KeyedStream {
2665 value_order,
2666 value_retry,
2667 ..
2668 } => {
2669 *value_order == StreamOrder::TotalOrder && *value_retry == StreamRetry::ExactlyOnce
2670 }
2671 CollectionKind::Singleton { .. }
2674 | CollectionKind::Optional { .. }
2675 | CollectionKind::KeyedSingleton { .. } => true,
2676 }
2677 }
2678
2679 pub fn strict_kind(&self) -> CollectionKind {
2681 match self {
2682 CollectionKind::Stream {
2683 bound,
2684 element_type,
2685 ..
2686 } => CollectionKind::Stream {
2687 bound: bound.clone(),
2688 order: StreamOrder::TotalOrder,
2689 retry: StreamRetry::ExactlyOnce,
2690 element_type: element_type.clone(),
2691 },
2692 CollectionKind::KeyedStream {
2693 bound,
2694 key_type,
2695 value_type,
2696 ..
2697 } => CollectionKind::KeyedStream {
2698 bound: bound.clone(),
2699 value_order: StreamOrder::TotalOrder,
2700 value_retry: StreamRetry::ExactlyOnce,
2701 key_type: key_type.clone(),
2702 value_type: value_type.clone(),
2703 },
2704 other => other.clone(),
2705 }
2706 }
2707}
2708
2709#[derive(Clone, serde::Serialize)]
2710pub struct HydroIrMetadata {
2711 pub location_id: LocationId,
2712 pub collection_kind: CollectionKind,
2713 pub consistency: Option<ClusterConsistency>,
2714 pub cardinality: Option<usize>,
2715 pub tag: Option<String>,
2716 pub op: HydroIrOpMetadata,
2717}
2718
2719impl Hash for HydroIrMetadata {
2721 fn hash<H: Hasher>(&self, _: &mut H) {}
2722}
2723
2724impl PartialEq for HydroIrMetadata {
2725 fn eq(&self, _: &Self) -> bool {
2726 true
2727 }
2728}
2729
2730impl Eq for HydroIrMetadata {}
2731
2732impl Debug for HydroIrMetadata {
2733 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2734 f.debug_struct("HydroIrMetadata")
2735 .field("location_id", &self.location_id)
2736 .field("collection_kind", &self.collection_kind)
2737 .finish()
2738 }
2739}
2740
2741#[derive(Clone, serde::Serialize)]
2744pub struct HydroIrOpMetadata {
2745 #[serde(rename = "span", serialize_with = "serialize_backtrace_as_span")]
2746 pub backtrace: Backtrace,
2747 pub cpu_usage: Option<f64>,
2748 pub network_recv_cpu_usage: Option<f64>,
2749 pub id: Option<usize>,
2750 #[serde(skip)]
2754 pub sim_hook_id: Option<usize>,
2755}
2756
2757impl HydroIrOpMetadata {
2758 #[expect(
2759 clippy::new_without_default,
2760 reason = "explicit calls to new ensure correct backtrace bounds"
2761 )]
2762 pub fn new() -> HydroIrOpMetadata {
2763 Self::new_with_skip(1)
2764 }
2765
2766 fn new_with_skip(skip_count: usize) -> HydroIrOpMetadata {
2767 HydroIrOpMetadata {
2768 backtrace: Backtrace::get_backtrace(2 + skip_count),
2769 cpu_usage: None,
2770 network_recv_cpu_usage: None,
2771 id: None,
2772 sim_hook_id: None,
2773 }
2774 }
2775}
2776
2777impl Debug for HydroIrOpMetadata {
2778 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2779 f.debug_struct("HydroIrOpMetadata").finish()
2780 }
2781}
2782
2783impl Hash for HydroIrOpMetadata {
2784 fn hash<H: Hasher>(&self, _: &mut H) {}
2785}
2786
2787#[derive(Debug, Clone, Hash, serde::Serialize)]
2793pub enum NetworkSend {
2794 Custom { serialize_fn: Option<DebugExpr> },
2797 Embedded {
2805 tag: Option<DebugType>,
2806 element_type: DebugType,
2807 },
2808}
2809
2810#[derive(Debug, Clone, Hash, serde::Serialize)]
2813pub enum NetworkRecv {
2814 Custom { deserialize_fn: Option<DebugExpr> },
2817 Embedded {
2824 tag: Option<DebugType>,
2825 element_type: DebugType,
2826 },
2827}
2828
2829#[cfg(feature = "build")]
2830impl NetworkSend {
2831 pub(crate) fn external_element_type(&self) -> Option<&syn::Type> {
2834 match self {
2835 NetworkSend::Custom { .. } => None,
2836 NetworkSend::Embedded { element_type, .. } => Some(&element_type.0),
2837 }
2838 }
2839}
2840
2841#[cfg(feature = "build")]
2842impl NetworkRecv {
2843 pub(crate) fn external_element_type(&self) -> Option<&syn::Type> {
2845 match self {
2846 NetworkRecv::Custom { .. } => None,
2847 NetworkRecv::Embedded { element_type, .. } => Some(&element_type.0),
2848 }
2849 }
2850}
2851
2852#[cfg(feature = "build")]
2853impl NetworkSend {
2854 pub(crate) fn pipeline(&self) -> Option<DebugExpr> {
2856 match self {
2857 NetworkSend::Custom { serialize_fn } => serialize_fn.clone(),
2858 NetworkSend::Embedded { tag, element_type } => {
2859 let root = crate::staging_util::get_this_crate();
2860 let element_type = &element_type.0;
2861 let expr: syn::Expr = if let Some(tag) = tag {
2862 let tag = &tag.0;
2863 parse_quote! {
2864 #root::runtime_support::stageleft::runtime_support::fn1_type_hint::<(#root::__staged::location::MemberId<#tag>, #element_type), _>(
2865 |(id, data)| (id.into_tagless(), data)
2866 )
2867 }
2868 } else {
2869 parse_quote! {
2870 #root::runtime_support::stageleft::runtime_support::fn1_type_hint::<#element_type, _>(
2871 |data| data
2872 )
2873 }
2874 };
2875 Some(expr.into())
2876 }
2877 }
2878 }
2879}
2880
2881#[cfg(feature = "build")]
2882impl NetworkRecv {
2883 pub(crate) fn pipeline(&self) -> Option<DebugExpr> {
2885 match self {
2886 NetworkRecv::Custom { deserialize_fn } => deserialize_fn.clone(),
2887 NetworkRecv::Embedded { tag, .. } => {
2892 let tag = tag.as_ref()?;
2893 let root = crate::staging_util::get_this_crate();
2894 let tag = &tag.0;
2895 let expr: syn::Expr = parse_quote! {
2896 |(id, b)| (#root::__staged::location::MemberId::<#tag>::from_tagless(id as #root::__staged::location::TaglessMemberId), b)
2897 };
2898 Some(expr.into())
2899 }
2900 }
2901 }
2902}
2903
2904#[derive(Debug, Hash, serde::Serialize)]
2907pub enum HydroNode {
2908 Placeholder,
2909
2910 Cast {
2918 inner: Box<HydroNode>,
2919 metadata: HydroIrMetadata,
2920 },
2921
2922 ObserveNonDet {
2928 inner: Box<HydroNode>,
2929 trusted: bool, metadata: HydroIrMetadata,
2931 },
2932
2933 Source {
2934 source: HydroSource,
2935 metadata: HydroIrMetadata,
2936 },
2937
2938 SingletonSource {
2939 value: DebugExpr,
2940 first_tick_only: bool,
2941 metadata: HydroIrMetadata,
2942 },
2943
2944 CycleSource {
2945 cycle_id: CycleId,
2946 metadata: HydroIrMetadata,
2947 },
2948
2949 Tee {
2950 inner: SharedNode,
2951 metadata: HydroIrMetadata,
2952 },
2953
2954 Reference {
2963 inner: SharedNode,
2964 kind: crate::handoff_ref::HandoffRefKind,
2965 access_counter: AccessCounter,
2966 metadata: HydroIrMetadata,
2967 },
2968
2969 PartitionSide {
2971 inner: SharedNode,
2972 is_true: bool,
2973 metadata: HydroIrMetadata,
2974 },
2975
2976 PartitionShared {
2978 input: Box<HydroNode>,
2979 f: ClosureExpr,
2980 metadata: HydroIrMetadata,
2981 },
2982
2983 BeginAtomic {
2984 inner: Box<HydroNode>,
2985 metadata: HydroIrMetadata,
2986 },
2987
2988 EndAtomic {
2989 inner: Box<HydroNode>,
2990 metadata: HydroIrMetadata,
2991 },
2992
2993 Batch {
2994 inner: Box<HydroNode>,
2995 metadata: HydroIrMetadata,
2996 },
2997
2998 YieldConcat {
2999 inner: Box<HydroNode>,
3000 metadata: HydroIrMetadata,
3001 },
3002
3003 Chain {
3004 first: Box<HydroNode>,
3005 second: Box<HydroNode>,
3006 metadata: HydroIrMetadata,
3007 },
3008
3009 MergeOrdered {
3010 first: Box<HydroNode>,
3011 second: Box<HydroNode>,
3012 metadata: HydroIrMetadata,
3013 },
3014
3015 ChainFirst {
3016 first: Box<HydroNode>,
3017 second: Box<HydroNode>,
3018 metadata: HydroIrMetadata,
3019 },
3020
3021 CrossProduct {
3022 left: Box<HydroNode>,
3023 right: Box<HydroNode>,
3024 metadata: HydroIrMetadata,
3025 },
3026
3027 CrossSingleton {
3028 left: Box<HydroNode>,
3029 right: Box<HydroNode>,
3030 metadata: HydroIrMetadata,
3031 },
3032
3033 Join {
3034 left: Box<HydroNode>,
3035 right: Box<HydroNode>,
3036 metadata: HydroIrMetadata,
3037 },
3038
3039 JoinHalf {
3043 left: Box<HydroNode>,
3044 right: Box<HydroNode>,
3045 metadata: HydroIrMetadata,
3046 },
3047
3048 Difference {
3049 pos: Box<HydroNode>,
3050 neg: Box<HydroNode>,
3051 metadata: HydroIrMetadata,
3052 },
3053
3054 AntiJoin {
3055 pos: Box<HydroNode>,
3056 neg: Box<HydroNode>,
3057 metadata: HydroIrMetadata,
3058 },
3059
3060 ResolveFutures {
3061 input: Box<HydroNode>,
3062 metadata: HydroIrMetadata,
3063 },
3064 ResolveFuturesBlocking {
3065 input: Box<HydroNode>,
3066 metadata: HydroIrMetadata,
3067 },
3068 ResolveFuturesOrdered {
3069 input: Box<HydroNode>,
3070 metadata: HydroIrMetadata,
3071 },
3072
3073 Map {
3074 f: ClosureExpr,
3075 input: Box<HydroNode>,
3076 metadata: HydroIrMetadata,
3077 },
3078 FlatMap {
3079 f: ClosureExpr,
3080 input: Box<HydroNode>,
3081 metadata: HydroIrMetadata,
3082 },
3083 FlatMapStreamBlocking {
3084 f: ClosureExpr,
3085 input: Box<HydroNode>,
3086 metadata: HydroIrMetadata,
3087 },
3088 Filter {
3089 f: ClosureExpr,
3090 input: Box<HydroNode>,
3091 metadata: HydroIrMetadata,
3092 },
3093 FilterMap {
3094 f: ClosureExpr,
3095 input: Box<HydroNode>,
3096 metadata: HydroIrMetadata,
3097 },
3098
3099 DeferTick {
3100 input: Box<HydroNode>,
3101 metadata: HydroIrMetadata,
3102 },
3103 Enumerate {
3104 input: Box<HydroNode>,
3105 metadata: HydroIrMetadata,
3106 },
3107 Inspect {
3108 f: ClosureExpr,
3109 input: Box<HydroNode>,
3110 metadata: HydroIrMetadata,
3111 },
3112
3113 Unique {
3114 input: Box<HydroNode>,
3115 metadata: HydroIrMetadata,
3116 },
3117
3118 Sort {
3119 input: Box<HydroNode>,
3120 metadata: HydroIrMetadata,
3121 },
3122 Fold {
3123 init: ClosureExpr,
3124 acc: ClosureExpr,
3125 input: Box<HydroNode>,
3126 metadata: HydroIrMetadata,
3127 },
3128
3129 Scan {
3130 init: ClosureExpr,
3131 acc: ClosureExpr,
3132 input: Box<HydroNode>,
3133 metadata: HydroIrMetadata,
3134 },
3135 ScanAsyncBlocking {
3136 init: ClosureExpr,
3137 acc: ClosureExpr,
3138 input: Box<HydroNode>,
3139 metadata: HydroIrMetadata,
3140 },
3141 FoldKeyed {
3142 init: ClosureExpr,
3143 acc: ClosureExpr,
3144 input: Box<HydroNode>,
3145 metadata: HydroIrMetadata,
3146 },
3147
3148 Reduce {
3149 f: ClosureExpr,
3150 input: Box<HydroNode>,
3151 metadata: HydroIrMetadata,
3152 },
3153 ReduceKeyed {
3154 f: ClosureExpr,
3155 input: Box<HydroNode>,
3156 metadata: HydroIrMetadata,
3157 },
3158 ReduceKeyedWatermark {
3159 f: ClosureExpr,
3160 input: Box<HydroNode>,
3161 watermark: Box<HydroNode>,
3162 metadata: HydroIrMetadata,
3163 },
3164
3165 Network {
3166 name: Option<String>,
3167 networking_info: crate::networking::NetworkingInfo,
3168 serialize: NetworkSend,
3169 deserialize: NetworkRecv,
3170 instantiate_fn: DebugInstantiate,
3171 input: Box<HydroNode>,
3172 metadata: HydroIrMetadata,
3173 },
3174
3175 VersionedNetworkFork {
3176 channel_id: u32,
3177 channel_name: String,
3178 senders: Vec<(u32, Box<HydroNode>, NetworkSend)>,
3179 metadata: HydroIrMetadata,
3180 },
3181
3182 VersionedNetwork {
3183 fork: SharedNode,
3184 version: u32,
3185 deserialize: NetworkRecv,
3186 metadata: HydroIrMetadata,
3187 },
3188
3189 ExternalInput {
3190 from_external_key: LocationKey,
3191 from_port_id: ExternalPortId,
3192 from_many: bool,
3193 codec_type: DebugType,
3194 #[serde(skip)]
3195 port_hint: NetworkHint,
3196 instantiate_fn: DebugInstantiate,
3197 deserialize_fn: Option<DebugExpr>,
3198 metadata: HydroIrMetadata,
3199 },
3200
3201 Counter {
3202 tag: String,
3203 duration: DebugExpr,
3204 prefix: String,
3205 input: Box<HydroNode>,
3206 metadata: HydroIrMetadata,
3207 },
3208
3209 AssertIsConsistent {
3210 inner: Box<HydroNode>,
3211 trusted: bool,
3212 metadata: HydroIrMetadata,
3213 },
3214
3215 UnboundSingleton {
3216 inner: Box<HydroNode>,
3217 metadata: HydroIrMetadata,
3218 },
3219}
3220
3221pub type SeenSharedNodes = HashMap<*const RefCell<HydroNode>, Rc<RefCell<HydroNode>>>;
3222pub type SeenSharedNodeLocations = HashMap<*const RefCell<HydroNode>, LocationId>;
3223
3224#[cfg(feature = "build")]
3228fn maybe_observe_for_mut(
3229 f: &ClosureExpr,
3230 in_ident: syn::Ident,
3231 in_location: &LocationId,
3232 in_kind: &CollectionKind,
3233 op_meta: &HydroIrOpMetadata,
3234 builders_or_callback: &mut BuildersOrCallback<
3235 '_,
3236 impl FnMut(&mut HydroRoot, &mut crate::Counter<StmtId>),
3237 impl FnMut(&mut HydroNode, &mut crate::Counter<StmtId>),
3238 >,
3239 next_stmt_id: &mut crate::Counter<StmtId>,
3240) -> syn::Ident {
3241 if f.has_mut_ref() && !in_kind.is_strict() {
3242 let observe_stmt_id = next_stmt_id.get_and_increment();
3243 let observe_ident =
3244 syn::Ident::new(&format!("stream_{}", observe_stmt_id), Span::call_site());
3245 if let BuildersOrCallback::Builders(graph_builders) = builders_or_callback {
3246 graph_builders.observe_for_mut(in_location, in_ident, in_kind, &observe_ident, op_meta);
3247 }
3248 observe_ident
3249 } else {
3250 in_ident
3251 }
3252}
3253
3254impl HydroNode {
3255 pub fn transform_bottom_up(
3256 &mut self,
3257 transform: &mut impl FnMut(&mut HydroNode),
3258 seen_tees: &mut SeenSharedNodes,
3259 check_well_formed: bool,
3260 ) {
3261 self.transform_children(
3262 |n, s| n.transform_bottom_up(transform, s, check_well_formed),
3263 seen_tees,
3264 );
3265
3266 transform(self);
3267
3268 let self_location = self.metadata().location_id.root();
3269
3270 if check_well_formed {
3271 match &*self {
3272 HydroNode::Network { .. } => {}
3273 _ => {
3274 self.input_metadata().iter().for_each(|i| {
3275 if i.location_id.root() != self_location {
3276 panic!(
3277 "Mismatching IR locations, child: {:?} ({:?}) of: {:?} ({:?})",
3278 i,
3279 i.location_id.root(),
3280 self,
3281 self_location
3282 )
3283 }
3284 });
3285 }
3286 }
3287 }
3288 }
3289
3290 #[inline(always)]
3291 pub fn transform_children(
3292 &mut self,
3293 mut transform: impl FnMut(&mut HydroNode, &mut SeenSharedNodes),
3294 seen_tees: &mut SeenSharedNodes,
3295 ) {
3296 match self {
3297 HydroNode::Placeholder => {
3298 panic!();
3299 }
3300
3301 HydroNode::Source { .. }
3302 | HydroNode::SingletonSource { .. }
3303 | HydroNode::CycleSource { .. }
3304 | HydroNode::ExternalInput { .. } => {}
3305
3306 HydroNode::Tee { inner, .. } | HydroNode::Reference { inner, .. } => {
3307 if let Some(transformed) = seen_tees.get(&inner.as_ptr()) {
3308 *inner = SharedNode(transformed.clone());
3309 } else {
3310 let transformed_cell = Rc::new(RefCell::new(HydroNode::Placeholder));
3311 seen_tees.insert(inner.as_ptr(), transformed_cell.clone());
3312 let mut orig = inner.0.replace(HydroNode::Placeholder);
3313 transform(&mut orig, seen_tees);
3314 *transformed_cell.borrow_mut() = orig;
3315 *inner = SharedNode(transformed_cell);
3316 }
3317 }
3318
3319 HydroNode::PartitionSide { inner, .. } => {
3320 if let Some(transformed) = seen_tees.get(&inner.as_ptr()) {
3321 *inner = SharedNode(transformed.clone());
3322 } else {
3323 let transformed_cell = Rc::new(RefCell::new(HydroNode::Placeholder));
3324 seen_tees.insert(inner.as_ptr(), transformed_cell.clone());
3325 let mut orig: HydroNode = inner.0.replace(HydroNode::Placeholder);
3326 transform(&mut orig, seen_tees);
3327 *transformed_cell.borrow_mut() = orig;
3328 *inner = SharedNode(transformed_cell);
3329 }
3330 }
3331 HydroNode::PartitionShared { input, f, .. } => {
3332 f.transform_children(&mut transform, seen_tees);
3333 transform(input.as_mut(), seen_tees);
3334 }
3335
3336 HydroNode::Cast { inner, .. }
3337 | HydroNode::ObserveNonDet { inner, .. }
3338 | HydroNode::BeginAtomic { inner, .. }
3339 | HydroNode::EndAtomic { inner, .. }
3340 | HydroNode::Batch { inner, .. }
3341 | HydroNode::YieldConcat { inner, .. }
3342 | HydroNode::UnboundSingleton { inner, .. }
3343 | HydroNode::AssertIsConsistent { inner, .. } => {
3344 transform(inner.as_mut(), seen_tees);
3345 }
3346
3347 HydroNode::Chain { first, second, .. } => {
3348 transform(first.as_mut(), seen_tees);
3349 transform(second.as_mut(), seen_tees);
3350 }
3351
3352 HydroNode::MergeOrdered { first, second, .. } => {
3353 transform(first.as_mut(), seen_tees);
3354 transform(second.as_mut(), seen_tees);
3355 }
3356
3357 HydroNode::ChainFirst { first, second, .. } => {
3358 transform(first.as_mut(), seen_tees);
3359 transform(second.as_mut(), seen_tees);
3360 }
3361
3362 HydroNode::CrossSingleton { left, right, .. }
3363 | HydroNode::CrossProduct { left, right, .. }
3364 | HydroNode::Join { left, right, .. }
3365 | HydroNode::JoinHalf { left, right, .. } => {
3366 transform(left.as_mut(), seen_tees);
3367 transform(right.as_mut(), seen_tees);
3368 }
3369
3370 HydroNode::Difference { pos, neg, .. } | HydroNode::AntiJoin { pos, neg, .. } => {
3371 transform(pos.as_mut(), seen_tees);
3372 transform(neg.as_mut(), seen_tees);
3373 }
3374
3375 HydroNode::Map { f, input, .. } => {
3376 f.transform_children(&mut transform, seen_tees);
3377 transform(input.as_mut(), seen_tees);
3378 }
3379 HydroNode::FlatMap { f, input, .. }
3380 | HydroNode::FlatMapStreamBlocking { f, input, .. }
3381 | HydroNode::Filter { f, input, .. }
3382 | HydroNode::FilterMap { f, input, .. }
3383 | HydroNode::Inspect { f, input, .. }
3384 | HydroNode::Reduce { f, input, .. }
3385 | HydroNode::ReduceKeyed { f, input, .. } => {
3386 f.transform_children(&mut transform, seen_tees);
3387 transform(input.as_mut(), seen_tees);
3388 }
3389 HydroNode::ReduceKeyedWatermark {
3390 f,
3391 input,
3392 watermark,
3393 ..
3394 } => {
3395 f.transform_children(&mut transform, seen_tees);
3396 transform(input.as_mut(), seen_tees);
3397 transform(watermark.as_mut(), seen_tees);
3398 }
3399 HydroNode::Fold {
3400 init, acc, input, ..
3401 }
3402 | HydroNode::Scan {
3403 init, acc, input, ..
3404 }
3405 | HydroNode::ScanAsyncBlocking {
3406 init, acc, input, ..
3407 }
3408 | HydroNode::FoldKeyed {
3409 init, acc, input, ..
3410 } => {
3411 init.transform_children(&mut transform, seen_tees);
3412 acc.transform_children(&mut transform, seen_tees);
3413 transform(input.as_mut(), seen_tees);
3414 }
3415 HydroNode::ResolveFutures { input, .. }
3416 | HydroNode::ResolveFuturesBlocking { input, .. }
3417 | HydroNode::ResolveFuturesOrdered { input, .. }
3418 | HydroNode::Sort { input, .. }
3419 | HydroNode::DeferTick { input, .. }
3420 | HydroNode::Enumerate { input, .. }
3421 | HydroNode::Unique { input, .. }
3422 | HydroNode::Network { input, .. }
3423 | HydroNode::Counter { input, .. } => {
3424 transform(input.as_mut(), seen_tees);
3425 }
3426
3427 HydroNode::VersionedNetworkFork { senders, .. } => {
3428 for (_version, sender, _serialize) in senders.iter_mut() {
3429 transform(sender.as_mut(), seen_tees);
3430 }
3431 }
3432
3433 HydroNode::VersionedNetwork { fork, .. } => {
3434 if let Some(transformed) = seen_tees.get(&fork.as_ptr()) {
3435 *fork = SharedNode(transformed.clone());
3436 } else {
3437 let transformed_cell = Rc::new(RefCell::new(HydroNode::Placeholder));
3438 seen_tees.insert(fork.as_ptr(), transformed_cell.clone());
3439 let mut orig = fork.0.replace(HydroNode::Placeholder);
3440 transform(&mut orig, seen_tees);
3441 *transformed_cell.borrow_mut() = orig;
3442 *fork = SharedNode(transformed_cell);
3443 }
3444 }
3445 }
3446 }
3447
3448 pub fn deep_clone(&self, seen_tees: &mut SeenSharedNodes) -> HydroNode {
3449 match self {
3450 HydroNode::Placeholder => HydroNode::Placeholder,
3451 HydroNode::Cast { inner, metadata } => HydroNode::Cast {
3452 inner: Box::new(inner.deep_clone(seen_tees)),
3453 metadata: metadata.clone(),
3454 },
3455 HydroNode::UnboundSingleton { inner, metadata } => HydroNode::UnboundSingleton {
3456 inner: Box::new(inner.deep_clone(seen_tees)),
3457 metadata: metadata.clone(),
3458 },
3459 HydroNode::ObserveNonDet {
3460 inner,
3461 trusted,
3462 metadata,
3463 } => HydroNode::ObserveNonDet {
3464 inner: Box::new(inner.deep_clone(seen_tees)),
3465 trusted: *trusted,
3466 metadata: metadata.clone(),
3467 },
3468 HydroNode::AssertIsConsistent {
3469 inner,
3470 trusted,
3471 metadata,
3472 } => HydroNode::AssertIsConsistent {
3473 inner: Box::new(inner.deep_clone(seen_tees)),
3474 trusted: *trusted,
3475 metadata: metadata.clone(),
3476 },
3477 HydroNode::Source { source, metadata } => HydroNode::Source {
3478 source: source.clone(),
3479 metadata: metadata.clone(),
3480 },
3481 HydroNode::SingletonSource {
3482 value,
3483 first_tick_only,
3484 metadata,
3485 } => HydroNode::SingletonSource {
3486 value: value.clone(),
3487 first_tick_only: *first_tick_only,
3488 metadata: metadata.clone(),
3489 },
3490 HydroNode::CycleSource { cycle_id, metadata } => HydroNode::CycleSource {
3491 cycle_id: *cycle_id,
3492 metadata: metadata.clone(),
3493 },
3494 HydroNode::Tee { inner, metadata }
3495 | HydroNode::Reference {
3496 inner, metadata, ..
3497 } => {
3498 let cloned_inner = if let Some(transformed) = seen_tees.get(&inner.as_ptr()) {
3499 SharedNode(transformed.clone())
3500 } else {
3501 let new_rc = Rc::new(RefCell::new(HydroNode::Placeholder));
3502 seen_tees.insert(inner.as_ptr(), new_rc.clone());
3503 let cloned = inner.0.borrow().deep_clone(seen_tees);
3504 *new_rc.borrow_mut() = cloned;
3505 SharedNode(new_rc)
3506 };
3507 if let HydroNode::Reference {
3508 kind,
3509 access_counter,
3510 ..
3511 } = self
3512 {
3513 HydroNode::Reference {
3514 inner: cloned_inner,
3515 kind: *kind,
3516 access_counter: access_counter.freeze(),
3517 metadata: metadata.clone(),
3518 }
3519 } else {
3520 HydroNode::Tee {
3521 inner: cloned_inner,
3522 metadata: metadata.clone(),
3523 }
3524 }
3525 }
3526 HydroNode::PartitionSide {
3527 inner,
3528 is_true,
3529 metadata,
3530 } => {
3531 if let Some(transformed) = seen_tees.get(&inner.as_ptr()) {
3532 HydroNode::PartitionSide {
3533 inner: SharedNode(transformed.clone()),
3534 is_true: *is_true,
3535 metadata: metadata.clone(),
3536 }
3537 } else {
3538 let new_rc = Rc::new(RefCell::new(HydroNode::Placeholder));
3539 seen_tees.insert(inner.as_ptr(), new_rc.clone());
3540 let cloned = inner.0.borrow().deep_clone(seen_tees);
3541 *new_rc.borrow_mut() = cloned;
3542 HydroNode::PartitionSide {
3543 inner: SharedNode(new_rc),
3544 is_true: *is_true,
3545 metadata: metadata.clone(),
3546 }
3547 }
3548 }
3549 HydroNode::PartitionShared { input, f, metadata } => HydroNode::PartitionShared {
3550 input: Box::new(input.deep_clone(seen_tees)),
3551 f: f.deep_clone(seen_tees),
3552 metadata: metadata.clone(),
3553 },
3554 HydroNode::YieldConcat { inner, metadata } => HydroNode::YieldConcat {
3555 inner: Box::new(inner.deep_clone(seen_tees)),
3556 metadata: metadata.clone(),
3557 },
3558 HydroNode::BeginAtomic { inner, metadata } => HydroNode::BeginAtomic {
3559 inner: Box::new(inner.deep_clone(seen_tees)),
3560 metadata: metadata.clone(),
3561 },
3562 HydroNode::EndAtomic { inner, metadata } => HydroNode::EndAtomic {
3563 inner: Box::new(inner.deep_clone(seen_tees)),
3564 metadata: metadata.clone(),
3565 },
3566 HydroNode::Batch { inner, metadata } => HydroNode::Batch {
3567 inner: Box::new(inner.deep_clone(seen_tees)),
3568 metadata: metadata.clone(),
3569 },
3570 HydroNode::Chain {
3571 first,
3572 second,
3573 metadata,
3574 } => HydroNode::Chain {
3575 first: Box::new(first.deep_clone(seen_tees)),
3576 second: Box::new(second.deep_clone(seen_tees)),
3577 metadata: metadata.clone(),
3578 },
3579 HydroNode::MergeOrdered {
3580 first,
3581 second,
3582 metadata,
3583 } => HydroNode::MergeOrdered {
3584 first: Box::new(first.deep_clone(seen_tees)),
3585 second: Box::new(second.deep_clone(seen_tees)),
3586 metadata: metadata.clone(),
3587 },
3588 HydroNode::ChainFirst {
3589 first,
3590 second,
3591 metadata,
3592 } => HydroNode::ChainFirst {
3593 first: Box::new(first.deep_clone(seen_tees)),
3594 second: Box::new(second.deep_clone(seen_tees)),
3595 metadata: metadata.clone(),
3596 },
3597 HydroNode::CrossProduct {
3598 left,
3599 right,
3600 metadata,
3601 } => HydroNode::CrossProduct {
3602 left: Box::new(left.deep_clone(seen_tees)),
3603 right: Box::new(right.deep_clone(seen_tees)),
3604 metadata: metadata.clone(),
3605 },
3606 HydroNode::CrossSingleton {
3607 left,
3608 right,
3609 metadata,
3610 } => HydroNode::CrossSingleton {
3611 left: Box::new(left.deep_clone(seen_tees)),
3612 right: Box::new(right.deep_clone(seen_tees)),
3613 metadata: metadata.clone(),
3614 },
3615 HydroNode::Join {
3616 left,
3617 right,
3618 metadata,
3619 } => HydroNode::Join {
3620 left: Box::new(left.deep_clone(seen_tees)),
3621 right: Box::new(right.deep_clone(seen_tees)),
3622 metadata: metadata.clone(),
3623 },
3624 HydroNode::JoinHalf {
3625 left,
3626 right,
3627 metadata,
3628 } => HydroNode::JoinHalf {
3629 left: Box::new(left.deep_clone(seen_tees)),
3630 right: Box::new(right.deep_clone(seen_tees)),
3631 metadata: metadata.clone(),
3632 },
3633 HydroNode::Difference { pos, neg, metadata } => HydroNode::Difference {
3634 pos: Box::new(pos.deep_clone(seen_tees)),
3635 neg: Box::new(neg.deep_clone(seen_tees)),
3636 metadata: metadata.clone(),
3637 },
3638 HydroNode::AntiJoin { pos, neg, metadata } => HydroNode::AntiJoin {
3639 pos: Box::new(pos.deep_clone(seen_tees)),
3640 neg: Box::new(neg.deep_clone(seen_tees)),
3641 metadata: metadata.clone(),
3642 },
3643 HydroNode::ResolveFutures { input, metadata } => HydroNode::ResolveFutures {
3644 input: Box::new(input.deep_clone(seen_tees)),
3645 metadata: metadata.clone(),
3646 },
3647 HydroNode::ResolveFuturesBlocking { input, metadata } => {
3648 HydroNode::ResolveFuturesBlocking {
3649 input: Box::new(input.deep_clone(seen_tees)),
3650 metadata: metadata.clone(),
3651 }
3652 }
3653 HydroNode::ResolveFuturesOrdered { input, metadata } => {
3654 HydroNode::ResolveFuturesOrdered {
3655 input: Box::new(input.deep_clone(seen_tees)),
3656 metadata: metadata.clone(),
3657 }
3658 }
3659 HydroNode::Map { f, input, metadata } => HydroNode::Map {
3660 f: f.deep_clone(seen_tees),
3661 input: Box::new(input.deep_clone(seen_tees)),
3662 metadata: metadata.clone(),
3663 },
3664 HydroNode::FlatMap { f, input, metadata } => HydroNode::FlatMap {
3665 f: f.deep_clone(seen_tees),
3666 input: Box::new(input.deep_clone(seen_tees)),
3667 metadata: metadata.clone(),
3668 },
3669 HydroNode::FlatMapStreamBlocking { f, input, metadata } => {
3670 HydroNode::FlatMapStreamBlocking {
3671 f: f.deep_clone(seen_tees),
3672 input: Box::new(input.deep_clone(seen_tees)),
3673 metadata: metadata.clone(),
3674 }
3675 }
3676 HydroNode::Filter { f, input, metadata } => HydroNode::Filter {
3677 f: f.deep_clone(seen_tees),
3678 input: Box::new(input.deep_clone(seen_tees)),
3679 metadata: metadata.clone(),
3680 },
3681 HydroNode::FilterMap { f, input, metadata } => HydroNode::FilterMap {
3682 f: f.deep_clone(seen_tees),
3683 input: Box::new(input.deep_clone(seen_tees)),
3684 metadata: metadata.clone(),
3685 },
3686 HydroNode::DeferTick { input, metadata } => HydroNode::DeferTick {
3687 input: Box::new(input.deep_clone(seen_tees)),
3688 metadata: metadata.clone(),
3689 },
3690 HydroNode::Enumerate { input, metadata } => HydroNode::Enumerate {
3691 input: Box::new(input.deep_clone(seen_tees)),
3692 metadata: metadata.clone(),
3693 },
3694 HydroNode::Inspect { f, input, metadata } => HydroNode::Inspect {
3695 f: f.deep_clone(seen_tees),
3696 input: Box::new(input.deep_clone(seen_tees)),
3697 metadata: metadata.clone(),
3698 },
3699 HydroNode::Unique { input, metadata } => HydroNode::Unique {
3700 input: Box::new(input.deep_clone(seen_tees)),
3701 metadata: metadata.clone(),
3702 },
3703 HydroNode::Sort { input, metadata } => HydroNode::Sort {
3704 input: Box::new(input.deep_clone(seen_tees)),
3705 metadata: metadata.clone(),
3706 },
3707 HydroNode::Fold {
3708 init,
3709 acc,
3710 input,
3711 metadata,
3712 } => HydroNode::Fold {
3713 init: init.deep_clone(seen_tees),
3714 acc: acc.deep_clone(seen_tees),
3715 input: Box::new(input.deep_clone(seen_tees)),
3716 metadata: metadata.clone(),
3717 },
3718 HydroNode::Scan {
3719 init,
3720 acc,
3721 input,
3722 metadata,
3723 } => HydroNode::Scan {
3724 init: init.deep_clone(seen_tees),
3725 acc: acc.deep_clone(seen_tees),
3726 input: Box::new(input.deep_clone(seen_tees)),
3727 metadata: metadata.clone(),
3728 },
3729 HydroNode::ScanAsyncBlocking {
3730 init,
3731 acc,
3732 input,
3733 metadata,
3734 } => HydroNode::ScanAsyncBlocking {
3735 init: init.deep_clone(seen_tees),
3736 acc: acc.deep_clone(seen_tees),
3737 input: Box::new(input.deep_clone(seen_tees)),
3738 metadata: metadata.clone(),
3739 },
3740 HydroNode::FoldKeyed {
3741 init,
3742 acc,
3743 input,
3744 metadata,
3745 } => HydroNode::FoldKeyed {
3746 init: init.deep_clone(seen_tees),
3747 acc: acc.deep_clone(seen_tees),
3748 input: Box::new(input.deep_clone(seen_tees)),
3749 metadata: metadata.clone(),
3750 },
3751 HydroNode::ReduceKeyedWatermark {
3752 f,
3753 input,
3754 watermark,
3755 metadata,
3756 } => HydroNode::ReduceKeyedWatermark {
3757 f: f.deep_clone(seen_tees),
3758 input: Box::new(input.deep_clone(seen_tees)),
3759 watermark: Box::new(watermark.deep_clone(seen_tees)),
3760 metadata: metadata.clone(),
3761 },
3762 HydroNode::Reduce { f, input, metadata } => HydroNode::Reduce {
3763 f: f.deep_clone(seen_tees),
3764 input: Box::new(input.deep_clone(seen_tees)),
3765 metadata: metadata.clone(),
3766 },
3767 HydroNode::ReduceKeyed { f, input, metadata } => HydroNode::ReduceKeyed {
3768 f: f.deep_clone(seen_tees),
3769 input: Box::new(input.deep_clone(seen_tees)),
3770 metadata: metadata.clone(),
3771 },
3772 HydroNode::Network {
3773 name,
3774 networking_info,
3775 serialize,
3776 deserialize,
3777 instantiate_fn,
3778 input,
3779 metadata,
3780 } => HydroNode::Network {
3781 name: name.clone(),
3782 networking_info: networking_info.clone(),
3783 serialize: serialize.clone(),
3784 deserialize: deserialize.clone(),
3785 instantiate_fn: instantiate_fn.clone(),
3786 input: Box::new(input.deep_clone(seen_tees)),
3787 metadata: metadata.clone(),
3788 },
3789 HydroNode::ExternalInput {
3790 from_external_key,
3791 from_port_id,
3792 from_many,
3793 codec_type,
3794 port_hint,
3795 instantiate_fn,
3796 deserialize_fn,
3797 metadata,
3798 } => HydroNode::ExternalInput {
3799 from_external_key: *from_external_key,
3800 from_port_id: *from_port_id,
3801 from_many: *from_many,
3802 codec_type: codec_type.clone(),
3803 port_hint: *port_hint,
3804 instantiate_fn: instantiate_fn.clone(),
3805 deserialize_fn: deserialize_fn.clone(),
3806 metadata: metadata.clone(),
3807 },
3808 HydroNode::Counter {
3809 tag,
3810 duration,
3811 prefix,
3812 input,
3813 metadata,
3814 } => HydroNode::Counter {
3815 tag: tag.clone(),
3816 duration: duration.clone(),
3817 prefix: prefix.clone(),
3818 input: Box::new(input.deep_clone(seen_tees)),
3819 metadata: metadata.clone(),
3820 },
3821 HydroNode::VersionedNetworkFork {
3822 channel_id,
3823 channel_name,
3824 senders,
3825 metadata,
3826 } => HydroNode::VersionedNetworkFork {
3827 channel_id: *channel_id,
3828 channel_name: channel_name.clone(),
3829 senders: senders
3830 .iter()
3831 .map(|(version, sender, serialize)| {
3832 (
3833 *version,
3834 Box::new(sender.deep_clone(seen_tees)),
3835 serialize.clone(),
3836 )
3837 })
3838 .collect(),
3839 metadata: metadata.clone(),
3840 },
3841 HydroNode::VersionedNetwork {
3842 fork,
3843 version,
3844 deserialize,
3845 metadata,
3846 } => {
3847 let cloned_fork = if let Some(transformed) = seen_tees.get(&fork.as_ptr()) {
3848 SharedNode(transformed.clone())
3849 } else {
3850 let new_rc = Rc::new(RefCell::new(HydroNode::Placeholder));
3851 seen_tees.insert(fork.as_ptr(), new_rc.clone());
3852 let cloned = fork.0.borrow().deep_clone(seen_tees);
3853 *new_rc.borrow_mut() = cloned;
3854 SharedNode(new_rc)
3855 };
3856 HydroNode::VersionedNetwork {
3857 fork: cloned_fork,
3858 version: *version,
3859 deserialize: deserialize.clone(),
3860 metadata: metadata.clone(),
3861 }
3862 }
3863 }
3864 }
3865
3866 #[cfg(feature = "build")]
3867 pub fn emit_core(
3868 &mut self,
3869 builders_or_callback: &mut BuildersOrCallback<
3870 '_,
3871 impl FnMut(&mut HydroRoot, &mut crate::Counter<StmtId>),
3872 impl FnMut(&mut HydroNode, &mut crate::Counter<StmtId>),
3873 >,
3874 seen_tees: &mut SeenSharedNodes,
3875 built_tees: &mut HashMap<*const RefCell<HydroNode>, Vec<syn::Ident>>,
3876 next_stmt_id: &mut crate::Counter<StmtId>,
3877 fold_hooked_idents: &mut HashSet<String>,
3878 ) -> syn::Ident {
3879 let mut ident_stack: Vec<syn::Ident> = Vec::new();
3880
3881 self.transform_bottom_up(
3882 &mut |node: &mut HydroNode| {
3883 let out_location = node.metadata().location_id.clone();
3884 match node {
3885 HydroNode::Placeholder => {
3886 panic!()
3887 }
3888
3889 HydroNode::Cast { .. } => {
3890 let _ = next_stmt_id.get_and_increment();
3893 match builders_or_callback {
3894 BuildersOrCallback::Builders(_) => {}
3895 BuildersOrCallback::Callback(_, node_callback) => {
3896 node_callback(node, next_stmt_id);
3897 }
3898 }
3899 }
3901
3902 HydroNode::UnboundSingleton { .. } => {
3903 let inner_ident = ident_stack.pop().unwrap();
3904
3905 let stmt_id = next_stmt_id.get_and_increment();
3906 let out_ident =
3907 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3908
3909 match builders_or_callback {
3910 BuildersOrCallback::Builders(graph_builders) => {
3911 if graph_builders.singleton_intermediates() {
3912 graph_builders.add_dfir_at(
3913 &out_location,
3914 parse_quote! {
3915 #out_ident = #inner_ident;
3916 },
3917 None,
3918 );
3919 } else {
3920 graph_builders.add_dfir_at(
3921 &out_location,
3922 parse_quote! {
3923 #out_ident = #inner_ident -> persist::<'static>();
3924 },
3925 None,
3926 );
3927 }
3928 }
3929 BuildersOrCallback::Callback(_, node_callback) => {
3930 node_callback(node, next_stmt_id);
3931 }
3932 }
3933
3934 ident_stack.push(out_ident);
3935 }
3936
3937 HydroNode::AssertIsConsistent { inner, trusted, .. } => {
3938 let inner_ident = ident_stack.pop().unwrap();
3939
3940 let stmt_id = next_stmt_id.get_and_increment();
3941 let out_ident =
3942 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3943
3944 match builders_or_callback {
3945 BuildersOrCallback::Builders(graph_builders) => {
3946 graph_builders.assert_is_consistent(
3947 *trusted,
3948 &inner.metadata().location_id,
3949 inner_ident,
3950 &out_ident,
3951 );
3952 }
3953 BuildersOrCallback::Callback(_, node_callback) => {
3954 node_callback(node, next_stmt_id);
3955 }
3956 }
3957
3958 ident_stack.push(out_ident);
3959 }
3960
3961 HydroNode::ObserveNonDet {
3962 inner,
3963 trusted,
3964 metadata,
3965 ..
3966 } => {
3967 let inner_ident = ident_stack.pop().unwrap();
3968
3969 let stmt_id = next_stmt_id.get_and_increment();
3970 let observe_ident =
3971 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3972
3973 match builders_or_callback {
3974 BuildersOrCallback::Builders(graph_builders) => {
3975 graph_builders.observe_nondet(
3976 *trusted,
3977 &inner.metadata().location_id,
3978 inner_ident,
3979 &inner.metadata().collection_kind,
3980 &observe_ident,
3981 &metadata.collection_kind,
3982 &metadata.op,
3983 );
3984 }
3985 BuildersOrCallback::Callback(_, node_callback) => {
3986 node_callback(node, next_stmt_id);
3987 }
3988 }
3989
3990 ident_stack.push(observe_ident);
3991 }
3992
3993 HydroNode::Batch {
3994 inner, metadata, ..
3995 } => {
3996 let inner_ident = ident_stack.pop().unwrap();
3997
3998 let stmt_id = next_stmt_id.get_and_increment();
3999 let batch_ident =
4000 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4001
4002 match builders_or_callback {
4003 BuildersOrCallback::Builders(graph_builders) => {
4004 graph_builders.batch(
4005 inner_ident,
4006 &inner.metadata().location_id,
4007 &inner.metadata().collection_kind,
4008 &batch_ident,
4009 &out_location,
4010 &metadata.op,
4011 fold_hooked_idents,
4012 );
4013 }
4014 BuildersOrCallback::Callback(_, node_callback) => {
4015 node_callback(node, next_stmt_id);
4016 }
4017 }
4018
4019 ident_stack.push(batch_ident);
4020 }
4021
4022 HydroNode::YieldConcat { inner, .. } => {
4023 let inner_ident = ident_stack.pop().unwrap();
4024
4025 let stmt_id = next_stmt_id.get_and_increment();
4026 let yield_ident =
4027 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4028
4029 match builders_or_callback {
4030 BuildersOrCallback::Builders(graph_builders) => {
4031 graph_builders.yield_from_tick(
4032 inner_ident,
4033 &inner.metadata().location_id,
4034 &inner.metadata().collection_kind,
4035 &yield_ident,
4036 &out_location,
4037 );
4038 }
4039 BuildersOrCallback::Callback(_, node_callback) => {
4040 node_callback(node, next_stmt_id);
4041 }
4042 }
4043
4044 ident_stack.push(yield_ident);
4045 }
4046
4047 HydroNode::BeginAtomic { inner, metadata } => {
4048 let inner_ident = ident_stack.pop().unwrap();
4049
4050 let stmt_id = next_stmt_id.get_and_increment();
4051 let begin_ident =
4052 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4053
4054 match builders_or_callback {
4055 BuildersOrCallback::Builders(graph_builders) => {
4056 graph_builders.begin_atomic(
4057 inner_ident,
4058 &inner.metadata().location_id,
4059 &inner.metadata().collection_kind,
4060 &begin_ident,
4061 &out_location,
4062 &metadata.op,
4063 );
4064 }
4065 BuildersOrCallback::Callback(_, node_callback) => {
4066 node_callback(node, next_stmt_id);
4067 }
4068 }
4069
4070 ident_stack.push(begin_ident);
4071 }
4072
4073 HydroNode::EndAtomic { inner, .. } => {
4074 let inner_ident = ident_stack.pop().unwrap();
4075
4076 let stmt_id = next_stmt_id.get_and_increment();
4077 let end_ident =
4078 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4079
4080 match builders_or_callback {
4081 BuildersOrCallback::Builders(graph_builders) => {
4082 graph_builders.end_atomic(
4083 inner_ident,
4084 &inner.metadata().location_id,
4085 &inner.metadata().collection_kind,
4086 &end_ident,
4087 );
4088 }
4089 BuildersOrCallback::Callback(_, node_callback) => {
4090 node_callback(node, next_stmt_id);
4091 }
4092 }
4093
4094 ident_stack.push(end_ident);
4095 }
4096
4097 HydroNode::Source {
4098 source, metadata, ..
4099 } => {
4100 if let HydroSource::ExternalNetwork() = source {
4101 ident_stack.push(syn::Ident::new("DUMMY", Span::call_site()));
4102 } else {
4103 let stmt_id = next_stmt_id.get_and_increment();
4104 let source_ident =
4105 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4106
4107 let mut tick_source_rhs: Option<TokenStream> = None;
4111
4112 let source_stmt = match source {
4113 HydroSource::Stream(expr) => {
4114 debug_assert!(metadata.location_id.is_top_level());
4115 parse_quote! {
4116 #source_ident = source_stream(#expr);
4117 }
4118 }
4119
4120 HydroSource::ExternalNetwork() => {
4121 unreachable!()
4122 }
4123
4124 HydroSource::Iter(expr) => {
4125 if metadata.location_id.is_root() {
4126 parse_quote! {
4127 #source_ident = source_iter(#expr);
4128 }
4129 } else {
4130 tick_source_rhs = Some(quote! { source_iter(#expr) });
4136 parse_quote! {
4137 #source_ident = source_iter(#expr) -> persist::<'static>();
4138 }
4139 }
4140 }
4141
4142 HydroSource::Spin() => {
4143 debug_assert!(metadata.location_id.is_top_level());
4144 parse_quote! {
4145 #source_ident = spin();
4146 }
4147 }
4148
4149 HydroSource::ClusterMembers(target_loc, state) => {
4150 debug_assert!(metadata.location_id.is_top_level());
4151
4152 let members_tee_ident = syn::Ident::new(
4153 &format!(
4154 "__cluster_members_tee_{}_{}",
4155 metadata.location_id.root().key(),
4156 target_loc.key(),
4157 ),
4158 Span::call_site(),
4159 );
4160
4161 match state {
4162 ClusterMembersState::Stream(d) => {
4163 parse_quote! {
4164 #members_tee_ident = source_stream(#d) -> tee();
4165 #source_ident = #members_tee_ident;
4166 }
4167 },
4168 ClusterMembersState::Uninit => syn::parse_quote! {
4169 #source_ident = source_stream(DUMMY);
4170 },
4171 ClusterMembersState::Tee(..) => parse_quote! {
4172 #source_ident = #members_tee_ident;
4173 },
4174 }
4175 }
4176
4177 HydroSource::Embedded(ident) => {
4178 parse_quote! {
4179 #source_ident = source_stream(#ident);
4180 }
4181 }
4182
4183 HydroSource::EmbeddedSingleton(ident) => {
4184 parse_quote! {
4185 #source_ident = source_iter([#ident]);
4186 }
4187 }
4188 };
4189
4190 match builders_or_callback {
4191 BuildersOrCallback::Builders(graph_builders) => {
4192 if let Some(source_rhs) = tick_source_rhs {
4193 graph_builders.add_tick_source(
4194 &out_location,
4195 source_rhs,
4196 &source_ident,
4197 true,
4198 Some(&stmt_id.to_string()),
4199 );
4200 } else {
4201 graph_builders.add_dfir_at(
4202 &out_location,
4203 source_stmt,
4204 Some(&stmt_id.to_string()),
4205 );
4206 }
4207 }
4208 BuildersOrCallback::Callback(_, node_callback) => {
4209 node_callback(node, next_stmt_id);
4210 }
4211 }
4212
4213 ident_stack.push(source_ident);
4214 }
4215 }
4216
4217 HydroNode::SingletonSource { value, first_tick_only, metadata } => {
4218 let stmt_id = next_stmt_id.get_and_increment();
4219 let source_ident =
4220 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4221
4222 match builders_or_callback {
4223 BuildersOrCallback::Builders(graph_builders) => {
4224 if *first_tick_only {
4225 assert!(
4226 !metadata.location_id.is_top_level(),
4227 "first_tick_only SingletonSource must be inside a tick"
4228 );
4229 graph_builders.add_tick_source(
4231 &out_location,
4232 quote! { source_iter([#value]) },
4233 &source_ident,
4234 false,
4235 Some(&stmt_id.to_string()),
4236 );
4237 } else if metadata.location_id.is_root() {
4238 if metadata.collection_kind.is_bounded() {
4239 graph_builders.add_dfir_at(
4240 &out_location,
4241 parse_quote! {
4242 #source_ident = source_iter([#value]);
4243 },
4244 Some(&stmt_id.to_string()),
4245 );
4246 } else {
4247 graph_builders.add_dfir_at(
4248 &out_location,
4249 parse_quote! {
4250 #source_ident = source_iter([#value]) -> persist::<'static>();
4251 },
4252 Some(&stmt_id.to_string()),
4253 );
4254 }
4255 } else {
4256 graph_builders.add_tick_source(
4261 &out_location,
4262 quote! { source_iter([#value]) },
4263 &source_ident,
4264 true,
4265 Some(&stmt_id.to_string()),
4266 );
4267 }
4268 }
4269 BuildersOrCallback::Callback(_, node_callback) => {
4270 node_callback(node, next_stmt_id);
4271 }
4272 }
4273
4274 ident_stack.push(source_ident);
4275 }
4276
4277 HydroNode::CycleSource { cycle_id, .. } => {
4278 let ident = cycle_id.as_ident();
4279
4280 let _ = next_stmt_id.get_and_increment();
4282
4283 match builders_or_callback {
4284 BuildersOrCallback::Builders(_) => {}
4285 BuildersOrCallback::Callback(_, node_callback) => {
4286 node_callback(node, next_stmt_id);
4287 }
4288 }
4289
4290 ident_stack.push(ident);
4291 }
4292
4293 HydroNode::Tee { inner, .. } => {
4294 let stmt_id = next_stmt_id.get_and_increment();
4297
4298 let ret_ident = if let Some(built_idents) =
4299 built_tees.get(&(std::ptr::from_ref(inner.0.as_ref())))
4300 {
4301 match builders_or_callback {
4302 BuildersOrCallback::Builders(_) => {}
4303 BuildersOrCallback::Callback(_, node_callback) => {
4304 node_callback(node, next_stmt_id);
4305 }
4306 }
4307
4308 built_idents[0].clone()
4309 } else {
4310 let inner_ident = ident_stack.pop().unwrap();
4313
4314 let tee_ident =
4315 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4316
4317 built_tees.insert(
4318 std::ptr::from_ref(inner.0.as_ref()),
4319 vec![tee_ident.clone()],
4320 );
4321
4322 match builders_or_callback {
4323 BuildersOrCallback::Builders(graph_builders) => {
4324 if fold_hooked_idents.contains(&inner_ident.to_string()) {
4336 fold_hooked_idents.insert(tee_ident.to_string());
4337 }
4338 graph_builders.add_dfir_at(
4339 &out_location,
4340 parse_quote! {
4341 #tee_ident = #inner_ident -> tee();
4342 },
4343 Some(&stmt_id.to_string()),
4344 );
4345 }
4346 BuildersOrCallback::Callback(_, node_callback) => {
4347 node_callback(node, next_stmt_id);
4348 }
4349 }
4350
4351 tee_ident
4352 };
4353
4354 ident_stack.push(ret_ident);
4355 }
4356
4357 HydroNode::Reference { inner, kind, .. } => {
4358 let stmt_id = next_stmt_id.get_and_increment();
4361
4362 let ret_ident = if let Some(built_idents) =
4363 built_tees.get(&(std::ptr::from_ref(inner.0.as_ref())))
4364 {
4365 built_idents[0].clone()
4366 } else {
4367 let inner_ident = ident_stack.pop().unwrap();
4368
4369 let ref_ident =
4370 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4371
4372 built_tees.insert(
4373 std::ptr::from_ref(inner.0.as_ref()),
4374 vec![ref_ident.clone()],
4375 );
4376
4377 match builders_or_callback {
4378 BuildersOrCallback::Builders(graph_builders) => {
4379 let op_ident = syn::Ident::new(
4380 match kind {
4381 crate::handoff_ref::HandoffRefKind::Singleton => "singleton",
4382 crate::handoff_ref::HandoffRefKind::Optional => "optional",
4383 crate::handoff_ref::HandoffRefKind::Vec => "handoff",
4384 },
4385 Span::call_site(),
4386 );
4387 graph_builders.add_dfir_at(
4388 &out_location,
4389 parse_quote! {
4390 #ref_ident = #inner_ident -> #op_ident();
4391 },
4392 Some(&stmt_id.to_string()),
4393 );
4394 }
4395 BuildersOrCallback::Callback(_, node_callback) => {
4396 node_callback(node, next_stmt_id);
4397 }
4398 }
4399
4400 ref_ident
4401 };
4402
4403 ident_stack.push(ret_ident);
4404 }
4405
4406 HydroNode::PartitionSide {
4407 inner, is_true, metadata: _,
4408 } => {
4409 let is_true = *is_true; let ptr = std::ptr::from_ref(inner.0.as_ref());
4411 let stmt_id = next_stmt_id.get_and_increment();
4412
4413 let ret_ident = if let Some(built_idents) = built_tees.get(&ptr) {
4414 match builders_or_callback {
4415 BuildersOrCallback::Builders(_) => {}
4416 BuildersOrCallback::Callback(_, node_callback) => {
4417 node_callback(node, next_stmt_id);
4418 }
4419 }
4420
4421 let idx = if is_true { 0 } else { 1 };
4422 built_idents[idx].clone()
4423 } else {
4424 let partition_ident = ident_stack.pop().unwrap();
4427
4428 let true_ident = syn::Ident::new(
4429 &format!("stream_{}_true", stmt_id),
4430 Span::call_site(),
4431 );
4432 let false_ident = syn::Ident::new(
4433 &format!("stream_{}_false", stmt_id),
4434 Span::call_site(),
4435 );
4436
4437 built_tees.insert(
4438 ptr,
4439 vec![true_ident.clone(), false_ident.clone()],
4440 );
4441
4442 let stmt_id = next_stmt_id.get_and_increment();
4443 match builders_or_callback {
4444 BuildersOrCallback::Builders(graph_builders) => {
4445 graph_builders.add_dfir_at(
4446 &out_location,
4447 parse_quote! {
4448 #true_ident = #partition_ident[0];
4449 #false_ident = #partition_ident[1];
4450 },
4451 Some(&stmt_id.to_string()),
4452 );
4453 }
4454 BuildersOrCallback::Callback(_, node_callback) => {
4455 node_callback(node, next_stmt_id);
4456 }
4457 }
4458
4459 if is_true { true_ident } else { false_ident }
4460 };
4461
4462 ident_stack.push(ret_ident);
4463 }
4464
4465 HydroNode::PartitionShared { input, f, metadata } => {
4466 let inner_ident = ident_stack.pop().unwrap();
4469 let f_tokens = f.emit_tokens(&mut ident_stack);
4470
4471 let inner_ident = {
4472 maybe_observe_for_mut(
4473 f, inner_ident,
4474 &input.metadata().location_id,
4475 &input.metadata().collection_kind,
4476 &metadata.op,
4477 builders_or_callback, next_stmt_id,
4478 )
4479 };
4480
4481 let stmt_id = next_stmt_id.get_and_increment();
4482 let partition_ident = syn::Ident::new(
4483 &format!("stream_{}_partition", stmt_id),
4484 Span::call_site(),
4485 );
4486
4487 let stmt_id = next_stmt_id.get_and_increment();
4488 match builders_or_callback {
4489 BuildersOrCallback::Builders(graph_builders) => {
4490 graph_builders.add_dfir_at(
4491 &out_location,
4492 parse_quote! {
4493 #partition_ident = #inner_ident -> partition(|__item, __num_outputs| if (#f_tokens)(__item) { 0_usize } else { 1_usize });
4494 },
4495 Some(&stmt_id.to_string()),
4496 );
4497 }
4498 BuildersOrCallback::Callback(_, node_callback) => {
4499 node_callback(node, next_stmt_id);
4500 }
4501 }
4502 ident_stack.push(partition_ident);
4503 }
4504
4505 HydroNode::Chain { .. } => {
4506 let second_ident = ident_stack.pop().unwrap();
4508 let first_ident = ident_stack.pop().unwrap();
4509
4510 let stmt_id = next_stmt_id.get_and_increment();
4511 let chain_ident =
4512 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4513
4514 match builders_or_callback {
4515 BuildersOrCallback::Builders(graph_builders) => {
4516 graph_builders.add_dfir_at(
4517 &out_location,
4518 parse_quote! {
4519 #chain_ident = chain();
4520 #first_ident -> [0]#chain_ident;
4521 #second_ident -> [1]#chain_ident;
4522 },
4523 Some(&stmt_id.to_string()),
4524 );
4525 }
4526 BuildersOrCallback::Callback(_, node_callback) => {
4527 node_callback(node, next_stmt_id);
4528 }
4529 }
4530
4531 ident_stack.push(chain_ident);
4532 }
4533
4534 HydroNode::MergeOrdered { first, metadata, .. } => {
4535 let second_ident = ident_stack.pop().unwrap();
4536 let first_ident = ident_stack.pop().unwrap();
4537
4538 let stmt_id = next_stmt_id.get_and_increment();
4539 let merge_ident =
4540 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4541
4542 match builders_or_callback {
4543 BuildersOrCallback::Builders(graph_builders) => {
4544 graph_builders.merge_ordered(
4545 &first.metadata().location_id,
4546 first_ident,
4547 second_ident,
4548 &merge_ident,
4549 &first.metadata().collection_kind,
4550 &metadata.op,
4551 Some(&stmt_id.to_string()),
4552 );
4553 }
4554 BuildersOrCallback::Callback(_, node_callback) => {
4555 node_callback(node, next_stmt_id);
4556 }
4557 }
4558
4559 ident_stack.push(merge_ident);
4560 }
4561
4562 HydroNode::ChainFirst { .. } => {
4563 let second_ident = ident_stack.pop().unwrap();
4564 let first_ident = ident_stack.pop().unwrap();
4565
4566 let stmt_id = next_stmt_id.get_and_increment();
4567 let chain_ident =
4568 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4569
4570 match builders_or_callback {
4571 BuildersOrCallback::Builders(graph_builders) => {
4572 graph_builders.add_dfir_at(
4573 &out_location,
4574 parse_quote! {
4575 #chain_ident = chain_first_n(1);
4576 #first_ident -> [0]#chain_ident;
4577 #second_ident -> [1]#chain_ident;
4578 },
4579 Some(&stmt_id.to_string()),
4580 );
4581 }
4582 BuildersOrCallback::Callback(_, node_callback) => {
4583 node_callback(node, next_stmt_id);
4584 }
4585 }
4586
4587 ident_stack.push(chain_ident);
4588 }
4589
4590 HydroNode::CrossSingleton { right, .. } => {
4591 let right_ident = ident_stack.pop().unwrap();
4592 let left_ident = ident_stack.pop().unwrap();
4593
4594 let stmt_id = next_stmt_id.get_and_increment();
4595 let cross_ident =
4596 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4597
4598 match builders_or_callback {
4599 BuildersOrCallback::Builders(graph_builders) => {
4600 if right.metadata().location_id.is_top_level()
4601 && right.metadata().collection_kind.is_bounded()
4602 {
4603 let lifetime =
4604 graph_builders.cross_tick_state_lifetime(&out_location);
4605 graph_builders.add_dfir_at(
4606 &out_location,
4607 parse_quote! {
4608 #cross_ident = cross_singleton::<#lifetime>();
4609 #left_ident -> [input]#cross_ident;
4610 #right_ident -> [single]#cross_ident;
4611 },
4612 Some(&stmt_id.to_string()),
4613 );
4614 } else {
4615 graph_builders.add_dfir_at(
4616 &out_location,
4617 parse_quote! {
4618 #cross_ident = cross_singleton();
4619 #left_ident -> [input]#cross_ident;
4620 #right_ident -> [single]#cross_ident;
4621 },
4622 Some(&stmt_id.to_string()),
4623 );
4624 }
4625 }
4626 BuildersOrCallback::Callback(_, node_callback) => {
4627 node_callback(node, next_stmt_id);
4628 }
4629 }
4630
4631 ident_stack.push(cross_ident);
4632 }
4633
4634 HydroNode::CrossProduct { .. } | HydroNode::Join { .. } => {
4635 let operator: syn::Ident = if matches!(node, HydroNode::CrossProduct { .. }) {
4636 parse_quote!(cross_join_multiset)
4637 } else {
4638 parse_quote!(join_multiset)
4639 };
4640
4641 let (HydroNode::CrossProduct { left, right, .. }
4642 | HydroNode::Join { left, right, .. }) = node
4643 else {
4644 unreachable!()
4645 };
4646
4647 let is_top_level = left.metadata().location_id.is_top_level()
4648 && right.metadata().location_id.is_top_level();
4649 let left_top_level = left.metadata().location_id.is_top_level();
4650 let right_top_level = right.metadata().location_id.is_top_level();
4651
4652 let right_ident = ident_stack.pop().unwrap();
4653 let left_ident = ident_stack.pop().unwrap();
4654
4655 let stmt_id = next_stmt_id.get_and_increment();
4656 let stream_ident =
4657 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4658
4659 match builders_or_callback {
4660 BuildersOrCallback::Builders(graph_builders) => {
4661 let left_lifetime = if left_top_level {
4662 graph_builders.cross_tick_state_lifetime(&out_location)
4663 } else {
4664 graph_builders.tick_state_lifetime(&out_location)
4665 };
4666
4667 let right_lifetime = if right_top_level {
4668 graph_builders.cross_tick_state_lifetime(&out_location)
4669 } else {
4670 graph_builders.tick_state_lifetime(&out_location)
4671 };
4672
4673 graph_builders.add_dfir_at(
4674 &out_location,
4675 if is_top_level {
4676 parse_quote! {
4679 #stream_ident = #operator::<#left_lifetime, #right_lifetime>() -> multiset_delta();
4680 #left_ident -> [0]#stream_ident;
4681 #right_ident -> [1]#stream_ident;
4682 }
4683 } else {
4684 parse_quote! {
4685 #stream_ident = #operator::<#left_lifetime, #right_lifetime>();
4686 #left_ident -> [0]#stream_ident;
4687 #right_ident -> [1]#stream_ident;
4688 }
4689 },
4690 Some(&stmt_id.to_string()),
4691 );
4692 }
4693 BuildersOrCallback::Callback(_, node_callback) => {
4694 node_callback(node, next_stmt_id);
4695 }
4696 }
4697
4698 ident_stack.push(stream_ident);
4699 }
4700
4701 HydroNode::Difference { .. } | HydroNode::AntiJoin { .. } => {
4702 let operator: syn::Ident = if matches!(node, HydroNode::Difference { .. }) {
4703 parse_quote!(difference)
4704 } else {
4705 parse_quote!(anti_join)
4706 };
4707
4708 let (HydroNode::Difference { neg, .. } | HydroNode::AntiJoin { neg, .. }) =
4709 node
4710 else {
4711 unreachable!()
4712 };
4713
4714 let neg_top_level = neg.metadata().location_id.is_top_level();
4715
4716 let neg_ident = ident_stack.pop().unwrap();
4717 let pos_ident = ident_stack.pop().unwrap();
4718
4719 let stmt_id = next_stmt_id.get_and_increment();
4720 let stream_ident =
4721 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4722
4723 match builders_or_callback {
4724 BuildersOrCallback::Builders(graph_builders) => {
4725 let neg_lifetime = if neg_top_level {
4726 graph_builders.cross_tick_state_lifetime(&out_location)
4727 } else {
4728 graph_builders.tick_state_lifetime(&out_location)
4729 };
4730 let pos_lifetime =
4731 graph_builders.tick_state_lifetime(&out_location);
4732
4733 graph_builders.add_dfir_at(
4734 &out_location,
4735 parse_quote! {
4736 #stream_ident = #operator::<#pos_lifetime, #neg_lifetime>();
4737 #pos_ident -> [pos]#stream_ident;
4738 #neg_ident -> [neg]#stream_ident;
4739 },
4740 Some(&stmt_id.to_string()),
4741 );
4742 }
4743 BuildersOrCallback::Callback(_, node_callback) => {
4744 node_callback(node, next_stmt_id);
4745 }
4746 }
4747
4748 ident_stack.push(stream_ident);
4749 }
4750
4751 HydroNode::JoinHalf { .. } => {
4752 let HydroNode::JoinHalf { right, .. } = node else {
4753 unreachable!()
4754 };
4755
4756 assert!(
4757 right.metadata().collection_kind.is_bounded(),
4758 "JoinHalf requires the right (build) side to be Bounded, got {:?}",
4759 right.metadata().collection_kind
4760 );
4761
4762 let build_top_level = right.metadata().location_id.is_top_level();
4763
4764 let build_ident = ident_stack.pop().unwrap();
4765 let probe_ident = ident_stack.pop().unwrap();
4766
4767 let stmt_id = next_stmt_id.get_and_increment();
4768 let stream_ident =
4769 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4770
4771 match builders_or_callback {
4772 BuildersOrCallback::Builders(graph_builders) => {
4773 let build_lifetime = if build_top_level {
4774 graph_builders.cross_tick_state_lifetime(&out_location)
4775 } else {
4776 graph_builders.tick_state_lifetime(&out_location)
4777 };
4778 let probe_lifetime =
4779 graph_builders.tick_state_lifetime(&out_location);
4780
4781 graph_builders.add_dfir_at(
4782 &out_location,
4783 parse_quote! {
4784 #stream_ident = join_multiset_half::<#build_lifetime, #probe_lifetime>();
4785 #probe_ident -> [probe]#stream_ident;
4786 #build_ident -> [build]#stream_ident;
4787 },
4788 Some(&stmt_id.to_string()),
4789 );
4790 }
4791 BuildersOrCallback::Callback(_, node_callback) => {
4792 node_callback(node, next_stmt_id);
4793 }
4794 }
4795
4796 ident_stack.push(stream_ident);
4797 }
4798
4799 HydroNode::ResolveFutures { .. } => {
4800 let input_ident = ident_stack.pop().unwrap();
4801
4802 let stmt_id = next_stmt_id.get_and_increment();
4803 let futures_ident =
4804 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4805
4806 match builders_or_callback {
4807 BuildersOrCallback::Builders(graph_builders) => {
4808 graph_builders.add_dfir_at(
4809 &out_location,
4810 parse_quote! {
4811 #futures_ident = #input_ident -> resolve_futures();
4812 },
4813 Some(&stmt_id.to_string()),
4814 );
4815 }
4816 BuildersOrCallback::Callback(_, node_callback) => {
4817 node_callback(node, next_stmt_id);
4818 }
4819 }
4820
4821 ident_stack.push(futures_ident);
4822 }
4823
4824 HydroNode::ResolveFuturesBlocking { .. } => {
4825 let input_ident = ident_stack.pop().unwrap();
4826
4827 let stmt_id = next_stmt_id.get_and_increment();
4828 let futures_ident =
4829 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4830
4831 match builders_or_callback {
4832 BuildersOrCallback::Builders(graph_builders) => {
4833 graph_builders.add_dfir_at(
4834 &out_location,
4835 parse_quote! {
4836 #futures_ident = #input_ident -> resolve_futures_blocking();
4837 },
4838 Some(&stmt_id.to_string()),
4839 );
4840 }
4841 BuildersOrCallback::Callback(_, node_callback) => {
4842 node_callback(node, next_stmt_id);
4843 }
4844 }
4845
4846 ident_stack.push(futures_ident);
4847 }
4848
4849 HydroNode::ResolveFuturesOrdered { .. } => {
4850 let input_ident = ident_stack.pop().unwrap();
4851
4852 let stmt_id = next_stmt_id.get_and_increment();
4853 let futures_ident =
4854 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4855
4856 match builders_or_callback {
4857 BuildersOrCallback::Builders(graph_builders) => {
4858 graph_builders.add_dfir_at(
4859 &out_location,
4860 parse_quote! {
4861 #futures_ident = #input_ident -> resolve_futures_ordered();
4862 },
4863 Some(&stmt_id.to_string()),
4864 );
4865 }
4866 BuildersOrCallback::Callback(_, node_callback) => {
4867 node_callback(node, next_stmt_id);
4868 }
4869 }
4870
4871 ident_stack.push(futures_ident);
4872 }
4873
4874 HydroNode::Map {
4875 f,
4876 input,
4877 metadata,
4878 } => {
4879 let input_ident = ident_stack.pop().unwrap();
4881 let f_tokens = f.emit_tokens(&mut ident_stack);
4882
4883 let input_ident = maybe_observe_for_mut(
4884 f,
4885 input_ident,
4886 &input.metadata().location_id,
4887 &input.metadata().collection_kind,
4888 &metadata.op,
4889 builders_or_callback,
4890 next_stmt_id,
4891 );
4892
4893 let stmt_id = next_stmt_id.get_and_increment();
4894 let map_ident =
4895 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4896
4897 match builders_or_callback {
4898 BuildersOrCallback::Builders(graph_builders) => {
4899 graph_builders.add_dfir_at(
4900 &out_location,
4901 parse_quote! {
4902 #map_ident = #input_ident -> map(#f_tokens);
4903 },
4904 Some(&stmt_id.to_string()),
4905 );
4906 }
4907 BuildersOrCallback::Callback(_, node_callback) => {
4908 node_callback(node, next_stmt_id);
4909 }
4910 }
4911
4912 ident_stack.push(map_ident);
4913 }
4914
4915 HydroNode::FlatMap { f, input, metadata } => {
4916 let input_ident = ident_stack.pop().unwrap();
4917 let f_tokens = f.emit_tokens(&mut ident_stack);
4918
4919 let input_ident = maybe_observe_for_mut(
4920 f, input_ident,
4921 &input.metadata().location_id,
4922 &input.metadata().collection_kind,
4923 &metadata.op,
4924 builders_or_callback, next_stmt_id,
4925 );
4926
4927 let stmt_id = next_stmt_id.get_and_increment();
4928 let flat_map_ident =
4929 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4930
4931 match builders_or_callback {
4932 BuildersOrCallback::Builders(graph_builders) => {
4933 graph_builders.add_dfir_at(
4934 &out_location,
4935 parse_quote! {
4936 #flat_map_ident = #input_ident -> flat_map(#f_tokens);
4937 },
4938 Some(&stmt_id.to_string()),
4939 );
4940 }
4941 BuildersOrCallback::Callback(_, node_callback) => {
4942 node_callback(node, next_stmt_id);
4943 }
4944 }
4945
4946 ident_stack.push(flat_map_ident);
4947 }
4948
4949 HydroNode::FlatMapStreamBlocking { f, input, metadata } => {
4950 let input_ident = ident_stack.pop().unwrap();
4951 let f_tokens = f.emit_tokens(&mut ident_stack);
4952
4953 let input_ident = maybe_observe_for_mut(
4954 f, input_ident,
4955 &input.metadata().location_id,
4956 &input.metadata().collection_kind,
4957 &metadata.op,
4958 builders_or_callback, next_stmt_id,
4959 );
4960
4961 let stmt_id = next_stmt_id.get_and_increment();
4962 let flat_map_stream_blocking_ident =
4963 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4964
4965 match builders_or_callback {
4966 BuildersOrCallback::Builders(graph_builders) => {
4967 graph_builders.add_dfir_at(
4968 &out_location,
4969 parse_quote! {
4970 #flat_map_stream_blocking_ident = #input_ident -> flat_map_stream_blocking(#f_tokens);
4971 },
4972 Some(&stmt_id.to_string()),
4973 );
4974 }
4975 BuildersOrCallback::Callback(_, node_callback) => {
4976 node_callback(node, next_stmt_id);
4977 }
4978 }
4979
4980 ident_stack.push(flat_map_stream_blocking_ident);
4981 }
4982
4983 HydroNode::Filter { f, input, metadata } => {
4984 let input_ident = ident_stack.pop().unwrap();
4985 let f_tokens = f.emit_tokens(&mut ident_stack);
4986
4987 let input_ident = maybe_observe_for_mut(
4988 f, input_ident,
4989 &input.metadata().location_id,
4990 &input.metadata().collection_kind,
4991 &metadata.op,
4992 builders_or_callback, next_stmt_id,
4993 );
4994
4995 let stmt_id = next_stmt_id.get_and_increment();
4996 let filter_ident =
4997 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4998
4999 match builders_or_callback {
5000 BuildersOrCallback::Builders(graph_builders) => {
5001 graph_builders.add_dfir_at(
5002 &out_location,
5003 parse_quote! {
5004 #filter_ident = #input_ident -> filter(#f_tokens);
5005 },
5006 Some(&stmt_id.to_string()),
5007 );
5008 }
5009 BuildersOrCallback::Callback(_, node_callback) => {
5010 node_callback(node, next_stmt_id);
5011 }
5012 }
5013
5014 ident_stack.push(filter_ident);
5015 }
5016
5017 HydroNode::FilterMap { f, input, metadata } => {
5018 let input_ident = ident_stack.pop().unwrap();
5019 let f_tokens = f.emit_tokens(&mut ident_stack);
5020
5021 let input_ident = maybe_observe_for_mut(
5022 f, input_ident,
5023 &input.metadata().location_id,
5024 &input.metadata().collection_kind,
5025 &metadata.op,
5026 builders_or_callback, next_stmt_id,
5027 );
5028
5029 let stmt_id = next_stmt_id.get_and_increment();
5030 let filter_map_ident =
5031 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5032
5033 match builders_or_callback {
5034 BuildersOrCallback::Builders(graph_builders) => {
5035 graph_builders.add_dfir_at(
5036 &out_location,
5037 parse_quote! {
5038 #filter_map_ident = #input_ident -> filter_map(#f_tokens);
5039 },
5040 Some(&stmt_id.to_string()),
5041 );
5042 }
5043 BuildersOrCallback::Callback(_, node_callback) => {
5044 node_callback(node, next_stmt_id);
5045 }
5046 }
5047
5048 ident_stack.push(filter_map_ident);
5049 }
5050
5051 HydroNode::Sort { .. } => {
5052 let input_ident = ident_stack.pop().unwrap();
5053
5054 let stmt_id = next_stmt_id.get_and_increment();
5055 let sort_ident =
5056 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5057
5058 match builders_or_callback {
5059 BuildersOrCallback::Builders(graph_builders) => {
5060 graph_builders.add_dfir_at(
5061 &out_location,
5062 parse_quote! {
5063 #sort_ident = #input_ident -> sort();
5064 },
5065 Some(&stmt_id.to_string()),
5066 );
5067 }
5068 BuildersOrCallback::Callback(_, node_callback) => {
5069 node_callback(node, next_stmt_id);
5070 }
5071 }
5072
5073 ident_stack.push(sort_ident);
5074 }
5075
5076 HydroNode::DeferTick { .. } => {
5077 let input_ident = ident_stack.pop().unwrap();
5078
5079 let stmt_id = next_stmt_id.get_and_increment();
5080 let defer_tick_ident =
5081 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5082
5083 match builders_or_callback {
5084 BuildersOrCallback::Builders(graph_builders) => {
5085 graph_builders.add_dfir_at(
5086 &out_location,
5087 parse_quote! {
5088 #defer_tick_ident = #input_ident -> defer_tick_lazy();
5089 },
5090 Some(&stmt_id.to_string()),
5091 );
5092 }
5093 BuildersOrCallback::Callback(_, node_callback) => {
5094 node_callback(node, next_stmt_id);
5095 }
5096 }
5097
5098 ident_stack.push(defer_tick_ident);
5099 }
5100
5101 HydroNode::Enumerate { input, .. } => {
5102 let input_ident = ident_stack.pop().unwrap();
5103
5104 let stmt_id = next_stmt_id.get_and_increment();
5105 let enumerate_ident =
5106 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5107
5108 match builders_or_callback {
5109 BuildersOrCallback::Builders(graph_builders) => {
5110 let lifetime = if input.metadata().location_id.is_top_level() {
5111 graph_builders.cross_tick_state_lifetime(&out_location)
5112 } else {
5113 graph_builders.tick_state_lifetime(&out_location)
5114 };
5115 graph_builders.add_dfir_at(
5116 &out_location,
5117 parse_quote! {
5118 #enumerate_ident = #input_ident -> enumerate::<#lifetime>();
5119 },
5120 Some(&stmt_id.to_string()),
5121 );
5122 }
5123 BuildersOrCallback::Callback(_, node_callback) => {
5124 node_callback(node, next_stmt_id);
5125 }
5126 }
5127
5128 ident_stack.push(enumerate_ident);
5129 }
5130
5131 HydroNode::Inspect { f, input, metadata } => {
5132 let input_ident = ident_stack.pop().unwrap();
5133 let f_tokens = f.emit_tokens(&mut ident_stack);
5134
5135 let input_ident = maybe_observe_for_mut(
5136 f, input_ident,
5137 &input.metadata().location_id,
5138 &input.metadata().collection_kind,
5139 &metadata.op,
5140 builders_or_callback, next_stmt_id,
5141 );
5142
5143 let stmt_id = next_stmt_id.get_and_increment();
5144 let inspect_ident =
5145 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5146
5147 match builders_or_callback {
5148 BuildersOrCallback::Builders(graph_builders) => {
5149 graph_builders.add_dfir_at(
5150 &out_location,
5151 parse_quote! {
5152 #inspect_ident = #input_ident -> inspect(#f_tokens);
5153 },
5154 Some(&stmt_id.to_string()),
5155 );
5156 }
5157 BuildersOrCallback::Callback(_, node_callback) => {
5158 node_callback(node, next_stmt_id);
5159 }
5160 }
5161
5162 ident_stack.push(inspect_ident);
5163 }
5164
5165 HydroNode::Unique { input, .. } => {
5166 let input_ident = ident_stack.pop().unwrap();
5167
5168 let stmt_id = next_stmt_id.get_and_increment();
5169 let unique_ident =
5170 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5171
5172 match builders_or_callback {
5173 BuildersOrCallback::Builders(graph_builders) => {
5174 let lifetime = if input.metadata().location_id.is_top_level() {
5175 graph_builders.cross_tick_state_lifetime(&out_location)
5176 } else {
5177 graph_builders.tick_state_lifetime(&out_location)
5178 };
5179
5180 graph_builders.add_dfir_at(
5181 &out_location,
5182 parse_quote! {
5183 #unique_ident = #input_ident -> unique::<#lifetime>();
5184 },
5185 Some(&stmt_id.to_string()),
5186 );
5187 }
5188 BuildersOrCallback::Callback(_, node_callback) => {
5189 node_callback(node, next_stmt_id);
5190 }
5191 }
5192
5193 ident_stack.push(unique_ident);
5194 }
5195
5196 HydroNode::Fold { .. } | HydroNode::FoldKeyed { .. } | HydroNode::Scan { .. } | HydroNode::ScanAsyncBlocking { .. } => {
5197 let operator: syn::Ident = if let HydroNode::Fold { input, .. } = node {
5198 if input.metadata().location_id.is_root()
5199 && input.metadata().collection_kind.is_bounded()
5200 {
5201 parse_quote!(fold_no_replay)
5202 } else {
5203 parse_quote!(fold)
5204 }
5205 } else if matches!(node, HydroNode::Scan { .. }) {
5206 parse_quote!(scan)
5207 } else if matches!(node, HydroNode::ScanAsyncBlocking { .. }) {
5208 parse_quote!(scan_async_blocking)
5209 } else if let HydroNode::FoldKeyed { input, .. } = node {
5210 if input.metadata().location_id.is_root()
5211 && input.metadata().collection_kind.is_bounded()
5212 {
5213 todo!("Fold keyed on a top-level bounded collection is not yet supported")
5214 } else {
5215 parse_quote!(fold_keyed)
5216 }
5217 } else {
5218 unreachable!()
5219 };
5220
5221 let (HydroNode::Fold { input, .. }
5222 | HydroNode::FoldKeyed { input, .. }
5223 | HydroNode::Scan { input, .. }
5224 | HydroNode::ScanAsyncBlocking { input, .. }) = node
5225 else {
5226 unreachable!()
5227 };
5228
5229 let input_top_level = input.metadata().location_id.is_top_level();
5230
5231 let input_ident = ident_stack.pop().unwrap();
5232
5233 let (HydroNode::Fold { init, acc, .. }
5234 | HydroNode::FoldKeyed { init, acc, .. }
5235 | HydroNode::Scan { init, acc, .. }
5236 | HydroNode::ScanAsyncBlocking { init, acc, .. }) = &*node
5237 else {
5238 unreachable!()
5239 };
5240
5241 let acc_tokens = acc.emit_tokens(&mut ident_stack);
5242 let init_tokens = init.emit_tokens(&mut ident_stack);
5243
5244 let stmt_id = next_stmt_id.get_and_increment();
5245 let fold_ident =
5246 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5247
5248 match builders_or_callback {
5249 BuildersOrCallback::Builders(graph_builders) => {
5250 let lifetime = if input_top_level {
5251 graph_builders.cross_tick_state_lifetime(&out_location)
5252 } else {
5253 graph_builders.tick_state_lifetime(&out_location)
5254 };
5255
5256 if matches!(node, HydroNode::Fold { .. })
5257 && node.metadata().location_id.is_root()
5258 && graph_builders.singleton_intermediates()
5259 && !node.metadata().collection_kind.is_bounded()
5260 {
5261 let HydroNode::Fold { input, .. } = &*node else { unreachable!() };
5262 let hooked_input_ident = graph_builders.emit_fold_hook(
5263 &input.metadata().location_id,
5264 &input_ident,
5265 &input.metadata().collection_kind,
5266 &node.metadata().op,
5267 );
5268
5269 let (effective_input, wrapped_acc) = if let Some(ref hooked) = hooked_input_ident {
5270 let acc: syn::Expr = parse_quote!({
5271 let mut __inner = #acc_tokens;
5272 move |__state, __batch: Vec<_>| {
5273 if __batch.is_empty() {
5274 return None;
5275 }
5276 for __value in __batch {
5277 __inner(__state, __value);
5278 }
5279 Some(__state.clone())
5280 }
5281 });
5282 (hooked, acc)
5283 } else {
5284 let acc: syn::Expr = parse_quote!({
5285 let mut __inner = #acc_tokens;
5286 move |__state, __value| {
5287 __inner(__state, __value);
5288 Some(__state.clone())
5289 }
5290 });
5291 (&input_ident, acc)
5292 };
5293
5294 graph_builders.add_dfir_at(
5295 &out_location,
5296 parse_quote! {
5297 source_iter([(#init_tokens)()]) -> [0]#fold_ident;
5298 #effective_input -> scan::<#lifetime>(#init_tokens, #wrapped_acc) -> [1]#fold_ident;
5299 #fold_ident = chain();
5300 },
5301 Some(&stmt_id.to_string()),
5302 );
5303
5304 if hooked_input_ident.is_some()
5309 && node.metadata().op.sim_hook_id.is_none()
5310 {
5311 fold_hooked_idents.insert(fold_ident.to_string());
5312 }
5313 } else if matches!(node, HydroNode::FoldKeyed { .. })
5314 && node.metadata().location_id.is_root()
5315 && graph_builders.singleton_intermediates()
5316 && !node.metadata().collection_kind.is_bounded()
5317 {
5318 let HydroNode::FoldKeyed { input, .. } = &*node else { unreachable!() };
5319 let hooked_input_ident = graph_builders.emit_fold_hook(
5320 &input.metadata().location_id,
5321 &input_ident,
5322 &input.metadata().collection_kind,
5323 &node.metadata().op,
5324 );
5325
5326 let wrapped_acc: syn::Expr = parse_quote!({
5327 let mut __init = #init_tokens;
5328 let mut __inner = #acc_tokens;
5329 move |__state, __kv: (_, _)| {
5330 let __state = __state
5332 .entry(::std::clone::Clone::clone(&__kv.0))
5333 .or_insert_with(|| (__init)());
5334 __inner(__state, __kv.1);
5335 Some((__kv.0, ::std::clone::Clone::clone(&*__state)))
5336 }
5337 });
5338
5339 if let Some(hooked_input_ident) = hooked_input_ident {
5340 graph_builders.add_dfir_at(
5341 &out_location,
5342 parse_quote! {
5343 #fold_ident = #hooked_input_ident -> flatten() -> scan::<#lifetime>(|| ::std::collections::HashMap::new(), #wrapped_acc);
5344 },
5345 Some(&stmt_id.to_string()),
5346 );
5347
5348 fold_hooked_idents.insert(fold_ident.to_string());
5349 } else {
5350 graph_builders.add_dfir_at(
5351 &out_location,
5352 parse_quote! {
5353 #fold_ident = #input_ident -> scan::<#lifetime>(|| ::std::collections::HashMap::new(), #wrapped_acc);
5354 },
5355 Some(&stmt_id.to_string()),
5356 );
5357 }
5358 } else if (matches!(node, HydroNode::Fold { .. })
5359 || matches!(node, HydroNode::FoldKeyed { .. }))
5360 && !node.metadata().location_id.is_top_level()
5361 && graph_builders.singleton_intermediates()
5362 {
5363 let input_ref = match &*node {
5364 HydroNode::Fold { input, .. } => input,
5365 HydroNode::FoldKeyed { input, .. } => input,
5366 _ => unreachable!(),
5367 };
5368 let hooked_input_ident = graph_builders.emit_fold_hook(
5369 &input_ref.metadata().location_id,
5370 &input_ident,
5371 &input_ref.metadata().collection_kind,
5372 &node.metadata().op,
5373 );
5374
5375 let actual_input = hooked_input_ident.as_ref().unwrap_or(&input_ident);
5376 graph_builders.add_dfir_at(
5377 &out_location,
5378 parse_quote! {
5379 #fold_ident = #actual_input -> #operator::<#lifetime>(#init_tokens, #acc_tokens);
5380 },
5381 Some(&stmt_id.to_string()),
5382 );
5383 } else {
5384 graph_builders.add_dfir_at(
5385 &out_location,
5386 parse_quote! {
5387 #fold_ident = #input_ident -> #operator::<#lifetime>(#init_tokens, #acc_tokens);
5388 },
5389 Some(&stmt_id.to_string()),
5390 );
5391 }
5392 }
5393 BuildersOrCallback::Callback(_, node_callback) => {
5394 node_callback(node, next_stmt_id);
5395 }
5396 }
5397
5398 ident_stack.push(fold_ident);
5399 }
5400
5401 HydroNode::Reduce { .. } | HydroNode::ReduceKeyed { .. } => {
5402 let operator: syn::Ident = if let HydroNode::Reduce { input, .. } = node {
5403 if input.metadata().location_id.is_root()
5404 && input.metadata().collection_kind.is_bounded()
5405 {
5406 parse_quote!(reduce_no_replay)
5407 } else {
5408 parse_quote!(reduce)
5409 }
5410 } else if let HydroNode::ReduceKeyed { input, .. } = node {
5411 if input.metadata().location_id.is_root()
5412 && input.metadata().collection_kind.is_bounded()
5413 {
5414 todo!(
5415 "Calling keyed reduce on a top-level bounded collection is not supported"
5416 )
5417 } else {
5418 parse_quote!(reduce_keyed)
5419 }
5420 } else {
5421 unreachable!()
5422 };
5423
5424 let (HydroNode::Reduce { input, .. } | HydroNode::ReduceKeyed { input, .. }) = node
5425 else {
5426 unreachable!()
5427 };
5428
5429 let input_top_level = input.metadata().location_id.is_top_level();
5430
5431 let input_ident = ident_stack.pop().unwrap();
5432
5433 let (HydroNode::Reduce { f, .. } | HydroNode::ReduceKeyed { f, .. }) = &*node
5434 else {
5435 unreachable!()
5436 };
5437
5438 let f_tokens = f.emit_tokens(&mut ident_stack);
5439
5440 let stmt_id = next_stmt_id.get_and_increment();
5441 let reduce_ident =
5442 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5443
5444 match builders_or_callback {
5445 BuildersOrCallback::Builders(graph_builders) => {
5446 let lifetime = if input_top_level {
5447 graph_builders.cross_tick_state_lifetime(&out_location)
5448 } else {
5449 graph_builders.tick_state_lifetime(&out_location)
5450 };
5451
5452 if matches!(node, HydroNode::Reduce { .. })
5453 && node.metadata().location_id.is_root()
5454 && graph_builders.singleton_intermediates()
5455 && !node.metadata().collection_kind.is_bounded()
5456 {
5457 todo!(
5458 "Reduce with optional intermediates is not yet supported in simulator"
5459 );
5460 } else if matches!(node, HydroNode::ReduceKeyed { .. })
5461 && node.metadata().location_id.is_root()
5462 && graph_builders.singleton_intermediates()
5463 && !node.metadata().collection_kind.is_bounded()
5464 {
5465 todo!(
5466 "Reduce keyed with optional intermediates is not yet supported in simulator"
5467 );
5468 } else {
5469 graph_builders.add_dfir_at(
5470 &out_location,
5471 parse_quote! {
5472 #reduce_ident = #input_ident -> #operator::<#lifetime>(#f_tokens);
5473 },
5474 Some(&stmt_id.to_string()),
5475 );
5476 }
5477 }
5478 BuildersOrCallback::Callback(_, node_callback) => {
5479 node_callback(node, next_stmt_id);
5480 }
5481 }
5482
5483 ident_stack.push(reduce_ident);
5484 }
5485
5486 HydroNode::ReduceKeyedWatermark {
5487 f,
5488 input,
5489 watermark,
5490 metadata,
5491 } => {
5492
5493 let watermark_ident = ident_stack.pop().unwrap();
5495 let watermark_location = watermark.metadata().location_id.clone();
5496 let input_ident = ident_stack.pop().unwrap();
5497 let f_tokens = f.emit_tokens(&mut ident_stack);
5498
5499 let stmt_id = next_stmt_id.get_and_increment();
5500 let chain_ident = syn::Ident::new(
5501 &format!("reduce_keyed_watermark_chain_{}", stmt_id),
5502 Span::call_site(),
5503 );
5504
5505 let fold_ident =
5506 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5507
5508 let agg_operator: syn::Ident = if input.metadata().location_id.is_root()
5509 && input.metadata().collection_kind.is_bounded()
5510 {
5511 parse_quote!(fold_no_replay)
5512 } else {
5513 parse_quote!(fold)
5514 };
5515
5516 match builders_or_callback {
5517 BuildersOrCallback::Builders(graph_builders) => {
5518 let watermark_ident = graph_builders.unwindow_for_consume(
5523 watermark_ident,
5524 &watermark_location,
5525 &out_location,
5526 );
5527
5528 let lifetime = if input.metadata().location_id.is_top_level() {
5529 graph_builders.cross_tick_state_lifetime(&out_location)
5530 } else {
5531 graph_builders.tick_state_lifetime(&out_location)
5532 };
5533
5534 if metadata.location_id.is_root()
5535 && graph_builders.singleton_intermediates()
5536 && !metadata.collection_kind.is_bounded()
5537 {
5538 todo!(
5539 "Reduce keyed watermarked on a top-level bounded collection is not yet supported"
5540 )
5541 } else {
5542 graph_builders.add_dfir_at(
5543 &out_location,
5544 parse_quote! {
5545 #chain_ident = chain();
5546 #input_ident
5547 -> map(|x| (Some(x), None))
5548 -> [0]#chain_ident;
5549 #watermark_ident
5550 -> map(|watermark| (None, Some(watermark)))
5551 -> [1]#chain_ident;
5552
5553 #fold_ident = #chain_ident
5554 -> #agg_operator::<#lifetime>(|| (::std::collections::HashMap::new(), None), {
5555 let __reduce_keyed_fn = #f_tokens;
5556 move |(map, opt_curr_watermark), (opt_payload, opt_watermark)| {
5557 if let Some((k, v)) = opt_payload {
5558 if let Some(curr_watermark) = *opt_curr_watermark {
5559 if k < curr_watermark {
5560 return;
5561 }
5562 }
5563 match map.entry(k) {
5564 ::std::collections::hash_map::Entry::Vacant(e) => {
5565 e.insert(v);
5566 }
5567 ::std::collections::hash_map::Entry::Occupied(mut e) => {
5568 __reduce_keyed_fn(e.get_mut(), v);
5569 }
5570 }
5571 } else {
5572 let watermark = opt_watermark.unwrap();
5573 if let Some(curr_watermark) = *opt_curr_watermark {
5574 if watermark <= curr_watermark {
5575 return;
5576 }
5577 }
5578 map.retain(|k, _| *k >= watermark);
5579 *opt_curr_watermark = Some(watermark);
5580 }
5581 }
5582 })
5583 -> flat_map(|(map, _curr_watermark)| map);
5584 },
5585 Some(&stmt_id.to_string()),
5586 );
5587 }
5588 }
5589 BuildersOrCallback::Callback(_, node_callback) => {
5590 node_callback(node, next_stmt_id);
5591 }
5592 }
5593
5594 ident_stack.push(fold_ident);
5595 }
5596
5597 HydroNode::Network {
5598 networking_info,
5599 serialize,
5600 deserialize,
5601 instantiate_fn,
5602 input,
5603 ..
5604 } => {
5605 let input_ident = ident_stack.pop().unwrap();
5606
5607 let stmt_id = next_stmt_id.get_and_increment();
5608 let receiver_stream_ident =
5609 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5610
5611 let serialize_pipeline = serialize.pipeline();
5614 let deserialize_pipeline = deserialize.pipeline();
5615
5616 match builders_or_callback {
5617 BuildersOrCallback::Builders(graph_builders) => {
5618 let (sink_expr, source_expr) = match instantiate_fn {
5619 DebugInstantiate::Building => (
5620 syn::parse_quote!(DUMMY_SINK),
5621 syn::parse_quote!(DUMMY_SOURCE),
5622 ),
5623
5624 DebugInstantiate::Finalized(finalized) => {
5625 (finalized.sink.clone(), finalized.source.clone())
5626 }
5627 };
5628
5629 graph_builders.create_network(
5630 &input.metadata().location_id,
5631 &out_location,
5632 input_ident,
5633 &receiver_stream_ident,
5634 serialize_pipeline.as_ref(),
5635 sink_expr,
5636 source_expr,
5637 deserialize_pipeline.as_ref(),
5638 serialize.external_element_type(),
5639 stmt_id,
5640 networking_info,
5641 );
5642 }
5643 BuildersOrCallback::Callback(_, node_callback) => {
5644 node_callback(node, next_stmt_id);
5645 }
5646 }
5647
5648 ident_stack.push(receiver_stream_ident);
5649 }
5650
5651 HydroNode::ExternalInput {
5652 instantiate_fn,
5653 deserialize_fn: deserialize_pipeline,
5654 ..
5655 } => {
5656 let stmt_id = next_stmt_id.get_and_increment();
5657 let receiver_stream_ident =
5658 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5659
5660 match builders_or_callback {
5661 BuildersOrCallback::Builders(graph_builders) => {
5662 let (_, source_expr) = match instantiate_fn {
5663 DebugInstantiate::Building => (
5664 syn::parse_quote!(DUMMY_SINK),
5665 syn::parse_quote!(DUMMY_SOURCE),
5666 ),
5667
5668 DebugInstantiate::Finalized(finalized) => {
5669 (finalized.sink.clone(), finalized.source.clone())
5670 }
5671 };
5672
5673 graph_builders.create_external_source(
5674 &out_location,
5675 source_expr,
5676 &receiver_stream_ident,
5677 deserialize_pipeline.as_ref(),
5678 stmt_id,
5679 );
5680 }
5681 BuildersOrCallback::Callback(_, node_callback) => {
5682 node_callback(node, next_stmt_id);
5683 }
5684 }
5685
5686 ident_stack.push(receiver_stream_ident);
5687 }
5688
5689 HydroNode::Counter {
5690 tag,
5691 duration,
5692 prefix,
5693 ..
5694 } => {
5695 let input_ident = ident_stack.pop().unwrap();
5696
5697 let stmt_id = next_stmt_id.get_and_increment();
5698 let counter_ident =
5699 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5700
5701 match builders_or_callback {
5702 BuildersOrCallback::Builders(graph_builders) => {
5703 let arg = format!("{}({})", prefix, tag);
5704 graph_builders.add_dfir_at(
5705 &out_location,
5706 parse_quote! {
5707 #counter_ident = #input_ident -> _counter(#arg, #duration);
5708 },
5709 Some(&stmt_id.to_string()),
5710 );
5711 }
5712 BuildersOrCallback::Callback(_, node_callback) => {
5713 node_callback(node, next_stmt_id);
5714 }
5715 }
5716
5717 ident_stack.push(counter_ident);
5718 }
5719
5720 HydroNode::VersionedNetworkFork {
5721 channel_id,
5722 senders,
5723 metadata,
5724 ..
5725 } => {
5726 let split_at = ident_stack.len() - senders.len();
5728 let sender_idents = ident_stack.split_off(split_at);
5729
5730 let stmt_id = next_stmt_id.get_and_increment();
5731
5732 let external_element_type =
5735 senders.first().and_then(|(_, _, s)| s.external_element_type());
5736
5737 match builders_or_callback {
5738 BuildersOrCallback::Builders(graph_builders) => {
5739 let sender_args: Vec<(LocationId, syn::Ident, Option<DebugExpr>)> =
5740 senders
5741 .iter()
5742 .zip(sender_idents)
5743 .map(|((_version, sender, serialize), ident)| {
5744 (
5745 sender.metadata().location_id.clone(),
5746 ident,
5747 serialize.pipeline(),
5748 )
5749 })
5750 .collect();
5751 graph_builders.create_versioned_network_fork(
5752 *channel_id,
5753 &metadata.location_id,
5754 sender_args,
5755 external_element_type,
5756 stmt_id,
5757 );
5758 }
5759 BuildersOrCallback::Callback(_, node_callback) => {
5760 node_callback(node, next_stmt_id);
5761 }
5762 }
5763 }
5764
5765 HydroNode::VersionedNetwork {
5766 fork,
5767 deserialize,
5768 metadata,
5769 ..
5770 } => {
5771 let stmt_id = next_stmt_id.get_and_increment();
5772 let receiver_stream_ident =
5773 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5774
5775 let (channel_id, source_loc) = {
5778 let fork_ref = fork.0.borrow();
5779 let HydroNode::VersionedNetworkFork {
5780 channel_id,
5781 senders,
5782 ..
5783 } = &*fork_ref
5784 else {
5785 unreachable!("VersionedNetwork.fork must be a VersionedNetworkFork");
5786 };
5787 let source_loc = senders
5788 .first()
5789 .map(|(_v, sender, _s)| sender.metadata().location_id.clone())
5790 .expect("a VersionedNetworkFork always has at least one sender");
5791 (*channel_id, source_loc)
5792 };
5793
5794 let deserialize_pipeline = deserialize.pipeline();
5795 let external_element_type = deserialize.external_element_type();
5796
5797 match builders_or_callback {
5798 BuildersOrCallback::Builders(graph_builders) => {
5799 graph_builders.create_versioned_network(
5800 channel_id,
5801 &source_loc,
5802 &metadata.location_id,
5803 &receiver_stream_ident,
5804 deserialize_pipeline.as_ref(),
5805 external_element_type,
5806 stmt_id,
5807 );
5808 }
5809 BuildersOrCallback::Callback(_, node_callback) => {
5810 node_callback(node, next_stmt_id);
5811 }
5812 }
5813
5814 ident_stack.push(receiver_stream_ident);
5815 }
5816 }
5817 },
5818 seen_tees,
5819 false,
5820 );
5821
5822 let ret = ident_stack
5823 .pop()
5824 .expect("ident_stack should have exactly one element after traversal");
5825 assert!(
5826 ident_stack.is_empty(),
5827 "ident_stack should be empty after popping the final ident, but has {} remaining element(s). \
5828 This indicates a bug in the code gen: some node pushed idents that were never consumed.",
5829 ident_stack.len()
5830 );
5831 ret
5832 }
5833
5834 pub fn visit_debug_expr(&mut self, mut transform: impl FnMut(&mut DebugExpr)) {
5835 match self {
5836 HydroNode::Placeholder => {
5837 panic!()
5838 }
5839 HydroNode::Cast { .. }
5840 | HydroNode::ObserveNonDet { .. }
5841 | HydroNode::UnboundSingleton { .. }
5842 | HydroNode::AssertIsConsistent { .. } => {}
5843 HydroNode::Source { source, .. } => match source {
5844 HydroSource::Stream(expr) | HydroSource::Iter(expr) => transform(expr),
5845 HydroSource::ExternalNetwork()
5846 | HydroSource::Spin()
5847 | HydroSource::ClusterMembers(_, _)
5848 | HydroSource::Embedded(_)
5849 | HydroSource::EmbeddedSingleton(_) => {} },
5851 HydroNode::SingletonSource { value, .. } => {
5852 transform(value);
5853 }
5854 HydroNode::CycleSource { .. }
5855 | HydroNode::Tee { .. }
5856 | HydroNode::Reference { .. }
5857 | HydroNode::YieldConcat { .. }
5858 | HydroNode::BeginAtomic { .. }
5859 | HydroNode::EndAtomic { .. }
5860 | HydroNode::Batch { .. }
5861 | HydroNode::Chain { .. }
5862 | HydroNode::MergeOrdered { .. }
5863 | HydroNode::ChainFirst { .. }
5864 | HydroNode::CrossProduct { .. }
5865 | HydroNode::CrossSingleton { .. }
5866 | HydroNode::ResolveFutures { .. }
5867 | HydroNode::ResolveFuturesBlocking { .. }
5868 | HydroNode::ResolveFuturesOrdered { .. }
5869 | HydroNode::Join { .. }
5870 | HydroNode::JoinHalf { .. }
5871 | HydroNode::Difference { .. }
5872 | HydroNode::AntiJoin { .. }
5873 | HydroNode::DeferTick { .. }
5874 | HydroNode::Enumerate { .. }
5875 | HydroNode::Unique { .. }
5876 | HydroNode::Sort { .. }
5877 | HydroNode::PartitionSide { .. }
5878 | HydroNode::VersionedNetworkFork { .. }
5879 | HydroNode::VersionedNetwork { .. } => {}
5880 HydroNode::Map { f, .. }
5881 | HydroNode::FlatMap { f, .. }
5882 | HydroNode::FlatMapStreamBlocking { f, .. }
5883 | HydroNode::Filter { f, .. }
5884 | HydroNode::FilterMap { f, .. }
5885 | HydroNode::Inspect { f, .. }
5886 | HydroNode::PartitionShared { f, .. }
5887 | HydroNode::Reduce { f, .. }
5888 | HydroNode::ReduceKeyed { f, .. }
5889 | HydroNode::ReduceKeyedWatermark { f, .. } => {
5890 transform(&mut f.expr);
5891 }
5892 HydroNode::Fold { init, acc, .. }
5893 | HydroNode::Scan { init, acc, .. }
5894 | HydroNode::ScanAsyncBlocking { init, acc, .. }
5895 | HydroNode::FoldKeyed { init, acc, .. } => {
5896 transform(&mut init.expr);
5897 transform(&mut acc.expr);
5898 }
5899 HydroNode::Network {
5900 serialize,
5901 deserialize,
5902 ..
5903 } => {
5904 if let NetworkSend::Custom {
5905 serialize_fn: Some(serialize_fn),
5906 } = serialize
5907 {
5908 transform(serialize_fn);
5909 }
5910 if let NetworkRecv::Custom {
5911 deserialize_fn: Some(deserialize_fn),
5912 } = deserialize
5913 {
5914 transform(deserialize_fn);
5915 }
5916 }
5917 HydroNode::ExternalInput { deserialize_fn, .. } => {
5918 if let Some(deserialize_fn) = deserialize_fn {
5919 transform(deserialize_fn);
5920 }
5921 }
5922 HydroNode::Counter { duration, .. } => {
5923 transform(duration);
5924 }
5925 }
5926 }
5927
5928 pub fn op_metadata(&self) -> &HydroIrOpMetadata {
5929 &self.metadata().op
5930 }
5931
5932 pub fn metadata(&self) -> &HydroIrMetadata {
5933 match self {
5934 HydroNode::Placeholder => {
5935 panic!()
5936 }
5937 HydroNode::VersionedNetworkFork { metadata, .. }
5938 | HydroNode::VersionedNetwork { metadata, .. } => metadata,
5939 HydroNode::Cast { metadata, .. }
5940 | HydroNode::ObserveNonDet { metadata, .. }
5941 | HydroNode::AssertIsConsistent { metadata, .. }
5942 | HydroNode::UnboundSingleton { metadata, .. }
5943 | HydroNode::Source { metadata, .. }
5944 | HydroNode::SingletonSource { metadata, .. }
5945 | HydroNode::CycleSource { metadata, .. }
5946 | HydroNode::Tee { metadata, .. }
5947 | HydroNode::Reference { metadata, .. }
5948 | HydroNode::PartitionSide { metadata, .. }
5949 | HydroNode::PartitionShared { metadata, .. }
5950 | HydroNode::YieldConcat { metadata, .. }
5951 | HydroNode::BeginAtomic { metadata, .. }
5952 | HydroNode::EndAtomic { metadata, .. }
5953 | HydroNode::Batch { metadata, .. }
5954 | HydroNode::Chain { metadata, .. }
5955 | HydroNode::MergeOrdered { metadata, .. }
5956 | HydroNode::ChainFirst { metadata, .. }
5957 | HydroNode::CrossProduct { metadata, .. }
5958 | HydroNode::CrossSingleton { metadata, .. }
5959 | HydroNode::Join { metadata, .. }
5960 | HydroNode::JoinHalf { metadata, .. }
5961 | HydroNode::Difference { metadata, .. }
5962 | HydroNode::AntiJoin { metadata, .. }
5963 | HydroNode::ResolveFutures { metadata, .. }
5964 | HydroNode::ResolveFuturesBlocking { metadata, .. }
5965 | HydroNode::ResolveFuturesOrdered { metadata, .. }
5966 | HydroNode::Map { metadata, .. }
5967 | HydroNode::FlatMap { metadata, .. }
5968 | HydroNode::FlatMapStreamBlocking { metadata, .. }
5969 | HydroNode::Filter { metadata, .. }
5970 | HydroNode::FilterMap { metadata, .. }
5971 | HydroNode::DeferTick { metadata, .. }
5972 | HydroNode::Enumerate { metadata, .. }
5973 | HydroNode::Inspect { metadata, .. }
5974 | HydroNode::Unique { metadata, .. }
5975 | HydroNode::Sort { metadata, .. }
5976 | HydroNode::Scan { metadata, .. }
5977 | HydroNode::ScanAsyncBlocking { metadata, .. }
5978 | HydroNode::Fold { metadata, .. }
5979 | HydroNode::FoldKeyed { metadata, .. }
5980 | HydroNode::Reduce { metadata, .. }
5981 | HydroNode::ReduceKeyed { metadata, .. }
5982 | HydroNode::ReduceKeyedWatermark { metadata, .. }
5983 | HydroNode::ExternalInput { metadata, .. }
5984 | HydroNode::Network { metadata, .. }
5985 | HydroNode::Counter { metadata, .. } => metadata,
5986 }
5987 }
5988
5989 pub fn op_metadata_mut(&mut self) -> &mut HydroIrOpMetadata {
5990 &mut self.metadata_mut().op
5991 }
5992
5993 pub fn metadata_mut(&mut self) -> &mut HydroIrMetadata {
5994 match self {
5995 HydroNode::Placeholder => {
5996 panic!()
5997 }
5998 HydroNode::VersionedNetworkFork { metadata, .. }
5999 | HydroNode::VersionedNetwork { metadata, .. } => metadata,
6000 HydroNode::Cast { metadata, .. }
6001 | HydroNode::ObserveNonDet { metadata, .. }
6002 | HydroNode::AssertIsConsistent { metadata, .. }
6003 | HydroNode::UnboundSingleton { metadata, .. }
6004 | HydroNode::Source { metadata, .. }
6005 | HydroNode::SingletonSource { metadata, .. }
6006 | HydroNode::CycleSource { metadata, .. }
6007 | HydroNode::Tee { metadata, .. }
6008 | HydroNode::Reference { metadata, .. }
6009 | HydroNode::PartitionSide { metadata, .. }
6010 | HydroNode::PartitionShared { metadata, .. }
6011 | HydroNode::YieldConcat { metadata, .. }
6012 | HydroNode::BeginAtomic { metadata, .. }
6013 | HydroNode::EndAtomic { metadata, .. }
6014 | HydroNode::Batch { metadata, .. }
6015 | HydroNode::Chain { metadata, .. }
6016 | HydroNode::MergeOrdered { metadata, .. }
6017 | HydroNode::ChainFirst { metadata, .. }
6018 | HydroNode::CrossProduct { metadata, .. }
6019 | HydroNode::CrossSingleton { metadata, .. }
6020 | HydroNode::Join { metadata, .. }
6021 | HydroNode::JoinHalf { metadata, .. }
6022 | HydroNode::Difference { metadata, .. }
6023 | HydroNode::AntiJoin { metadata, .. }
6024 | HydroNode::ResolveFutures { metadata, .. }
6025 | HydroNode::ResolveFuturesBlocking { metadata, .. }
6026 | HydroNode::ResolveFuturesOrdered { metadata, .. }
6027 | HydroNode::Map { metadata, .. }
6028 | HydroNode::FlatMap { metadata, .. }
6029 | HydroNode::FlatMapStreamBlocking { metadata, .. }
6030 | HydroNode::Filter { metadata, .. }
6031 | HydroNode::FilterMap { metadata, .. }
6032 | HydroNode::DeferTick { metadata, .. }
6033 | HydroNode::Enumerate { metadata, .. }
6034 | HydroNode::Inspect { metadata, .. }
6035 | HydroNode::Unique { metadata, .. }
6036 | HydroNode::Sort { metadata, .. }
6037 | HydroNode::Scan { metadata, .. }
6038 | HydroNode::ScanAsyncBlocking { metadata, .. }
6039 | HydroNode::Fold { metadata, .. }
6040 | HydroNode::FoldKeyed { metadata, .. }
6041 | HydroNode::Reduce { metadata, .. }
6042 | HydroNode::ReduceKeyed { metadata, .. }
6043 | HydroNode::ReduceKeyedWatermark { metadata, .. }
6044 | HydroNode::ExternalInput { metadata, .. }
6045 | HydroNode::Network { metadata, .. }
6046 | HydroNode::Counter { metadata, .. } => metadata,
6047 }
6048 }
6049
6050 pub fn input(&self) -> Vec<&HydroNode> {
6051 match self {
6052 HydroNode::Placeholder => {
6053 panic!()
6054 }
6055 HydroNode::Source { .. }
6056 | HydroNode::SingletonSource { .. }
6057 | HydroNode::ExternalInput { .. }
6058 | HydroNode::CycleSource { .. }
6059 | HydroNode::Tee { .. }
6060 | HydroNode::Reference { .. }
6061 | HydroNode::PartitionSide { .. }
6062 | HydroNode::VersionedNetwork { .. } => {
6063 vec![]
6065 }
6066 HydroNode::Cast { inner, .. }
6067 | HydroNode::ObserveNonDet { inner, .. }
6068 | HydroNode::YieldConcat { inner, .. }
6069 | HydroNode::BeginAtomic { inner, .. }
6070 | HydroNode::EndAtomic { inner, .. }
6071 | HydroNode::Batch { inner, .. }
6072 | HydroNode::UnboundSingleton { inner, .. }
6073 | HydroNode::AssertIsConsistent { inner, .. } => {
6074 vec![inner]
6075 }
6076 HydroNode::Chain { first, second, .. }
6077 | HydroNode::MergeOrdered { first, second, .. }
6078 | HydroNode::ChainFirst { first, second, .. } => {
6079 vec![first, second]
6080 }
6081 HydroNode::CrossProduct { left, right, .. }
6082 | HydroNode::CrossSingleton { left, right, .. }
6083 | HydroNode::Join { left, right, .. }
6084 | HydroNode::JoinHalf { left, right, .. } => {
6085 vec![left, right]
6086 }
6087 HydroNode::Difference { pos, neg, .. } | HydroNode::AntiJoin { pos, neg, .. } => {
6088 vec![pos, neg]
6089 }
6090 HydroNode::Counter { input, .. }
6091 | HydroNode::DeferTick { input, .. }
6092 | HydroNode::Enumerate { input, .. }
6093 | HydroNode::Filter { input, .. }
6094 | HydroNode::FilterMap { input, .. }
6095 | HydroNode::FlatMap { input, .. }
6096 | HydroNode::FlatMapStreamBlocking { input, .. }
6097 | HydroNode::Fold { input, .. }
6098 | HydroNode::FoldKeyed { input, .. }
6099 | HydroNode::Inspect { input, .. }
6100 | HydroNode::Map { input, .. }
6101 | HydroNode::Network { input, .. }
6102 | HydroNode::PartitionShared { input, .. }
6103 | HydroNode::Reduce { input, .. }
6104 | HydroNode::ReduceKeyed { input, .. }
6105 | HydroNode::ResolveFutures { input, .. }
6106 | HydroNode::ResolveFuturesBlocking { input, .. }
6107 | HydroNode::ResolveFuturesOrdered { input, .. }
6108 | HydroNode::Scan { input, .. }
6109 | HydroNode::ScanAsyncBlocking { input, .. }
6110 | HydroNode::Sort { input, .. }
6111 | HydroNode::Unique { input, .. } => {
6112 vec![input]
6113 }
6114 HydroNode::ReduceKeyedWatermark {
6115 input, watermark, ..
6116 } => {
6117 vec![input, watermark]
6118 }
6119 HydroNode::VersionedNetworkFork { senders, .. } => senders
6120 .iter()
6121 .map(|(_version, sender, _serialize)| sender.as_ref())
6122 .collect(),
6123 }
6124 }
6125
6126 pub fn input_metadata(&self) -> Vec<&HydroIrMetadata> {
6127 self.input()
6128 .iter()
6129 .map(|input_node| input_node.metadata())
6130 .collect()
6131 }
6132
6133 pub fn is_shared_with_others(&self) -> bool {
6137 match self {
6138 HydroNode::Tee { inner, .. } | HydroNode::PartitionSide { inner, .. } => {
6139 Rc::strong_count(&inner.0) > 1
6140 }
6141 HydroNode::Reference { .. } => false,
6144 _ => false,
6145 }
6146 }
6147
6148 pub fn print_root(&self) -> String {
6149 match self {
6150 HydroNode::Placeholder => {
6151 panic!()
6152 }
6153 HydroNode::Cast { .. } => "Cast()".to_owned(),
6154 HydroNode::UnboundSingleton { .. } => "UnboundSingleton()".to_owned(),
6155 HydroNode::ObserveNonDet { .. } => "ObserveNonDet()".to_owned(),
6156 HydroNode::AssertIsConsistent { .. } => "AssertIsConsistent()".to_owned(),
6157 HydroNode::Source { source, .. } => format!("Source({:?})", source),
6158 HydroNode::SingletonSource {
6159 value,
6160 first_tick_only,
6161 ..
6162 } => format!(
6163 "SingletonSource({:?}, first_tick_only={})",
6164 value, first_tick_only
6165 ),
6166 HydroNode::CycleSource { cycle_id, .. } => format!("CycleSource({})", cycle_id),
6167 HydroNode::Tee { inner, .. } => {
6168 format!("Tee({})", inner.0.borrow().print_root())
6169 }
6170 HydroNode::Reference { inner, kind, .. } => {
6171 format!("Reference({:?}, {})", kind, inner.0.borrow().print_root())
6172 }
6173 HydroNode::PartitionSide { inner, is_true, .. } => {
6174 format!(
6175 "PartitionSide(is_true={}, {})",
6176 is_true,
6177 inner.0.borrow().print_root(),
6178 )
6179 }
6180 HydroNode::PartitionShared { f, .. } => format!("PartitionShared({:?})", f),
6181 HydroNode::YieldConcat { .. } => "YieldConcat()".to_owned(),
6182 HydroNode::BeginAtomic { .. } => "BeginAtomic()".to_owned(),
6183 HydroNode::EndAtomic { .. } => "EndAtomic()".to_owned(),
6184 HydroNode::Batch { .. } => "Batch()".to_owned(),
6185 HydroNode::Chain { first, second, .. } => {
6186 format!("Chain({}, {})", first.print_root(), second.print_root())
6187 }
6188 HydroNode::MergeOrdered { first, second, .. } => {
6189 format!(
6190 "MergeOrdered({}, {})",
6191 first.print_root(),
6192 second.print_root()
6193 )
6194 }
6195 HydroNode::ChainFirst { first, second, .. } => {
6196 format!(
6197 "ChainFirst({}, {})",
6198 first.print_root(),
6199 second.print_root()
6200 )
6201 }
6202 HydroNode::CrossProduct { left, right, .. } => {
6203 format!(
6204 "CrossProduct({}, {})",
6205 left.print_root(),
6206 right.print_root()
6207 )
6208 }
6209 HydroNode::CrossSingleton { left, right, .. } => {
6210 format!(
6211 "CrossSingleton({}, {})",
6212 left.print_root(),
6213 right.print_root()
6214 )
6215 }
6216 HydroNode::Join { left, right, .. } => {
6217 format!("Join({}, {})", left.print_root(), right.print_root())
6218 }
6219 HydroNode::JoinHalf { left, right, .. } => {
6220 format!("JoinHalf({}, {})", left.print_root(), right.print_root())
6221 }
6222 HydroNode::Difference { pos, neg, .. } => {
6223 format!("Difference({}, {})", pos.print_root(), neg.print_root())
6224 }
6225 HydroNode::AntiJoin { pos, neg, .. } => {
6226 format!("AntiJoin({}, {})", pos.print_root(), neg.print_root())
6227 }
6228 HydroNode::ResolveFutures { .. } => "ResolveFutures()".to_owned(),
6229 HydroNode::ResolveFuturesBlocking { .. } => "ResolveFuturesBlocking()".to_owned(),
6230 HydroNode::ResolveFuturesOrdered { .. } => "ResolveFuturesOrdered()".to_owned(),
6231 HydroNode::Map { f, .. } => format!("Map({:?})", f),
6232 HydroNode::FlatMap { f, .. } => format!("FlatMap({:?})", f),
6233 HydroNode::FlatMapStreamBlocking { f, .. } => format!("FlatMapStreamBlocking({:?})", f),
6234 HydroNode::Filter { f, .. } => format!("Filter({:?})", f),
6235 HydroNode::FilterMap { f, .. } => format!("FilterMap({:?})", f),
6236 HydroNode::DeferTick { .. } => "DeferTick()".to_owned(),
6237 HydroNode::Enumerate { .. } => "Enumerate()".to_owned(),
6238 HydroNode::Inspect { f, .. } => format!("Inspect({:?})", f),
6239 HydroNode::Unique { .. } => "Unique()".to_owned(),
6240 HydroNode::Sort { .. } => "Sort()".to_owned(),
6241 HydroNode::Fold { init, acc, .. } => format!("Fold({:?}, {:?})", init, acc),
6242 HydroNode::Scan { init, acc, .. } => format!("Scan({:?}, {:?})", init, acc),
6243 HydroNode::ScanAsyncBlocking { init, acc, .. } => {
6244 format!("ScanAsyncBlocking({:?}, {:?})", init, acc)
6245 }
6246 HydroNode::FoldKeyed { init, acc, .. } => format!("FoldKeyed({:?}, {:?})", init, acc),
6247 HydroNode::Reduce { f, .. } => format!("Reduce({:?})", f),
6248 HydroNode::ReduceKeyed { f, .. } => format!("ReduceKeyed({:?})", f),
6249 HydroNode::ReduceKeyedWatermark { f, .. } => format!("ReduceKeyedWatermark({:?})", f),
6250 HydroNode::Network { .. } => "Network()".to_owned(),
6251 HydroNode::ExternalInput { .. } => "ExternalInput()".to_owned(),
6252 HydroNode::Counter { tag, duration, .. } => {
6253 format!("Counter({:?}, {:?})", tag, duration)
6254 }
6255 HydroNode::VersionedNetworkFork {
6256 channel_name,
6257 senders,
6258 ..
6259 } => {
6260 let versions: Vec<u32> = senders.iter().map(|(v, _, _)| *v).collect();
6261 format!(
6262 "VersionedNetworkFork({}, senders={:?})",
6263 channel_name, versions
6264 )
6265 }
6266 HydroNode::VersionedNetwork { version, .. } => {
6267 format!("VersionedNetwork(v{})", version)
6268 }
6269 }
6270 }
6271}
6272
6273#[cfg(feature = "build")]
6274#[expect(clippy::too_many_arguments, reason = "networking codegen")]
6275fn instantiate_network<'a, D>(
6276 env: &mut D::InstantiateEnv,
6277 from_location: &LocationId,
6278 to_location: &LocationId,
6279 processes: &SparseSecondaryMap<LocationKey, D::Process>,
6280 clusters: &SparseSecondaryMap<LocationKey, D::Cluster>,
6281 name: Option<&str>,
6282 networking_info: &crate::networking::NetworkingInfo,
6283 external_types: Option<(&syn::Type, &syn::Type)>,
6284) -> (syn::Expr, syn::Expr, Box<dyn FnOnce()>)
6285where
6286 D: Deploy<'a>,
6287{
6288 if external_types.is_some() && !D::SUPPORTS_EXTERNAL_SERIALIZATION {
6289 panic!(
6290 "`.embedded()` serialization leaves serialization to code outside of Hydro and is \
6291 only supported by the embedded deployment backend. Use `.bincode()` (or another \
6292 supported serialization backend) for this deployment target instead."
6293 );
6294 }
6295
6296 let ((sink, source), connect_fn) = match (from_location, to_location) {
6297 (&LocationId::Process(from), &LocationId::Process(to)) => {
6298 let from_node = processes
6299 .get(from)
6300 .unwrap_or_else(|| {
6301 panic!("A process used in the graph was not instantiated: {}", from)
6302 })
6303 .clone();
6304 let to_node = processes
6305 .get(to)
6306 .unwrap_or_else(|| {
6307 panic!("A process used in the graph was not instantiated: {}", to)
6308 })
6309 .clone();
6310
6311 let sink_port = from_node.next_port();
6312 let source_port = to_node.next_port();
6313
6314 (
6315 D::o2o_sink_source(
6316 env,
6317 &from_node,
6318 &sink_port,
6319 &to_node,
6320 &source_port,
6321 name,
6322 networking_info,
6323 external_types,
6324 ),
6325 D::o2o_connect(&from_node, &sink_port, &to_node, &source_port),
6326 )
6327 }
6328 (&LocationId::Process(from), &LocationId::Cluster(to)) => {
6329 let from_node = processes
6330 .get(from)
6331 .unwrap_or_else(|| {
6332 panic!("A process used in the graph was not instantiated: {}", from)
6333 })
6334 .clone();
6335 let to_node = clusters
6336 .get(to)
6337 .unwrap_or_else(|| {
6338 panic!("A cluster used in the graph was not instantiated: {}", to)
6339 })
6340 .clone();
6341
6342 let sink_port = from_node.next_port();
6343 let source_port = to_node.next_port();
6344
6345 (
6346 D::o2m_sink_source(
6347 env,
6348 &from_node,
6349 &sink_port,
6350 &to_node,
6351 &source_port,
6352 name,
6353 networking_info,
6354 external_types,
6355 ),
6356 D::o2m_connect(&from_node, &sink_port, &to_node, &source_port),
6357 )
6358 }
6359 (&LocationId::Cluster(from), &LocationId::Process(to)) => {
6360 let from_node = clusters
6361 .get(from)
6362 .unwrap_or_else(|| {
6363 panic!("A cluster used in the graph was not instantiated: {}", from)
6364 })
6365 .clone();
6366 let to_node = processes
6367 .get(to)
6368 .unwrap_or_else(|| {
6369 panic!("A process used in the graph was not instantiated: {}", to)
6370 })
6371 .clone();
6372
6373 let sink_port = from_node.next_port();
6374 let source_port = to_node.next_port();
6375
6376 (
6377 D::m2o_sink_source(
6378 env,
6379 &from_node,
6380 &sink_port,
6381 &to_node,
6382 &source_port,
6383 name,
6384 networking_info,
6385 external_types,
6386 ),
6387 D::m2o_connect(&from_node, &sink_port, &to_node, &source_port),
6388 )
6389 }
6390 (&LocationId::Cluster(from), &LocationId::Cluster(to)) => {
6391 let from_node = clusters
6392 .get(from)
6393 .unwrap_or_else(|| {
6394 panic!("A cluster used in the graph was not instantiated: {}", from)
6395 })
6396 .clone();
6397 let to_node = clusters
6398 .get(to)
6399 .unwrap_or_else(|| {
6400 panic!("A cluster used in the graph was not instantiated: {}", to)
6401 })
6402 .clone();
6403
6404 let sink_port = from_node.next_port();
6405 let source_port = to_node.next_port();
6406
6407 (
6408 D::m2m_sink_source(
6409 env,
6410 &from_node,
6411 &sink_port,
6412 &to_node,
6413 &source_port,
6414 name,
6415 networking_info,
6416 external_types,
6417 ),
6418 D::m2m_connect(&from_node, &sink_port, &to_node, &source_port),
6419 )
6420 }
6421 (LocationId::Tick { .. }, _) => panic!(),
6422 (_, LocationId::Tick { .. }) => panic!(),
6423 (LocationId::Atomic(_), _) => panic!(),
6424 (_, LocationId::Atomic(_)) => panic!(),
6425 };
6426 (sink, source, connect_fn)
6427}
6428
6429#[cfg(test)]
6430mod serde_test;
6431
6432#[cfg(test)]
6433mod test {
6434 use std::mem::size_of;
6435
6436 use stageleft::{QuotedWithContext, q};
6437
6438 use super::*;
6439
6440 #[test]
6441 #[cfg_attr(
6442 not(feature = "build"),
6443 ignore = "expects inclusion of feature-gated fields"
6444 )]
6445 fn hydro_node_size() {
6446 assert_eq!(size_of::<HydroNode>(), 280);
6447 }
6448
6449 #[test]
6450 #[cfg_attr(
6451 not(feature = "build"),
6452 ignore = "expects inclusion of feature-gated fields"
6453 )]
6454 fn hydro_root_size() {
6455 assert_eq!(size_of::<HydroRoot>(), 152);
6456 }
6457
6458 #[test]
6459 fn test_simplify_q_macro_basic() {
6460 let simple_expr: syn::Expr = syn::parse_str("x + y").unwrap();
6462 let result = simplify_q_macro(simple_expr.clone());
6463 assert_eq!(result, simple_expr);
6464 }
6465
6466 #[test]
6467 fn test_simplify_q_macro_actual_stageleft_call() {
6468 let stageleft_call = q!(|x: usize| x + 1).splice_fn1_ctx(&());
6470 let result = simplify_q_macro(stageleft_call);
6471 hydro_build_utils::assert_snapshot!(result.to_token_stream().to_string());
6474 }
6475
6476 #[test]
6477 fn test_closure_no_pipe_at_start() {
6478 let stageleft_call = q!({
6480 let foo = 123;
6481 move |b: usize| b + foo
6482 })
6483 .splice_fn1_ctx(&());
6484 let result = simplify_q_macro(stageleft_call);
6485 hydro_build_utils::assert_snapshot!(result.to_token_stream().to_string());
6486 }
6487}