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
2565#[cfg(feature = "build")]
2566impl NetworkSend {
2567 pub(crate) fn external_element_type(&self) -> Option<&syn::Type> {
2570 match self {
2571 NetworkSend::Custom { .. } => None,
2572 NetworkSend::Embedded { element_type, .. } => Some(&element_type.0),
2573 }
2574 }
2575}
2576
2577#[cfg(feature = "build")]
2578impl NetworkRecv {
2579 pub(crate) fn external_element_type(&self) -> Option<&syn::Type> {
2581 match self {
2582 NetworkRecv::Custom { .. } => None,
2583 NetworkRecv::Embedded { element_type, .. } => Some(&element_type.0),
2584 }
2585 }
2586}
2587
2588#[cfg(feature = "build")]
2589impl NetworkSend {
2590 pub(crate) fn pipeline(&self) -> Option<DebugExpr> {
2592 match self {
2593 NetworkSend::Custom { serialize_fn } => serialize_fn.clone(),
2594 NetworkSend::Embedded { tag, element_type } => {
2595 let root = crate::staging_util::get_this_crate();
2596 let element_type = &element_type.0;
2597 let expr: syn::Expr = if let Some(tag) = tag {
2598 let tag = &tag.0;
2599 parse_quote! {
2600 #root::runtime_support::stageleft::runtime_support::fn1_type_hint::<(#root::__staged::location::MemberId<#tag>, #element_type), _>(
2601 |(id, data)| (id.into_tagless(), data)
2602 )
2603 }
2604 } else {
2605 parse_quote! {
2606 #root::runtime_support::stageleft::runtime_support::fn1_type_hint::<#element_type, _>(
2607 |data| data
2608 )
2609 }
2610 };
2611 Some(expr.into())
2612 }
2613 }
2614 }
2615}
2616
2617#[cfg(feature = "build")]
2618impl NetworkRecv {
2619 pub(crate) fn pipeline(&self) -> Option<DebugExpr> {
2621 match self {
2622 NetworkRecv::Custom { deserialize_fn } => deserialize_fn.clone(),
2623 NetworkRecv::Embedded { tag, .. } => {
2628 let tag = tag.as_ref()?;
2629 let root = crate::staging_util::get_this_crate();
2630 let tag = &tag.0;
2631 let expr: syn::Expr = parse_quote! {
2632 |(id, b)| (#root::__staged::location::MemberId::<#tag>::from_tagless(id as #root::__staged::location::TaglessMemberId), b)
2633 };
2634 Some(expr.into())
2635 }
2636 }
2637 }
2638}
2639
2640#[derive(Debug, Hash, serde::Serialize)]
2643pub enum HydroNode {
2644 Placeholder,
2645
2646 Cast {
2654 inner: Box<HydroNode>,
2655 metadata: HydroIrMetadata,
2656 },
2657
2658 ObserveNonDet {
2664 inner: Box<HydroNode>,
2665 trusted: bool, metadata: HydroIrMetadata,
2667 },
2668
2669 Source {
2670 source: HydroSource,
2671 metadata: HydroIrMetadata,
2672 },
2673
2674 SingletonSource {
2675 value: DebugExpr,
2676 first_tick_only: bool,
2677 metadata: HydroIrMetadata,
2678 },
2679
2680 CycleSource {
2681 cycle_id: CycleId,
2682 metadata: HydroIrMetadata,
2683 },
2684
2685 Tee {
2686 inner: SharedNode,
2687 metadata: HydroIrMetadata,
2688 },
2689
2690 Reference {
2699 inner: SharedNode,
2700 kind: crate::handoff_ref::HandoffRefKind,
2701 access_counter: AccessCounter,
2702 metadata: HydroIrMetadata,
2703 },
2704
2705 PartitionSide {
2707 inner: SharedNode,
2708 is_true: bool,
2709 metadata: HydroIrMetadata,
2710 },
2711
2712 PartitionShared {
2714 input: Box<HydroNode>,
2715 f: ClosureExpr,
2716 metadata: HydroIrMetadata,
2717 },
2718
2719 BeginAtomic {
2720 inner: Box<HydroNode>,
2721 metadata: HydroIrMetadata,
2722 },
2723
2724 EndAtomic {
2725 inner: Box<HydroNode>,
2726 metadata: HydroIrMetadata,
2727 },
2728
2729 Batch {
2730 inner: Box<HydroNode>,
2731 metadata: HydroIrMetadata,
2732 },
2733
2734 YieldConcat {
2735 inner: Box<HydroNode>,
2736 metadata: HydroIrMetadata,
2737 },
2738
2739 Chain {
2740 first: Box<HydroNode>,
2741 second: Box<HydroNode>,
2742 metadata: HydroIrMetadata,
2743 },
2744
2745 MergeOrdered {
2746 first: Box<HydroNode>,
2747 second: Box<HydroNode>,
2748 metadata: HydroIrMetadata,
2749 },
2750
2751 ChainFirst {
2752 first: Box<HydroNode>,
2753 second: Box<HydroNode>,
2754 metadata: HydroIrMetadata,
2755 },
2756
2757 CrossProduct {
2758 left: Box<HydroNode>,
2759 right: Box<HydroNode>,
2760 metadata: HydroIrMetadata,
2761 },
2762
2763 CrossSingleton {
2764 left: Box<HydroNode>,
2765 right: Box<HydroNode>,
2766 metadata: HydroIrMetadata,
2767 },
2768
2769 Join {
2770 left: Box<HydroNode>,
2771 right: Box<HydroNode>,
2772 metadata: HydroIrMetadata,
2773 },
2774
2775 JoinHalf {
2779 left: Box<HydroNode>,
2780 right: Box<HydroNode>,
2781 metadata: HydroIrMetadata,
2782 },
2783
2784 Difference {
2785 pos: Box<HydroNode>,
2786 neg: Box<HydroNode>,
2787 metadata: HydroIrMetadata,
2788 },
2789
2790 AntiJoin {
2791 pos: Box<HydroNode>,
2792 neg: Box<HydroNode>,
2793 metadata: HydroIrMetadata,
2794 },
2795
2796 ResolveFutures {
2797 input: Box<HydroNode>,
2798 metadata: HydroIrMetadata,
2799 },
2800 ResolveFuturesBlocking {
2801 input: Box<HydroNode>,
2802 metadata: HydroIrMetadata,
2803 },
2804 ResolveFuturesOrdered {
2805 input: Box<HydroNode>,
2806 metadata: HydroIrMetadata,
2807 },
2808
2809 Map {
2810 f: ClosureExpr,
2811 input: Box<HydroNode>,
2812 metadata: HydroIrMetadata,
2813 },
2814 FlatMap {
2815 f: ClosureExpr,
2816 input: Box<HydroNode>,
2817 metadata: HydroIrMetadata,
2818 },
2819 FlatMapStreamBlocking {
2820 f: ClosureExpr,
2821 input: Box<HydroNode>,
2822 metadata: HydroIrMetadata,
2823 },
2824 Filter {
2825 f: ClosureExpr,
2826 input: Box<HydroNode>,
2827 metadata: HydroIrMetadata,
2828 },
2829 FilterMap {
2830 f: ClosureExpr,
2831 input: Box<HydroNode>,
2832 metadata: HydroIrMetadata,
2833 },
2834
2835 DeferTick {
2836 input: Box<HydroNode>,
2837 metadata: HydroIrMetadata,
2838 },
2839 Enumerate {
2840 input: Box<HydroNode>,
2841 metadata: HydroIrMetadata,
2842 },
2843 Inspect {
2844 f: ClosureExpr,
2845 input: Box<HydroNode>,
2846 metadata: HydroIrMetadata,
2847 },
2848
2849 Unique {
2850 input: Box<HydroNode>,
2851 metadata: HydroIrMetadata,
2852 },
2853
2854 Sort {
2855 input: Box<HydroNode>,
2856 metadata: HydroIrMetadata,
2857 },
2858 Fold {
2859 init: ClosureExpr,
2860 acc: ClosureExpr,
2861 input: Box<HydroNode>,
2862 metadata: HydroIrMetadata,
2863 },
2864
2865 Scan {
2866 init: ClosureExpr,
2867 acc: ClosureExpr,
2868 input: Box<HydroNode>,
2869 metadata: HydroIrMetadata,
2870 },
2871 ScanAsyncBlocking {
2872 init: ClosureExpr,
2873 acc: ClosureExpr,
2874 input: Box<HydroNode>,
2875 metadata: HydroIrMetadata,
2876 },
2877 FoldKeyed {
2878 init: ClosureExpr,
2879 acc: ClosureExpr,
2880 input: Box<HydroNode>,
2881 metadata: HydroIrMetadata,
2882 },
2883
2884 Reduce {
2885 f: ClosureExpr,
2886 input: Box<HydroNode>,
2887 metadata: HydroIrMetadata,
2888 },
2889 ReduceKeyed {
2890 f: ClosureExpr,
2891 input: Box<HydroNode>,
2892 metadata: HydroIrMetadata,
2893 },
2894 ReduceKeyedWatermark {
2895 f: ClosureExpr,
2896 input: Box<HydroNode>,
2897 watermark: Box<HydroNode>,
2898 metadata: HydroIrMetadata,
2899 },
2900
2901 Network {
2902 name: Option<String>,
2903 networking_info: crate::networking::NetworkingInfo,
2904 serialize: NetworkSend,
2905 deserialize: NetworkRecv,
2906 instantiate_fn: DebugInstantiate,
2907 input: Box<HydroNode>,
2908 metadata: HydroIrMetadata,
2909 },
2910
2911 VersionedNetworkFork {
2912 channel_id: u32,
2913 channel_name: String,
2914 senders: Vec<(u32, Box<HydroNode>, NetworkSend)>,
2915 metadata: HydroIrMetadata,
2916 },
2917
2918 VersionedNetwork {
2919 fork: SharedNode,
2920 version: u32,
2921 deserialize: NetworkRecv,
2922 metadata: HydroIrMetadata,
2923 },
2924
2925 ExternalInput {
2926 from_external_key: LocationKey,
2927 from_port_id: ExternalPortId,
2928 from_many: bool,
2929 codec_type: DebugType,
2930 #[serde(skip)]
2931 port_hint: NetworkHint,
2932 instantiate_fn: DebugInstantiate,
2933 deserialize_fn: Option<DebugExpr>,
2934 metadata: HydroIrMetadata,
2935 },
2936
2937 Counter {
2938 tag: String,
2939 duration: DebugExpr,
2940 prefix: String,
2941 input: Box<HydroNode>,
2942 metadata: HydroIrMetadata,
2943 },
2944
2945 AssertIsConsistent {
2946 inner: Box<HydroNode>,
2947 trusted: bool,
2948 metadata: HydroIrMetadata,
2949 },
2950
2951 UnboundSingleton {
2952 inner: Box<HydroNode>,
2953 metadata: HydroIrMetadata,
2954 },
2955}
2956
2957pub type SeenSharedNodes = HashMap<*const RefCell<HydroNode>, Rc<RefCell<HydroNode>>>;
2958pub type SeenSharedNodeLocations = HashMap<*const RefCell<HydroNode>, LocationId>;
2959
2960#[cfg(feature = "build")]
2964fn maybe_observe_for_mut(
2965 f: &ClosureExpr,
2966 in_ident: syn::Ident,
2967 in_location: &LocationId,
2968 in_kind: &CollectionKind,
2969 op_meta: &HydroIrOpMetadata,
2970 builders_or_callback: &mut BuildersOrCallback<
2971 '_,
2972 impl FnMut(&mut HydroRoot, &mut crate::Counter<StmtId>),
2973 impl FnMut(&mut HydroNode, &mut crate::Counter<StmtId>),
2974 >,
2975 next_stmt_id: &mut crate::Counter<StmtId>,
2976) -> syn::Ident {
2977 if f.has_mut_ref() && !in_kind.is_strict() {
2978 let observe_stmt_id = next_stmt_id.get_and_increment();
2979 let observe_ident =
2980 syn::Ident::new(&format!("stream_{}", observe_stmt_id), Span::call_site());
2981 if let BuildersOrCallback::Builders(graph_builders) = builders_or_callback {
2982 graph_builders.observe_for_mut(in_location, in_ident, in_kind, &observe_ident, op_meta);
2983 }
2984 observe_ident
2985 } else {
2986 in_ident
2987 }
2988}
2989
2990impl HydroNode {
2991 pub fn transform_bottom_up(
2992 &mut self,
2993 transform: &mut impl FnMut(&mut HydroNode),
2994 seen_tees: &mut SeenSharedNodes,
2995 check_well_formed: bool,
2996 ) {
2997 self.transform_children(
2998 |n, s| n.transform_bottom_up(transform, s, check_well_formed),
2999 seen_tees,
3000 );
3001
3002 transform(self);
3003
3004 let self_location = self.metadata().location_id.root();
3005
3006 if check_well_formed {
3007 match &*self {
3008 HydroNode::Network { .. } => {}
3009 _ => {
3010 self.input_metadata().iter().for_each(|i| {
3011 if i.location_id.root() != self_location {
3012 panic!(
3013 "Mismatching IR locations, child: {:?} ({:?}) of: {:?} ({:?})",
3014 i,
3015 i.location_id.root(),
3016 self,
3017 self_location
3018 )
3019 }
3020 });
3021 }
3022 }
3023 }
3024 }
3025
3026 #[inline(always)]
3027 pub fn transform_children(
3028 &mut self,
3029 mut transform: impl FnMut(&mut HydroNode, &mut SeenSharedNodes),
3030 seen_tees: &mut SeenSharedNodes,
3031 ) {
3032 match self {
3033 HydroNode::Placeholder => {
3034 panic!();
3035 }
3036
3037 HydroNode::Source { .. }
3038 | HydroNode::SingletonSource { .. }
3039 | HydroNode::CycleSource { .. }
3040 | HydroNode::ExternalInput { .. } => {}
3041
3042 HydroNode::Tee { inner, .. } | HydroNode::Reference { inner, .. } => {
3043 if let Some(transformed) = seen_tees.get(&inner.as_ptr()) {
3044 *inner = SharedNode(transformed.clone());
3045 } else {
3046 let transformed_cell = Rc::new(RefCell::new(HydroNode::Placeholder));
3047 seen_tees.insert(inner.as_ptr(), transformed_cell.clone());
3048 let mut orig = inner.0.replace(HydroNode::Placeholder);
3049 transform(&mut orig, seen_tees);
3050 *transformed_cell.borrow_mut() = orig;
3051 *inner = SharedNode(transformed_cell);
3052 }
3053 }
3054
3055 HydroNode::PartitionSide { inner, .. } => {
3056 if let Some(transformed) = seen_tees.get(&inner.as_ptr()) {
3057 *inner = SharedNode(transformed.clone());
3058 } else {
3059 let transformed_cell = Rc::new(RefCell::new(HydroNode::Placeholder));
3060 seen_tees.insert(inner.as_ptr(), transformed_cell.clone());
3061 let mut orig: HydroNode = inner.0.replace(HydroNode::Placeholder);
3062 transform(&mut orig, seen_tees);
3063 *transformed_cell.borrow_mut() = orig;
3064 *inner = SharedNode(transformed_cell);
3065 }
3066 }
3067 HydroNode::PartitionShared { input, f, .. } => {
3068 f.transform_children(&mut transform, seen_tees);
3069 transform(input.as_mut(), seen_tees);
3070 }
3071
3072 HydroNode::Cast { inner, .. }
3073 | HydroNode::ObserveNonDet { inner, .. }
3074 | HydroNode::BeginAtomic { inner, .. }
3075 | HydroNode::EndAtomic { inner, .. }
3076 | HydroNode::Batch { inner, .. }
3077 | HydroNode::YieldConcat { inner, .. }
3078 | HydroNode::UnboundSingleton { inner, .. }
3079 | HydroNode::AssertIsConsistent { inner, .. } => {
3080 transform(inner.as_mut(), seen_tees);
3081 }
3082
3083 HydroNode::Chain { first, second, .. } => {
3084 transform(first.as_mut(), seen_tees);
3085 transform(second.as_mut(), seen_tees);
3086 }
3087
3088 HydroNode::MergeOrdered { first, second, .. } => {
3089 transform(first.as_mut(), seen_tees);
3090 transform(second.as_mut(), seen_tees);
3091 }
3092
3093 HydroNode::ChainFirst { first, second, .. } => {
3094 transform(first.as_mut(), seen_tees);
3095 transform(second.as_mut(), seen_tees);
3096 }
3097
3098 HydroNode::CrossSingleton { left, right, .. }
3099 | HydroNode::CrossProduct { left, right, .. }
3100 | HydroNode::Join { left, right, .. }
3101 | HydroNode::JoinHalf { left, right, .. } => {
3102 transform(left.as_mut(), seen_tees);
3103 transform(right.as_mut(), seen_tees);
3104 }
3105
3106 HydroNode::Difference { pos, neg, .. } | HydroNode::AntiJoin { pos, neg, .. } => {
3107 transform(pos.as_mut(), seen_tees);
3108 transform(neg.as_mut(), seen_tees);
3109 }
3110
3111 HydroNode::Map { f, input, .. } => {
3112 f.transform_children(&mut transform, seen_tees);
3113 transform(input.as_mut(), seen_tees);
3114 }
3115 HydroNode::FlatMap { f, input, .. }
3116 | HydroNode::FlatMapStreamBlocking { f, input, .. }
3117 | HydroNode::Filter { f, input, .. }
3118 | HydroNode::FilterMap { f, input, .. }
3119 | HydroNode::Inspect { f, input, .. }
3120 | HydroNode::Reduce { f, input, .. }
3121 | HydroNode::ReduceKeyed { f, input, .. } => {
3122 f.transform_children(&mut transform, seen_tees);
3123 transform(input.as_mut(), seen_tees);
3124 }
3125 HydroNode::ReduceKeyedWatermark {
3126 f,
3127 input,
3128 watermark,
3129 ..
3130 } => {
3131 f.transform_children(&mut transform, seen_tees);
3132 transform(input.as_mut(), seen_tees);
3133 transform(watermark.as_mut(), seen_tees);
3134 }
3135 HydroNode::Fold {
3136 init, acc, input, ..
3137 }
3138 | HydroNode::Scan {
3139 init, acc, input, ..
3140 }
3141 | HydroNode::ScanAsyncBlocking {
3142 init, acc, input, ..
3143 }
3144 | HydroNode::FoldKeyed {
3145 init, acc, input, ..
3146 } => {
3147 init.transform_children(&mut transform, seen_tees);
3148 acc.transform_children(&mut transform, seen_tees);
3149 transform(input.as_mut(), seen_tees);
3150 }
3151 HydroNode::ResolveFutures { input, .. }
3152 | HydroNode::ResolveFuturesBlocking { input, .. }
3153 | HydroNode::ResolveFuturesOrdered { input, .. }
3154 | HydroNode::Sort { input, .. }
3155 | HydroNode::DeferTick { input, .. }
3156 | HydroNode::Enumerate { input, .. }
3157 | HydroNode::Unique { input, .. }
3158 | HydroNode::Network { input, .. }
3159 | HydroNode::Counter { input, .. } => {
3160 transform(input.as_mut(), seen_tees);
3161 }
3162
3163 HydroNode::VersionedNetworkFork { senders, .. } => {
3164 for (_version, sender, _serialize) in senders.iter_mut() {
3165 transform(sender.as_mut(), seen_tees);
3166 }
3167 }
3168
3169 HydroNode::VersionedNetwork { fork, .. } => {
3170 if let Some(transformed) = seen_tees.get(&fork.as_ptr()) {
3171 *fork = SharedNode(transformed.clone());
3172 } else {
3173 let transformed_cell = Rc::new(RefCell::new(HydroNode::Placeholder));
3174 seen_tees.insert(fork.as_ptr(), transformed_cell.clone());
3175 let mut orig = fork.0.replace(HydroNode::Placeholder);
3176 transform(&mut orig, seen_tees);
3177 *transformed_cell.borrow_mut() = orig;
3178 *fork = SharedNode(transformed_cell);
3179 }
3180 }
3181 }
3182 }
3183
3184 pub fn deep_clone(&self, seen_tees: &mut SeenSharedNodes) -> HydroNode {
3185 match self {
3186 HydroNode::Placeholder => HydroNode::Placeholder,
3187 HydroNode::Cast { inner, metadata } => HydroNode::Cast {
3188 inner: Box::new(inner.deep_clone(seen_tees)),
3189 metadata: metadata.clone(),
3190 },
3191 HydroNode::UnboundSingleton { inner, metadata } => HydroNode::UnboundSingleton {
3192 inner: Box::new(inner.deep_clone(seen_tees)),
3193 metadata: metadata.clone(),
3194 },
3195 HydroNode::ObserveNonDet {
3196 inner,
3197 trusted,
3198 metadata,
3199 } => HydroNode::ObserveNonDet {
3200 inner: Box::new(inner.deep_clone(seen_tees)),
3201 trusted: *trusted,
3202 metadata: metadata.clone(),
3203 },
3204 HydroNode::AssertIsConsistent {
3205 inner,
3206 trusted,
3207 metadata,
3208 } => HydroNode::AssertIsConsistent {
3209 inner: Box::new(inner.deep_clone(seen_tees)),
3210 trusted: *trusted,
3211 metadata: metadata.clone(),
3212 },
3213 HydroNode::Source { source, metadata } => HydroNode::Source {
3214 source: source.clone(),
3215 metadata: metadata.clone(),
3216 },
3217 HydroNode::SingletonSource {
3218 value,
3219 first_tick_only,
3220 metadata,
3221 } => HydroNode::SingletonSource {
3222 value: value.clone(),
3223 first_tick_only: *first_tick_only,
3224 metadata: metadata.clone(),
3225 },
3226 HydroNode::CycleSource { cycle_id, metadata } => HydroNode::CycleSource {
3227 cycle_id: *cycle_id,
3228 metadata: metadata.clone(),
3229 },
3230 HydroNode::Tee { inner, metadata }
3231 | HydroNode::Reference {
3232 inner, metadata, ..
3233 } => {
3234 let cloned_inner = if let Some(transformed) = seen_tees.get(&inner.as_ptr()) {
3235 SharedNode(transformed.clone())
3236 } else {
3237 let new_rc = Rc::new(RefCell::new(HydroNode::Placeholder));
3238 seen_tees.insert(inner.as_ptr(), new_rc.clone());
3239 let cloned = inner.0.borrow().deep_clone(seen_tees);
3240 *new_rc.borrow_mut() = cloned;
3241 SharedNode(new_rc)
3242 };
3243 if let HydroNode::Reference {
3244 kind,
3245 access_counter,
3246 ..
3247 } = self
3248 {
3249 HydroNode::Reference {
3250 inner: cloned_inner,
3251 kind: *kind,
3252 access_counter: access_counter.freeze(),
3253 metadata: metadata.clone(),
3254 }
3255 } else {
3256 HydroNode::Tee {
3257 inner: cloned_inner,
3258 metadata: metadata.clone(),
3259 }
3260 }
3261 }
3262 HydroNode::PartitionSide {
3263 inner,
3264 is_true,
3265 metadata,
3266 } => {
3267 if let Some(transformed) = seen_tees.get(&inner.as_ptr()) {
3268 HydroNode::PartitionSide {
3269 inner: SharedNode(transformed.clone()),
3270 is_true: *is_true,
3271 metadata: metadata.clone(),
3272 }
3273 } else {
3274 let new_rc = Rc::new(RefCell::new(HydroNode::Placeholder));
3275 seen_tees.insert(inner.as_ptr(), new_rc.clone());
3276 let cloned = inner.0.borrow().deep_clone(seen_tees);
3277 *new_rc.borrow_mut() = cloned;
3278 HydroNode::PartitionSide {
3279 inner: SharedNode(new_rc),
3280 is_true: *is_true,
3281 metadata: metadata.clone(),
3282 }
3283 }
3284 }
3285 HydroNode::PartitionShared { input, f, metadata } => HydroNode::PartitionShared {
3286 input: Box::new(input.deep_clone(seen_tees)),
3287 f: f.deep_clone(seen_tees),
3288 metadata: metadata.clone(),
3289 },
3290 HydroNode::YieldConcat { inner, metadata } => HydroNode::YieldConcat {
3291 inner: Box::new(inner.deep_clone(seen_tees)),
3292 metadata: metadata.clone(),
3293 },
3294 HydroNode::BeginAtomic { inner, metadata } => HydroNode::BeginAtomic {
3295 inner: Box::new(inner.deep_clone(seen_tees)),
3296 metadata: metadata.clone(),
3297 },
3298 HydroNode::EndAtomic { inner, metadata } => HydroNode::EndAtomic {
3299 inner: Box::new(inner.deep_clone(seen_tees)),
3300 metadata: metadata.clone(),
3301 },
3302 HydroNode::Batch { inner, metadata } => HydroNode::Batch {
3303 inner: Box::new(inner.deep_clone(seen_tees)),
3304 metadata: metadata.clone(),
3305 },
3306 HydroNode::Chain {
3307 first,
3308 second,
3309 metadata,
3310 } => HydroNode::Chain {
3311 first: Box::new(first.deep_clone(seen_tees)),
3312 second: Box::new(second.deep_clone(seen_tees)),
3313 metadata: metadata.clone(),
3314 },
3315 HydroNode::MergeOrdered {
3316 first,
3317 second,
3318 metadata,
3319 } => HydroNode::MergeOrdered {
3320 first: Box::new(first.deep_clone(seen_tees)),
3321 second: Box::new(second.deep_clone(seen_tees)),
3322 metadata: metadata.clone(),
3323 },
3324 HydroNode::ChainFirst {
3325 first,
3326 second,
3327 metadata,
3328 } => HydroNode::ChainFirst {
3329 first: Box::new(first.deep_clone(seen_tees)),
3330 second: Box::new(second.deep_clone(seen_tees)),
3331 metadata: metadata.clone(),
3332 },
3333 HydroNode::CrossProduct {
3334 left,
3335 right,
3336 metadata,
3337 } => HydroNode::CrossProduct {
3338 left: Box::new(left.deep_clone(seen_tees)),
3339 right: Box::new(right.deep_clone(seen_tees)),
3340 metadata: metadata.clone(),
3341 },
3342 HydroNode::CrossSingleton {
3343 left,
3344 right,
3345 metadata,
3346 } => HydroNode::CrossSingleton {
3347 left: Box::new(left.deep_clone(seen_tees)),
3348 right: Box::new(right.deep_clone(seen_tees)),
3349 metadata: metadata.clone(),
3350 },
3351 HydroNode::Join {
3352 left,
3353 right,
3354 metadata,
3355 } => HydroNode::Join {
3356 left: Box::new(left.deep_clone(seen_tees)),
3357 right: Box::new(right.deep_clone(seen_tees)),
3358 metadata: metadata.clone(),
3359 },
3360 HydroNode::JoinHalf {
3361 left,
3362 right,
3363 metadata,
3364 } => HydroNode::JoinHalf {
3365 left: Box::new(left.deep_clone(seen_tees)),
3366 right: Box::new(right.deep_clone(seen_tees)),
3367 metadata: metadata.clone(),
3368 },
3369 HydroNode::Difference { pos, neg, metadata } => HydroNode::Difference {
3370 pos: Box::new(pos.deep_clone(seen_tees)),
3371 neg: Box::new(neg.deep_clone(seen_tees)),
3372 metadata: metadata.clone(),
3373 },
3374 HydroNode::AntiJoin { pos, neg, metadata } => HydroNode::AntiJoin {
3375 pos: Box::new(pos.deep_clone(seen_tees)),
3376 neg: Box::new(neg.deep_clone(seen_tees)),
3377 metadata: metadata.clone(),
3378 },
3379 HydroNode::ResolveFutures { input, metadata } => HydroNode::ResolveFutures {
3380 input: Box::new(input.deep_clone(seen_tees)),
3381 metadata: metadata.clone(),
3382 },
3383 HydroNode::ResolveFuturesBlocking { input, metadata } => {
3384 HydroNode::ResolveFuturesBlocking {
3385 input: Box::new(input.deep_clone(seen_tees)),
3386 metadata: metadata.clone(),
3387 }
3388 }
3389 HydroNode::ResolveFuturesOrdered { input, metadata } => {
3390 HydroNode::ResolveFuturesOrdered {
3391 input: Box::new(input.deep_clone(seen_tees)),
3392 metadata: metadata.clone(),
3393 }
3394 }
3395 HydroNode::Map { f, input, metadata } => HydroNode::Map {
3396 f: f.deep_clone(seen_tees),
3397 input: Box::new(input.deep_clone(seen_tees)),
3398 metadata: metadata.clone(),
3399 },
3400 HydroNode::FlatMap { f, input, metadata } => HydroNode::FlatMap {
3401 f: f.deep_clone(seen_tees),
3402 input: Box::new(input.deep_clone(seen_tees)),
3403 metadata: metadata.clone(),
3404 },
3405 HydroNode::FlatMapStreamBlocking { f, input, metadata } => {
3406 HydroNode::FlatMapStreamBlocking {
3407 f: f.deep_clone(seen_tees),
3408 input: Box::new(input.deep_clone(seen_tees)),
3409 metadata: metadata.clone(),
3410 }
3411 }
3412 HydroNode::Filter { f, input, metadata } => HydroNode::Filter {
3413 f: f.deep_clone(seen_tees),
3414 input: Box::new(input.deep_clone(seen_tees)),
3415 metadata: metadata.clone(),
3416 },
3417 HydroNode::FilterMap { f, input, metadata } => HydroNode::FilterMap {
3418 f: f.deep_clone(seen_tees),
3419 input: Box::new(input.deep_clone(seen_tees)),
3420 metadata: metadata.clone(),
3421 },
3422 HydroNode::DeferTick { input, metadata } => HydroNode::DeferTick {
3423 input: Box::new(input.deep_clone(seen_tees)),
3424 metadata: metadata.clone(),
3425 },
3426 HydroNode::Enumerate { input, metadata } => HydroNode::Enumerate {
3427 input: Box::new(input.deep_clone(seen_tees)),
3428 metadata: metadata.clone(),
3429 },
3430 HydroNode::Inspect { f, input, metadata } => HydroNode::Inspect {
3431 f: f.deep_clone(seen_tees),
3432 input: Box::new(input.deep_clone(seen_tees)),
3433 metadata: metadata.clone(),
3434 },
3435 HydroNode::Unique { input, metadata } => HydroNode::Unique {
3436 input: Box::new(input.deep_clone(seen_tees)),
3437 metadata: metadata.clone(),
3438 },
3439 HydroNode::Sort { input, metadata } => HydroNode::Sort {
3440 input: Box::new(input.deep_clone(seen_tees)),
3441 metadata: metadata.clone(),
3442 },
3443 HydroNode::Fold {
3444 init,
3445 acc,
3446 input,
3447 metadata,
3448 } => HydroNode::Fold {
3449 init: init.deep_clone(seen_tees),
3450 acc: acc.deep_clone(seen_tees),
3451 input: Box::new(input.deep_clone(seen_tees)),
3452 metadata: metadata.clone(),
3453 },
3454 HydroNode::Scan {
3455 init,
3456 acc,
3457 input,
3458 metadata,
3459 } => HydroNode::Scan {
3460 init: init.deep_clone(seen_tees),
3461 acc: acc.deep_clone(seen_tees),
3462 input: Box::new(input.deep_clone(seen_tees)),
3463 metadata: metadata.clone(),
3464 },
3465 HydroNode::ScanAsyncBlocking {
3466 init,
3467 acc,
3468 input,
3469 metadata,
3470 } => HydroNode::ScanAsyncBlocking {
3471 init: init.deep_clone(seen_tees),
3472 acc: acc.deep_clone(seen_tees),
3473 input: Box::new(input.deep_clone(seen_tees)),
3474 metadata: metadata.clone(),
3475 },
3476 HydroNode::FoldKeyed {
3477 init,
3478 acc,
3479 input,
3480 metadata,
3481 } => HydroNode::FoldKeyed {
3482 init: init.deep_clone(seen_tees),
3483 acc: acc.deep_clone(seen_tees),
3484 input: Box::new(input.deep_clone(seen_tees)),
3485 metadata: metadata.clone(),
3486 },
3487 HydroNode::ReduceKeyedWatermark {
3488 f,
3489 input,
3490 watermark,
3491 metadata,
3492 } => HydroNode::ReduceKeyedWatermark {
3493 f: f.deep_clone(seen_tees),
3494 input: Box::new(input.deep_clone(seen_tees)),
3495 watermark: Box::new(watermark.deep_clone(seen_tees)),
3496 metadata: metadata.clone(),
3497 },
3498 HydroNode::Reduce { f, input, metadata } => HydroNode::Reduce {
3499 f: f.deep_clone(seen_tees),
3500 input: Box::new(input.deep_clone(seen_tees)),
3501 metadata: metadata.clone(),
3502 },
3503 HydroNode::ReduceKeyed { f, input, metadata } => HydroNode::ReduceKeyed {
3504 f: f.deep_clone(seen_tees),
3505 input: Box::new(input.deep_clone(seen_tees)),
3506 metadata: metadata.clone(),
3507 },
3508 HydroNode::Network {
3509 name,
3510 networking_info,
3511 serialize,
3512 deserialize,
3513 instantiate_fn,
3514 input,
3515 metadata,
3516 } => HydroNode::Network {
3517 name: name.clone(),
3518 networking_info: networking_info.clone(),
3519 serialize: serialize.clone(),
3520 deserialize: deserialize.clone(),
3521 instantiate_fn: instantiate_fn.clone(),
3522 input: Box::new(input.deep_clone(seen_tees)),
3523 metadata: metadata.clone(),
3524 },
3525 HydroNode::ExternalInput {
3526 from_external_key,
3527 from_port_id,
3528 from_many,
3529 codec_type,
3530 port_hint,
3531 instantiate_fn,
3532 deserialize_fn,
3533 metadata,
3534 } => HydroNode::ExternalInput {
3535 from_external_key: *from_external_key,
3536 from_port_id: *from_port_id,
3537 from_many: *from_many,
3538 codec_type: codec_type.clone(),
3539 port_hint: *port_hint,
3540 instantiate_fn: instantiate_fn.clone(),
3541 deserialize_fn: deserialize_fn.clone(),
3542 metadata: metadata.clone(),
3543 },
3544 HydroNode::Counter {
3545 tag,
3546 duration,
3547 prefix,
3548 input,
3549 metadata,
3550 } => HydroNode::Counter {
3551 tag: tag.clone(),
3552 duration: duration.clone(),
3553 prefix: prefix.clone(),
3554 input: Box::new(input.deep_clone(seen_tees)),
3555 metadata: metadata.clone(),
3556 },
3557 HydroNode::VersionedNetworkFork {
3558 channel_id,
3559 channel_name,
3560 senders,
3561 metadata,
3562 } => HydroNode::VersionedNetworkFork {
3563 channel_id: *channel_id,
3564 channel_name: channel_name.clone(),
3565 senders: senders
3566 .iter()
3567 .map(|(version, sender, serialize)| {
3568 (
3569 *version,
3570 Box::new(sender.deep_clone(seen_tees)),
3571 serialize.clone(),
3572 )
3573 })
3574 .collect(),
3575 metadata: metadata.clone(),
3576 },
3577 HydroNode::VersionedNetwork {
3578 fork,
3579 version,
3580 deserialize,
3581 metadata,
3582 } => {
3583 let cloned_fork = if let Some(transformed) = seen_tees.get(&fork.as_ptr()) {
3584 SharedNode(transformed.clone())
3585 } else {
3586 let new_rc = Rc::new(RefCell::new(HydroNode::Placeholder));
3587 seen_tees.insert(fork.as_ptr(), new_rc.clone());
3588 let cloned = fork.0.borrow().deep_clone(seen_tees);
3589 *new_rc.borrow_mut() = cloned;
3590 SharedNode(new_rc)
3591 };
3592 HydroNode::VersionedNetwork {
3593 fork: cloned_fork,
3594 version: *version,
3595 deserialize: deserialize.clone(),
3596 metadata: metadata.clone(),
3597 }
3598 }
3599 }
3600 }
3601
3602 #[cfg(feature = "build")]
3603 pub fn emit_core(
3604 &mut self,
3605 builders_or_callback: &mut BuildersOrCallback<
3606 '_,
3607 impl FnMut(&mut HydroRoot, &mut crate::Counter<StmtId>),
3608 impl FnMut(&mut HydroNode, &mut crate::Counter<StmtId>),
3609 >,
3610 seen_tees: &mut SeenSharedNodes,
3611 built_tees: &mut HashMap<*const RefCell<HydroNode>, Vec<syn::Ident>>,
3612 next_stmt_id: &mut crate::Counter<StmtId>,
3613 fold_hooked_idents: &mut HashSet<String>,
3614 ) -> syn::Ident {
3615 let mut ident_stack: Vec<syn::Ident> = Vec::new();
3616
3617 self.transform_bottom_up(
3618 &mut |node: &mut HydroNode| {
3619 let out_location = node.metadata().location_id.clone();
3620 match node {
3621 HydroNode::Placeholder => {
3622 panic!()
3623 }
3624
3625 HydroNode::Cast { .. } => {
3626 let _ = next_stmt_id.get_and_increment();
3629 match builders_or_callback {
3630 BuildersOrCallback::Builders(_) => {}
3631 BuildersOrCallback::Callback(_, node_callback) => {
3632 node_callback(node, next_stmt_id);
3633 }
3634 }
3635 }
3637
3638 HydroNode::UnboundSingleton { .. } => {
3639 let inner_ident = ident_stack.pop().unwrap();
3640
3641 let stmt_id = next_stmt_id.get_and_increment();
3642 let out_ident =
3643 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3644
3645 match builders_or_callback {
3646 BuildersOrCallback::Builders(graph_builders) => {
3647 if graph_builders.singleton_intermediates() {
3648 graph_builders.add_dfir_at(
3649 &out_location,
3650 parse_quote! {
3651 #out_ident = #inner_ident;
3652 },
3653 None,
3654 );
3655 } else {
3656 graph_builders.add_dfir_at(
3657 &out_location,
3658 parse_quote! {
3659 #out_ident = #inner_ident -> persist::<'static>();
3660 },
3661 None,
3662 );
3663 }
3664 }
3665 BuildersOrCallback::Callback(_, node_callback) => {
3666 node_callback(node, next_stmt_id);
3667 }
3668 }
3669
3670 ident_stack.push(out_ident);
3671 }
3672
3673 HydroNode::AssertIsConsistent { inner, trusted, .. } => {
3674 let inner_ident = ident_stack.pop().unwrap();
3675
3676 let stmt_id = next_stmt_id.get_and_increment();
3677 let out_ident =
3678 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3679
3680 match builders_or_callback {
3681 BuildersOrCallback::Builders(graph_builders) => {
3682 graph_builders.assert_is_consistent(
3683 *trusted,
3684 &inner.metadata().location_id,
3685 inner_ident,
3686 &out_ident,
3687 );
3688 }
3689 BuildersOrCallback::Callback(_, node_callback) => {
3690 node_callback(node, next_stmt_id);
3691 }
3692 }
3693
3694 ident_stack.push(out_ident);
3695 }
3696
3697 HydroNode::ObserveNonDet {
3698 inner,
3699 trusted,
3700 metadata,
3701 ..
3702 } => {
3703 let inner_ident = ident_stack.pop().unwrap();
3704
3705 let stmt_id = next_stmt_id.get_and_increment();
3706 let observe_ident =
3707 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3708
3709 match builders_or_callback {
3710 BuildersOrCallback::Builders(graph_builders) => {
3711 graph_builders.observe_nondet(
3712 *trusted,
3713 &inner.metadata().location_id,
3714 inner_ident,
3715 &inner.metadata().collection_kind,
3716 &observe_ident,
3717 &metadata.collection_kind,
3718 &metadata.op,
3719 );
3720 }
3721 BuildersOrCallback::Callback(_, node_callback) => {
3722 node_callback(node, next_stmt_id);
3723 }
3724 }
3725
3726 ident_stack.push(observe_ident);
3727 }
3728
3729 HydroNode::Batch {
3730 inner, metadata, ..
3731 } => {
3732 let inner_ident = ident_stack.pop().unwrap();
3733
3734 let stmt_id = next_stmt_id.get_and_increment();
3735 let batch_ident =
3736 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3737
3738 match builders_or_callback {
3739 BuildersOrCallback::Builders(graph_builders) => {
3740 graph_builders.batch(
3741 inner_ident,
3742 &inner.metadata().location_id,
3743 &inner.metadata().collection_kind,
3744 &batch_ident,
3745 &out_location,
3746 &metadata.op,
3747 fold_hooked_idents,
3748 );
3749 }
3750 BuildersOrCallback::Callback(_, node_callback) => {
3751 node_callback(node, next_stmt_id);
3752 }
3753 }
3754
3755 ident_stack.push(batch_ident);
3756 }
3757
3758 HydroNode::YieldConcat { inner, .. } => {
3759 let inner_ident = ident_stack.pop().unwrap();
3760
3761 let stmt_id = next_stmt_id.get_and_increment();
3762 let yield_ident =
3763 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3764
3765 match builders_or_callback {
3766 BuildersOrCallback::Builders(graph_builders) => {
3767 graph_builders.yield_from_tick(
3768 inner_ident,
3769 &inner.metadata().location_id,
3770 &inner.metadata().collection_kind,
3771 &yield_ident,
3772 &out_location,
3773 );
3774 }
3775 BuildersOrCallback::Callback(_, node_callback) => {
3776 node_callback(node, next_stmt_id);
3777 }
3778 }
3779
3780 ident_stack.push(yield_ident);
3781 }
3782
3783 HydroNode::BeginAtomic { inner, metadata } => {
3784 let inner_ident = ident_stack.pop().unwrap();
3785
3786 let stmt_id = next_stmt_id.get_and_increment();
3787 let begin_ident =
3788 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3789
3790 match builders_or_callback {
3791 BuildersOrCallback::Builders(graph_builders) => {
3792 graph_builders.begin_atomic(
3793 inner_ident,
3794 &inner.metadata().location_id,
3795 &inner.metadata().collection_kind,
3796 &begin_ident,
3797 &out_location,
3798 &metadata.op,
3799 );
3800 }
3801 BuildersOrCallback::Callback(_, node_callback) => {
3802 node_callback(node, next_stmt_id);
3803 }
3804 }
3805
3806 ident_stack.push(begin_ident);
3807 }
3808
3809 HydroNode::EndAtomic { inner, .. } => {
3810 let inner_ident = ident_stack.pop().unwrap();
3811
3812 let stmt_id = next_stmt_id.get_and_increment();
3813 let end_ident =
3814 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3815
3816 match builders_or_callback {
3817 BuildersOrCallback::Builders(graph_builders) => {
3818 graph_builders.end_atomic(
3819 inner_ident,
3820 &inner.metadata().location_id,
3821 &inner.metadata().collection_kind,
3822 &end_ident,
3823 );
3824 }
3825 BuildersOrCallback::Callback(_, node_callback) => {
3826 node_callback(node, next_stmt_id);
3827 }
3828 }
3829
3830 ident_stack.push(end_ident);
3831 }
3832
3833 HydroNode::Source {
3834 source, metadata, ..
3835 } => {
3836 if let HydroSource::ExternalNetwork() = source {
3837 ident_stack.push(syn::Ident::new("DUMMY", Span::call_site()));
3838 } else {
3839 let stmt_id = next_stmt_id.get_and_increment();
3840 let source_ident =
3841 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3842
3843 let source_stmt = match source {
3844 HydroSource::Stream(expr) => {
3845 debug_assert!(metadata.location_id.is_top_level());
3846 parse_quote! {
3847 #source_ident = source_stream(#expr);
3848 }
3849 }
3850
3851 HydroSource::ExternalNetwork() => {
3852 unreachable!()
3853 }
3854
3855 HydroSource::Iter(expr) => {
3856 if metadata.location_id.is_top_level() {
3857 parse_quote! {
3858 #source_ident = source_iter(#expr);
3859 }
3860 } else {
3861 parse_quote! {
3863 #source_ident = source_iter(#expr) -> persist::<'static>();
3864 }
3865 }
3866 }
3867
3868 HydroSource::Spin() => {
3869 debug_assert!(metadata.location_id.is_top_level());
3870 parse_quote! {
3871 #source_ident = spin();
3872 }
3873 }
3874
3875 HydroSource::ClusterMembers(target_loc, state) => {
3876 debug_assert!(metadata.location_id.is_top_level());
3877
3878 let members_tee_ident = syn::Ident::new(
3879 &format!(
3880 "__cluster_members_tee_{}_{}",
3881 metadata.location_id.root().key(),
3882 target_loc.key(),
3883 ),
3884 Span::call_site(),
3885 );
3886
3887 match state {
3888 ClusterMembersState::Stream(d) => {
3889 parse_quote! {
3890 #members_tee_ident = source_stream(#d) -> tee();
3891 #source_ident = #members_tee_ident;
3892 }
3893 },
3894 ClusterMembersState::Uninit => syn::parse_quote! {
3895 #source_ident = source_stream(DUMMY);
3896 },
3897 ClusterMembersState::Tee(..) => parse_quote! {
3898 #source_ident = #members_tee_ident;
3899 },
3900 }
3901 }
3902
3903 HydroSource::Embedded(ident) => {
3904 parse_quote! {
3905 #source_ident = source_stream(#ident);
3906 }
3907 }
3908
3909 HydroSource::EmbeddedSingleton(ident) => {
3910 parse_quote! {
3911 #source_ident = source_iter([#ident]);
3912 }
3913 }
3914 };
3915
3916 match builders_or_callback {
3917 BuildersOrCallback::Builders(graph_builders) => {
3918 graph_builders.add_dfir_at(
3919 &out_location,
3920 source_stmt,
3921 Some(&stmt_id.to_string()),
3922 );
3923 }
3924 BuildersOrCallback::Callback(_, node_callback) => {
3925 node_callback(node, next_stmt_id);
3926 }
3927 }
3928
3929 ident_stack.push(source_ident);
3930 }
3931 }
3932
3933 HydroNode::SingletonSource { value, first_tick_only, metadata } => {
3934 let stmt_id = next_stmt_id.get_and_increment();
3935 let source_ident =
3936 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
3937
3938 match builders_or_callback {
3939 BuildersOrCallback::Builders(graph_builders) => {
3940 if *first_tick_only {
3941 assert!(
3942 !metadata.location_id.is_top_level(),
3943 "first_tick_only SingletonSource must be inside a tick"
3944 );
3945 }
3946
3947 if *first_tick_only
3948 || (metadata.location_id.is_top_level()
3949 && metadata.collection_kind.is_bounded())
3950 {
3951 graph_builders.add_dfir_at(
3952 &out_location,
3953 parse_quote! {
3954 #source_ident = source_iter([#value]);
3955 },
3956 Some(&stmt_id.to_string()),
3957 );
3958 } else {
3959 graph_builders.add_dfir_at(
3960 &out_location,
3961 parse_quote! {
3962 #source_ident = source_iter([#value]) -> persist::<'static>();
3963 },
3964 Some(&stmt_id.to_string()),
3965 );
3966 }
3967 }
3968 BuildersOrCallback::Callback(_, node_callback) => {
3969 node_callback(node, next_stmt_id);
3970 }
3971 }
3972
3973 ident_stack.push(source_ident);
3974 }
3975
3976 HydroNode::CycleSource { cycle_id, .. } => {
3977 let ident = cycle_id.as_ident();
3978
3979 let _ = next_stmt_id.get_and_increment();
3981
3982 match builders_or_callback {
3983 BuildersOrCallback::Builders(_) => {}
3984 BuildersOrCallback::Callback(_, node_callback) => {
3985 node_callback(node, next_stmt_id);
3986 }
3987 }
3988
3989 ident_stack.push(ident);
3990 }
3991
3992 HydroNode::Tee { inner, .. } => {
3993 let stmt_id = next_stmt_id.get_and_increment();
3996
3997 let ret_ident = if let Some(built_idents) =
3998 built_tees.get(&(std::ptr::from_ref(inner.0.as_ref())))
3999 {
4000 match builders_or_callback {
4001 BuildersOrCallback::Builders(_) => {}
4002 BuildersOrCallback::Callback(_, node_callback) => {
4003 node_callback(node, next_stmt_id);
4004 }
4005 }
4006
4007 built_idents[0].clone()
4008 } else {
4009 let inner_ident = ident_stack.pop().unwrap();
4012
4013 let tee_ident =
4014 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4015
4016 built_tees.insert(
4017 std::ptr::from_ref(inner.0.as_ref()),
4018 vec![tee_ident.clone()],
4019 );
4020
4021 match builders_or_callback {
4022 BuildersOrCallback::Builders(graph_builders) => {
4023 if fold_hooked_idents.contains(&inner_ident.to_string()) {
4035 fold_hooked_idents.insert(tee_ident.to_string());
4036 }
4037 graph_builders.add_dfir_at(
4038 &out_location,
4039 parse_quote! {
4040 #tee_ident = #inner_ident -> tee();
4041 },
4042 Some(&stmt_id.to_string()),
4043 );
4044 }
4045 BuildersOrCallback::Callback(_, node_callback) => {
4046 node_callback(node, next_stmt_id);
4047 }
4048 }
4049
4050 tee_ident
4051 };
4052
4053 ident_stack.push(ret_ident);
4054 }
4055
4056 HydroNode::Reference { inner, kind, .. } => {
4057 let stmt_id = next_stmt_id.get_and_increment();
4060
4061 let ret_ident = if let Some(built_idents) =
4062 built_tees.get(&(std::ptr::from_ref(inner.0.as_ref())))
4063 {
4064 built_idents[0].clone()
4065 } else {
4066 let inner_ident = ident_stack.pop().unwrap();
4067
4068 let ref_ident =
4069 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4070
4071 built_tees.insert(
4072 std::ptr::from_ref(inner.0.as_ref()),
4073 vec![ref_ident.clone()],
4074 );
4075
4076 match builders_or_callback {
4077 BuildersOrCallback::Builders(graph_builders) => {
4078 let op_ident = syn::Ident::new(
4079 match kind {
4080 crate::handoff_ref::HandoffRefKind::Singleton => "singleton",
4081 crate::handoff_ref::HandoffRefKind::Optional => "optional",
4082 crate::handoff_ref::HandoffRefKind::Vec => "handoff",
4083 },
4084 Span::call_site(),
4085 );
4086 graph_builders.add_dfir_at(
4087 &out_location,
4088 parse_quote! {
4089 #ref_ident = #inner_ident -> #op_ident();
4090 },
4091 Some(&stmt_id.to_string()),
4092 );
4093 }
4094 BuildersOrCallback::Callback(_, node_callback) => {
4095 node_callback(node, next_stmt_id);
4096 }
4097 }
4098
4099 ref_ident
4100 };
4101
4102 ident_stack.push(ret_ident);
4103 }
4104
4105 HydroNode::PartitionSide {
4106 inner, is_true, metadata: _,
4107 } => {
4108 let is_true = *is_true; let ptr = std::ptr::from_ref(inner.0.as_ref());
4110 let stmt_id = next_stmt_id.get_and_increment();
4111
4112 let ret_ident = if let Some(built_idents) = built_tees.get(&ptr) {
4113 match builders_or_callback {
4114 BuildersOrCallback::Builders(_) => {}
4115 BuildersOrCallback::Callback(_, node_callback) => {
4116 node_callback(node, next_stmt_id);
4117 }
4118 }
4119
4120 let idx = if is_true { 0 } else { 1 };
4121 built_idents[idx].clone()
4122 } else {
4123 let partition_ident = ident_stack.pop().unwrap();
4126
4127 let true_ident = syn::Ident::new(
4128 &format!("stream_{}_true", stmt_id),
4129 Span::call_site(),
4130 );
4131 let false_ident = syn::Ident::new(
4132 &format!("stream_{}_false", stmt_id),
4133 Span::call_site(),
4134 );
4135
4136 built_tees.insert(
4137 ptr,
4138 vec![true_ident.clone(), false_ident.clone()],
4139 );
4140
4141 let stmt_id = next_stmt_id.get_and_increment();
4142 match builders_or_callback {
4143 BuildersOrCallback::Builders(graph_builders) => {
4144 graph_builders.add_dfir_at(
4145 &out_location,
4146 parse_quote! {
4147 #true_ident = #partition_ident[0];
4148 #false_ident = #partition_ident[1];
4149 },
4150 Some(&stmt_id.to_string()),
4151 );
4152 }
4153 BuildersOrCallback::Callback(_, node_callback) => {
4154 node_callback(node, next_stmt_id);
4155 }
4156 }
4157
4158 if is_true { true_ident } else { false_ident }
4159 };
4160
4161 ident_stack.push(ret_ident);
4162 }
4163
4164 HydroNode::PartitionShared { input, f, metadata } => {
4165 let inner_ident = ident_stack.pop().unwrap();
4168 let f_tokens = f.emit_tokens(&mut ident_stack);
4169
4170 let inner_ident = {
4171 maybe_observe_for_mut(
4172 f, inner_ident,
4173 &input.metadata().location_id,
4174 &input.metadata().collection_kind,
4175 &metadata.op,
4176 builders_or_callback, next_stmt_id,
4177 )
4178 };
4179
4180 let stmt_id = next_stmt_id.get_and_increment();
4181 let partition_ident = syn::Ident::new(
4182 &format!("stream_{}_partition", stmt_id),
4183 Span::call_site(),
4184 );
4185
4186 let stmt_id = next_stmt_id.get_and_increment();
4187 match builders_or_callback {
4188 BuildersOrCallback::Builders(graph_builders) => {
4189 graph_builders.add_dfir_at(
4190 &out_location,
4191 parse_quote! {
4192 #partition_ident = #inner_ident -> partition(|__item, __num_outputs| if (#f_tokens)(__item) { 0_usize } else { 1_usize });
4193 },
4194 Some(&stmt_id.to_string()),
4195 );
4196 }
4197 BuildersOrCallback::Callback(_, node_callback) => {
4198 node_callback(node, next_stmt_id);
4199 }
4200 }
4201 ident_stack.push(partition_ident);
4202 }
4203
4204 HydroNode::Chain { .. } => {
4205 let second_ident = ident_stack.pop().unwrap();
4207 let first_ident = ident_stack.pop().unwrap();
4208
4209 let stmt_id = next_stmt_id.get_and_increment();
4210 let chain_ident =
4211 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4212
4213 match builders_or_callback {
4214 BuildersOrCallback::Builders(graph_builders) => {
4215 graph_builders.add_dfir_at(
4216 &out_location,
4217 parse_quote! {
4218 #chain_ident = chain();
4219 #first_ident -> [0]#chain_ident;
4220 #second_ident -> [1]#chain_ident;
4221 },
4222 Some(&stmt_id.to_string()),
4223 );
4224 }
4225 BuildersOrCallback::Callback(_, node_callback) => {
4226 node_callback(node, next_stmt_id);
4227 }
4228 }
4229
4230 ident_stack.push(chain_ident);
4231 }
4232
4233 HydroNode::MergeOrdered { first, metadata, .. } => {
4234 let second_ident = ident_stack.pop().unwrap();
4235 let first_ident = ident_stack.pop().unwrap();
4236
4237 let stmt_id = next_stmt_id.get_and_increment();
4238 let merge_ident =
4239 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4240
4241 match builders_or_callback {
4242 BuildersOrCallback::Builders(graph_builders) => {
4243 graph_builders.merge_ordered(
4244 &first.metadata().location_id,
4245 first_ident,
4246 second_ident,
4247 &merge_ident,
4248 &first.metadata().collection_kind,
4249 &metadata.op,
4250 Some(&stmt_id.to_string()),
4251 );
4252 }
4253 BuildersOrCallback::Callback(_, node_callback) => {
4254 node_callback(node, next_stmt_id);
4255 }
4256 }
4257
4258 ident_stack.push(merge_ident);
4259 }
4260
4261 HydroNode::ChainFirst { .. } => {
4262 let second_ident = ident_stack.pop().unwrap();
4263 let first_ident = ident_stack.pop().unwrap();
4264
4265 let stmt_id = next_stmt_id.get_and_increment();
4266 let chain_ident =
4267 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4268
4269 match builders_or_callback {
4270 BuildersOrCallback::Builders(graph_builders) => {
4271 graph_builders.add_dfir_at(
4272 &out_location,
4273 parse_quote! {
4274 #chain_ident = chain_first_n(1);
4275 #first_ident -> [0]#chain_ident;
4276 #second_ident -> [1]#chain_ident;
4277 },
4278 Some(&stmt_id.to_string()),
4279 );
4280 }
4281 BuildersOrCallback::Callback(_, node_callback) => {
4282 node_callback(node, next_stmt_id);
4283 }
4284 }
4285
4286 ident_stack.push(chain_ident);
4287 }
4288
4289 HydroNode::CrossSingleton { right, .. } => {
4290 let right_ident = ident_stack.pop().unwrap();
4291 let left_ident = ident_stack.pop().unwrap();
4292
4293 let stmt_id = next_stmt_id.get_and_increment();
4294 let cross_ident =
4295 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4296
4297 match builders_or_callback {
4298 BuildersOrCallback::Builders(graph_builders) => {
4299 if right.metadata().location_id.is_top_level()
4300 && right.metadata().collection_kind.is_bounded()
4301 {
4302 let lifetime =
4303 graph_builders.cross_tick_state_lifetime(&out_location);
4304 graph_builders.add_dfir_at(
4305 &out_location,
4306 parse_quote! {
4307 #cross_ident = cross_singleton::<#lifetime>();
4308 #left_ident -> [input]#cross_ident;
4309 #right_ident -> [single]#cross_ident;
4310 },
4311 Some(&stmt_id.to_string()),
4312 );
4313 } else {
4314 graph_builders.add_dfir_at(
4315 &out_location,
4316 parse_quote! {
4317 #cross_ident = cross_singleton();
4318 #left_ident -> [input]#cross_ident;
4319 #right_ident -> [single]#cross_ident;
4320 },
4321 Some(&stmt_id.to_string()),
4322 );
4323 }
4324 }
4325 BuildersOrCallback::Callback(_, node_callback) => {
4326 node_callback(node, next_stmt_id);
4327 }
4328 }
4329
4330 ident_stack.push(cross_ident);
4331 }
4332
4333 HydroNode::CrossProduct { .. } | HydroNode::Join { .. } => {
4334 let operator: syn::Ident = if matches!(node, HydroNode::CrossProduct { .. }) {
4335 parse_quote!(cross_join_multiset)
4336 } else {
4337 parse_quote!(join_multiset)
4338 };
4339
4340 let (HydroNode::CrossProduct { left, right, .. }
4341 | HydroNode::Join { left, right, .. }) = node
4342 else {
4343 unreachable!()
4344 };
4345
4346 let is_top_level = left.metadata().location_id.is_top_level()
4347 && right.metadata().location_id.is_top_level();
4348 let left_top_level = left.metadata().location_id.is_top_level();
4349 let right_top_level = right.metadata().location_id.is_top_level();
4350
4351 let right_ident = ident_stack.pop().unwrap();
4352 let left_ident = ident_stack.pop().unwrap();
4353
4354 let stmt_id = next_stmt_id.get_and_increment();
4355 let stream_ident =
4356 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4357
4358 match builders_or_callback {
4359 BuildersOrCallback::Builders(graph_builders) => {
4360 let left_lifetime = if left_top_level {
4361 graph_builders.cross_tick_state_lifetime(&out_location)
4362 } else {
4363 graph_builders.tick_state_lifetime(&out_location)
4364 };
4365
4366 let right_lifetime = if right_top_level {
4367 graph_builders.cross_tick_state_lifetime(&out_location)
4368 } else {
4369 graph_builders.tick_state_lifetime(&out_location)
4370 };
4371
4372 graph_builders.add_dfir_at(
4373 &out_location,
4374 if is_top_level {
4375 parse_quote! {
4378 #stream_ident = #operator::<#left_lifetime, #right_lifetime>() -> multiset_delta();
4379 #left_ident -> [0]#stream_ident;
4380 #right_ident -> [1]#stream_ident;
4381 }
4382 } else {
4383 parse_quote! {
4384 #stream_ident = #operator::<#left_lifetime, #right_lifetime>();
4385 #left_ident -> [0]#stream_ident;
4386 #right_ident -> [1]#stream_ident;
4387 }
4388 },
4389 Some(&stmt_id.to_string()),
4390 );
4391 }
4392 BuildersOrCallback::Callback(_, node_callback) => {
4393 node_callback(node, next_stmt_id);
4394 }
4395 }
4396
4397 ident_stack.push(stream_ident);
4398 }
4399
4400 HydroNode::Difference { .. } | HydroNode::AntiJoin { .. } => {
4401 let operator: syn::Ident = if matches!(node, HydroNode::Difference { .. }) {
4402 parse_quote!(difference)
4403 } else {
4404 parse_quote!(anti_join)
4405 };
4406
4407 let (HydroNode::Difference { neg, .. } | HydroNode::AntiJoin { neg, .. }) =
4408 node
4409 else {
4410 unreachable!()
4411 };
4412
4413 let neg_top_level = neg.metadata().location_id.is_top_level();
4414
4415 let neg_ident = ident_stack.pop().unwrap();
4416 let pos_ident = ident_stack.pop().unwrap();
4417
4418 let stmt_id = next_stmt_id.get_and_increment();
4419 let stream_ident =
4420 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4421
4422 match builders_or_callback {
4423 BuildersOrCallback::Builders(graph_builders) => {
4424 let neg_lifetime = if neg_top_level {
4425 graph_builders.cross_tick_state_lifetime(&out_location)
4426 } else {
4427 graph_builders.tick_state_lifetime(&out_location)
4428 };
4429 let pos_lifetime =
4430 graph_builders.tick_state_lifetime(&out_location);
4431
4432 graph_builders.add_dfir_at(
4433 &out_location,
4434 parse_quote! {
4435 #stream_ident = #operator::<#pos_lifetime, #neg_lifetime>();
4436 #pos_ident -> [pos]#stream_ident;
4437 #neg_ident -> [neg]#stream_ident;
4438 },
4439 Some(&stmt_id.to_string()),
4440 );
4441 }
4442 BuildersOrCallback::Callback(_, node_callback) => {
4443 node_callback(node, next_stmt_id);
4444 }
4445 }
4446
4447 ident_stack.push(stream_ident);
4448 }
4449
4450 HydroNode::JoinHalf { .. } => {
4451 let HydroNode::JoinHalf { right, .. } = node else {
4452 unreachable!()
4453 };
4454
4455 assert!(
4456 right.metadata().collection_kind.is_bounded(),
4457 "JoinHalf requires the right (build) side to be Bounded, got {:?}",
4458 right.metadata().collection_kind
4459 );
4460
4461 let build_top_level = right.metadata().location_id.is_top_level();
4462
4463 let build_ident = ident_stack.pop().unwrap();
4464 let probe_ident = ident_stack.pop().unwrap();
4465
4466 let stmt_id = next_stmt_id.get_and_increment();
4467 let stream_ident =
4468 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4469
4470 match builders_or_callback {
4471 BuildersOrCallback::Builders(graph_builders) => {
4472 let build_lifetime = if build_top_level {
4473 graph_builders.cross_tick_state_lifetime(&out_location)
4474 } else {
4475 graph_builders.tick_state_lifetime(&out_location)
4476 };
4477 let probe_lifetime =
4478 graph_builders.tick_state_lifetime(&out_location);
4479
4480 graph_builders.add_dfir_at(
4481 &out_location,
4482 parse_quote! {
4483 #stream_ident = join_multiset_half::<#build_lifetime, #probe_lifetime>();
4484 #probe_ident -> [probe]#stream_ident;
4485 #build_ident -> [build]#stream_ident;
4486 },
4487 Some(&stmt_id.to_string()),
4488 );
4489 }
4490 BuildersOrCallback::Callback(_, node_callback) => {
4491 node_callback(node, next_stmt_id);
4492 }
4493 }
4494
4495 ident_stack.push(stream_ident);
4496 }
4497
4498 HydroNode::ResolveFutures { .. } => {
4499 let input_ident = ident_stack.pop().unwrap();
4500
4501 let stmt_id = next_stmt_id.get_and_increment();
4502 let futures_ident =
4503 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4504
4505 match builders_or_callback {
4506 BuildersOrCallback::Builders(graph_builders) => {
4507 graph_builders.add_dfir_at(
4508 &out_location,
4509 parse_quote! {
4510 #futures_ident = #input_ident -> resolve_futures();
4511 },
4512 Some(&stmt_id.to_string()),
4513 );
4514 }
4515 BuildersOrCallback::Callback(_, node_callback) => {
4516 node_callback(node, next_stmt_id);
4517 }
4518 }
4519
4520 ident_stack.push(futures_ident);
4521 }
4522
4523 HydroNode::ResolveFuturesBlocking { .. } => {
4524 let input_ident = ident_stack.pop().unwrap();
4525
4526 let stmt_id = next_stmt_id.get_and_increment();
4527 let futures_ident =
4528 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4529
4530 match builders_or_callback {
4531 BuildersOrCallback::Builders(graph_builders) => {
4532 graph_builders.add_dfir_at(
4533 &out_location,
4534 parse_quote! {
4535 #futures_ident = #input_ident -> resolve_futures_blocking();
4536 },
4537 Some(&stmt_id.to_string()),
4538 );
4539 }
4540 BuildersOrCallback::Callback(_, node_callback) => {
4541 node_callback(node, next_stmt_id);
4542 }
4543 }
4544
4545 ident_stack.push(futures_ident);
4546 }
4547
4548 HydroNode::ResolveFuturesOrdered { .. } => {
4549 let input_ident = ident_stack.pop().unwrap();
4550
4551 let stmt_id = next_stmt_id.get_and_increment();
4552 let futures_ident =
4553 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4554
4555 match builders_or_callback {
4556 BuildersOrCallback::Builders(graph_builders) => {
4557 graph_builders.add_dfir_at(
4558 &out_location,
4559 parse_quote! {
4560 #futures_ident = #input_ident -> resolve_futures_ordered();
4561 },
4562 Some(&stmt_id.to_string()),
4563 );
4564 }
4565 BuildersOrCallback::Callback(_, node_callback) => {
4566 node_callback(node, next_stmt_id);
4567 }
4568 }
4569
4570 ident_stack.push(futures_ident);
4571 }
4572
4573 HydroNode::Map {
4574 f,
4575 input,
4576 metadata,
4577 } => {
4578 let input_ident = ident_stack.pop().unwrap();
4580 let f_tokens = f.emit_tokens(&mut ident_stack);
4581
4582 let input_ident = maybe_observe_for_mut(
4583 f,
4584 input_ident,
4585 &input.metadata().location_id,
4586 &input.metadata().collection_kind,
4587 &metadata.op,
4588 builders_or_callback,
4589 next_stmt_id,
4590 );
4591
4592 let stmt_id = next_stmt_id.get_and_increment();
4593 let map_ident =
4594 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4595
4596 match builders_or_callback {
4597 BuildersOrCallback::Builders(graph_builders) => {
4598 graph_builders.add_dfir_at(
4599 &out_location,
4600 parse_quote! {
4601 #map_ident = #input_ident -> map(#f_tokens);
4602 },
4603 Some(&stmt_id.to_string()),
4604 );
4605 }
4606 BuildersOrCallback::Callback(_, node_callback) => {
4607 node_callback(node, next_stmt_id);
4608 }
4609 }
4610
4611 ident_stack.push(map_ident);
4612 }
4613
4614 HydroNode::FlatMap { f, input, metadata } => {
4615 let input_ident = ident_stack.pop().unwrap();
4616 let f_tokens = f.emit_tokens(&mut ident_stack);
4617
4618 let input_ident = maybe_observe_for_mut(
4619 f, input_ident,
4620 &input.metadata().location_id,
4621 &input.metadata().collection_kind,
4622 &metadata.op,
4623 builders_or_callback, next_stmt_id,
4624 );
4625
4626 let stmt_id = next_stmt_id.get_and_increment();
4627 let flat_map_ident =
4628 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4629
4630 match builders_or_callback {
4631 BuildersOrCallback::Builders(graph_builders) => {
4632 graph_builders.add_dfir_at(
4633 &out_location,
4634 parse_quote! {
4635 #flat_map_ident = #input_ident -> flat_map(#f_tokens);
4636 },
4637 Some(&stmt_id.to_string()),
4638 );
4639 }
4640 BuildersOrCallback::Callback(_, node_callback) => {
4641 node_callback(node, next_stmt_id);
4642 }
4643 }
4644
4645 ident_stack.push(flat_map_ident);
4646 }
4647
4648 HydroNode::FlatMapStreamBlocking { f, input, metadata } => {
4649 let input_ident = ident_stack.pop().unwrap();
4650 let f_tokens = f.emit_tokens(&mut ident_stack);
4651
4652 let input_ident = maybe_observe_for_mut(
4653 f, input_ident,
4654 &input.metadata().location_id,
4655 &input.metadata().collection_kind,
4656 &metadata.op,
4657 builders_or_callback, next_stmt_id,
4658 );
4659
4660 let stmt_id = next_stmt_id.get_and_increment();
4661 let flat_map_stream_blocking_ident =
4662 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4663
4664 match builders_or_callback {
4665 BuildersOrCallback::Builders(graph_builders) => {
4666 graph_builders.add_dfir_at(
4667 &out_location,
4668 parse_quote! {
4669 #flat_map_stream_blocking_ident = #input_ident -> flat_map_stream_blocking(#f_tokens);
4670 },
4671 Some(&stmt_id.to_string()),
4672 );
4673 }
4674 BuildersOrCallback::Callback(_, node_callback) => {
4675 node_callback(node, next_stmt_id);
4676 }
4677 }
4678
4679 ident_stack.push(flat_map_stream_blocking_ident);
4680 }
4681
4682 HydroNode::Filter { f, input, metadata } => {
4683 let input_ident = ident_stack.pop().unwrap();
4684 let f_tokens = f.emit_tokens(&mut ident_stack);
4685
4686 let input_ident = maybe_observe_for_mut(
4687 f, input_ident,
4688 &input.metadata().location_id,
4689 &input.metadata().collection_kind,
4690 &metadata.op,
4691 builders_or_callback, next_stmt_id,
4692 );
4693
4694 let stmt_id = next_stmt_id.get_and_increment();
4695 let filter_ident =
4696 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4697
4698 match builders_or_callback {
4699 BuildersOrCallback::Builders(graph_builders) => {
4700 graph_builders.add_dfir_at(
4701 &out_location,
4702 parse_quote! {
4703 #filter_ident = #input_ident -> filter(#f_tokens);
4704 },
4705 Some(&stmt_id.to_string()),
4706 );
4707 }
4708 BuildersOrCallback::Callback(_, node_callback) => {
4709 node_callback(node, next_stmt_id);
4710 }
4711 }
4712
4713 ident_stack.push(filter_ident);
4714 }
4715
4716 HydroNode::FilterMap { f, input, metadata } => {
4717 let input_ident = ident_stack.pop().unwrap();
4718 let f_tokens = f.emit_tokens(&mut ident_stack);
4719
4720 let input_ident = maybe_observe_for_mut(
4721 f, input_ident,
4722 &input.metadata().location_id,
4723 &input.metadata().collection_kind,
4724 &metadata.op,
4725 builders_or_callback, next_stmt_id,
4726 );
4727
4728 let stmt_id = next_stmt_id.get_and_increment();
4729 let filter_map_ident =
4730 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4731
4732 match builders_or_callback {
4733 BuildersOrCallback::Builders(graph_builders) => {
4734 graph_builders.add_dfir_at(
4735 &out_location,
4736 parse_quote! {
4737 #filter_map_ident = #input_ident -> filter_map(#f_tokens);
4738 },
4739 Some(&stmt_id.to_string()),
4740 );
4741 }
4742 BuildersOrCallback::Callback(_, node_callback) => {
4743 node_callback(node, next_stmt_id);
4744 }
4745 }
4746
4747 ident_stack.push(filter_map_ident);
4748 }
4749
4750 HydroNode::Sort { .. } => {
4751 let input_ident = ident_stack.pop().unwrap();
4752
4753 let stmt_id = next_stmt_id.get_and_increment();
4754 let sort_ident =
4755 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4756
4757 match builders_or_callback {
4758 BuildersOrCallback::Builders(graph_builders) => {
4759 graph_builders.add_dfir_at(
4760 &out_location,
4761 parse_quote! {
4762 #sort_ident = #input_ident -> sort();
4763 },
4764 Some(&stmt_id.to_string()),
4765 );
4766 }
4767 BuildersOrCallback::Callback(_, node_callback) => {
4768 node_callback(node, next_stmt_id);
4769 }
4770 }
4771
4772 ident_stack.push(sort_ident);
4773 }
4774
4775 HydroNode::DeferTick { .. } => {
4776 let input_ident = ident_stack.pop().unwrap();
4777
4778 let stmt_id = next_stmt_id.get_and_increment();
4779 let defer_tick_ident =
4780 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4781
4782 match builders_or_callback {
4783 BuildersOrCallback::Builders(graph_builders) => {
4784 graph_builders.add_dfir_at(
4785 &out_location,
4786 parse_quote! {
4787 #defer_tick_ident = #input_ident -> defer_tick_lazy();
4788 },
4789 Some(&stmt_id.to_string()),
4790 );
4791 }
4792 BuildersOrCallback::Callback(_, node_callback) => {
4793 node_callback(node, next_stmt_id);
4794 }
4795 }
4796
4797 ident_stack.push(defer_tick_ident);
4798 }
4799
4800 HydroNode::Enumerate { input, .. } => {
4801 let input_ident = ident_stack.pop().unwrap();
4802
4803 let stmt_id = next_stmt_id.get_and_increment();
4804 let enumerate_ident =
4805 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4806
4807 match builders_or_callback {
4808 BuildersOrCallback::Builders(graph_builders) => {
4809 let lifetime = if input.metadata().location_id.is_top_level() {
4810 graph_builders.cross_tick_state_lifetime(&out_location)
4811 } else {
4812 graph_builders.tick_state_lifetime(&out_location)
4813 };
4814 graph_builders.add_dfir_at(
4815 &out_location,
4816 parse_quote! {
4817 #enumerate_ident = #input_ident -> enumerate::<#lifetime>();
4818 },
4819 Some(&stmt_id.to_string()),
4820 );
4821 }
4822 BuildersOrCallback::Callback(_, node_callback) => {
4823 node_callback(node, next_stmt_id);
4824 }
4825 }
4826
4827 ident_stack.push(enumerate_ident);
4828 }
4829
4830 HydroNode::Inspect { f, input, metadata } => {
4831 let input_ident = ident_stack.pop().unwrap();
4832 let f_tokens = f.emit_tokens(&mut ident_stack);
4833
4834 let input_ident = maybe_observe_for_mut(
4835 f, input_ident,
4836 &input.metadata().location_id,
4837 &input.metadata().collection_kind,
4838 &metadata.op,
4839 builders_or_callback, next_stmt_id,
4840 );
4841
4842 let stmt_id = next_stmt_id.get_and_increment();
4843 let inspect_ident =
4844 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4845
4846 match builders_or_callback {
4847 BuildersOrCallback::Builders(graph_builders) => {
4848 graph_builders.add_dfir_at(
4849 &out_location,
4850 parse_quote! {
4851 #inspect_ident = #input_ident -> inspect(#f_tokens);
4852 },
4853 Some(&stmt_id.to_string()),
4854 );
4855 }
4856 BuildersOrCallback::Callback(_, node_callback) => {
4857 node_callback(node, next_stmt_id);
4858 }
4859 }
4860
4861 ident_stack.push(inspect_ident);
4862 }
4863
4864 HydroNode::Unique { input, .. } => {
4865 let input_ident = ident_stack.pop().unwrap();
4866
4867 let stmt_id = next_stmt_id.get_and_increment();
4868 let unique_ident =
4869 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4870
4871 match builders_or_callback {
4872 BuildersOrCallback::Builders(graph_builders) => {
4873 let lifetime = if input.metadata().location_id.is_top_level() {
4874 graph_builders.cross_tick_state_lifetime(&out_location)
4875 } else {
4876 graph_builders.tick_state_lifetime(&out_location)
4877 };
4878
4879 graph_builders.add_dfir_at(
4880 &out_location,
4881 parse_quote! {
4882 #unique_ident = #input_ident -> unique::<#lifetime>();
4883 },
4884 Some(&stmt_id.to_string()),
4885 );
4886 }
4887 BuildersOrCallback::Callback(_, node_callback) => {
4888 node_callback(node, next_stmt_id);
4889 }
4890 }
4891
4892 ident_stack.push(unique_ident);
4893 }
4894
4895 HydroNode::Fold { .. } | HydroNode::FoldKeyed { .. } | HydroNode::Scan { .. } | HydroNode::ScanAsyncBlocking { .. } => {
4896 let operator: syn::Ident = if let HydroNode::Fold { input, .. } = node {
4897 if input.metadata().location_id.is_top_level()
4898 && input.metadata().collection_kind.is_bounded()
4899 {
4900 parse_quote!(fold_no_replay)
4901 } else {
4902 parse_quote!(fold)
4903 }
4904 } else if matches!(node, HydroNode::Scan { .. }) {
4905 parse_quote!(scan)
4906 } else if matches!(node, HydroNode::ScanAsyncBlocking { .. }) {
4907 parse_quote!(scan_async_blocking)
4908 } else if let HydroNode::FoldKeyed { input, .. } = node {
4909 if input.metadata().location_id.is_top_level()
4910 && input.metadata().collection_kind.is_bounded()
4911 {
4912 todo!("Fold keyed on a top-level bounded collection is not yet supported")
4913 } else {
4914 parse_quote!(fold_keyed)
4915 }
4916 } else {
4917 unreachable!()
4918 };
4919
4920 let (HydroNode::Fold { input, .. }
4921 | HydroNode::FoldKeyed { input, .. }
4922 | HydroNode::Scan { input, .. }
4923 | HydroNode::ScanAsyncBlocking { input, .. }) = node
4924 else {
4925 unreachable!()
4926 };
4927
4928 let input_top_level = input.metadata().location_id.is_top_level();
4929
4930 let input_ident = ident_stack.pop().unwrap();
4931
4932 let (HydroNode::Fold { init, acc, .. }
4933 | HydroNode::FoldKeyed { init, acc, .. }
4934 | HydroNode::Scan { init, acc, .. }
4935 | HydroNode::ScanAsyncBlocking { init, acc, .. }) = &*node
4936 else {
4937 unreachable!()
4938 };
4939
4940 let acc_tokens = acc.emit_tokens(&mut ident_stack);
4941 let init_tokens = init.emit_tokens(&mut ident_stack);
4942
4943 let stmt_id = next_stmt_id.get_and_increment();
4944 let fold_ident =
4945 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4946
4947 match builders_or_callback {
4948 BuildersOrCallback::Builders(graph_builders) => {
4949 let lifetime = if input_top_level {
4950 graph_builders.cross_tick_state_lifetime(&out_location)
4951 } else {
4952 graph_builders.tick_state_lifetime(&out_location)
4953 };
4954
4955 if matches!(node, HydroNode::Fold { .. })
4956 && node.metadata().location_id.is_top_level()
4957 && !(matches!(node.metadata().location_id, LocationId::Atomic(_)))
4958 && graph_builders.singleton_intermediates()
4959 && !node.metadata().collection_kind.is_bounded()
4960 {
4961 let HydroNode::Fold { input, .. } = &*node else { unreachable!() };
4962 let hooked_input_ident = graph_builders.emit_fold_hook(
4963 &input.metadata().location_id,
4964 &input_ident,
4965 &input.metadata().collection_kind,
4966 &node.metadata().op,
4967 );
4968
4969 let (effective_input, wrapped_acc) = if let Some(ref hooked) = hooked_input_ident {
4970 let acc: syn::Expr = parse_quote!({
4971 let mut __inner = #acc_tokens;
4972 move |__state, __batch: Vec<_>| {
4973 if __batch.is_empty() {
4974 return None;
4975 }
4976 for __value in __batch {
4977 __inner(__state, __value);
4978 }
4979 Some(__state.clone())
4980 }
4981 });
4982 (hooked, acc)
4983 } else {
4984 let acc: syn::Expr = parse_quote!({
4985 let mut __inner = #acc_tokens;
4986 move |__state, __value| {
4987 __inner(__state, __value);
4988 Some(__state.clone())
4989 }
4990 });
4991 (&input_ident, acc)
4992 };
4993
4994 graph_builders.add_dfir_at(
4995 &out_location,
4996 parse_quote! {
4997 source_iter([(#init_tokens)()]) -> [0]#fold_ident;
4998 #effective_input -> scan::<#lifetime>(#init_tokens, #wrapped_acc) -> [1]#fold_ident;
4999 #fold_ident = chain();
5000 },
5001 Some(&stmt_id.to_string()),
5002 );
5003
5004 if hooked_input_ident.is_some() {
5005 fold_hooked_idents.insert(fold_ident.to_string());
5006 }
5007 } else if matches!(node, HydroNode::FoldKeyed { .. })
5008 && node.metadata().location_id.is_top_level()
5009 && !(matches!(node.metadata().location_id, LocationId::Atomic(_)))
5010 && graph_builders.singleton_intermediates()
5011 && !node.metadata().collection_kind.is_bounded()
5012 {
5013 let HydroNode::FoldKeyed { input, .. } = &*node else { unreachable!() };
5014 let hooked_input_ident = graph_builders.emit_fold_hook(
5015 &input.metadata().location_id,
5016 &input_ident,
5017 &input.metadata().collection_kind,
5018 &node.metadata().op,
5019 );
5020
5021 let wrapped_acc: syn::Expr = parse_quote!({
5022 let mut __init = #init_tokens;
5023 let mut __inner = #acc_tokens;
5024 move |__state, __kv: (_, _)| {
5025 let __state = __state
5027 .entry(::std::clone::Clone::clone(&__kv.0))
5028 .or_insert_with(|| (__init)());
5029 __inner(__state, __kv.1);
5030 Some((__kv.0, ::std::clone::Clone::clone(&*__state)))
5031 }
5032 });
5033
5034 if let Some(hooked_input_ident) = hooked_input_ident {
5035 graph_builders.add_dfir_at(
5036 &out_location,
5037 parse_quote! {
5038 #fold_ident = #hooked_input_ident -> flatten() -> scan::<#lifetime>(|| ::std::collections::HashMap::new(), #wrapped_acc);
5039 },
5040 Some(&stmt_id.to_string()),
5041 );
5042
5043 fold_hooked_idents.insert(fold_ident.to_string());
5044 } else {
5045 graph_builders.add_dfir_at(
5046 &out_location,
5047 parse_quote! {
5048 #fold_ident = #input_ident -> scan::<#lifetime>(|| ::std::collections::HashMap::new(), #wrapped_acc);
5049 },
5050 Some(&stmt_id.to_string()),
5051 );
5052 }
5053 } else if (matches!(node, HydroNode::Fold { .. })
5054 || matches!(node, HydroNode::FoldKeyed { .. }))
5055 && !node.metadata().location_id.is_top_level()
5056 && graph_builders.singleton_intermediates()
5057 {
5058 let input_ref = match &*node {
5059 HydroNode::Fold { input, .. } => input,
5060 HydroNode::FoldKeyed { input, .. } => input,
5061 _ => unreachable!(),
5062 };
5063 let hooked_input_ident = graph_builders.emit_fold_hook(
5064 &input_ref.metadata().location_id,
5065 &input_ident,
5066 &input_ref.metadata().collection_kind,
5067 &node.metadata().op,
5068 );
5069
5070 let actual_input = hooked_input_ident.as_ref().unwrap_or(&input_ident);
5071 graph_builders.add_dfir_at(
5072 &out_location,
5073 parse_quote! {
5074 #fold_ident = #actual_input -> #operator::<#lifetime>(#init_tokens, #acc_tokens);
5075 },
5076 Some(&stmt_id.to_string()),
5077 );
5078 } else {
5079 graph_builders.add_dfir_at(
5080 &out_location,
5081 parse_quote! {
5082 #fold_ident = #input_ident -> #operator::<#lifetime>(#init_tokens, #acc_tokens);
5083 },
5084 Some(&stmt_id.to_string()),
5085 );
5086 }
5087 }
5088 BuildersOrCallback::Callback(_, node_callback) => {
5089 node_callback(node, next_stmt_id);
5090 }
5091 }
5092
5093 ident_stack.push(fold_ident);
5094 }
5095
5096 HydroNode::Reduce { .. } | HydroNode::ReduceKeyed { .. } => {
5097 let operator: syn::Ident = if let HydroNode::Reduce { input, .. } = node {
5098 if input.metadata().location_id.is_top_level()
5099 && input.metadata().collection_kind.is_bounded()
5100 {
5101 parse_quote!(reduce_no_replay)
5102 } else {
5103 parse_quote!(reduce)
5104 }
5105 } else if let HydroNode::ReduceKeyed { input, .. } = node {
5106 if input.metadata().location_id.is_top_level()
5107 && input.metadata().collection_kind.is_bounded()
5108 {
5109 todo!(
5110 "Calling keyed reduce on a top-level bounded collection is not supported"
5111 )
5112 } else {
5113 parse_quote!(reduce_keyed)
5114 }
5115 } else {
5116 unreachable!()
5117 };
5118
5119 let (HydroNode::Reduce { input, .. } | HydroNode::ReduceKeyed { input, .. }) = node
5120 else {
5121 unreachable!()
5122 };
5123
5124 let input_top_level = input.metadata().location_id.is_top_level();
5125
5126 let input_ident = ident_stack.pop().unwrap();
5127
5128 let (HydroNode::Reduce { f, .. } | HydroNode::ReduceKeyed { f, .. }) = &*node
5129 else {
5130 unreachable!()
5131 };
5132
5133 let f_tokens = f.emit_tokens(&mut ident_stack);
5134
5135 let stmt_id = next_stmt_id.get_and_increment();
5136 let reduce_ident =
5137 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5138
5139 match builders_or_callback {
5140 BuildersOrCallback::Builders(graph_builders) => {
5141 let lifetime = if input_top_level {
5142 graph_builders.cross_tick_state_lifetime(&out_location)
5143 } else {
5144 graph_builders.tick_state_lifetime(&out_location)
5145 };
5146
5147 if matches!(node, HydroNode::Reduce { .. })
5148 && node.metadata().location_id.is_top_level()
5149 && !(matches!(node.metadata().location_id, LocationId::Atomic(_)))
5150 && graph_builders.singleton_intermediates()
5151 && !node.metadata().collection_kind.is_bounded()
5152 {
5153 todo!(
5154 "Reduce with optional intermediates is not yet supported in simulator"
5155 );
5156 } else if matches!(node, HydroNode::ReduceKeyed { .. })
5157 && node.metadata().location_id.is_top_level()
5158 && !(matches!(node.metadata().location_id, LocationId::Atomic(_)))
5159 && graph_builders.singleton_intermediates()
5160 && !node.metadata().collection_kind.is_bounded()
5161 {
5162 todo!(
5163 "Reduce keyed with optional intermediates is not yet supported in simulator"
5164 );
5165 } else {
5166 graph_builders.add_dfir_at(
5167 &out_location,
5168 parse_quote! {
5169 #reduce_ident = #input_ident -> #operator::<#lifetime>(#f_tokens);
5170 },
5171 Some(&stmt_id.to_string()),
5172 );
5173 }
5174 }
5175 BuildersOrCallback::Callback(_, node_callback) => {
5176 node_callback(node, next_stmt_id);
5177 }
5178 }
5179
5180 ident_stack.push(reduce_ident);
5181 }
5182
5183 HydroNode::ReduceKeyedWatermark {
5184 f,
5185 input,
5186 metadata,
5187 ..
5188 } => {
5189 let input_top_level = input.metadata().location_id.is_top_level();
5190
5191 let watermark_ident = ident_stack.pop().unwrap();
5193 let input_ident = ident_stack.pop().unwrap();
5194 let f_tokens = f.emit_tokens(&mut ident_stack);
5195
5196 let stmt_id = next_stmt_id.get_and_increment();
5197 let chain_ident = syn::Ident::new(
5198 &format!("reduce_keyed_watermark_chain_{}", stmt_id),
5199 Span::call_site(),
5200 );
5201
5202 let fold_ident =
5203 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5204
5205 let agg_operator: syn::Ident = if input.metadata().location_id.is_top_level()
5206 && input.metadata().collection_kind.is_bounded()
5207 {
5208 parse_quote!(fold_no_replay)
5209 } else {
5210 parse_quote!(fold)
5211 };
5212
5213 match builders_or_callback {
5214 BuildersOrCallback::Builders(graph_builders) => {
5215 let lifetime = if input_top_level {
5216 graph_builders.cross_tick_state_lifetime(&out_location)
5217 } else {
5218 graph_builders.tick_state_lifetime(&out_location)
5219 };
5220
5221 if metadata.location_id.is_top_level()
5222 && !(matches!(metadata.location_id, LocationId::Atomic(_)))
5223 && graph_builders.singleton_intermediates()
5224 && !metadata.collection_kind.is_bounded()
5225 {
5226 todo!(
5227 "Reduce keyed watermarked on a top-level bounded collection is not yet supported"
5228 )
5229 } else {
5230 graph_builders.add_dfir_at(
5231 &out_location,
5232 parse_quote! {
5233 #chain_ident = chain();
5234 #input_ident
5235 -> map(|x| (Some(x), None))
5236 -> [0]#chain_ident;
5237 #watermark_ident
5238 -> map(|watermark| (None, Some(watermark)))
5239 -> [1]#chain_ident;
5240
5241 #fold_ident = #chain_ident
5242 -> #agg_operator::<#lifetime>(|| (::std::collections::HashMap::new(), None), {
5243 let __reduce_keyed_fn = #f_tokens;
5244 move |(map, opt_curr_watermark), (opt_payload, opt_watermark)| {
5245 if let Some((k, v)) = opt_payload {
5246 if let Some(curr_watermark) = *opt_curr_watermark {
5247 if k < curr_watermark {
5248 return;
5249 }
5250 }
5251 match map.entry(k) {
5252 ::std::collections::hash_map::Entry::Vacant(e) => {
5253 e.insert(v);
5254 }
5255 ::std::collections::hash_map::Entry::Occupied(mut e) => {
5256 __reduce_keyed_fn(e.get_mut(), v);
5257 }
5258 }
5259 } else {
5260 let watermark = opt_watermark.unwrap();
5261 if let Some(curr_watermark) = *opt_curr_watermark {
5262 if watermark <= curr_watermark {
5263 return;
5264 }
5265 }
5266 map.retain(|k, _| *k >= watermark);
5267 *opt_curr_watermark = Some(watermark);
5268 }
5269 }
5270 })
5271 -> flat_map(|(map, _curr_watermark)| map);
5272 },
5273 Some(&stmt_id.to_string()),
5274 );
5275 }
5276 }
5277 BuildersOrCallback::Callback(_, node_callback) => {
5278 node_callback(node, next_stmt_id);
5279 }
5280 }
5281
5282 ident_stack.push(fold_ident);
5283 }
5284
5285 HydroNode::Network {
5286 networking_info,
5287 serialize,
5288 deserialize,
5289 instantiate_fn,
5290 input,
5291 ..
5292 } => {
5293 let input_ident = ident_stack.pop().unwrap();
5294
5295 let stmt_id = next_stmt_id.get_and_increment();
5296 let receiver_stream_ident =
5297 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5298
5299 let serialize_pipeline = serialize.pipeline();
5302 let deserialize_pipeline = deserialize.pipeline();
5303
5304 match builders_or_callback {
5305 BuildersOrCallback::Builders(graph_builders) => {
5306 let (sink_expr, source_expr) = match instantiate_fn {
5307 DebugInstantiate::Building => (
5308 syn::parse_quote!(DUMMY_SINK),
5309 syn::parse_quote!(DUMMY_SOURCE),
5310 ),
5311
5312 DebugInstantiate::Finalized(finalized) => {
5313 (finalized.sink.clone(), finalized.source.clone())
5314 }
5315 };
5316
5317 graph_builders.create_network(
5318 &input.metadata().location_id,
5319 &out_location,
5320 input_ident,
5321 &receiver_stream_ident,
5322 serialize_pipeline.as_ref(),
5323 sink_expr,
5324 source_expr,
5325 deserialize_pipeline.as_ref(),
5326 serialize.external_element_type(),
5327 stmt_id,
5328 networking_info,
5329 );
5330 }
5331 BuildersOrCallback::Callback(_, node_callback) => {
5332 node_callback(node, next_stmt_id);
5333 }
5334 }
5335
5336 ident_stack.push(receiver_stream_ident);
5337 }
5338
5339 HydroNode::ExternalInput {
5340 instantiate_fn,
5341 deserialize_fn: deserialize_pipeline,
5342 ..
5343 } => {
5344 let stmt_id = next_stmt_id.get_and_increment();
5345 let receiver_stream_ident =
5346 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5347
5348 match builders_or_callback {
5349 BuildersOrCallback::Builders(graph_builders) => {
5350 let (_, source_expr) = match instantiate_fn {
5351 DebugInstantiate::Building => (
5352 syn::parse_quote!(DUMMY_SINK),
5353 syn::parse_quote!(DUMMY_SOURCE),
5354 ),
5355
5356 DebugInstantiate::Finalized(finalized) => {
5357 (finalized.sink.clone(), finalized.source.clone())
5358 }
5359 };
5360
5361 graph_builders.create_external_source(
5362 &out_location,
5363 source_expr,
5364 &receiver_stream_ident,
5365 deserialize_pipeline.as_ref(),
5366 stmt_id,
5367 );
5368 }
5369 BuildersOrCallback::Callback(_, node_callback) => {
5370 node_callback(node, next_stmt_id);
5371 }
5372 }
5373
5374 ident_stack.push(receiver_stream_ident);
5375 }
5376
5377 HydroNode::Counter {
5378 tag,
5379 duration,
5380 prefix,
5381 ..
5382 } => {
5383 let input_ident = ident_stack.pop().unwrap();
5384
5385 let stmt_id = next_stmt_id.get_and_increment();
5386 let counter_ident =
5387 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5388
5389 match builders_or_callback {
5390 BuildersOrCallback::Builders(graph_builders) => {
5391 let arg = format!("{}({})", prefix, tag);
5392 graph_builders.add_dfir_at(
5393 &out_location,
5394 parse_quote! {
5395 #counter_ident = #input_ident -> _counter(#arg, #duration);
5396 },
5397 Some(&stmt_id.to_string()),
5398 );
5399 }
5400 BuildersOrCallback::Callback(_, node_callback) => {
5401 node_callback(node, next_stmt_id);
5402 }
5403 }
5404
5405 ident_stack.push(counter_ident);
5406 }
5407
5408 HydroNode::VersionedNetworkFork {
5409 channel_id,
5410 senders,
5411 metadata,
5412 ..
5413 } => {
5414 let split_at = ident_stack.len() - senders.len();
5416 let sender_idents = ident_stack.split_off(split_at);
5417
5418 let stmt_id = next_stmt_id.get_and_increment();
5419
5420 let external_element_type =
5423 senders.first().and_then(|(_, _, s)| s.external_element_type());
5424
5425 match builders_or_callback {
5426 BuildersOrCallback::Builders(graph_builders) => {
5427 let sender_args: Vec<(LocationId, syn::Ident, Option<DebugExpr>)> =
5428 senders
5429 .iter()
5430 .zip(sender_idents)
5431 .map(|((_version, sender, serialize), ident)| {
5432 (
5433 sender.metadata().location_id.clone(),
5434 ident,
5435 serialize.pipeline(),
5436 )
5437 })
5438 .collect();
5439 graph_builders.create_versioned_network_fork(
5440 *channel_id,
5441 &metadata.location_id,
5442 sender_args,
5443 external_element_type,
5444 stmt_id,
5445 );
5446 }
5447 BuildersOrCallback::Callback(_, node_callback) => {
5448 node_callback(node, next_stmt_id);
5449 }
5450 }
5451 }
5452
5453 HydroNode::VersionedNetwork {
5454 fork,
5455 deserialize,
5456 metadata,
5457 ..
5458 } => {
5459 let stmt_id = next_stmt_id.get_and_increment();
5460 let receiver_stream_ident =
5461 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5462
5463 let (channel_id, source_loc) = {
5466 let fork_ref = fork.0.borrow();
5467 let HydroNode::VersionedNetworkFork {
5468 channel_id,
5469 senders,
5470 ..
5471 } = &*fork_ref
5472 else {
5473 unreachable!("VersionedNetwork.fork must be a VersionedNetworkFork");
5474 };
5475 let source_loc = senders
5476 .first()
5477 .map(|(_v, sender, _s)| sender.metadata().location_id.clone())
5478 .expect("a VersionedNetworkFork always has at least one sender");
5479 (*channel_id, source_loc)
5480 };
5481
5482 let deserialize_pipeline = deserialize.pipeline();
5483 let external_element_type = deserialize.external_element_type();
5484
5485 match builders_or_callback {
5486 BuildersOrCallback::Builders(graph_builders) => {
5487 graph_builders.create_versioned_network(
5488 channel_id,
5489 &source_loc,
5490 &metadata.location_id,
5491 &receiver_stream_ident,
5492 deserialize_pipeline.as_ref(),
5493 external_element_type,
5494 stmt_id,
5495 );
5496 }
5497 BuildersOrCallback::Callback(_, node_callback) => {
5498 node_callback(node, next_stmt_id);
5499 }
5500 }
5501
5502 ident_stack.push(receiver_stream_ident);
5503 }
5504 }
5505 },
5506 seen_tees,
5507 false,
5508 );
5509
5510 let ret = ident_stack
5511 .pop()
5512 .expect("ident_stack should have exactly one element after traversal");
5513 assert!(
5514 ident_stack.is_empty(),
5515 "ident_stack should be empty after popping the final ident, but has {} remaining element(s). \
5516 This indicates a bug in the code gen: some node pushed idents that were never consumed.",
5517 ident_stack.len()
5518 );
5519 ret
5520 }
5521
5522 pub fn visit_debug_expr(&mut self, mut transform: impl FnMut(&mut DebugExpr)) {
5523 match self {
5524 HydroNode::Placeholder => {
5525 panic!()
5526 }
5527 HydroNode::Cast { .. }
5528 | HydroNode::ObserveNonDet { .. }
5529 | HydroNode::UnboundSingleton { .. }
5530 | HydroNode::AssertIsConsistent { .. } => {}
5531 HydroNode::Source { source, .. } => match source {
5532 HydroSource::Stream(expr) | HydroSource::Iter(expr) => transform(expr),
5533 HydroSource::ExternalNetwork()
5534 | HydroSource::Spin()
5535 | HydroSource::ClusterMembers(_, _)
5536 | HydroSource::Embedded(_)
5537 | HydroSource::EmbeddedSingleton(_) => {} },
5539 HydroNode::SingletonSource { value, .. } => {
5540 transform(value);
5541 }
5542 HydroNode::CycleSource { .. }
5543 | HydroNode::Tee { .. }
5544 | HydroNode::Reference { .. }
5545 | HydroNode::YieldConcat { .. }
5546 | HydroNode::BeginAtomic { .. }
5547 | HydroNode::EndAtomic { .. }
5548 | HydroNode::Batch { .. }
5549 | HydroNode::Chain { .. }
5550 | HydroNode::MergeOrdered { .. }
5551 | HydroNode::ChainFirst { .. }
5552 | HydroNode::CrossProduct { .. }
5553 | HydroNode::CrossSingleton { .. }
5554 | HydroNode::ResolveFutures { .. }
5555 | HydroNode::ResolveFuturesBlocking { .. }
5556 | HydroNode::ResolveFuturesOrdered { .. }
5557 | HydroNode::Join { .. }
5558 | HydroNode::JoinHalf { .. }
5559 | HydroNode::Difference { .. }
5560 | HydroNode::AntiJoin { .. }
5561 | HydroNode::DeferTick { .. }
5562 | HydroNode::Enumerate { .. }
5563 | HydroNode::Unique { .. }
5564 | HydroNode::Sort { .. }
5565 | HydroNode::PartitionSide { .. }
5566 | HydroNode::VersionedNetworkFork { .. }
5567 | HydroNode::VersionedNetwork { .. } => {}
5568 HydroNode::Map { f, .. }
5569 | HydroNode::FlatMap { f, .. }
5570 | HydroNode::FlatMapStreamBlocking { f, .. }
5571 | HydroNode::Filter { f, .. }
5572 | HydroNode::FilterMap { f, .. }
5573 | HydroNode::Inspect { f, .. }
5574 | HydroNode::PartitionShared { f, .. }
5575 | HydroNode::Reduce { f, .. }
5576 | HydroNode::ReduceKeyed { f, .. }
5577 | HydroNode::ReduceKeyedWatermark { f, .. } => {
5578 transform(&mut f.expr);
5579 }
5580 HydroNode::Fold { init, acc, .. }
5581 | HydroNode::Scan { init, acc, .. }
5582 | HydroNode::ScanAsyncBlocking { init, acc, .. }
5583 | HydroNode::FoldKeyed { init, acc, .. } => {
5584 transform(&mut init.expr);
5585 transform(&mut acc.expr);
5586 }
5587 HydroNode::Network {
5588 serialize,
5589 deserialize,
5590 ..
5591 } => {
5592 if let NetworkSend::Custom {
5593 serialize_fn: Some(serialize_fn),
5594 } = serialize
5595 {
5596 transform(serialize_fn);
5597 }
5598 if let NetworkRecv::Custom {
5599 deserialize_fn: Some(deserialize_fn),
5600 } = deserialize
5601 {
5602 transform(deserialize_fn);
5603 }
5604 }
5605 HydroNode::ExternalInput { deserialize_fn, .. } => {
5606 if let Some(deserialize_fn) = deserialize_fn {
5607 transform(deserialize_fn);
5608 }
5609 }
5610 HydroNode::Counter { duration, .. } => {
5611 transform(duration);
5612 }
5613 }
5614 }
5615
5616 pub fn op_metadata(&self) -> &HydroIrOpMetadata {
5617 &self.metadata().op
5618 }
5619
5620 pub fn metadata(&self) -> &HydroIrMetadata {
5621 match self {
5622 HydroNode::Placeholder => {
5623 panic!()
5624 }
5625 HydroNode::VersionedNetworkFork { metadata, .. }
5626 | HydroNode::VersionedNetwork { metadata, .. } => metadata,
5627 HydroNode::Cast { metadata, .. }
5628 | HydroNode::ObserveNonDet { metadata, .. }
5629 | HydroNode::AssertIsConsistent { metadata, .. }
5630 | HydroNode::UnboundSingleton { metadata, .. }
5631 | HydroNode::Source { metadata, .. }
5632 | HydroNode::SingletonSource { metadata, .. }
5633 | HydroNode::CycleSource { metadata, .. }
5634 | HydroNode::Tee { metadata, .. }
5635 | HydroNode::Reference { metadata, .. }
5636 | HydroNode::PartitionSide { metadata, .. }
5637 | HydroNode::PartitionShared { metadata, .. }
5638 | HydroNode::YieldConcat { metadata, .. }
5639 | HydroNode::BeginAtomic { metadata, .. }
5640 | HydroNode::EndAtomic { metadata, .. }
5641 | HydroNode::Batch { metadata, .. }
5642 | HydroNode::Chain { metadata, .. }
5643 | HydroNode::MergeOrdered { metadata, .. }
5644 | HydroNode::ChainFirst { metadata, .. }
5645 | HydroNode::CrossProduct { metadata, .. }
5646 | HydroNode::CrossSingleton { metadata, .. }
5647 | HydroNode::Join { metadata, .. }
5648 | HydroNode::JoinHalf { metadata, .. }
5649 | HydroNode::Difference { metadata, .. }
5650 | HydroNode::AntiJoin { metadata, .. }
5651 | HydroNode::ResolveFutures { metadata, .. }
5652 | HydroNode::ResolveFuturesBlocking { metadata, .. }
5653 | HydroNode::ResolveFuturesOrdered { metadata, .. }
5654 | HydroNode::Map { metadata, .. }
5655 | HydroNode::FlatMap { metadata, .. }
5656 | HydroNode::FlatMapStreamBlocking { metadata, .. }
5657 | HydroNode::Filter { metadata, .. }
5658 | HydroNode::FilterMap { metadata, .. }
5659 | HydroNode::DeferTick { metadata, .. }
5660 | HydroNode::Enumerate { metadata, .. }
5661 | HydroNode::Inspect { metadata, .. }
5662 | HydroNode::Unique { metadata, .. }
5663 | HydroNode::Sort { metadata, .. }
5664 | HydroNode::Scan { metadata, .. }
5665 | HydroNode::ScanAsyncBlocking { metadata, .. }
5666 | HydroNode::Fold { metadata, .. }
5667 | HydroNode::FoldKeyed { metadata, .. }
5668 | HydroNode::Reduce { metadata, .. }
5669 | HydroNode::ReduceKeyed { metadata, .. }
5670 | HydroNode::ReduceKeyedWatermark { metadata, .. }
5671 | HydroNode::ExternalInput { metadata, .. }
5672 | HydroNode::Network { metadata, .. }
5673 | HydroNode::Counter { metadata, .. } => metadata,
5674 }
5675 }
5676
5677 pub fn op_metadata_mut(&mut self) -> &mut HydroIrOpMetadata {
5678 &mut self.metadata_mut().op
5679 }
5680
5681 pub fn metadata_mut(&mut self) -> &mut HydroIrMetadata {
5682 match self {
5683 HydroNode::Placeholder => {
5684 panic!()
5685 }
5686 HydroNode::VersionedNetworkFork { metadata, .. }
5687 | HydroNode::VersionedNetwork { metadata, .. } => metadata,
5688 HydroNode::Cast { metadata, .. }
5689 | HydroNode::ObserveNonDet { metadata, .. }
5690 | HydroNode::AssertIsConsistent { metadata, .. }
5691 | HydroNode::UnboundSingleton { metadata, .. }
5692 | HydroNode::Source { metadata, .. }
5693 | HydroNode::SingletonSource { metadata, .. }
5694 | HydroNode::CycleSource { metadata, .. }
5695 | HydroNode::Tee { metadata, .. }
5696 | HydroNode::Reference { metadata, .. }
5697 | HydroNode::PartitionSide { metadata, .. }
5698 | HydroNode::PartitionShared { metadata, .. }
5699 | HydroNode::YieldConcat { metadata, .. }
5700 | HydroNode::BeginAtomic { metadata, .. }
5701 | HydroNode::EndAtomic { metadata, .. }
5702 | HydroNode::Batch { metadata, .. }
5703 | HydroNode::Chain { metadata, .. }
5704 | HydroNode::MergeOrdered { metadata, .. }
5705 | HydroNode::ChainFirst { metadata, .. }
5706 | HydroNode::CrossProduct { metadata, .. }
5707 | HydroNode::CrossSingleton { metadata, .. }
5708 | HydroNode::Join { metadata, .. }
5709 | HydroNode::JoinHalf { metadata, .. }
5710 | HydroNode::Difference { metadata, .. }
5711 | HydroNode::AntiJoin { metadata, .. }
5712 | HydroNode::ResolveFutures { metadata, .. }
5713 | HydroNode::ResolveFuturesBlocking { metadata, .. }
5714 | HydroNode::ResolveFuturesOrdered { metadata, .. }
5715 | HydroNode::Map { metadata, .. }
5716 | HydroNode::FlatMap { metadata, .. }
5717 | HydroNode::FlatMapStreamBlocking { metadata, .. }
5718 | HydroNode::Filter { metadata, .. }
5719 | HydroNode::FilterMap { metadata, .. }
5720 | HydroNode::DeferTick { metadata, .. }
5721 | HydroNode::Enumerate { metadata, .. }
5722 | HydroNode::Inspect { metadata, .. }
5723 | HydroNode::Unique { metadata, .. }
5724 | HydroNode::Sort { metadata, .. }
5725 | HydroNode::Scan { metadata, .. }
5726 | HydroNode::ScanAsyncBlocking { metadata, .. }
5727 | HydroNode::Fold { metadata, .. }
5728 | HydroNode::FoldKeyed { metadata, .. }
5729 | HydroNode::Reduce { metadata, .. }
5730 | HydroNode::ReduceKeyed { metadata, .. }
5731 | HydroNode::ReduceKeyedWatermark { metadata, .. }
5732 | HydroNode::ExternalInput { metadata, .. }
5733 | HydroNode::Network { metadata, .. }
5734 | HydroNode::Counter { metadata, .. } => metadata,
5735 }
5736 }
5737
5738 pub fn input(&self) -> Vec<&HydroNode> {
5739 match self {
5740 HydroNode::Placeholder => {
5741 panic!()
5742 }
5743 HydroNode::Source { .. }
5744 | HydroNode::SingletonSource { .. }
5745 | HydroNode::ExternalInput { .. }
5746 | HydroNode::CycleSource { .. }
5747 | HydroNode::Tee { .. }
5748 | HydroNode::Reference { .. }
5749 | HydroNode::PartitionSide { .. }
5750 | HydroNode::VersionedNetwork { .. } => {
5751 vec![]
5753 }
5754 HydroNode::Cast { inner, .. }
5755 | HydroNode::ObserveNonDet { inner, .. }
5756 | HydroNode::YieldConcat { inner, .. }
5757 | HydroNode::BeginAtomic { inner, .. }
5758 | HydroNode::EndAtomic { inner, .. }
5759 | HydroNode::Batch { inner, .. }
5760 | HydroNode::UnboundSingleton { inner, .. }
5761 | HydroNode::AssertIsConsistent { inner, .. } => {
5762 vec![inner]
5763 }
5764 HydroNode::Chain { first, second, .. }
5765 | HydroNode::MergeOrdered { first, second, .. }
5766 | HydroNode::ChainFirst { first, second, .. } => {
5767 vec![first, second]
5768 }
5769 HydroNode::CrossProduct { left, right, .. }
5770 | HydroNode::CrossSingleton { left, right, .. }
5771 | HydroNode::Join { left, right, .. }
5772 | HydroNode::JoinHalf { left, right, .. } => {
5773 vec![left, right]
5774 }
5775 HydroNode::Difference { pos, neg, .. } | HydroNode::AntiJoin { pos, neg, .. } => {
5776 vec![pos, neg]
5777 }
5778 HydroNode::Counter { input, .. }
5779 | HydroNode::DeferTick { input, .. }
5780 | HydroNode::Enumerate { input, .. }
5781 | HydroNode::Filter { input, .. }
5782 | HydroNode::FilterMap { input, .. }
5783 | HydroNode::FlatMap { input, .. }
5784 | HydroNode::FlatMapStreamBlocking { input, .. }
5785 | HydroNode::Fold { input, .. }
5786 | HydroNode::FoldKeyed { input, .. }
5787 | HydroNode::Inspect { input, .. }
5788 | HydroNode::Map { input, .. }
5789 | HydroNode::Network { input, .. }
5790 | HydroNode::PartitionShared { input, .. }
5791 | HydroNode::Reduce { input, .. }
5792 | HydroNode::ReduceKeyed { input, .. }
5793 | HydroNode::ResolveFutures { input, .. }
5794 | HydroNode::ResolveFuturesBlocking { input, .. }
5795 | HydroNode::ResolveFuturesOrdered { input, .. }
5796 | HydroNode::Scan { input, .. }
5797 | HydroNode::ScanAsyncBlocking { input, .. }
5798 | HydroNode::Sort { input, .. }
5799 | HydroNode::Unique { input, .. } => {
5800 vec![input]
5801 }
5802 HydroNode::ReduceKeyedWatermark {
5803 input, watermark, ..
5804 } => {
5805 vec![input, watermark]
5806 }
5807 HydroNode::VersionedNetworkFork { senders, .. } => senders
5808 .iter()
5809 .map(|(_version, sender, _serialize)| sender.as_ref())
5810 .collect(),
5811 }
5812 }
5813
5814 pub fn input_metadata(&self) -> Vec<&HydroIrMetadata> {
5815 self.input()
5816 .iter()
5817 .map(|input_node| input_node.metadata())
5818 .collect()
5819 }
5820
5821 pub fn is_shared_with_others(&self) -> bool {
5825 match self {
5826 HydroNode::Tee { inner, .. } | HydroNode::PartitionSide { inner, .. } => {
5827 Rc::strong_count(&inner.0) > 1
5828 }
5829 HydroNode::Reference { .. } => false,
5832 _ => false,
5833 }
5834 }
5835
5836 pub fn print_root(&self) -> String {
5837 match self {
5838 HydroNode::Placeholder => {
5839 panic!()
5840 }
5841 HydroNode::Cast { .. } => "Cast()".to_owned(),
5842 HydroNode::UnboundSingleton { .. } => "UnboundSingleton()".to_owned(),
5843 HydroNode::ObserveNonDet { .. } => "ObserveNonDet()".to_owned(),
5844 HydroNode::AssertIsConsistent { .. } => "AssertIsConsistent()".to_owned(),
5845 HydroNode::Source { source, .. } => format!("Source({:?})", source),
5846 HydroNode::SingletonSource {
5847 value,
5848 first_tick_only,
5849 ..
5850 } => format!(
5851 "SingletonSource({:?}, first_tick_only={})",
5852 value, first_tick_only
5853 ),
5854 HydroNode::CycleSource { cycle_id, .. } => format!("CycleSource({})", cycle_id),
5855 HydroNode::Tee { inner, .. } => {
5856 format!("Tee({})", inner.0.borrow().print_root())
5857 }
5858 HydroNode::Reference { inner, kind, .. } => {
5859 format!("Reference({:?}, {})", kind, inner.0.borrow().print_root())
5860 }
5861 HydroNode::PartitionSide { inner, is_true, .. } => {
5862 format!(
5863 "PartitionSide(is_true={}, {})",
5864 is_true,
5865 inner.0.borrow().print_root(),
5866 )
5867 }
5868 HydroNode::PartitionShared { f, .. } => format!("PartitionShared({:?})", f),
5869 HydroNode::YieldConcat { .. } => "YieldConcat()".to_owned(),
5870 HydroNode::BeginAtomic { .. } => "BeginAtomic()".to_owned(),
5871 HydroNode::EndAtomic { .. } => "EndAtomic()".to_owned(),
5872 HydroNode::Batch { .. } => "Batch()".to_owned(),
5873 HydroNode::Chain { first, second, .. } => {
5874 format!("Chain({}, {})", first.print_root(), second.print_root())
5875 }
5876 HydroNode::MergeOrdered { first, second, .. } => {
5877 format!(
5878 "MergeOrdered({}, {})",
5879 first.print_root(),
5880 second.print_root()
5881 )
5882 }
5883 HydroNode::ChainFirst { first, second, .. } => {
5884 format!(
5885 "ChainFirst({}, {})",
5886 first.print_root(),
5887 second.print_root()
5888 )
5889 }
5890 HydroNode::CrossProduct { left, right, .. } => {
5891 format!(
5892 "CrossProduct({}, {})",
5893 left.print_root(),
5894 right.print_root()
5895 )
5896 }
5897 HydroNode::CrossSingleton { left, right, .. } => {
5898 format!(
5899 "CrossSingleton({}, {})",
5900 left.print_root(),
5901 right.print_root()
5902 )
5903 }
5904 HydroNode::Join { left, right, .. } => {
5905 format!("Join({}, {})", left.print_root(), right.print_root())
5906 }
5907 HydroNode::JoinHalf { left, right, .. } => {
5908 format!("JoinHalf({}, {})", left.print_root(), right.print_root())
5909 }
5910 HydroNode::Difference { pos, neg, .. } => {
5911 format!("Difference({}, {})", pos.print_root(), neg.print_root())
5912 }
5913 HydroNode::AntiJoin { pos, neg, .. } => {
5914 format!("AntiJoin({}, {})", pos.print_root(), neg.print_root())
5915 }
5916 HydroNode::ResolveFutures { .. } => "ResolveFutures()".to_owned(),
5917 HydroNode::ResolveFuturesBlocking { .. } => "ResolveFuturesBlocking()".to_owned(),
5918 HydroNode::ResolveFuturesOrdered { .. } => "ResolveFuturesOrdered()".to_owned(),
5919 HydroNode::Map { f, .. } => format!("Map({:?})", f),
5920 HydroNode::FlatMap { f, .. } => format!("FlatMap({:?})", f),
5921 HydroNode::FlatMapStreamBlocking { f, .. } => format!("FlatMapStreamBlocking({:?})", f),
5922 HydroNode::Filter { f, .. } => format!("Filter({:?})", f),
5923 HydroNode::FilterMap { f, .. } => format!("FilterMap({:?})", f),
5924 HydroNode::DeferTick { .. } => "DeferTick()".to_owned(),
5925 HydroNode::Enumerate { .. } => "Enumerate()".to_owned(),
5926 HydroNode::Inspect { f, .. } => format!("Inspect({:?})", f),
5927 HydroNode::Unique { .. } => "Unique()".to_owned(),
5928 HydroNode::Sort { .. } => "Sort()".to_owned(),
5929 HydroNode::Fold { init, acc, .. } => format!("Fold({:?}, {:?})", init, acc),
5930 HydroNode::Scan { init, acc, .. } => format!("Scan({:?}, {:?})", init, acc),
5931 HydroNode::ScanAsyncBlocking { init, acc, .. } => {
5932 format!("ScanAsyncBlocking({:?}, {:?})", init, acc)
5933 }
5934 HydroNode::FoldKeyed { init, acc, .. } => format!("FoldKeyed({:?}, {:?})", init, acc),
5935 HydroNode::Reduce { f, .. } => format!("Reduce({:?})", f),
5936 HydroNode::ReduceKeyed { f, .. } => format!("ReduceKeyed({:?})", f),
5937 HydroNode::ReduceKeyedWatermark { f, .. } => format!("ReduceKeyedWatermark({:?})", f),
5938 HydroNode::Network { .. } => "Network()".to_owned(),
5939 HydroNode::ExternalInput { .. } => "ExternalInput()".to_owned(),
5940 HydroNode::Counter { tag, duration, .. } => {
5941 format!("Counter({:?}, {:?})", tag, duration)
5942 }
5943 HydroNode::VersionedNetworkFork {
5944 channel_name,
5945 senders,
5946 ..
5947 } => {
5948 let versions: Vec<u32> = senders.iter().map(|(v, _, _)| *v).collect();
5949 format!(
5950 "VersionedNetworkFork({}, senders={:?})",
5951 channel_name, versions
5952 )
5953 }
5954 HydroNode::VersionedNetwork { version, .. } => {
5955 format!("VersionedNetwork(v{})", version)
5956 }
5957 }
5958 }
5959}
5960
5961#[cfg(feature = "build")]
5962#[expect(clippy::too_many_arguments, reason = "networking codegen")]
5963fn instantiate_network<'a, D>(
5964 env: &mut D::InstantiateEnv,
5965 from_location: &LocationId,
5966 to_location: &LocationId,
5967 processes: &SparseSecondaryMap<LocationKey, D::Process>,
5968 clusters: &SparseSecondaryMap<LocationKey, D::Cluster>,
5969 name: Option<&str>,
5970 networking_info: &crate::networking::NetworkingInfo,
5971 external_types: Option<(&syn::Type, &syn::Type)>,
5972) -> (syn::Expr, syn::Expr, Box<dyn FnOnce()>)
5973where
5974 D: Deploy<'a>,
5975{
5976 if external_types.is_some() && !D::SUPPORTS_EXTERNAL_SERIALIZATION {
5977 panic!(
5978 "`.embedded()` serialization leaves serialization to code outside of Hydro and is \
5979 only supported by the embedded deployment backend. Use `.bincode()` (or another \
5980 supported serialization backend) for this deployment target instead."
5981 );
5982 }
5983
5984 let ((sink, source), connect_fn) = match (from_location, to_location) {
5985 (&LocationId::Process(from), &LocationId::Process(to)) => {
5986 let from_node = processes
5987 .get(from)
5988 .unwrap_or_else(|| {
5989 panic!("A process used in the graph was not instantiated: {}", from)
5990 })
5991 .clone();
5992 let to_node = processes
5993 .get(to)
5994 .unwrap_or_else(|| {
5995 panic!("A process used in the graph was not instantiated: {}", to)
5996 })
5997 .clone();
5998
5999 let sink_port = from_node.next_port();
6000 let source_port = to_node.next_port();
6001
6002 (
6003 D::o2o_sink_source(
6004 env,
6005 &from_node,
6006 &sink_port,
6007 &to_node,
6008 &source_port,
6009 name,
6010 networking_info,
6011 external_types,
6012 ),
6013 D::o2o_connect(&from_node, &sink_port, &to_node, &source_port),
6014 )
6015 }
6016 (&LocationId::Process(from), &LocationId::Cluster(to)) => {
6017 let from_node = processes
6018 .get(from)
6019 .unwrap_or_else(|| {
6020 panic!("A process used in the graph was not instantiated: {}", from)
6021 })
6022 .clone();
6023 let to_node = clusters
6024 .get(to)
6025 .unwrap_or_else(|| {
6026 panic!("A cluster used in the graph was not instantiated: {}", to)
6027 })
6028 .clone();
6029
6030 let sink_port = from_node.next_port();
6031 let source_port = to_node.next_port();
6032
6033 (
6034 D::o2m_sink_source(
6035 env,
6036 &from_node,
6037 &sink_port,
6038 &to_node,
6039 &source_port,
6040 name,
6041 networking_info,
6042 external_types,
6043 ),
6044 D::o2m_connect(&from_node, &sink_port, &to_node, &source_port),
6045 )
6046 }
6047 (&LocationId::Cluster(from), &LocationId::Process(to)) => {
6048 let from_node = clusters
6049 .get(from)
6050 .unwrap_or_else(|| {
6051 panic!("A cluster used in the graph was not instantiated: {}", from)
6052 })
6053 .clone();
6054 let to_node = processes
6055 .get(to)
6056 .unwrap_or_else(|| {
6057 panic!("A process used in the graph was not instantiated: {}", to)
6058 })
6059 .clone();
6060
6061 let sink_port = from_node.next_port();
6062 let source_port = to_node.next_port();
6063
6064 (
6065 D::m2o_sink_source(
6066 env,
6067 &from_node,
6068 &sink_port,
6069 &to_node,
6070 &source_port,
6071 name,
6072 networking_info,
6073 external_types,
6074 ),
6075 D::m2o_connect(&from_node, &sink_port, &to_node, &source_port),
6076 )
6077 }
6078 (&LocationId::Cluster(from), &LocationId::Cluster(to)) => {
6079 let from_node = clusters
6080 .get(from)
6081 .unwrap_or_else(|| {
6082 panic!("A cluster used in the graph was not instantiated: {}", from)
6083 })
6084 .clone();
6085 let to_node = clusters
6086 .get(to)
6087 .unwrap_or_else(|| {
6088 panic!("A cluster used in the graph was not instantiated: {}", to)
6089 })
6090 .clone();
6091
6092 let sink_port = from_node.next_port();
6093 let source_port = to_node.next_port();
6094
6095 (
6096 D::m2m_sink_source(
6097 env,
6098 &from_node,
6099 &sink_port,
6100 &to_node,
6101 &source_port,
6102 name,
6103 networking_info,
6104 external_types,
6105 ),
6106 D::m2m_connect(&from_node, &sink_port, &to_node, &source_port),
6107 )
6108 }
6109 (LocationId::Tick(_, _), _) => panic!(),
6110 (_, LocationId::Tick(_, _)) => panic!(),
6111 (LocationId::Atomic(_), _) => panic!(),
6112 (_, LocationId::Atomic(_)) => panic!(),
6113 };
6114 (sink, source, connect_fn)
6115}
6116
6117#[cfg(test)]
6118mod serde_test;
6119
6120#[cfg(test)]
6121mod test {
6122 use std::mem::size_of;
6123
6124 use stageleft::{QuotedWithContext, q};
6125
6126 use super::*;
6127
6128 #[test]
6129 #[cfg_attr(
6130 not(feature = "build"),
6131 ignore = "expects inclusion of feature-gated fields"
6132 )]
6133 fn hydro_node_size() {
6134 assert_eq!(size_of::<HydroNode>(), 264);
6135 }
6136
6137 #[test]
6138 #[cfg_attr(
6139 not(feature = "build"),
6140 ignore = "expects inclusion of feature-gated fields"
6141 )]
6142 fn hydro_root_size() {
6143 assert_eq!(size_of::<HydroRoot>(), 136);
6144 }
6145
6146 #[test]
6147 fn test_simplify_q_macro_basic() {
6148 let simple_expr: syn::Expr = syn::parse_str("x + y").unwrap();
6150 let result = simplify_q_macro(simple_expr.clone());
6151 assert_eq!(result, simple_expr);
6152 }
6153
6154 #[test]
6155 fn test_simplify_q_macro_actual_stageleft_call() {
6156 let stageleft_call = q!(|x: usize| x + 1).splice_fn1_ctx(&());
6158 let result = simplify_q_macro(stageleft_call);
6159 hydro_build_utils::assert_snapshot!(result.to_token_stream().to_string());
6162 }
6163
6164 #[test]
6165 fn test_closure_no_pipe_at_start() {
6166 let stageleft_call = q!({
6168 let foo = 123;
6169 move |b: usize| b + foo
6170 })
6171 .splice_fn1_ctx(&());
6172 let result = simplify_q_macro(stageleft_call);
6173 hydro_build_utils::assert_snapshot!(result.to_token_stream().to_string());
6174 }
6175}