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