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