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