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