1use core::panic;
2use std::cell::{Cell, RefCell};
3use std::collections::HashMap;
4#[cfg(feature = "build")]
5use std::collections::HashSet;
6use std::fmt::{Debug, Display};
7use std::hash::{Hash, Hasher};
8use std::ops::Deref;
9use std::rc::Rc;
10
11#[cfg(feature = "build")]
12use dfir_lang::graph::FlatGraphBuilder;
13#[cfg(feature = "build")]
14use proc_macro2::Span;
15use proc_macro2::TokenStream;
16use quote::ToTokens;
17#[cfg(feature = "build")]
18use quote::quote;
19#[cfg(feature = "build")]
20use slotmap::{SecondaryMap, SparseSecondaryMap};
21#[cfg(feature = "build")]
22use syn::parse_quote;
23
24#[cfg(feature = "build")]
25use crate::compile::builder::ClockId;
26#[cfg(feature = "build")]
27use crate::compile::builder::StmtId;
28use crate::compile::builder::{CycleId, ExternalPortId};
29#[cfg(feature = "build")]
30use crate::compile::deploy_provider::{Deploy, Node, RegisterPort};
31#[cfg(feature = "build")]
32use crate::handoff_ref::handoff_ref_ident;
33use crate::location::dynamic::{ClusterConsistency, LocationId};
34use crate::location::{LocationKey, NetworkHint};
35
36pub mod backtrace;
37use backtrace::Backtrace;
38
39pub struct ClosureExpr {
45 pub(crate) expr: DebugExpr,
46 pub(crate) singleton_refs: Vec<(HydroNode, bool)>,
50}
51
52impl Clone for ClosureExpr {
53 fn clone(&self) -> Self {
54 Self {
55 expr: self.expr.clone(),
56 singleton_refs: self
57 .singleton_refs
58 .iter()
59 .map(|(node, is_mut)| {
60 let HydroNode::Reference {
61 inner,
62 kind,
63 access_counter,
64 metadata,
65 } = node
66 else {
67 panic!("singleton_refs should only contain HydroNode::Reference");
68 };
69 (
70 HydroNode::Reference {
71 inner: SharedNode(Rc::clone(&inner.0)),
72 kind: *kind,
73 access_counter: access_counter.freeze(),
74 metadata: metadata.clone(),
75 },
76 *is_mut,
77 )
78 })
79 .collect(),
80 }
81 }
82}
83
84impl Hash for ClosureExpr {
85 fn hash<H: Hasher>(&self, state: &mut H) {
86 self.expr.hash(state);
87 }
91}
92
93impl serde::Serialize for ClosureExpr {
94 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
95 use serde::ser::SerializeStruct;
96 let mut s = serializer.serialize_struct("ClosureExpr", 2)?;
97 s.serialize_field("expr", &self.expr)?;
98 s.serialize_field(
99 "singleton_refs",
100 &SerializableSingletonRefs(&self.singleton_refs),
101 )?;
102 s.end()
103 }
104}
105
106struct SerializableSingletonRefs<'a>(&'a [(HydroNode, bool)]);
107
108impl serde::Serialize for SerializableSingletonRefs<'_> {
109 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
110 use serde::ser::SerializeSeq;
111 let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
112 for (node, is_mut) in self.0.iter() {
113 seq.serialize_element(&(node, is_mut))?;
114 }
115 seq.end()
116 }
117}
118
119impl Debug for ClosureExpr {
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121 Debug::fmt(&self.expr, f)
122 }
123}
124
125impl Display for ClosureExpr {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 Display::fmt(&self.expr, f)
128 }
129}
130
131impl From<syn::Expr> for ClosureExpr {
132 fn from(expr: syn::Expr) -> Self {
133 Self {
134 expr: DebugExpr(Box::new(expr)),
135 singleton_refs: Vec::new(),
136 }
137 }
138}
139
140impl From<DebugExpr> for ClosureExpr {
141 fn from(expr: DebugExpr) -> Self {
142 Self {
143 expr,
144 singleton_refs: Vec::new(),
145 }
146 }
147}
148
149impl ClosureExpr {
150 pub fn new(expr: DebugExpr, singleton_refs: Vec<(HydroNode, bool)>) -> Self {
151 Self {
152 expr,
153 singleton_refs,
154 }
155 }
156
157 pub fn has_mut_ref(&self) -> bool {
158 self.singleton_refs.iter().any(|(_, is_mut)| *is_mut)
159 }
160
161 pub fn deep_clone(&self, seen_tees: &mut SeenSharedNodes) -> Self {
162 Self {
163 expr: self.expr.clone(),
164 singleton_refs: self
165 .singleton_refs
166 .iter()
167 .map(|(node, is_mut)| (node.deep_clone(seen_tees), *is_mut))
168 .collect(),
169 }
170 }
171
172 pub fn transform_children(
173 &mut self,
174 transform: &mut impl FnMut(&mut HydroNode, &mut SeenSharedNodes),
175 seen_tees: &mut SeenSharedNodes,
176 ) {
177 for (ref_node, _is_mut) in self.singleton_refs.iter_mut() {
178 transform(ref_node, seen_tees);
179 }
180 }
181
182 #[cfg(feature = "build")]
185 pub fn emit_tokens(&self, ident_stack: &mut Vec<syn::Ident>) -> TokenStream {
186 if self.singleton_refs.is_empty() {
187 self.expr.0.to_token_stream()
188 } else {
189 assert!(
190 ident_stack.len() >= self.singleton_refs.len(),
191 "ident_stack has {} entries but expected at least {} for singleton_refs",
192 ident_stack.len(),
193 self.singleton_refs.len()
194 );
195 let ref_idents = ident_stack.drain(ident_stack.len() - self.singleton_refs.len()..);
196
197 let mut let_bindings = Vec::new();
198 for ((i, (ref_node, is_mut)), ref_ident) in
199 self.singleton_refs.iter().enumerate().zip(ref_idents)
200 {
201 let HydroNode::Reference { access_counter, .. } = ref_node else {
202 panic!("ClosureExpression expected references to `HydroNode::Reference`");
203 };
204 let group = access_counter.frozen_group();
205 let local_ident = handoff_ref_ident(i);
207 let hash = proc_macro2::Punct::new('#', proc_macro2::Spacing::Alone);
208 let group_lit = proc_macro2::Literal::u32_unsuffixed(group);
209 let mut_token = is_mut.then(|| quote!(mut));
210 let binding = quote! {
211 let #local_ident = #hash {#group_lit} #mut_token #ref_ident;
212 };
213 let_bindings.push(binding);
214 }
215
216 let expr = &self.expr.0;
217 quote! {
218 {
219 #( #let_bindings )*
220 #expr
221 }
222 }
223 }
224 }
225}
226
227#[derive(Clone, Hash)]
231pub struct DebugExpr(pub Box<syn::Expr>);
232
233impl serde::Serialize for DebugExpr {
234 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
235 serializer.serialize_str(&self.to_string())
236 }
237}
238
239impl From<syn::Expr> for DebugExpr {
240 fn from(expr: syn::Expr) -> Self {
241 Self(Box::new(expr))
242 }
243}
244
245impl Deref for DebugExpr {
246 type Target = syn::Expr;
247
248 fn deref(&self) -> &Self::Target {
249 &self.0
250 }
251}
252
253impl ToTokens for DebugExpr {
254 fn to_tokens(&self, tokens: &mut TokenStream) {
255 self.0.to_tokens(tokens);
256 }
257}
258
259impl Debug for DebugExpr {
260 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
261 write!(f, "{}", self.0.to_token_stream())
262 }
263}
264
265impl Display for DebugExpr {
266 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
267 let original = self.0.as_ref().clone();
268 let simplified = simplify_q_macro(original);
269
270 write!(f, "q!({})", quote::quote!(#simplified))
273 }
274}
275
276fn simplify_q_macro(expr: syn::Expr) -> syn::Expr {
278 if let syn::Expr::Call(ref call) = expr && let syn::Expr::Path(path_expr) = call.func.as_ref()
279 && is_stageleft_runtime_support_call(&path_expr.path)
281 && let syn::Expr::Block(b) = &call.args[0]
282 && b.block.stmts.len() == 3
283 && let Some(syn::Stmt::Expr(e, _)) = b.block.stmts.get(2)
284 {
286 let mut e = e.clone();
287 while let syn::Expr::Block(ref mut block) = e
288 && block.block.stmts.len() == 1
289 && let syn::Stmt::Expr(inner_e, _) = block.block.stmts.remove(0)
290 {
291 e = inner_e;
292 }
293
294 e
295 } else {
296 expr
297 }
298}
299
300fn is_stageleft_runtime_support_call(path: &syn::Path) -> bool {
301 if let Some(last_segment) = path.segments.last() {
303 let fn_name = last_segment.ident.to_string();
304 path.segments.len() > 2
305 && path.segments[0].ident == "stageleft"
306 && path.segments[1].ident == "runtime_support"
307 && fn_name.contains("_type_hint")
308 } else {
309 false
310 }
311}
312
313#[derive(Clone, PartialEq, Eq, Hash)]
317pub struct DebugType(pub Box<syn::Type>);
318
319impl From<syn::Type> for DebugType {
320 fn from(t: syn::Type) -> Self {
321 Self(Box::new(t))
322 }
323}
324
325impl Deref for DebugType {
326 type Target = syn::Type;
327
328 fn deref(&self) -> &Self::Target {
329 &self.0
330 }
331}
332
333impl ToTokens for DebugType {
334 fn to_tokens(&self, tokens: &mut TokenStream) {
335 self.0.to_tokens(tokens);
336 }
337}
338
339impl Debug for DebugType {
340 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
341 write!(f, "{}", self.0.to_token_stream())
342 }
343}
344
345impl serde::Serialize for DebugType {
346 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
347 serializer.serialize_str(&format!("{}", self.0.to_token_stream()))
348 }
349}
350
351fn serialize_backtrace_as_span<S: serde::Serializer>(
352 backtrace: &Backtrace,
353 serializer: S,
354) -> Result<S::Ok, S::Error> {
355 match backtrace.format_span() {
356 Some(span) => serializer.serialize_some(&span),
357 None => serializer.serialize_none(),
358 }
359}
360
361fn serialize_ident<S: serde::Serializer>(
362 ident: &syn::Ident,
363 serializer: S,
364) -> Result<S::Ok, S::Error> {
365 serializer.serialize_str(&ident.to_string())
366}
367
368pub enum DebugInstantiate {
369 Building,
370 Finalized(Box<DebugInstantiateFinalized>),
371}
372
373impl serde::Serialize for DebugInstantiate {
374 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
375 match self {
376 DebugInstantiate::Building => {
377 serializer.serialize_unit_variant("DebugInstantiate", 0, "Building")
378 }
379 DebugInstantiate::Finalized(_) => {
380 panic!(
381 "cannot serialize DebugInstantiate::Finalized: contains non-serializable runtime state (closures)"
382 )
383 }
384 }
385 }
386}
387
388#[cfg_attr(
389 not(feature = "build"),
390 expect(
391 dead_code,
392 reason = "sink, source unused without `feature = \"build\"`."
393 )
394)]
395pub struct DebugInstantiateFinalized {
396 sink: syn::Expr,
397 source: syn::Expr,
398 connect_fn: Option<Box<dyn FnOnce()>>,
399}
400
401impl From<DebugInstantiateFinalized> for DebugInstantiate {
402 fn from(f: DebugInstantiateFinalized) -> Self {
403 Self::Finalized(Box::new(f))
404 }
405}
406
407impl Debug for DebugInstantiate {
408 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
409 write!(f, "<network instantiate>")
410 }
411}
412
413impl Hash for DebugInstantiate {
414 fn hash<H: Hasher>(&self, _state: &mut H) {
415 }
417}
418
419impl Clone for DebugInstantiate {
420 fn clone(&self) -> Self {
421 match self {
422 DebugInstantiate::Building => DebugInstantiate::Building,
423 DebugInstantiate::Finalized(_) => {
424 panic!("DebugInstantiate::Finalized should not be cloned")
425 }
426 }
427 }
428}
429
430#[derive(Debug, Hash, Clone, serde::Serialize)]
439pub enum ClusterMembersState {
440 Uninit,
442 Stream(DebugExpr),
445 Tee(LocationId, LocationId),
449}
450
451#[derive(Debug, Hash, Clone, serde::Serialize)]
453pub enum HydroSource {
454 Stream(DebugExpr),
455 ExternalNetwork(),
456 Iter(DebugExpr),
457 Spin(),
458 ClusterMembers(LocationId, ClusterMembersState),
459 Embedded(#[serde(serialize_with = "serialize_ident")] syn::Ident),
460 EmbeddedSingleton(#[serde(serialize_with = "serialize_ident")] syn::Ident),
461}
462
463#[cfg(feature = "build")]
464pub trait DfirBuilder {
470 fn singleton_intermediates(&self) -> bool;
472
473 fn add_dfir_at(
480 &mut self,
481 location: &LocationId,
482 dfir: dfir_lang::parse::DfirCode,
483 operator_tag: Option<&str>,
484 );
485
486 fn add_tick_source(
503 &mut self,
504 location: &LocationId,
505 source_rhs: TokenStream,
506 out_ident: &syn::Ident,
507 replay_each_tick: bool,
508 operator_tag: Option<&str>,
509 );
510
511 fn tick_state_lifetime(&self, _op_location: &LocationId) -> TokenStream {
517 quote!('tick)
518 }
519
520 fn cross_tick_state_lifetime(&self, _op_location: &LocationId) -> TokenStream {
527 quote!('static)
528 }
529
530 #[expect(clippy::too_many_arguments, reason = "TODO")]
538 fn batch(
539 &mut self,
540 in_ident: syn::Ident,
541 in_location: &LocationId,
542 in_kind: &CollectionKind,
543 out_ident: &syn::Ident,
544 out_location: &LocationId,
545 op_meta: &HydroIrOpMetadata,
546 fold_hooked_idents: &HashSet<String>,
547 );
548
549 #[expect(clippy::too_many_arguments, reason = "TODO")]
556 fn snapshot(
557 &mut self,
558 in_ident: syn::Ident,
559 in_location: &LocationId,
560 in_kind: &CollectionKind,
561 out_ident: &syn::Ident,
562 out_location: &LocationId,
563 op_meta: &HydroIrOpMetadata,
564 fold_hooked_idents: &HashSet<String>,
565 );
566
567 fn yield_from_tick(
568 &mut self,
569 in_ident: syn::Ident,
570 in_location: &LocationId,
571 in_kind: &CollectionKind,
572 out_ident: &syn::Ident,
573 out_location: &LocationId,
574 out_kind: &CollectionKind,
575 );
576
577 fn unwindow_for_consume(
590 &mut self,
591 in_ident: syn::Ident,
592 in_location: &LocationId,
593 out_location: &LocationId,
594 ) -> syn::Ident;
595
596 fn begin_atomic(
597 &mut self,
598 in_ident: syn::Ident,
599 in_location: &LocationId,
600 in_kind: &CollectionKind,
601 out_ident: &syn::Ident,
602 out_location: &LocationId,
603 op_meta: &HydroIrOpMetadata,
604 );
605 fn end_atomic(
606 &mut self,
607 in_ident: syn::Ident,
608 in_location: &LocationId,
609 in_kind: &CollectionKind,
610 out_ident: &syn::Ident,
611 out_kind: &CollectionKind,
612 );
613
614 #[expect(clippy::too_many_arguments, reason = "TODO // internal")]
615 fn observe_nondet(
616 &mut self,
617 trusted: bool,
618 location: &LocationId,
619 in_ident: syn::Ident,
620 in_kind: &CollectionKind,
621 out_ident: &syn::Ident,
622 out_kind: &CollectionKind,
623 op_meta: &HydroIrOpMetadata,
624 );
625
626 #[expect(clippy::too_many_arguments, reason = "TODO")]
627 fn merge_ordered(
628 &mut self,
629 location: &LocationId,
630 first_ident: syn::Ident,
631 second_ident: syn::Ident,
632 out_ident: &syn::Ident,
633 in_kind: &CollectionKind,
634 op_meta: &HydroIrOpMetadata,
635 operator_tag: Option<&str>,
636 );
637
638 #[expect(clippy::too_many_arguments, reason = "TODO")]
639 fn create_network(
640 &mut self,
641 from: &LocationId,
642 to: &LocationId,
643 input_ident: syn::Ident,
644 out_ident: &syn::Ident,
645 serialize: Option<&DebugExpr>,
646 sink: syn::Expr,
647 source: syn::Expr,
648 deserialize: Option<&DebugExpr>,
649 external_element_type: Option<&syn::Type>,
650 tag_id: StmtId,
651 networking_info: &crate::networking::NetworkingInfo,
652 );
653
654 fn create_external_source(
655 &mut self,
656 on: &LocationId,
657 source_expr: syn::Expr,
658 out_ident: &syn::Ident,
659 deserialize: Option<&DebugExpr>,
660 tag_id: StmtId,
661 );
662
663 fn create_external_output(
664 &mut self,
665 on: &LocationId,
666 sink_expr: syn::Expr,
667 input_ident: &syn::Ident,
668 serialize: Option<&DebugExpr>,
669 tag_id: StmtId,
670 );
671
672 fn emit_fold_hook(
675 &mut self,
676 location: &LocationId,
677 in_ident: &syn::Ident,
678 in_kind: &CollectionKind,
679 op_meta: &HydroIrOpMetadata,
680 ) -> Option<syn::Ident>;
681
682 fn assert_is_consistent(
686 &mut self,
687 trusted: bool,
688 location: &LocationId,
689 in_ident: syn::Ident,
690 out_ident: &syn::Ident,
691 );
692
693 fn observe_for_mut(
697 &mut self,
698 location: &LocationId,
699 in_ident: syn::Ident,
700 in_kind: &CollectionKind,
701 out_ident: &syn::Ident,
702 op_meta: &HydroIrOpMetadata,
703 );
704
705 fn create_versioned_network_fork(
706 &mut self,
707 channel_id: u32,
708 dest: &LocationId,
709 senders: Vec<(LocationId, syn::Ident, Option<DebugExpr>)>,
710 external_element_type: Option<&syn::Type>,
711 tag_id: StmtId,
712 );
713
714 #[expect(clippy::too_many_arguments, reason = "networking codegen")]
715 fn create_versioned_network(
716 &mut self,
717 channel_id: u32,
718 source: &LocationId,
719 dest: &LocationId,
720 out_ident: &syn::Ident,
721 deserialize: Option<&DebugExpr>,
722 external_element_type: Option<&syn::Type>,
723 tag_id: StmtId,
724 );
725}
726
727#[cfg(feature = "build")]
734#[derive(Default)]
735pub struct ProdDfirBuilder {
736 pub graphs: SecondaryMap<LocationKey, FlatGraphBuilder>,
738 tick_loops: HashMap<LocationId, dfir_lang::graph::GraphLoopId>,
742 next_intermediate_id: usize,
744}
745
746#[cfg(feature = "build")]
747impl ProdDfirBuilder {
748 fn graph_mut(&mut self, location: &LocationId) -> &mut FlatGraphBuilder {
750 self.graphs
751 .entry(location.root().key())
752 .expect("location was removed")
753 .or_default()
754 }
755
756 fn loop_context(&mut self, location: &LocationId) -> Option<dfir_lang::graph::GraphLoopId> {
759 let tick_location = tick_location_of(location)?.clone();
760 if let Some(&loop_id) = self.tick_loops.get(&tick_location) {
761 return Some(loop_id);
762 }
763 let loop_id = self.graph_mut(location).insert_loop(None);
764 self.tick_loops.insert(tick_location, loop_id);
765 Some(loop_id)
766 }
767
768 fn add_dfir_in(
771 &mut self,
772 location: &LocationId,
773 dfir: dfir_lang::parse::DfirCode,
774 operator_tag: Option<&str>,
775 ) {
776 let loop_context = self.loop_context(location);
777 self.graph_mut(location)
778 .add_dfir(dfir, loop_context, operator_tag);
779 }
780
781 fn intermediate_ident(&mut self) -> syn::Ident {
783 let id = self.next_intermediate_id;
784 self.next_intermediate_id += 1;
785 syn::Ident::new(&format!("__loop_boundary_{}", id), Span::call_site())
786 }
787}
788
789#[cfg(feature = "build")]
790impl DfirBuilder for ProdDfirBuilder {
791 fn singleton_intermediates(&self) -> bool {
792 false
793 }
794
795 fn add_dfir_at(
796 &mut self,
797 location: &LocationId,
798 dfir: dfir_lang::parse::DfirCode,
799 operator_tag: Option<&str>,
800 ) {
801 self.add_dfir_in(location, dfir, operator_tag);
804 }
805
806 fn add_tick_source(
809 &mut self,
810 location: &LocationId,
811 source_rhs: TokenStream,
812 out_ident: &syn::Ident,
813 replay_each_tick: bool,
814 operator_tag: Option<&str>,
815 ) {
816 let source_ident = self.intermediate_ident();
820 let source_stmt = if replay_each_tick {
821 parse_quote! {
822 #source_ident = #source_rhs -> persist::<'static>();
823 }
824 } else {
825 parse_quote! {
826 #source_ident = #source_rhs;
827 }
828 };
829 self.graph_mut(location)
830 .add_dfir(source_stmt, None, operator_tag);
831 self.add_dfir_in(
832 location,
833 parse_quote! {
834 #out_ident = #source_ident -> batch_eager();
835 },
836 operator_tag,
837 );
838 }
839
840 fn batch(
841 &mut self,
842 in_ident: syn::Ident,
843 in_location: &LocationId,
844 in_kind: &CollectionKind,
845 out_ident: &syn::Ident,
846 out_location: &LocationId,
847 _op_meta: &HydroIrOpMetadata,
848 _fold_hooked_idents: &HashSet<String>,
849 ) {
850 match (
851 tick_location_of(in_location),
852 tick_location_of(out_location),
853 ) {
854 (Some(in_tick), Some(out_tick)) => {
855 assert_eq!(
858 in_tick, out_tick,
859 "batch between distinct ticks should have been unified"
860 );
861 self.add_dfir_in(
862 out_location,
863 parse_quote! {
864 #out_ident = #in_ident;
865 },
866 None,
867 );
868 }
869 (None, Some(_)) => {
870 match in_kind {
874 CollectionKind::Stream { .. }
879 | CollectionKind::KeyedStream { .. }
880 | CollectionKind::KeyedSingleton {
881 bound:
882 KeyedSingletonBoundKind::BoundedValue | KeyedSingletonBoundKind::Bounded,
883 ..
884 } => {
885 self.add_dfir_in(
886 out_location,
887 parse_quote! {
888 #out_ident = #in_ident -> batch_eager();
889 },
890 None,
891 );
892 }
893
894 CollectionKind::Singleton { .. }
895 | CollectionKind::Optional { .. }
896 | CollectionKind::KeyedSingleton {
897 bound:
898 KeyedSingletonBoundKind::Unbounded
899 | KeyedSingletonBoundKind::MonotonicKeys
900 | KeyedSingletonBoundKind::MonotonicValue,
901 ..
902 } => {
903 panic!(
904 "`Batch` is only defined for stream-like collections (streams, keyed \
905 streams, and keyed singletons with bounded per-key values), but was \
906 applied to a singleton-like collection {in_kind:?}; use `Snapshot` \
907 instead"
908 );
909 }
910 }
911 }
912 (Some(_) | None, None) => {
913 unreachable!("batch must target a tick location");
914 }
915 }
916 }
917
918 fn snapshot(
919 &mut self,
920 in_ident: syn::Ident,
921 in_location: &LocationId,
922 in_kind: &CollectionKind,
923 out_ident: &syn::Ident,
924 out_location: &LocationId,
925 _op_meta: &HydroIrOpMetadata,
926 _fold_hooked_idents: &HashSet<String>,
927 ) {
928 match (
929 tick_location_of(in_location),
930 tick_location_of(out_location),
931 ) {
932 (Some(in_tick), Some(out_tick)) => {
933 assert_eq!(
936 in_tick, out_tick,
937 "snapshot between distinct ticks should have been unified"
938 );
939 self.add_dfir_in(
944 out_location,
945 parse_quote! {
946 #out_ident = #in_ident;
947 },
948 None,
949 );
950 }
951 (None, Some(_)) => {
952 match in_kind {
966 CollectionKind::Singleton {
970 bound: SingletonBoundKind::Bounded,
971 ..
972 }
973 | CollectionKind::Optional {
974 bound: OptionalBoundKind::Bounded,
975 ..
976 }
977 | CollectionKind::KeyedSingleton {
978 bound: KeyedSingletonBoundKind::Bounded,
979 ..
980 } => {
981 let persisted_ident = self.intermediate_ident();
982 self.add_dfir_in(
983 in_location,
984 parse_quote! {
985 #persisted_ident = #in_ident -> persist::<'static>();
986 },
987 None,
988 );
989 self.add_dfir_in(
990 out_location,
991 parse_quote! {
992 #out_ident = #persisted_ident -> batch_eager();
993 },
994 None,
995 );
996 }
997
998 CollectionKind::Singleton {
1007 bound: SingletonBoundKind::Unbounded | SingletonBoundKind::Monotonic,
1008 ..
1009 }
1010 | CollectionKind::Optional {
1011 bound: OptionalBoundKind::InitNone,
1012 ..
1013 } => {
1014 let batched_ident = self.intermediate_ident();
1015 self.add_dfir_in(
1016 out_location,
1017 parse_quote! {
1018 #batched_ident = #in_ident -> batch_eager();
1019 },
1020 None,
1021 );
1022 let lifetime: TokenStream = self.cross_tick_state_lifetime(out_location);
1023 let lifetime: syn::Lifetime = syn::parse2(lifetime).unwrap();
1024 self.add_dfir_in(
1025 out_location,
1026 parse_quote! {
1027 #out_ident = #batched_ident
1028 -> fold::<#lifetime>(|| None, |current, update| { *current = Some(update); })
1029 -> filter_map(|current| current);
1030 },
1031 None,
1032 );
1033 }
1034
1035 CollectionKind::Optional {
1041 bound: OptionalBoundKind::Unbounded,
1042 ..
1043 } => {
1044 let batched_ident = self.intermediate_ident();
1045 self.add_dfir_in(
1046 out_location,
1047 parse_quote! {
1048 #batched_ident = #in_ident -> batch_eager();
1049 },
1050 None,
1051 );
1052 let lifetime: TokenStream = self.cross_tick_state_lifetime(out_location);
1053 let lifetime: syn::Lifetime = syn::parse2(lifetime).unwrap();
1054 self.add_dfir_in(
1055 out_location,
1056 parse_quote! {
1057 #out_ident = #batched_ident
1058 -> fold::<#lifetime>(|| None, |current, update| { *current = update; })
1059 -> filter_map(|current| current);
1060 },
1061 None,
1062 );
1063 }
1064
1065 CollectionKind::KeyedSingleton {
1071 bound: KeyedSingletonBoundKind::Unbounded,
1072 ..
1073 } => {
1074 let batched_ident = self.intermediate_ident();
1075 self.add_dfir_in(
1076 out_location,
1077 parse_quote! {
1078 #batched_ident = #in_ident -> batch_eager();
1079 },
1080 None,
1081 );
1082 let lifetime: TokenStream = self.cross_tick_state_lifetime(out_location);
1083 let lifetime: syn::Lifetime = syn::parse2(lifetime).unwrap();
1084 self.add_dfir_in(
1085 out_location,
1086 parse_quote! {
1087 #out_ident = #batched_ident
1088 -> fold::<#lifetime>(|| ::std::collections::HashMap::new(), |current, (key, update)| {
1089 match update {
1090 ::std::option::Option::Some(value) => {
1091 current.insert(key, value);
1092 }
1093 ::std::option::Option::None => {
1094 current.remove(&key);
1095 }
1096 }
1097 })
1098 -> flat_map(|current| current);
1099 },
1100 None,
1101 );
1102 }
1103
1104 CollectionKind::KeyedSingleton {
1109 bound:
1110 KeyedSingletonBoundKind::MonotonicKeys
1111 | KeyedSingletonBoundKind::MonotonicValue,
1112 ..
1113 } => {
1114 self.add_dfir_in(
1115 out_location,
1116 parse_quote! {
1117 #out_ident = #in_ident -> batch_eager();
1118 },
1119 None,
1120 );
1121 }
1122
1123 CollectionKind::Stream { .. }
1124 | CollectionKind::KeyedStream { .. }
1125 | CollectionKind::KeyedSingleton {
1126 bound: KeyedSingletonBoundKind::BoundedValue,
1127 ..
1128 } => {
1129 panic!(
1130 "`Snapshot` is only defined for singleton-like collections \
1131 (singletons, optionals, and keyed singletons whose per-key values \
1132 may change), but was applied to a stream-like collection \
1133 {in_kind:?}; use `Batch` instead"
1134 );
1135 }
1136 }
1137 }
1138 (Some(_) | None, None) => {
1139 unreachable!("snapshot must target a tick location");
1140 }
1141 }
1142 }
1143
1144 fn yield_from_tick(
1145 &mut self,
1146 in_ident: syn::Ident,
1147 in_location: &LocationId,
1148 _in_kind: &CollectionKind,
1149 out_ident: &syn::Ident,
1150 out_location: &LocationId,
1151 out_kind: &CollectionKind,
1152 ) {
1153 match (
1160 tick_location_of(in_location),
1161 tick_location_of(out_location),
1162 ) {
1163 (Some(in_tick), Some(out_tick)) => {
1164 assert_eq!(
1165 in_tick, out_tick,
1166 "atomic yield to a different tick should have been unified"
1167 );
1168 self.add_dfir_in(
1169 out_location,
1170 parse_quote! {
1171 #out_ident = #in_ident;
1172 },
1173 None,
1174 );
1175 }
1176 _ => {
1177 match out_kind {
1179 CollectionKind::Optional {
1180 bound: OptionalBoundKind::Unbounded,
1181 ..
1182 } => {
1183 let folded_ident = self.intermediate_ident();
1192 let lifetime: TokenStream = self.tick_state_lifetime(in_location);
1193 let lifetime: syn::Lifetime = syn::parse2(lifetime).unwrap();
1194 self.add_dfir_in(
1195 in_location,
1196 parse_quote! {
1197 #folded_ident = #in_ident
1198 -> fold::<#lifetime>(|| None, |update, value| { *update = Some(value); });
1199 },
1200 None,
1201 );
1202 self.graph_mut(in_location).add_dfir(
1203 parse_quote! {
1204 #out_ident = #folded_ident -> all_iterations();
1205 },
1206 None,
1207 None,
1208 );
1209 }
1210 CollectionKind::KeyedSingleton {
1211 bound: KeyedSingletonBoundKind::Unbounded,
1212 ..
1213 } => {
1214 todo!(
1221 "yielding an unbounded KeyedSingleton out of a tick is not yet supported"
1222 );
1223 }
1224 _ => {
1225 self.graph_mut(in_location).add_dfir(
1228 parse_quote! {
1229 #out_ident = #in_ident -> all_iterations();
1230 },
1231 None,
1232 None,
1233 );
1234 }
1235 }
1236 }
1237 }
1238 }
1239
1240 fn unwindow_for_consume(
1241 &mut self,
1242 in_ident: syn::Ident,
1243 in_location: &LocationId,
1244 out_location: &LocationId,
1245 ) -> syn::Ident {
1246 let in_loop = self.loop_context(in_location);
1251 let out_loop = self.loop_context(out_location);
1252 if in_loop.is_some() && in_loop != out_loop {
1253 let out_ident = self.intermediate_ident();
1254 self.graph_mut(in_location).add_dfir(
1255 parse_quote! {
1256 #out_ident = #in_ident -> all_iterations();
1257 },
1258 None,
1259 None,
1260 );
1261 out_ident
1262 } else {
1263 in_ident
1264 }
1265 }
1266
1267 fn begin_atomic(
1268 &mut self,
1269 in_ident: syn::Ident,
1270 in_location: &LocationId,
1271 in_kind: &CollectionKind,
1272 out_ident: &syn::Ident,
1273 out_location: &LocationId,
1274 op_meta: &HydroIrOpMetadata,
1275 ) {
1276 match (
1279 tick_location_of(in_location),
1280 tick_location_of(out_location),
1281 ) {
1282 (None, Some(_)) => {
1283 self.batch(
1288 in_ident,
1289 in_location,
1290 in_kind,
1291 out_ident,
1292 out_location,
1293 op_meta,
1294 &HashSet::new(),
1295 );
1296 }
1297 _ => {
1298 self.add_dfir_in(
1299 out_location,
1300 parse_quote! {
1301 #out_ident = #in_ident;
1302 },
1303 None,
1304 );
1305 }
1306 }
1307 }
1308
1309 fn end_atomic(
1310 &mut self,
1311 in_ident: syn::Ident,
1312 in_location: &LocationId,
1313 in_kind: &CollectionKind,
1314 out_ident: &syn::Ident,
1315 out_kind: &CollectionKind,
1316 ) {
1317 let out_location = in_location.root().clone();
1321 self.yield_from_tick(
1322 in_ident,
1323 in_location,
1324 in_kind,
1325 out_ident,
1326 &out_location,
1327 out_kind,
1328 );
1329 }
1330
1331 fn observe_nondet(
1332 &mut self,
1333 _trusted: bool,
1334 location: &LocationId,
1335 in_ident: syn::Ident,
1336 _in_kind: &CollectionKind,
1337 out_ident: &syn::Ident,
1338 _out_kind: &CollectionKind,
1339 _op_meta: &HydroIrOpMetadata,
1340 ) {
1341 let builder = self.graph_mut(location);
1342 builder.add_dfir(
1343 parse_quote! {
1344 #out_ident = #in_ident;
1345 },
1346 None,
1347 None,
1348 );
1349 }
1350
1351 fn merge_ordered(
1352 &mut self,
1353 location: &LocationId,
1354 first_ident: syn::Ident,
1355 second_ident: syn::Ident,
1356 out_ident: &syn::Ident,
1357 _in_kind: &CollectionKind,
1358 _op_meta: &HydroIrOpMetadata,
1359 operator_tag: Option<&str>,
1360 ) {
1361 let builder = self.graph_mut(location);
1362 builder.add_dfir(
1363 parse_quote! {
1364 #out_ident = union();
1365 #first_ident -> [0]#out_ident;
1366 #second_ident -> [1]#out_ident;
1367 },
1368 None,
1369 operator_tag,
1370 );
1371 }
1372
1373 fn create_network(
1374 &mut self,
1375 from: &LocationId,
1376 to: &LocationId,
1377 input_ident: syn::Ident,
1378 out_ident: &syn::Ident,
1379 serialize: Option<&DebugExpr>,
1380 sink: syn::Expr,
1381 source: syn::Expr,
1382 deserialize: Option<&DebugExpr>,
1383 _external_element_type: Option<&syn::Type>,
1384 tag_id: StmtId,
1385 _networking_info: &crate::networking::NetworkingInfo,
1386 ) {
1387 let sender_builder = self.graph_mut(from);
1388 if let Some(serialize_pipeline) = serialize {
1389 sender_builder.add_dfir(
1390 parse_quote! {
1391 #input_ident -> map(#serialize_pipeline) -> dest_sink(#sink);
1392 },
1393 None,
1394 Some(&format!("send{}", tag_id)),
1396 );
1397 } else {
1398 sender_builder.add_dfir(
1399 parse_quote! {
1400 #input_ident -> dest_sink(#sink);
1401 },
1402 None,
1403 Some(&format!("send{}", tag_id)),
1404 );
1405 }
1406
1407 let receiver_builder = self.graph_mut(to);
1408 if let Some(deserialize_pipeline) = deserialize {
1409 receiver_builder.add_dfir(
1410 parse_quote! {
1411 #out_ident = source_stream(#source) -> map(#deserialize_pipeline);
1412 },
1413 None,
1414 Some(&format!("recv{}", tag_id)),
1415 );
1416 } else {
1417 receiver_builder.add_dfir(
1418 parse_quote! {
1419 #out_ident = source_stream(#source);
1420 },
1421 None,
1422 Some(&format!("recv{}", tag_id)),
1423 );
1424 }
1425 }
1426
1427 fn create_external_source(
1428 &mut self,
1429 on: &LocationId,
1430 source_expr: syn::Expr,
1431 out_ident: &syn::Ident,
1432 deserialize: Option<&DebugExpr>,
1433 tag_id: StmtId,
1434 ) {
1435 let receiver_builder = self.graph_mut(on);
1436 if let Some(deserialize_pipeline) = deserialize {
1437 receiver_builder.add_dfir(
1438 parse_quote! {
1439 #out_ident = source_stream(#source_expr) -> map(#deserialize_pipeline);
1440 },
1441 None,
1442 Some(&format!("recv{}", tag_id)),
1443 );
1444 } else {
1445 receiver_builder.add_dfir(
1446 parse_quote! {
1447 #out_ident = source_stream(#source_expr);
1448 },
1449 None,
1450 Some(&format!("recv{}", tag_id)),
1451 );
1452 }
1453 }
1454
1455 fn create_external_output(
1456 &mut self,
1457 on: &LocationId,
1458 sink_expr: syn::Expr,
1459 input_ident: &syn::Ident,
1460 serialize: Option<&DebugExpr>,
1461 tag_id: StmtId,
1462 ) {
1463 let sender_builder = self.graph_mut(on);
1464 if let Some(serialize_fn) = serialize {
1465 sender_builder.add_dfir(
1466 parse_quote! {
1467 #input_ident -> map(#serialize_fn) -> dest_sink(#sink_expr);
1468 },
1469 None,
1470 Some(&format!("send{}", tag_id)),
1472 );
1473 } else {
1474 sender_builder.add_dfir(
1475 parse_quote! {
1476 #input_ident -> dest_sink(#sink_expr);
1477 },
1478 None,
1479 Some(&format!("send{}", tag_id)),
1480 );
1481 }
1482 }
1483
1484 fn emit_fold_hook(
1485 &mut self,
1486 _location: &LocationId,
1487 _in_ident: &syn::Ident,
1488 _in_kind: &CollectionKind,
1489 _op_meta: &HydroIrOpMetadata,
1490 ) -> Option<syn::Ident> {
1491 None
1492 }
1493
1494 fn assert_is_consistent(
1495 &mut self,
1496 _trusted: bool,
1497 location: &LocationId,
1498 in_ident: syn::Ident,
1499 out_ident: &syn::Ident,
1500 ) {
1501 let builder = self.graph_mut(location);
1502 builder.add_dfir(
1503 parse_quote! {
1504 #out_ident = #in_ident;
1505 },
1506 None,
1507 None,
1508 );
1509 }
1510
1511 fn observe_for_mut(
1512 &mut self,
1513 location: &LocationId,
1514 in_ident: syn::Ident,
1515 _in_kind: &CollectionKind,
1516 out_ident: &syn::Ident,
1517 _op_meta: &HydroIrOpMetadata,
1518 ) {
1519 let builder = self.graph_mut(location);
1520 builder.add_dfir(
1521 parse_quote! {
1522 #out_ident = #in_ident;
1523 },
1524 None,
1525 None,
1526 );
1527 }
1528
1529 fn create_versioned_network_fork(
1530 &mut self,
1531 _channel_id: u32,
1532 _dest: &LocationId,
1533 _senders: Vec<(LocationId, syn::Ident, Option<DebugExpr>)>,
1534 _external_element_type: Option<&syn::Type>,
1535 _tag_id: StmtId,
1536 ) {
1537 unreachable!(
1538 "HydroNode::VersionedNetworkFork is only produced by the multi-version simulator merge \
1539 pass and cannot be emitted by the non-simulation builder"
1540 );
1541 }
1542
1543 fn create_versioned_network(
1544 &mut self,
1545 _channel_id: u32,
1546 _source: &LocationId,
1547 _dest: &LocationId,
1548 _out_ident: &syn::Ident,
1549 _deserialize: Option<&DebugExpr>,
1550 _external_element_type: Option<&syn::Type>,
1551 _tag_id: StmtId,
1552 ) {
1553 unreachable!(
1554 "HydroNode::VersionedNetwork is only produced by the multi-version simulator merge \
1555 pass and cannot be emitted by the non-simulation builder"
1556 );
1557 }
1558}
1559
1560#[cfg(feature = "build")]
1561pub enum BuildersOrCallback<'a, L, N>
1562where
1563 L: FnMut(&mut HydroRoot, &mut crate::Counter<StmtId>),
1564 N: FnMut(&mut HydroNode, &mut crate::Counter<StmtId>),
1565{
1566 Builders(&'a mut dyn DfirBuilder),
1567 Callback(L, N),
1568}
1569
1570#[derive(Debug, Hash, serde::Serialize)]
1574pub enum HydroRoot {
1575 ForEach {
1576 f: ClosureExpr,
1577 input: Box<HydroNode>,
1578 op_metadata: HydroIrOpMetadata,
1579 },
1580 SendExternal {
1581 to_external_key: LocationKey,
1582 to_port_id: ExternalPortId,
1583 to_many: bool,
1584 unpaired: bool,
1585 serialize_fn: Option<DebugExpr>,
1586 instantiate_fn: DebugInstantiate,
1587 input: Box<HydroNode>,
1588 op_metadata: HydroIrOpMetadata,
1589 },
1590 DestSink {
1591 sink: DebugExpr,
1592 input: Box<HydroNode>,
1593 op_metadata: HydroIrOpMetadata,
1594 },
1595 CycleSink {
1596 cycle_id: CycleId,
1597 input: Box<HydroNode>,
1598 op_metadata: HydroIrOpMetadata,
1599 },
1600 EmbeddedOutput {
1601 #[serde(serialize_with = "serialize_ident")]
1602 ident: syn::Ident,
1603 input: Box<HydroNode>,
1604 op_metadata: HydroIrOpMetadata,
1605 },
1606 Null {
1607 input: Box<HydroNode>,
1608 op_metadata: HydroIrOpMetadata,
1609 },
1610}
1611
1612impl HydroRoot {
1613 #[cfg(feature = "build")]
1614 #[expect(clippy::too_many_arguments, reason = "TODO(internal)")]
1615 pub fn compile_network<'a, D>(
1616 &mut self,
1617 extra_stmts: &mut SparseSecondaryMap<LocationKey, Vec<syn::Stmt>>,
1618 seen_tees: &mut SeenSharedNodes,
1619 seen_cluster_members: &mut HashSet<(LocationId, LocationKey)>,
1620 processes: &SparseSecondaryMap<LocationKey, D::Process>,
1621 clusters: &SparseSecondaryMap<LocationKey, D::Cluster>,
1622 externals: &SparseSecondaryMap<LocationKey, D::External>,
1623 env: &mut D::InstantiateEnv,
1624 ) where
1625 D: Deploy<'a>,
1626 {
1627 let refcell_extra_stmts = RefCell::new(extra_stmts);
1628 let refcell_env = RefCell::new(env);
1629 let refcell_seen_cluster_members = RefCell::new(seen_cluster_members);
1630 self.transform_bottom_up(
1631 &mut |l| {
1632 if let HydroRoot::SendExternal {
1633 #[cfg(feature = "tokio")]
1634 input,
1635 #[cfg(feature = "tokio")]
1636 to_external_key,
1637 #[cfg(feature = "tokio")]
1638 to_port_id,
1639 #[cfg(feature = "tokio")]
1640 to_many,
1641 #[cfg(feature = "tokio")]
1642 unpaired,
1643 #[cfg(feature = "tokio")]
1644 instantiate_fn,
1645 ..
1646 } = l
1647 {
1648 #[cfg(feature = "tokio")]
1649 let ((sink_expr, source_expr), connect_fn) = match instantiate_fn {
1650 DebugInstantiate::Building => {
1651 let to_node = externals
1652 .get(*to_external_key)
1653 .unwrap_or_else(|| {
1654 panic!("A external used in the graph was not instantiated: {}", to_external_key)
1655 })
1656 .clone();
1657
1658 match input.metadata().location_id.root() {
1659 &LocationId::Process(process_key) => {
1660 if *to_many {
1661 (
1662 (
1663 D::e2o_many_sink(format!("{}_{}", *to_external_key, *to_port_id)),
1664 parse_quote!(DUMMY),
1665 ),
1666 Box::new(|| {}) as Box<dyn FnOnce()>,
1667 )
1668 } else {
1669 let from_node = processes
1670 .get(process_key)
1671 .unwrap_or_else(|| {
1672 panic!("A process used in the graph was not instantiated: {}", process_key)
1673 })
1674 .clone();
1675
1676 let sink_port = from_node.next_port();
1677 let source_port = to_node.next_port();
1678
1679 if *unpaired {
1680 use stageleft::quote_type;
1681 use tokio_util::codec::LengthDelimitedCodec;
1682
1683 to_node.register(*to_port_id, source_port.clone());
1684
1685 let _ = D::e2o_source(
1686 refcell_extra_stmts.borrow_mut().entry(process_key).expect("location was removed").or_default(),
1687 &to_node, &source_port,
1688 &from_node, &sink_port,
1689 "e_type::<LengthDelimitedCodec>(),
1690 format!("{}_{}", *to_external_key, *to_port_id)
1691 );
1692 }
1693
1694 (
1695 (
1696 D::o2e_sink(
1697 &from_node,
1698 &sink_port,
1699 &to_node,
1700 &source_port,
1701 format!("{}_{}", *to_external_key, *to_port_id)
1702 ),
1703 parse_quote!(DUMMY),
1704 ),
1705 if *unpaired {
1706 D::e2o_connect(
1707 &to_node,
1708 &source_port,
1709 &from_node,
1710 &sink_port,
1711 *to_many,
1712 NetworkHint::Auto,
1713 )
1714 } else {
1715 Box::new(|| {}) as Box<dyn FnOnce()>
1716 },
1717 )
1718 }
1719 }
1720 LocationId::Cluster(cluster_key) => {
1721 let from_node = clusters
1722 .get(*cluster_key)
1723 .unwrap_or_else(|| {
1724 panic!("A cluster used in the graph was not instantiated: {}", cluster_key)
1725 })
1726 .clone();
1727
1728 let sink_port = from_node.next_port();
1729 let source_port = to_node.next_port();
1730
1731 if *unpaired {
1732 to_node.register(*to_port_id, source_port.clone());
1733 }
1734
1735 (
1736 (
1737 D::m2e_sink(
1738 &from_node,
1739 &sink_port,
1740 &to_node,
1741 &source_port,
1742 format!("{}_{}", *to_external_key, *to_port_id)
1743 ),
1744 parse_quote!(DUMMY),
1745 ),
1746 Box::new(|| {}) as Box<dyn FnOnce()>,
1747 )
1748 }
1749 _ => panic!()
1750 }
1751 },
1752
1753 DebugInstantiate::Finalized(_) => panic!("network already finalized"),
1754 };
1755
1756 #[cfg(not(feature = "tokio"))]
1757 {
1758 panic!("Cannot instantiate external inputs without tokio");
1759 };
1760
1761 #[cfg(feature = "tokio")]
1762 {
1763 *instantiate_fn = DebugInstantiateFinalized {
1764 sink: sink_expr,
1765 source: source_expr,
1766 connect_fn: Some(connect_fn),
1767 }
1768 .into();
1769 };
1770 } else if let HydroRoot::EmbeddedOutput { ident, input, .. } = l {
1771 let element_type = match &input.metadata().collection_kind {
1772 CollectionKind::Stream { element_type, .. } => element_type.0.as_ref().clone(),
1773 _ => panic!("Embedded output must have Stream collection kind"),
1774 };
1775 let location_key = match input.metadata().location_id.root() {
1776 LocationId::Process(key) | LocationId::Cluster(key) => *key,
1777 _ => panic!("Embedded output must be on a process or cluster"),
1778 };
1779 D::register_embedded_output(
1780 &mut refcell_env.borrow_mut(),
1781 location_key,
1782 ident,
1783 &element_type,
1784 );
1785 }
1786 },
1787 &mut |n| {
1788 if let HydroNode::Network {
1789 name,
1790 networking_info,
1791 input,
1792 instantiate_fn,
1793 serialize,
1794 deserialize,
1795 metadata,
1796 ..
1797 } = n
1798 {
1799 let external_types = match (
1800 serialize.external_element_type(),
1801 deserialize.external_element_type(),
1802 ) {
1803 (Some(input_type), Some(output_type)) => Some((input_type, output_type)),
1804 _ => None,
1805 };
1806 let (sink_expr, source_expr, connect_fn) = match instantiate_fn {
1807 DebugInstantiate::Building => instantiate_network::<D>(
1808 &mut refcell_env.borrow_mut(),
1809 input.metadata().location_id.root(),
1810 metadata.location_id.root(),
1811 processes,
1812 clusters,
1813 name.as_deref(),
1814 networking_info,
1815 external_types,
1816 ),
1817
1818 DebugInstantiate::Finalized(_) => panic!("network already finalized"),
1819 };
1820
1821 *instantiate_fn = DebugInstantiateFinalized {
1822 sink: sink_expr,
1823 source: source_expr,
1824 connect_fn: Some(connect_fn),
1825 }
1826 .into();
1827 } else if let HydroNode::ExternalInput {
1828 from_external_key,
1829 from_port_id,
1830 from_many,
1831 codec_type,
1832 port_hint,
1833 instantiate_fn,
1834 metadata,
1835 ..
1836 } = n
1837 {
1838 let ((sink_expr, source_expr), connect_fn) = match instantiate_fn {
1839 DebugInstantiate::Building => {
1840 let from_node = externals
1841 .get(*from_external_key)
1842 .unwrap_or_else(|| {
1843 panic!(
1844 "A external used in the graph was not instantiated: {}",
1845 from_external_key,
1846 )
1847 })
1848 .clone();
1849
1850 match metadata.location_id.root() {
1851 &LocationId::Process(process_key) => {
1852 let to_node = processes
1853 .get(process_key)
1854 .unwrap_or_else(|| {
1855 panic!("A process used in the graph was not instantiated: {}", process_key)
1856 })
1857 .clone();
1858
1859 let sink_port = from_node.next_port();
1860 let source_port = to_node.next_port();
1861
1862 from_node.register(*from_port_id, sink_port.clone());
1863
1864 (
1865 (
1866 parse_quote!(DUMMY),
1867 if *from_many {
1868 D::e2o_many_source(
1869 refcell_extra_stmts.borrow_mut().entry(process_key).expect("location was removed").or_default(),
1870 &to_node, &source_port,
1871 codec_type.0.as_ref(),
1872 format!("{}_{}", *from_external_key, *from_port_id)
1873 )
1874 } else {
1875 D::e2o_source(
1876 refcell_extra_stmts.borrow_mut().entry(process_key).expect("location was removed").or_default(),
1877 &from_node, &sink_port,
1878 &to_node, &source_port,
1879 codec_type.0.as_ref(),
1880 format!("{}_{}", *from_external_key, *from_port_id)
1881 )
1882 },
1883 ),
1884 D::e2o_connect(&from_node, &sink_port, &to_node, &source_port, *from_many, *port_hint),
1885 )
1886 }
1887 LocationId::Cluster(cluster_key) => {
1888 let to_node = clusters
1889 .get(*cluster_key)
1890 .unwrap_or_else(|| {
1891 panic!("A cluster used in the graph was not instantiated: {}", cluster_key)
1892 })
1893 .clone();
1894
1895 let sink_port = from_node.next_port();
1896 let source_port = to_node.next_port();
1897
1898 from_node.register(*from_port_id, sink_port.clone());
1899
1900 (
1901 (
1902 parse_quote!(DUMMY),
1903 D::e2m_source(
1904 refcell_extra_stmts.borrow_mut().entry(*cluster_key).expect("location was removed").or_default(),
1905 &from_node, &sink_port,
1906 &to_node, &source_port,
1907 codec_type.0.as_ref(),
1908 format!("{}_{}", *from_external_key, *from_port_id)
1909 ),
1910 ),
1911 D::e2m_connect(&from_node, &sink_port, &to_node, &source_port, *port_hint),
1912 )
1913 }
1914 _ => panic!()
1915 }
1916 },
1917
1918 DebugInstantiate::Finalized(_) => panic!("network already finalized"),
1919 };
1920
1921 *instantiate_fn = DebugInstantiateFinalized {
1922 sink: sink_expr,
1923 source: source_expr,
1924 connect_fn: Some(connect_fn),
1925 }
1926 .into();
1927 } else if let HydroNode::Source { source: HydroSource::Embedded(ident), metadata } = n {
1928 let element_type = match &metadata.collection_kind {
1929 CollectionKind::Stream { element_type, .. } => element_type.0.as_ref().clone(),
1930 _ => panic!("Embedded source must have Stream collection kind"),
1931 };
1932 let location_key = match metadata.location_id.root() {
1933 LocationId::Process(key) | LocationId::Cluster(key) => *key,
1934 _ => panic!("Embedded source must be on a process or cluster"),
1935 };
1936 D::register_embedded_stream_input(
1937 &mut refcell_env.borrow_mut(),
1938 location_key,
1939 ident,
1940 &element_type,
1941 );
1942 } else if let HydroNode::Source { source: HydroSource::EmbeddedSingleton(ident), metadata } = n {
1943 let element_type = match &metadata.collection_kind {
1944 CollectionKind::Singleton { element_type, .. } => element_type.0.as_ref().clone(),
1945 _ => panic!("EmbeddedSingleton source must have Singleton collection kind"),
1946 };
1947 let location_key = match metadata.location_id.root() {
1948 LocationId::Process(key) | LocationId::Cluster(key) => *key,
1949 _ => panic!("EmbeddedSingleton source must be on a process or cluster"),
1950 };
1951 D::register_embedded_singleton_input(
1952 &mut refcell_env.borrow_mut(),
1953 location_key,
1954 ident,
1955 &element_type,
1956 );
1957 } else if let HydroNode::Source { source: HydroSource::ClusterMembers(location_id, state), metadata } = n {
1958 match state {
1959 ClusterMembersState::Uninit => {
1960 let at_location = metadata.location_id.root().clone();
1961 let key = (at_location.clone(), location_id.key());
1962 if refcell_seen_cluster_members.borrow_mut().insert(key) {
1963 let expr = stageleft::QuotedWithContext::splice_untyped_ctx(
1965 D::cluster_membership_stream(&mut refcell_env.borrow_mut(), &at_location, location_id),
1966 &(),
1967 );
1968 *state = ClusterMembersState::Stream(expr.into());
1969 } else {
1970 *state = ClusterMembersState::Tee(at_location, location_id.clone());
1972 }
1973 }
1974 ClusterMembersState::Stream(_) | ClusterMembersState::Tee(..) => {
1975 panic!("cluster members already finalized");
1976 }
1977 }
1978 }
1979 },
1980 seen_tees,
1981 false,
1982 );
1983 }
1984
1985 pub fn connect_network(&mut self, seen_tees: &mut SeenSharedNodes) {
1986 self.transform_bottom_up(
1987 &mut |l| {
1988 if let HydroRoot::SendExternal { instantiate_fn, .. } = l {
1989 match instantiate_fn {
1990 DebugInstantiate::Building => panic!("network not built"),
1991
1992 DebugInstantiate::Finalized(finalized) => {
1993 (finalized.connect_fn.take().unwrap())();
1994 }
1995 }
1996 }
1997 },
1998 &mut |n| {
1999 if let HydroNode::Network { instantiate_fn, .. }
2000 | HydroNode::ExternalInput { instantiate_fn, .. } = n
2001 {
2002 match instantiate_fn {
2003 DebugInstantiate::Building => panic!("network not built"),
2004
2005 DebugInstantiate::Finalized(finalized) => {
2006 (finalized.connect_fn.take().unwrap())();
2007 }
2008 }
2009 }
2010 },
2011 seen_tees,
2012 false,
2013 );
2014 }
2015
2016 pub fn transform_bottom_up(
2017 &mut self,
2018 transform_root: &mut impl FnMut(&mut HydroRoot),
2019 transform_node: &mut impl FnMut(&mut HydroNode),
2020 seen_tees: &mut SeenSharedNodes,
2021 check_well_formed: bool,
2022 ) {
2023 self.transform_children(
2024 |n, s| n.transform_bottom_up(transform_node, s, check_well_formed),
2025 seen_tees,
2026 );
2027
2028 transform_root(self);
2029 }
2030
2031 pub fn transform_children(
2032 &mut self,
2033 mut transform: impl FnMut(&mut HydroNode, &mut SeenSharedNodes),
2034 seen_tees: &mut SeenSharedNodes,
2035 ) {
2036 match self {
2037 HydroRoot::ForEach { f, input, .. } => {
2038 f.transform_children(&mut transform, seen_tees);
2039 transform(input, seen_tees);
2040 }
2041 HydroRoot::SendExternal { input, .. }
2042 | HydroRoot::DestSink { input, .. }
2043 | HydroRoot::CycleSink { input, .. }
2044 | HydroRoot::EmbeddedOutput { input, .. }
2045 | HydroRoot::Null { input, .. } => {
2046 transform(input, seen_tees);
2047 }
2048 }
2049 }
2050
2051 pub fn deep_clone(&self, seen_tees: &mut SeenSharedNodes) -> HydroRoot {
2052 match self {
2053 HydroRoot::ForEach {
2054 f,
2055 input,
2056 op_metadata,
2057 } => HydroRoot::ForEach {
2058 f: f.deep_clone(seen_tees),
2059 input: Box::new(input.deep_clone(seen_tees)),
2060 op_metadata: op_metadata.clone(),
2061 },
2062 HydroRoot::SendExternal {
2063 to_external_key,
2064 to_port_id,
2065 to_many,
2066 unpaired,
2067 serialize_fn,
2068 instantiate_fn,
2069 input,
2070 op_metadata,
2071 } => HydroRoot::SendExternal {
2072 to_external_key: *to_external_key,
2073 to_port_id: *to_port_id,
2074 to_many: *to_many,
2075 unpaired: *unpaired,
2076 serialize_fn: serialize_fn.clone(),
2077 instantiate_fn: instantiate_fn.clone(),
2078 input: Box::new(input.deep_clone(seen_tees)),
2079 op_metadata: op_metadata.clone(),
2080 },
2081 HydroRoot::DestSink {
2082 sink,
2083 input,
2084 op_metadata,
2085 } => HydroRoot::DestSink {
2086 sink: sink.clone(),
2087 input: Box::new(input.deep_clone(seen_tees)),
2088 op_metadata: op_metadata.clone(),
2089 },
2090 HydroRoot::CycleSink {
2091 cycle_id,
2092 input,
2093 op_metadata,
2094 } => HydroRoot::CycleSink {
2095 cycle_id: *cycle_id,
2096 input: Box::new(input.deep_clone(seen_tees)),
2097 op_metadata: op_metadata.clone(),
2098 },
2099 HydroRoot::EmbeddedOutput {
2100 ident,
2101 input,
2102 op_metadata,
2103 } => HydroRoot::EmbeddedOutput {
2104 ident: ident.clone(),
2105 input: Box::new(input.deep_clone(seen_tees)),
2106 op_metadata: op_metadata.clone(),
2107 },
2108 HydroRoot::Null { input, op_metadata } => HydroRoot::Null {
2109 input: Box::new(input.deep_clone(seen_tees)),
2110 op_metadata: op_metadata.clone(),
2111 },
2112 }
2113 }
2114
2115 #[cfg(feature = "build")]
2116 pub fn emit(
2117 &mut self,
2118 graph_builders: &mut dyn DfirBuilder,
2119 seen_tees: &mut SeenSharedNodes,
2120 built_tees: &mut HashMap<*const RefCell<HydroNode>, Vec<syn::Ident>>,
2121 next_stmt_id: &mut crate::Counter<StmtId>,
2122 fold_hooked_idents: &mut HashSet<String>,
2123 ) {
2124 self.emit_core(
2125 &mut BuildersOrCallback::<
2126 fn(&mut HydroRoot, &mut crate::Counter<StmtId>),
2127 fn(&mut HydroNode, &mut crate::Counter<StmtId>),
2128 >::Builders(graph_builders),
2129 seen_tees,
2130 built_tees,
2131 next_stmt_id,
2132 fold_hooked_idents,
2133 );
2134 }
2135
2136 #[cfg(feature = "build")]
2137 pub fn emit_core(
2138 &mut self,
2139 builders_or_callback: &mut BuildersOrCallback<
2140 '_,
2141 impl FnMut(&mut HydroRoot, &mut crate::Counter<StmtId>),
2142 impl FnMut(&mut HydroNode, &mut crate::Counter<StmtId>),
2143 >,
2144 seen_tees: &mut SeenSharedNodes,
2145 built_tees: &mut HashMap<*const RefCell<HydroNode>, Vec<syn::Ident>>,
2146 next_stmt_id: &mut crate::Counter<StmtId>,
2147 fold_hooked_idents: &mut HashSet<String>,
2148 ) {
2149 match self {
2150 HydroRoot::ForEach { f, input, .. } => {
2151 let input_ident = input.emit_core(
2152 builders_or_callback,
2153 seen_tees,
2154 built_tees,
2155 next_stmt_id,
2156 fold_hooked_idents,
2157 );
2158
2159 let input_ident = if !input.metadata().collection_kind.is_strict() {
2163 let observe_stmt_id = next_stmt_id.get_and_increment();
2164 let observe_ident =
2165 syn::Ident::new(&format!("stream_{}", observe_stmt_id), Span::call_site());
2166 if let BuildersOrCallback::Builders(graph_builders) = builders_or_callback {
2167 graph_builders.observe_for_mut(
2168 &input.metadata().location_id,
2169 input_ident,
2170 &input.metadata().collection_kind,
2171 &observe_ident,
2172 &input.metadata().op,
2173 );
2174 }
2175 observe_ident
2176 } else {
2177 input_ident
2178 };
2179
2180 let mut ref_idents = Vec::new();
2187 for (ref_node, _is_mut) in f.singleton_refs.iter_mut() {
2188 assert!(
2189 matches!(ref_node, HydroNode::Reference { .. }),
2190 "singleton_refs should only contain HydroNode::Reference"
2191 );
2192 ref_idents.push(ref_node.emit_core(
2193 builders_or_callback,
2194 seen_tees,
2195 built_tees,
2196 next_stmt_id,
2197 fold_hooked_idents,
2198 ));
2199 }
2200
2201 let stmt_id = next_stmt_id.get_and_increment();
2206
2207 match builders_or_callback {
2208 BuildersOrCallback::Builders(graph_builders) => {
2209 let mut ident_stack: Vec<syn::Ident> = ref_idents;
2212
2213 let f_tokens = f.emit_tokens(&mut ident_stack);
2214
2215 graph_builders.add_dfir_at(
2216 &input.metadata().location_id,
2217 parse_quote! {
2218 #input_ident -> for_each(#f_tokens);
2219 },
2220 Some(&stmt_id.to_string()),
2221 );
2222 }
2223 BuildersOrCallback::Callback(leaf_callback, _) => {
2224 leaf_callback(self, next_stmt_id);
2225 }
2226 }
2227 }
2228
2229 HydroRoot::SendExternal {
2230 serialize_fn,
2231 instantiate_fn,
2232 input,
2233 ..
2234 } => {
2235 let input_ident = input.emit_core(
2236 builders_or_callback,
2237 seen_tees,
2238 built_tees,
2239 next_stmt_id,
2240 fold_hooked_idents,
2241 );
2242
2243 let stmt_id = next_stmt_id.get_and_increment();
2244
2245 match builders_or_callback {
2246 BuildersOrCallback::Builders(graph_builders) => {
2247 let (sink_expr, _) = match instantiate_fn {
2248 DebugInstantiate::Building => (
2249 syn::parse_quote!(DUMMY_SINK),
2250 syn::parse_quote!(DUMMY_SOURCE),
2251 ),
2252
2253 DebugInstantiate::Finalized(finalized) => {
2254 (finalized.sink.clone(), finalized.source.clone())
2255 }
2256 };
2257
2258 graph_builders.create_external_output(
2259 &input.metadata().location_id,
2260 sink_expr,
2261 &input_ident,
2262 serialize_fn.as_ref(),
2263 stmt_id,
2264 );
2265 }
2266 BuildersOrCallback::Callback(leaf_callback, _) => {
2267 leaf_callback(self, next_stmt_id);
2268 }
2269 }
2270 }
2271
2272 HydroRoot::DestSink { sink, input, .. } => {
2273 let input_ident = input.emit_core(
2274 builders_or_callback,
2275 seen_tees,
2276 built_tees,
2277 next_stmt_id,
2278 fold_hooked_idents,
2279 );
2280
2281 let stmt_id = next_stmt_id.get_and_increment();
2282
2283 match builders_or_callback {
2284 BuildersOrCallback::Builders(graph_builders) => {
2285 graph_builders.add_dfir_at(
2286 &input.metadata().location_id,
2287 parse_quote! {
2288 #input_ident -> dest_sink(#sink);
2289 },
2290 Some(&stmt_id.to_string()),
2291 );
2292 }
2293 BuildersOrCallback::Callback(leaf_callback, _) => {
2294 leaf_callback(self, next_stmt_id);
2295 }
2296 }
2297 }
2298
2299 HydroRoot::CycleSink {
2300 cycle_id, input, ..
2301 } => {
2302 let input_ident = input.emit_core(
2303 builders_or_callback,
2304 seen_tees,
2305 built_tees,
2306 next_stmt_id,
2307 fold_hooked_idents,
2308 );
2309
2310 match builders_or_callback {
2311 BuildersOrCallback::Builders(graph_builders) => {
2312 let tombstoned =
2316 update_feed_repr(input.metadata()) == UpdateFeedRepr::Tombstoned;
2317 let elem_type: syn::Type = match &input.metadata().collection_kind {
2318 CollectionKind::KeyedSingleton {
2319 key_type,
2320 value_type,
2321 ..
2322 } if tombstoned => {
2323 parse_quote!((#key_type, ::std::option::Option<#value_type>))
2324 }
2325 CollectionKind::KeyedSingleton {
2326 key_type,
2327 value_type,
2328 ..
2329 }
2330 | CollectionKind::KeyedStream {
2331 key_type,
2332 value_type,
2333 ..
2334 } => {
2335 parse_quote!((#key_type, #value_type))
2336 }
2337 CollectionKind::Optional { element_type, .. } if tombstoned => {
2338 parse_quote!(::std::option::Option<#element_type>)
2339 }
2340 CollectionKind::Stream { element_type, .. }
2341 | CollectionKind::Singleton { element_type, .. }
2342 | CollectionKind::Optional { element_type, .. } => {
2343 parse_quote!(#element_type)
2344 }
2345 };
2346
2347 let cycle_id_ident = cycle_id.as_ident();
2348 graph_builders.add_dfir_at(
2349 &input.metadata().location_id,
2350 parse_quote! {
2351 #cycle_id_ident = #input_ident -> identity::<#elem_type>();
2352 },
2353 None,
2354 );
2355 }
2356 BuildersOrCallback::Callback(_, _) => {}
2358 }
2359 }
2360
2361 HydroRoot::EmbeddedOutput { ident, input, .. } => {
2362 let input_ident = input.emit_core(
2363 builders_or_callback,
2364 seen_tees,
2365 built_tees,
2366 next_stmt_id,
2367 fold_hooked_idents,
2368 );
2369
2370 let stmt_id = next_stmt_id.get_and_increment();
2371
2372 match builders_or_callback {
2373 BuildersOrCallback::Builders(graph_builders) => {
2374 graph_builders.add_dfir_at(
2375 &input.metadata().location_id,
2376 parse_quote! {
2377 #input_ident -> for_each(&mut #ident);
2378 },
2379 Some(&stmt_id.to_string()),
2380 );
2381 }
2382 BuildersOrCallback::Callback(leaf_callback, _) => {
2383 leaf_callback(self, next_stmt_id);
2384 }
2385 }
2386 }
2387
2388 HydroRoot::Null { input, .. } => {
2389 let input_ident = input.emit_core(
2390 builders_or_callback,
2391 seen_tees,
2392 built_tees,
2393 next_stmt_id,
2394 fold_hooked_idents,
2395 );
2396
2397 let stmt_id = next_stmt_id.get_and_increment();
2398
2399 match builders_or_callback {
2400 BuildersOrCallback::Builders(graph_builders) => {
2401 graph_builders.add_dfir_at(
2402 &input.metadata().location_id,
2403 parse_quote! {
2404 #input_ident -> for_each(|_| {});
2405 },
2406 Some(&stmt_id.to_string()),
2407 );
2408 }
2409 BuildersOrCallback::Callback(leaf_callback, _) => {
2410 leaf_callback(self, next_stmt_id);
2411 }
2412 }
2413 }
2414 }
2415 }
2416
2417 pub fn op_metadata(&self) -> &HydroIrOpMetadata {
2418 match self {
2419 HydroRoot::ForEach { op_metadata, .. }
2420 | HydroRoot::SendExternal { op_metadata, .. }
2421 | HydroRoot::DestSink { op_metadata, .. }
2422 | HydroRoot::CycleSink { op_metadata, .. }
2423 | HydroRoot::EmbeddedOutput { op_metadata, .. }
2424 | HydroRoot::Null { op_metadata, .. } => op_metadata,
2425 }
2426 }
2427
2428 pub fn op_metadata_mut(&mut self) -> &mut HydroIrOpMetadata {
2429 match self {
2430 HydroRoot::ForEach { op_metadata, .. }
2431 | HydroRoot::SendExternal { op_metadata, .. }
2432 | HydroRoot::DestSink { op_metadata, .. }
2433 | HydroRoot::CycleSink { op_metadata, .. }
2434 | HydroRoot::EmbeddedOutput { op_metadata, .. }
2435 | HydroRoot::Null { op_metadata, .. } => op_metadata,
2436 }
2437 }
2438
2439 pub fn input(&self) -> &HydroNode {
2440 match self {
2441 HydroRoot::ForEach { input, .. }
2442 | HydroRoot::SendExternal { input, .. }
2443 | HydroRoot::DestSink { input, .. }
2444 | HydroRoot::CycleSink { input, .. }
2445 | HydroRoot::EmbeddedOutput { input, .. }
2446 | HydroRoot::Null { input, .. } => input,
2447 }
2448 }
2449
2450 pub fn input_metadata(&self) -> &HydroIrMetadata {
2451 self.input().metadata()
2452 }
2453
2454 pub fn print_root(&self) -> String {
2455 match self {
2456 HydroRoot::ForEach { f, .. } => format!("ForEach({:?})", f),
2457 HydroRoot::SendExternal { .. } => "SendExternal".to_owned(),
2458 HydroRoot::DestSink { sink, .. } => format!("DestSink({:?})", sink),
2459 HydroRoot::CycleSink { cycle_id, .. } => format!("CycleSink({})", cycle_id),
2460 HydroRoot::EmbeddedOutput { ident, .. } => {
2461 format!("EmbeddedOutput({})", ident)
2462 }
2463 HydroRoot::Null { .. } => "Null".to_owned(),
2464 }
2465 }
2466
2467 pub fn visit_debug_expr(&mut self, mut transform: impl FnMut(&mut DebugExpr)) {
2468 match self {
2469 HydroRoot::ForEach { f, .. } => {
2470 transform(&mut f.expr);
2471 }
2472 HydroRoot::DestSink { sink, .. } => {
2473 transform(sink);
2474 }
2475 HydroRoot::SendExternal { .. }
2476 | HydroRoot::CycleSink { .. }
2477 | HydroRoot::EmbeddedOutput { .. }
2478 | HydroRoot::Null { .. } => {}
2479 }
2480 }
2481}
2482
2483#[cfg(feature = "build")]
2489fn tick_location_of(loc: &LocationId) -> Option<&LocationId> {
2490 match loc {
2491 LocationId::Tick {
2493 tick: Some(_),
2494 parent_location: _,
2495 } => Some(loc),
2496 LocationId::Tick {
2498 tick: None,
2499 parent_location,
2500 } => Some(
2501 tick_location_of(parent_location)
2502 .expect("Tick should have either own clock ID or clock ID within parent_location."),
2503 ),
2504 LocationId::Atomic(inner) => tick_location_of(inner),
2505 LocationId::Process(_) | LocationId::Cluster(_) => None,
2506 }
2507}
2508
2509#[cfg(feature = "build")]
2512fn tick_of(loc: &LocationId) -> Option<ClockId> {
2513 tick_location_of(loc).map(|tick_location| match tick_location {
2514 LocationId::Tick {
2515 tick: Some(tick), ..
2516 } => *tick,
2517 _ => unreachable!("`tick_location_of` only returns ticks with a clock ID"),
2518 })
2519}
2520
2521#[cfg(feature = "build")]
2522fn remap_location(loc: &mut LocationId, uf: &mut HashMap<ClockId, ClockId>) {
2523 match loc {
2524 LocationId::Tick {
2525 tick,
2526 parent_location,
2527 } => {
2528 if let Some(tick) = tick {
2529 *tick = uf_find(uf, *tick);
2530 }
2531 remap_location(parent_location, uf);
2532 }
2533 LocationId::Atomic(inner) => {
2534 remap_location(inner, uf);
2535 }
2536 LocationId::Process(_) | LocationId::Cluster(_) => {}
2537 }
2538}
2539
2540#[cfg(feature = "build")]
2541fn uf_find(parent: &mut HashMap<ClockId, ClockId>, x: ClockId) -> ClockId {
2542 let p = *parent.get(&x).unwrap_or(&x);
2543 if p == x {
2544 return x;
2545 }
2546 let root = uf_find(parent, p);
2547 parent.insert(x, root);
2548 root
2549}
2550
2551#[cfg(feature = "build")]
2552fn uf_union(parent: &mut HashMap<ClockId, ClockId>, a: ClockId, b: ClockId) {
2553 let ra = uf_find(parent, a);
2554 let rb = uf_find(parent, b);
2555 if ra != rb {
2556 parent.insert(ra, rb);
2557 }
2558}
2559
2560#[cfg(feature = "build")]
2564pub fn unify_atomic_ticks(ir: &mut [HydroRoot]) {
2565 let mut uf: HashMap<ClockId, ClockId> = HashMap::new();
2566
2567 transform_bottom_up(
2569 ir,
2570 &mut |_| {},
2571 &mut |node: &mut HydroNode| match node {
2572 HydroNode::Batch { inner, metadata }
2573 | HydroNode::Snapshot { inner, metadata }
2574 | HydroNode::YieldConcat { inner, metadata } => {
2575 if let (Some(a), Some(b)) = (
2576 tick_of(&inner.metadata().location_id),
2577 tick_of(&metadata.location_id),
2578 ) {
2579 uf_union(&mut uf, a, b);
2580 }
2581 }
2582 HydroNode::Chain {
2583 first,
2584 second,
2585 metadata,
2586 }
2587 | HydroNode::ChainFirst {
2588 first,
2589 second,
2590 metadata,
2591 }
2592 | HydroNode::MergeOrdered {
2593 first,
2594 second,
2595 metadata,
2596 } => {
2597 if let (Some(a), Some(b)) = (
2598 tick_of(&first.metadata().location_id),
2599 tick_of(&metadata.location_id),
2600 ) {
2601 uf_union(&mut uf, a, b);
2602 }
2603 if let (Some(a), Some(b)) = (
2604 tick_of(&second.metadata().location_id),
2605 tick_of(&metadata.location_id),
2606 ) {
2607 uf_union(&mut uf, a, b);
2608 }
2609 }
2610 _ => {}
2611 },
2612 false,
2613 );
2614
2615 transform_bottom_up(
2617 ir,
2618 &mut |_| {},
2619 &mut |node: &mut HydroNode| {
2620 remap_location(&mut node.metadata_mut().location_id, &mut uf);
2621 },
2622 false,
2623 );
2624}
2625
2626#[cfg(feature = "build")]
2627pub fn emit(ir: &mut Vec<HydroRoot>) -> SecondaryMap<LocationKey, FlatGraphBuilder> {
2628 let mut builders = ProdDfirBuilder::default();
2629 let mut seen_tees = HashMap::new();
2630 let mut built_tees = HashMap::new();
2631 let mut next_stmt_id = crate::Counter::<StmtId>::default();
2632 let mut fold_hooked_idents = HashSet::new();
2633 for leaf in ir {
2634 leaf.emit(
2635 &mut builders,
2636 &mut seen_tees,
2637 &mut built_tees,
2638 &mut next_stmt_id,
2639 &mut fold_hooked_idents,
2640 );
2641 }
2642 builders.graphs
2643}
2644
2645#[cfg(feature = "build")]
2646pub fn traverse_dfir(
2647 ir: &mut [HydroRoot],
2648 transform_root: impl FnMut(&mut HydroRoot, &mut crate::Counter<StmtId>),
2649 transform_node: impl FnMut(&mut HydroNode, &mut crate::Counter<StmtId>),
2650) {
2651 let mut seen_tees = HashMap::new();
2652 let mut built_tees = HashMap::new();
2653 let mut next_stmt_id = crate::Counter::<StmtId>::default();
2654 let mut fold_hooked_idents = HashSet::new();
2655 let mut callback = BuildersOrCallback::Callback(transform_root, transform_node);
2656 ir.iter_mut().for_each(|leaf| {
2657 leaf.emit_core(
2658 &mut callback,
2659 &mut seen_tees,
2660 &mut built_tees,
2661 &mut next_stmt_id,
2662 &mut fold_hooked_idents,
2663 );
2664 });
2665}
2666
2667pub fn transform_bottom_up(
2668 ir: &mut [HydroRoot],
2669 transform_root: &mut impl FnMut(&mut HydroRoot),
2670 transform_node: &mut impl FnMut(&mut HydroNode),
2671 check_well_formed: bool,
2672) {
2673 let mut seen_tees = HashMap::new();
2674 ir.iter_mut().for_each(|leaf| {
2675 leaf.transform_bottom_up(
2676 transform_root,
2677 transform_node,
2678 &mut seen_tees,
2679 check_well_formed,
2680 );
2681 });
2682}
2683
2684pub fn deep_clone(ir: &[HydroRoot]) -> Vec<HydroRoot> {
2685 let mut seen_tees = HashMap::new();
2686 ir.iter()
2687 .map(|leaf| leaf.deep_clone(&mut seen_tees))
2688 .collect()
2689}
2690
2691type PrintedTees = RefCell<Option<(usize, HashMap<*const RefCell<HydroNode>, usize>)>>;
2692thread_local! {
2693 static PRINTED_TEES: PrintedTees = const { RefCell::new(None) };
2694 static SERIALIZED_SHARED: PrintedTees
2698 = const { RefCell::new(None) };
2699}
2700
2701pub fn dbg_dedup_tee<T>(f: impl FnOnce() -> T) -> T {
2702 PRINTED_TEES.with(|printed_tees| {
2703 let mut printed_tees_mut = printed_tees.borrow_mut();
2704 *printed_tees_mut = Some((0, HashMap::new()));
2705 drop(printed_tees_mut);
2706
2707 let ret = f();
2708
2709 let mut printed_tees_mut = printed_tees.borrow_mut();
2710 *printed_tees_mut = None;
2711
2712 ret
2713 })
2714}
2715
2716pub fn serialize_dedup_shared<T>(f: impl FnOnce() -> T) -> T {
2721 let _guard = SerializedSharedGuard::enter();
2722 f()
2723}
2724
2725struct SerializedSharedGuard {
2728 previous: Option<(usize, HashMap<*const RefCell<HydroNode>, usize>)>,
2729}
2730
2731impl SerializedSharedGuard {
2732 fn enter() -> Self {
2733 let previous = SERIALIZED_SHARED.with(|cell| {
2734 let mut guard = cell.borrow_mut();
2735 guard.replace((0, HashMap::new()))
2736 });
2737 Self { previous }
2738 }
2739}
2740
2741impl Drop for SerializedSharedGuard {
2742 fn drop(&mut self) {
2743 SERIALIZED_SHARED.with(|cell| {
2744 *cell.borrow_mut() = self.previous.take();
2745 });
2746 }
2747}
2748
2749pub struct SharedNode(pub Rc<RefCell<HydroNode>>);
2750
2751impl serde::Serialize for SharedNode {
2752 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
2763 SERIALIZED_SHARED.with(|cell| {
2764 let mut guard = cell.borrow_mut();
2765 let state = guard.as_mut().ok_or_else(|| {
2767 serde::ser::Error::custom(
2768 "SharedNode serialization requires an active serialize_dedup_shared scope",
2769 )
2770 })?;
2771 let ptr = self.0.as_ptr() as *const RefCell<HydroNode>;
2772
2773 if let Some(&id) = state.1.get(&ptr) {
2774 drop(guard);
2775 use serde::ser::SerializeMap;
2776 let mut map = serializer.serialize_map(Some(1))?;
2777 map.serialize_entry("$shared_ref", &id)?;
2778 map.end()
2779 } else {
2780 let id = state.0;
2781 state.0 += 1;
2782 state.1.insert(ptr, id);
2783 drop(guard);
2784
2785 use serde::ser::SerializeMap;
2786 let mut map = serializer.serialize_map(Some(2))?;
2787 map.serialize_entry("$shared", &id)?;
2788 map.serialize_entry("node", &*self.0.borrow())?;
2789 map.end()
2790 }
2791 })
2792 }
2793}
2794
2795impl SharedNode {
2796 pub fn as_ptr(&self) -> *const RefCell<HydroNode> {
2797 Rc::as_ptr(&self.0)
2798 }
2799}
2800
2801impl Debug for SharedNode {
2802 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2803 PRINTED_TEES.with(|printed_tees| {
2804 let mut printed_tees_mut_borrow = printed_tees.borrow_mut();
2805 let printed_tees_mut = printed_tees_mut_borrow.as_mut();
2806
2807 if let Some(printed_tees_mut) = printed_tees_mut {
2808 if let Some(existing) = printed_tees_mut
2809 .1
2810 .get(&(std::ptr::from_ref(self.0.as_ref())))
2811 {
2812 write!(f, "<shared {}>", existing)
2813 } else {
2814 let next_id = printed_tees_mut.0;
2815 printed_tees_mut.0 += 1;
2816 printed_tees_mut
2817 .1
2818 .insert(std::ptr::from_ref(self.0.as_ref()), next_id);
2819 drop(printed_tees_mut_borrow);
2820 write!(f, "<shared {}>: ", next_id)?;
2821 Debug::fmt(&self.0.borrow(), f)
2822 }
2823 } else {
2824 drop(printed_tees_mut_borrow);
2825 write!(f, "<shared>: ")?;
2826 Debug::fmt(&self.0.borrow(), f)
2827 }
2828 })
2829 }
2830}
2831
2832impl Hash for SharedNode {
2833 fn hash<H: Hasher>(&self, state: &mut H) {
2834 self.0.borrow_mut().hash(state);
2835 }
2836}
2837
2838#[derive(Debug)]
2843pub enum AccessCounter {
2844 Counting(Cell<u32>),
2845 Frozen(u32),
2846}
2847
2848impl AccessCounter {
2849 pub fn new() -> Self {
2850 Self::Counting(Cell::new(0))
2851 }
2852
2853 pub fn next_group(&self, is_mut: bool) -> Self {
2857 let AccessCounter::Counting(count) = self else {
2858 panic!("Cannot count on `AccessCounter::Frozen`");
2859 };
2860 let c = if is_mut {
2861 let c = count.get() + 1;
2862 count.set(c + 1);
2863 c
2864 } else {
2865 count.get()
2866 };
2867 Self::Frozen(c)
2868 }
2869
2870 pub fn freeze(&self) -> Self {
2872 Self::Frozen(match self {
2873 Self::Counting(count) => count.get(),
2874 Self::Frozen(count) => *count,
2875 })
2876 }
2877
2878 pub fn frozen_group(&self) -> u32 {
2879 let Self::Frozen(count) = self else {
2880 panic!("`AccessCounter` not frozen");
2881 };
2882 *count
2883 }
2884}
2885
2886impl Default for AccessCounter {
2887 fn default() -> Self {
2888 Self::new()
2889 }
2890}
2891
2892impl Hash for AccessCounter {
2893 fn hash<H: Hasher>(&self, _state: &mut H) {
2894 }
2896}
2897
2898impl serde::Serialize for AccessCounter {
2899 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
2900 let count = match self {
2901 AccessCounter::Counting(count) => count.get(),
2902 AccessCounter::Frozen(count) => *count,
2903 };
2904 count.serialize(serializer)
2905 }
2906}
2907
2908#[derive(serde::Serialize, Clone, PartialEq, Eq, Debug)]
2909pub enum BoundKind {
2910 Unbounded,
2911 Bounded,
2912}
2913
2914#[derive(serde::Serialize, Clone, PartialEq, Eq, Debug)]
2915pub enum OptionalBoundKind {
2916 Unbounded,
2917 InitNone,
2920 Bounded,
2921}
2922
2923#[derive(serde::Serialize, Clone, PartialEq, Eq, Hash, Debug)]
2924pub enum StreamOrder {
2925 NoOrder,
2926 TotalOrder,
2927}
2928
2929#[derive(serde::Serialize, Clone, PartialEq, Eq, Debug)]
2930pub enum StreamRetry {
2931 AtLeastOnce,
2932 ExactlyOnce,
2933}
2934
2935#[derive(serde::Serialize, Clone, PartialEq, Eq, Debug)]
2936pub enum KeyedSingletonBoundKind {
2937 Unbounded,
2938 MonotonicKeys,
2939 MonotonicValue,
2940 BoundedValue,
2941 Bounded,
2942}
2943
2944#[derive(serde::Serialize, Clone, PartialEq, Eq, Debug)]
2945pub enum SingletonBoundKind {
2946 Unbounded,
2947 Monotonic,
2948 Bounded,
2949}
2950
2951#[derive(Clone, PartialEq, Eq, Debug, serde::Serialize)]
2952pub enum CollectionKind {
2953 Stream {
2954 bound: BoundKind,
2955 order: StreamOrder,
2956 retry: StreamRetry,
2957 element_type: DebugType,
2958 },
2959 Singleton {
2960 bound: SingletonBoundKind,
2961 element_type: DebugType,
2962 },
2963 Optional {
2964 bound: OptionalBoundKind,
2965 element_type: DebugType,
2966 },
2967 KeyedStream {
2968 bound: BoundKind,
2969 value_order: StreamOrder,
2970 value_retry: StreamRetry,
2971 key_type: DebugType,
2972 value_type: DebugType,
2973 },
2974 KeyedSingleton {
2975 bound: KeyedSingletonBoundKind,
2976 key_type: DebugType,
2977 value_type: DebugType,
2978 },
2979}
2980
2981impl CollectionKind {
2982 pub fn is_bounded(&self) -> bool {
2983 matches!(
2984 self,
2985 CollectionKind::Stream {
2986 bound: BoundKind::Bounded,
2987 ..
2988 } | CollectionKind::Singleton {
2989 bound: SingletonBoundKind::Bounded,
2990 ..
2991 } | CollectionKind::Optional {
2992 bound: OptionalBoundKind::Bounded,
2993 ..
2994 } | CollectionKind::KeyedStream {
2995 bound: BoundKind::Bounded,
2996 ..
2997 } | CollectionKind::KeyedSingleton {
2998 bound: KeyedSingletonBoundKind::Bounded,
2999 ..
3000 }
3001 )
3002 }
3003
3004 pub fn is_strict(&self) -> bool {
3007 match self {
3008 CollectionKind::Stream { order, retry, .. } => {
3009 *order == StreamOrder::TotalOrder && *retry == StreamRetry::ExactlyOnce
3010 }
3011 CollectionKind::KeyedStream {
3012 value_order,
3013 value_retry,
3014 ..
3015 } => {
3016 *value_order == StreamOrder::TotalOrder && *value_retry == StreamRetry::ExactlyOnce
3017 }
3018 CollectionKind::Singleton { .. }
3021 | CollectionKind::Optional { .. }
3022 | CollectionKind::KeyedSingleton { .. } => true,
3023 }
3024 }
3025
3026 pub fn root_repr_is_tombstoned_update_feed(&self) -> bool {
3052 matches!(
3053 self,
3054 CollectionKind::Optional {
3055 bound: OptionalBoundKind::Unbounded,
3056 ..
3057 } | CollectionKind::KeyedSingleton {
3058 bound: KeyedSingletonBoundKind::Unbounded,
3059 ..
3060 }
3061 )
3062 }
3063
3064 pub fn strict_kind(&self) -> CollectionKind {
3066 match self {
3067 CollectionKind::Stream {
3068 bound,
3069 element_type,
3070 ..
3071 } => CollectionKind::Stream {
3072 bound: bound.clone(),
3073 order: StreamOrder::TotalOrder,
3074 retry: StreamRetry::ExactlyOnce,
3075 element_type: element_type.clone(),
3076 },
3077 CollectionKind::KeyedStream {
3078 bound,
3079 key_type,
3080 value_type,
3081 ..
3082 } => CollectionKind::KeyedStream {
3083 bound: bound.clone(),
3084 value_order: StreamOrder::TotalOrder,
3085 value_retry: StreamRetry::ExactlyOnce,
3086 key_type: key_type.clone(),
3087 value_type: value_type.clone(),
3088 },
3089 other => other.clone(),
3090 }
3091 }
3092}
3093
3094#[derive(Clone, serde::Serialize)]
3095pub struct HydroIrMetadata {
3096 pub location_id: LocationId,
3097 pub collection_kind: CollectionKind,
3098 pub consistency: Option<ClusterConsistency>,
3099 pub cardinality: Option<usize>,
3100 pub tag: Option<String>,
3101 pub op: HydroIrOpMetadata,
3102}
3103
3104impl Hash for HydroIrMetadata {
3106 fn hash<H: Hasher>(&self, _: &mut H) {}
3107}
3108
3109impl PartialEq for HydroIrMetadata {
3110 fn eq(&self, _: &Self) -> bool {
3111 true
3112 }
3113}
3114
3115impl Eq for HydroIrMetadata {}
3116
3117impl Debug for HydroIrMetadata {
3118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3119 f.debug_struct("HydroIrMetadata")
3120 .field("location_id", &self.location_id)
3121 .field("collection_kind", &self.collection_kind)
3122 .finish()
3123 }
3124}
3125
3126#[derive(Clone, serde::Serialize)]
3129pub struct HydroIrOpMetadata {
3130 #[serde(rename = "span", serialize_with = "serialize_backtrace_as_span")]
3131 pub backtrace: Backtrace,
3132 pub cpu_usage: Option<f64>,
3133 pub network_recv_cpu_usage: Option<f64>,
3134 pub id: Option<usize>,
3135 #[serde(skip)]
3139 pub sim_hook_id: Option<usize>,
3140}
3141
3142impl HydroIrOpMetadata {
3143 #[expect(
3144 clippy::new_without_default,
3145 reason = "explicit calls to new ensure correct backtrace bounds"
3146 )]
3147 pub fn new() -> HydroIrOpMetadata {
3148 Self::new_with_skip(1)
3149 }
3150
3151 fn new_with_skip(skip_count: usize) -> HydroIrOpMetadata {
3152 HydroIrOpMetadata {
3153 backtrace: Backtrace::get_backtrace(2 + skip_count),
3154 cpu_usage: None,
3155 network_recv_cpu_usage: None,
3156 id: None,
3157 sim_hook_id: None,
3158 }
3159 }
3160}
3161
3162impl Debug for HydroIrOpMetadata {
3163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3164 f.debug_struct("HydroIrOpMetadata").finish()
3165 }
3166}
3167
3168impl Hash for HydroIrOpMetadata {
3169 fn hash<H: Hasher>(&self, _: &mut H) {}
3170}
3171
3172#[derive(Debug, Clone, Hash, serde::Serialize)]
3178pub enum NetworkSend {
3179 Custom { serialize_fn: Option<DebugExpr> },
3182 Embedded {
3190 tag: Option<DebugType>,
3191 element_type: DebugType,
3192 },
3193}
3194
3195#[derive(Debug, Clone, Hash, serde::Serialize)]
3198pub enum NetworkRecv {
3199 Custom { deserialize_fn: Option<DebugExpr> },
3202 Embedded {
3209 tag: Option<DebugType>,
3210 element_type: DebugType,
3211 },
3212}
3213
3214#[cfg(feature = "build")]
3215impl NetworkSend {
3216 pub(crate) fn external_element_type(&self) -> Option<&syn::Type> {
3219 match self {
3220 NetworkSend::Custom { .. } => None,
3221 NetworkSend::Embedded { element_type, .. } => Some(&element_type.0),
3222 }
3223 }
3224}
3225
3226#[cfg(feature = "build")]
3227impl NetworkRecv {
3228 pub(crate) fn external_element_type(&self) -> Option<&syn::Type> {
3230 match self {
3231 NetworkRecv::Custom { .. } => None,
3232 NetworkRecv::Embedded { element_type, .. } => Some(&element_type.0),
3233 }
3234 }
3235}
3236
3237#[cfg(feature = "build")]
3238impl NetworkSend {
3239 pub(crate) fn pipeline(&self) -> Option<DebugExpr> {
3241 match self {
3242 NetworkSend::Custom { serialize_fn } => serialize_fn.clone(),
3243 NetworkSend::Embedded { tag, element_type } => {
3244 let root = crate::staging_util::get_this_crate();
3245 let element_type = &element_type.0;
3246 let expr: syn::Expr = if let Some(tag) = tag {
3247 let tag = &tag.0;
3248 parse_quote! {
3249 #root::runtime_support::stageleft::runtime_support::fn1_type_hint::<(#root::__staged::location::MemberId<#tag>, #element_type), _>(
3250 |(id, data)| (id.into_tagless(), data)
3251 )
3252 }
3253 } else {
3254 parse_quote! {
3255 #root::runtime_support::stageleft::runtime_support::fn1_type_hint::<#element_type, _>(
3256 |data| data
3257 )
3258 }
3259 };
3260 Some(expr.into())
3261 }
3262 }
3263 }
3264}
3265
3266#[cfg(feature = "build")]
3267impl NetworkRecv {
3268 pub(crate) fn pipeline(&self) -> Option<DebugExpr> {
3270 match self {
3271 NetworkRecv::Custom { deserialize_fn } => deserialize_fn.clone(),
3272 NetworkRecv::Embedded { tag, .. } => {
3277 let tag = tag.as_ref()?;
3278 let root = crate::staging_util::get_this_crate();
3279 let tag = &tag.0;
3280 let expr: syn::Expr = parse_quote! {
3281 |(id, b)| (#root::__staged::location::MemberId::<#tag>::from_tagless(id as #root::__staged::location::TaglessMemberId), b)
3282 };
3283 Some(expr.into())
3284 }
3285 }
3286 }
3287}
3288
3289#[derive(Debug, Hash, serde::Serialize)]
3292pub enum HydroNode {
3293 Placeholder,
3294
3295 Cast {
3303 inner: Box<HydroNode>,
3304 metadata: HydroIrMetadata,
3305 },
3306
3307 ObserveNonDet {
3313 inner: Box<HydroNode>,
3314 trusted: bool, metadata: HydroIrMetadata,
3316 },
3317
3318 Source {
3319 source: HydroSource,
3320 metadata: HydroIrMetadata,
3321 },
3322
3323 SingletonSource {
3324 value: DebugExpr,
3325 first_tick_only: bool,
3326 metadata: HydroIrMetadata,
3327 },
3328
3329 CycleSource {
3330 cycle_id: CycleId,
3331 metadata: HydroIrMetadata,
3332 },
3333
3334 Tee {
3335 inner: SharedNode,
3336 metadata: HydroIrMetadata,
3337 },
3338
3339 Reference {
3348 inner: SharedNode,
3349 kind: crate::handoff_ref::HandoffRefKind,
3350 access_counter: AccessCounter,
3351 metadata: HydroIrMetadata,
3352 },
3353
3354 PartitionSide {
3356 inner: SharedNode,
3357 is_true: bool,
3358 metadata: HydroIrMetadata,
3359 },
3360
3361 PartitionShared {
3363 input: Box<HydroNode>,
3364 f: ClosureExpr,
3365 metadata: HydroIrMetadata,
3366 },
3367
3368 BeginAtomic {
3369 inner: Box<HydroNode>,
3370 metadata: HydroIrMetadata,
3371 },
3372
3373 EndAtomic {
3374 inner: Box<HydroNode>,
3375 metadata: HydroIrMetadata,
3376 },
3377
3378 Batch {
3385 inner: Box<HydroNode>,
3386 metadata: HydroIrMetadata,
3387 },
3388
3389 Snapshot {
3397 inner: Box<HydroNode>,
3398 metadata: HydroIrMetadata,
3399 },
3400
3401 YieldConcat {
3402 inner: Box<HydroNode>,
3403 metadata: HydroIrMetadata,
3404 },
3405
3406 Chain {
3407 first: Box<HydroNode>,
3408 second: Box<HydroNode>,
3409 metadata: HydroIrMetadata,
3410 },
3411
3412 MergeOrdered {
3413 first: Box<HydroNode>,
3414 second: Box<HydroNode>,
3415 metadata: HydroIrMetadata,
3416 },
3417
3418 ChainFirst {
3419 first: Box<HydroNode>,
3420 second: Box<HydroNode>,
3421 metadata: HydroIrMetadata,
3422 },
3423
3424 CrossProduct {
3425 left: Box<HydroNode>,
3426 right: Box<HydroNode>,
3427 metadata: HydroIrMetadata,
3428 },
3429
3430 CrossSingleton {
3431 left: Box<HydroNode>,
3432 right: Box<HydroNode>,
3433 metadata: HydroIrMetadata,
3434 },
3435
3436 Join {
3437 left: Box<HydroNode>,
3438 right: Box<HydroNode>,
3439 metadata: HydroIrMetadata,
3440 },
3441
3442 JoinHalf {
3446 left: Box<HydroNode>,
3447 right: Box<HydroNode>,
3448 metadata: HydroIrMetadata,
3449 },
3450
3451 Difference {
3452 pos: Box<HydroNode>,
3453 neg: Box<HydroNode>,
3454 metadata: HydroIrMetadata,
3455 },
3456
3457 AntiJoin {
3458 pos: Box<HydroNode>,
3459 neg: Box<HydroNode>,
3460 metadata: HydroIrMetadata,
3461 },
3462
3463 ResolveFutures {
3464 input: Box<HydroNode>,
3465 metadata: HydroIrMetadata,
3466 },
3467 ResolveFuturesBlocking {
3468 input: Box<HydroNode>,
3469 metadata: HydroIrMetadata,
3470 },
3471 ResolveFuturesOrdered {
3472 input: Box<HydroNode>,
3473 metadata: HydroIrMetadata,
3474 },
3475
3476 Map {
3477 f: ClosureExpr,
3478 input: Box<HydroNode>,
3479 metadata: HydroIrMetadata,
3480 },
3481 FlatMap {
3482 f: ClosureExpr,
3483 input: Box<HydroNode>,
3484 metadata: HydroIrMetadata,
3485 },
3486 FlatMapStreamBlocking {
3487 f: ClosureExpr,
3488 input: Box<HydroNode>,
3489 metadata: HydroIrMetadata,
3490 },
3491 Filter {
3492 f: ClosureExpr,
3493 input: Box<HydroNode>,
3494 metadata: HydroIrMetadata,
3495 },
3496 FilterMap {
3497 f: ClosureExpr,
3498 input: Box<HydroNode>,
3499 metadata: HydroIrMetadata,
3500 },
3501
3502 DeferTick {
3503 input: Box<HydroNode>,
3504 metadata: HydroIrMetadata,
3505 },
3506 Enumerate {
3507 input: Box<HydroNode>,
3508 metadata: HydroIrMetadata,
3509 },
3510 Inspect {
3511 f: ClosureExpr,
3512 input: Box<HydroNode>,
3513 metadata: HydroIrMetadata,
3514 },
3515
3516 Unique {
3517 input: Box<HydroNode>,
3518 metadata: HydroIrMetadata,
3519 },
3520
3521 Sort {
3522 input: Box<HydroNode>,
3523 metadata: HydroIrMetadata,
3524 },
3525 Fold {
3526 init: ClosureExpr,
3527 acc: ClosureExpr,
3528 input: Box<HydroNode>,
3529 metadata: HydroIrMetadata,
3530 },
3531
3532 Scan {
3533 init: ClosureExpr,
3534 acc: ClosureExpr,
3535 input: Box<HydroNode>,
3536 metadata: HydroIrMetadata,
3537 },
3538 ScanAsyncBlocking {
3539 init: ClosureExpr,
3540 acc: ClosureExpr,
3541 input: Box<HydroNode>,
3542 metadata: HydroIrMetadata,
3543 },
3544 FoldKeyed {
3545 init: ClosureExpr,
3546 acc: ClosureExpr,
3547 input: Box<HydroNode>,
3548 metadata: HydroIrMetadata,
3549 },
3550
3551 Reduce {
3552 f: ClosureExpr,
3553 input: Box<HydroNode>,
3554 metadata: HydroIrMetadata,
3555 },
3556 ReduceKeyed {
3557 f: ClosureExpr,
3558 input: Box<HydroNode>,
3559 metadata: HydroIrMetadata,
3560 },
3561 ReduceKeyedWatermark {
3562 f: ClosureExpr,
3563 input: Box<HydroNode>,
3564 watermark: Box<HydroNode>,
3565 metadata: HydroIrMetadata,
3566 },
3567
3568 Network {
3569 name: Option<String>,
3570 networking_info: crate::networking::NetworkingInfo,
3571 serialize: NetworkSend,
3572 deserialize: NetworkRecv,
3573 instantiate_fn: DebugInstantiate,
3574 input: Box<HydroNode>,
3575 metadata: HydroIrMetadata,
3576 },
3577
3578 VersionedNetworkFork {
3579 channel_id: u32,
3580 channel_name: String,
3581 senders: Vec<(u32, Box<HydroNode>, NetworkSend)>,
3582 metadata: HydroIrMetadata,
3583 },
3584
3585 VersionedNetwork {
3586 fork: SharedNode,
3587 version: u32,
3588 deserialize: NetworkRecv,
3589 metadata: HydroIrMetadata,
3590 },
3591
3592 ExternalInput {
3593 from_external_key: LocationKey,
3594 from_port_id: ExternalPortId,
3595 from_many: bool,
3596 codec_type: DebugType,
3597 #[serde(skip)]
3598 port_hint: NetworkHint,
3599 instantiate_fn: DebugInstantiate,
3600 deserialize_fn: Option<DebugExpr>,
3601 metadata: HydroIrMetadata,
3602 },
3603
3604 Counter {
3605 tag: String,
3606 duration: DebugExpr,
3607 prefix: String,
3608 input: Box<HydroNode>,
3609 metadata: HydroIrMetadata,
3610 },
3611
3612 AssertIsConsistent {
3613 inner: Box<HydroNode>,
3614 trusted: bool,
3615 metadata: HydroIrMetadata,
3616 },
3617
3618 UnboundSingleton {
3619 inner: Box<HydroNode>,
3620 metadata: HydroIrMetadata,
3621 },
3622}
3623
3624pub type SeenSharedNodes = HashMap<*const RefCell<HydroNode>, Rc<RefCell<HydroNode>>>;
3625pub type SeenSharedNodeLocations = HashMap<*const RefCell<HydroNode>, LocationId>;
3626
3627#[cfg(feature = "build")]
3635#[derive(PartialEq, Eq, Clone, Copy, Debug)]
3636enum UpdateFeedRepr {
3637 Plain,
3639 Tombstoned,
3641}
3642
3643#[cfg(feature = "build")]
3644fn update_feed_repr(metadata: &HydroIrMetadata) -> UpdateFeedRepr {
3645 if metadata.location_id.is_root()
3646 && metadata
3647 .collection_kind
3648 .root_repr_is_tombstoned_update_feed()
3649 {
3650 UpdateFeedRepr::Tombstoned
3651 } else {
3652 UpdateFeedRepr::Plain
3653 }
3654}
3655
3656#[cfg(feature = "build")]
3660fn maybe_observe_for_mut(
3661 f: &ClosureExpr,
3662 in_ident: syn::Ident,
3663 in_location: &LocationId,
3664 in_kind: &CollectionKind,
3665 op_meta: &HydroIrOpMetadata,
3666 builders_or_callback: &mut BuildersOrCallback<
3667 '_,
3668 impl FnMut(&mut HydroRoot, &mut crate::Counter<StmtId>),
3669 impl FnMut(&mut HydroNode, &mut crate::Counter<StmtId>),
3670 >,
3671 next_stmt_id: &mut crate::Counter<StmtId>,
3672) -> syn::Ident {
3673 if f.has_mut_ref() && !in_kind.is_strict() {
3674 let observe_stmt_id = next_stmt_id.get_and_increment();
3675 let observe_ident =
3676 syn::Ident::new(&format!("stream_{}", observe_stmt_id), Span::call_site());
3677 if let BuildersOrCallback::Builders(graph_builders) = builders_or_callback {
3678 graph_builders.observe_for_mut(in_location, in_ident, in_kind, &observe_ident, op_meta);
3679 }
3680 observe_ident
3681 } else {
3682 in_ident
3683 }
3684}
3685
3686impl HydroNode {
3687 pub fn transform_bottom_up(
3688 &mut self,
3689 transform: &mut impl FnMut(&mut HydroNode),
3690 seen_tees: &mut SeenSharedNodes,
3691 check_well_formed: bool,
3692 ) {
3693 self.transform_children(
3694 |n, s| n.transform_bottom_up(transform, s, check_well_formed),
3695 seen_tees,
3696 );
3697
3698 transform(self);
3699
3700 let self_location = self.metadata().location_id.root();
3701
3702 if check_well_formed {
3703 match &*self {
3704 HydroNode::Network { .. } => {}
3705 _ => {
3706 self.input_metadata().iter().for_each(|i| {
3707 assert!(
3708 i.location_id.root() == self_location,
3709 "Mismatching IR locations, child: {:?} ({:?}) of: {:?} ({:?})",
3710 i,
3711 i.location_id.root(),
3712 self,
3713 self_location
3714 );
3715 });
3716 }
3717 }
3718 }
3719 }
3720
3721 #[inline(always)]
3722 pub fn transform_children(
3723 &mut self,
3724 mut transform: impl FnMut(&mut HydroNode, &mut SeenSharedNodes),
3725 seen_tees: &mut SeenSharedNodes,
3726 ) {
3727 match self {
3728 HydroNode::Placeholder => {
3729 panic!();
3730 }
3731
3732 HydroNode::Source { .. }
3733 | HydroNode::SingletonSource { .. }
3734 | HydroNode::CycleSource { .. }
3735 | HydroNode::ExternalInput { .. } => {}
3736
3737 HydroNode::Tee { inner, .. } | HydroNode::Reference { inner, .. } => {
3738 if let Some(transformed) = seen_tees.get(&inner.as_ptr()) {
3739 *inner = SharedNode(transformed.clone());
3740 } else {
3741 let transformed_cell = Rc::new(RefCell::new(HydroNode::Placeholder));
3742 seen_tees.insert(inner.as_ptr(), transformed_cell.clone());
3743 let mut orig = inner.0.replace(HydroNode::Placeholder);
3744 transform(&mut orig, seen_tees);
3745 *transformed_cell.borrow_mut() = orig;
3746 *inner = SharedNode(transformed_cell);
3747 }
3748 }
3749
3750 HydroNode::PartitionSide { inner, .. } => {
3751 if let Some(transformed) = seen_tees.get(&inner.as_ptr()) {
3752 *inner = SharedNode(transformed.clone());
3753 } else {
3754 let transformed_cell = Rc::new(RefCell::new(HydroNode::Placeholder));
3755 seen_tees.insert(inner.as_ptr(), transformed_cell.clone());
3756 let mut orig: HydroNode = inner.0.replace(HydroNode::Placeholder);
3757 transform(&mut orig, seen_tees);
3758 *transformed_cell.borrow_mut() = orig;
3759 *inner = SharedNode(transformed_cell);
3760 }
3761 }
3762 HydroNode::PartitionShared { input, f, .. } => {
3763 f.transform_children(&mut transform, seen_tees);
3764 transform(input.as_mut(), seen_tees);
3765 }
3766
3767 HydroNode::Cast { inner, .. }
3768 | HydroNode::ObserveNonDet { inner, .. }
3769 | HydroNode::BeginAtomic { inner, .. }
3770 | HydroNode::EndAtomic { inner, .. }
3771 | HydroNode::Batch { inner, .. }
3772 | HydroNode::Snapshot { inner, .. }
3773 | HydroNode::YieldConcat { inner, .. }
3774 | HydroNode::UnboundSingleton { inner, .. }
3775 | HydroNode::AssertIsConsistent { inner, .. } => {
3776 transform(inner.as_mut(), seen_tees);
3777 }
3778
3779 HydroNode::Chain { first, second, .. } => {
3780 transform(first.as_mut(), seen_tees);
3781 transform(second.as_mut(), seen_tees);
3782 }
3783
3784 HydroNode::MergeOrdered { first, second, .. } => {
3785 transform(first.as_mut(), seen_tees);
3786 transform(second.as_mut(), seen_tees);
3787 }
3788
3789 HydroNode::ChainFirst { first, second, .. } => {
3790 transform(first.as_mut(), seen_tees);
3791 transform(second.as_mut(), seen_tees);
3792 }
3793
3794 HydroNode::CrossSingleton { left, right, .. }
3795 | HydroNode::CrossProduct { left, right, .. }
3796 | HydroNode::Join { left, right, .. }
3797 | HydroNode::JoinHalf { left, right, .. } => {
3798 transform(left.as_mut(), seen_tees);
3799 transform(right.as_mut(), seen_tees);
3800 }
3801
3802 HydroNode::Difference { pos, neg, .. } | HydroNode::AntiJoin { pos, neg, .. } => {
3803 transform(pos.as_mut(), seen_tees);
3804 transform(neg.as_mut(), seen_tees);
3805 }
3806
3807 HydroNode::Map { f, input, .. } => {
3808 f.transform_children(&mut transform, seen_tees);
3809 transform(input.as_mut(), seen_tees);
3810 }
3811 HydroNode::FlatMap { f, input, .. }
3812 | HydroNode::FlatMapStreamBlocking { f, input, .. }
3813 | HydroNode::Filter { f, input, .. }
3814 | HydroNode::FilterMap { f, input, .. }
3815 | HydroNode::Inspect { f, input, .. }
3816 | HydroNode::Reduce { f, input, .. }
3817 | HydroNode::ReduceKeyed { f, input, .. } => {
3818 f.transform_children(&mut transform, seen_tees);
3819 transform(input.as_mut(), seen_tees);
3820 }
3821 HydroNode::ReduceKeyedWatermark {
3822 f,
3823 input,
3824 watermark,
3825 ..
3826 } => {
3827 f.transform_children(&mut transform, seen_tees);
3828 transform(input.as_mut(), seen_tees);
3829 transform(watermark.as_mut(), seen_tees);
3830 }
3831 HydroNode::Fold {
3832 init, acc, input, ..
3833 }
3834 | HydroNode::Scan {
3835 init, acc, input, ..
3836 }
3837 | HydroNode::ScanAsyncBlocking {
3838 init, acc, input, ..
3839 }
3840 | HydroNode::FoldKeyed {
3841 init, acc, input, ..
3842 } => {
3843 init.transform_children(&mut transform, seen_tees);
3844 acc.transform_children(&mut transform, seen_tees);
3845 transform(input.as_mut(), seen_tees);
3846 }
3847 HydroNode::ResolveFutures { input, .. }
3848 | HydroNode::ResolveFuturesBlocking { input, .. }
3849 | HydroNode::ResolveFuturesOrdered { input, .. }
3850 | HydroNode::Sort { input, .. }
3851 | HydroNode::DeferTick { input, .. }
3852 | HydroNode::Enumerate { input, .. }
3853 | HydroNode::Unique { input, .. }
3854 | HydroNode::Network { input, .. }
3855 | HydroNode::Counter { input, .. } => {
3856 transform(input.as_mut(), seen_tees);
3857 }
3858
3859 HydroNode::VersionedNetworkFork { senders, .. } => {
3860 for (_version, sender, _serialize) in senders.iter_mut() {
3861 transform(sender.as_mut(), seen_tees);
3862 }
3863 }
3864
3865 HydroNode::VersionedNetwork { fork, .. } => {
3866 if let Some(transformed) = seen_tees.get(&fork.as_ptr()) {
3867 *fork = SharedNode(transformed.clone());
3868 } else {
3869 let transformed_cell = Rc::new(RefCell::new(HydroNode::Placeholder));
3870 seen_tees.insert(fork.as_ptr(), transformed_cell.clone());
3871 let mut orig = fork.0.replace(HydroNode::Placeholder);
3872 transform(&mut orig, seen_tees);
3873 *transformed_cell.borrow_mut() = orig;
3874 *fork = SharedNode(transformed_cell);
3875 }
3876 }
3877 }
3878 }
3879
3880 pub fn deep_clone(&self, seen_tees: &mut SeenSharedNodes) -> HydroNode {
3881 match self {
3882 HydroNode::Placeholder => HydroNode::Placeholder,
3883 HydroNode::Cast { inner, metadata } => HydroNode::Cast {
3884 inner: Box::new(inner.deep_clone(seen_tees)),
3885 metadata: metadata.clone(),
3886 },
3887 HydroNode::UnboundSingleton { inner, metadata } => HydroNode::UnboundSingleton {
3888 inner: Box::new(inner.deep_clone(seen_tees)),
3889 metadata: metadata.clone(),
3890 },
3891 HydroNode::ObserveNonDet {
3892 inner,
3893 trusted,
3894 metadata,
3895 } => HydroNode::ObserveNonDet {
3896 inner: Box::new(inner.deep_clone(seen_tees)),
3897 trusted: *trusted,
3898 metadata: metadata.clone(),
3899 },
3900 HydroNode::AssertIsConsistent {
3901 inner,
3902 trusted,
3903 metadata,
3904 } => HydroNode::AssertIsConsistent {
3905 inner: Box::new(inner.deep_clone(seen_tees)),
3906 trusted: *trusted,
3907 metadata: metadata.clone(),
3908 },
3909 HydroNode::Source { source, metadata } => HydroNode::Source {
3910 source: source.clone(),
3911 metadata: metadata.clone(),
3912 },
3913 HydroNode::SingletonSource {
3914 value,
3915 first_tick_only,
3916 metadata,
3917 } => HydroNode::SingletonSource {
3918 value: value.clone(),
3919 first_tick_only: *first_tick_only,
3920 metadata: metadata.clone(),
3921 },
3922 HydroNode::CycleSource { cycle_id, metadata } => HydroNode::CycleSource {
3923 cycle_id: *cycle_id,
3924 metadata: metadata.clone(),
3925 },
3926 HydroNode::Tee { inner, metadata }
3927 | HydroNode::Reference {
3928 inner, metadata, ..
3929 } => {
3930 let cloned_inner = if let Some(transformed) = seen_tees.get(&inner.as_ptr()) {
3931 SharedNode(transformed.clone())
3932 } else {
3933 let new_rc = Rc::new(RefCell::new(HydroNode::Placeholder));
3934 seen_tees.insert(inner.as_ptr(), new_rc.clone());
3935 let cloned = inner.0.borrow().deep_clone(seen_tees);
3936 *new_rc.borrow_mut() = cloned;
3937 SharedNode(new_rc)
3938 };
3939 if let HydroNode::Reference {
3940 kind,
3941 access_counter,
3942 ..
3943 } = self
3944 {
3945 HydroNode::Reference {
3946 inner: cloned_inner,
3947 kind: *kind,
3948 access_counter: access_counter.freeze(),
3949 metadata: metadata.clone(),
3950 }
3951 } else {
3952 HydroNode::Tee {
3953 inner: cloned_inner,
3954 metadata: metadata.clone(),
3955 }
3956 }
3957 }
3958 HydroNode::PartitionSide {
3959 inner,
3960 is_true,
3961 metadata,
3962 } => {
3963 if let Some(transformed) = seen_tees.get(&inner.as_ptr()) {
3964 HydroNode::PartitionSide {
3965 inner: SharedNode(transformed.clone()),
3966 is_true: *is_true,
3967 metadata: metadata.clone(),
3968 }
3969 } else {
3970 let new_rc = Rc::new(RefCell::new(HydroNode::Placeholder));
3971 seen_tees.insert(inner.as_ptr(), new_rc.clone());
3972 let cloned = inner.0.borrow().deep_clone(seen_tees);
3973 *new_rc.borrow_mut() = cloned;
3974 HydroNode::PartitionSide {
3975 inner: SharedNode(new_rc),
3976 is_true: *is_true,
3977 metadata: metadata.clone(),
3978 }
3979 }
3980 }
3981 HydroNode::PartitionShared { input, f, metadata } => HydroNode::PartitionShared {
3982 input: Box::new(input.deep_clone(seen_tees)),
3983 f: f.deep_clone(seen_tees),
3984 metadata: metadata.clone(),
3985 },
3986 HydroNode::YieldConcat { inner, metadata } => HydroNode::YieldConcat {
3987 inner: Box::new(inner.deep_clone(seen_tees)),
3988 metadata: metadata.clone(),
3989 },
3990 HydroNode::BeginAtomic { inner, metadata } => HydroNode::BeginAtomic {
3991 inner: Box::new(inner.deep_clone(seen_tees)),
3992 metadata: metadata.clone(),
3993 },
3994 HydroNode::EndAtomic { inner, metadata } => HydroNode::EndAtomic {
3995 inner: Box::new(inner.deep_clone(seen_tees)),
3996 metadata: metadata.clone(),
3997 },
3998 HydroNode::Batch { inner, metadata } => HydroNode::Batch {
3999 inner: Box::new(inner.deep_clone(seen_tees)),
4000 metadata: metadata.clone(),
4001 },
4002 HydroNode::Snapshot { inner, metadata } => HydroNode::Snapshot {
4003 inner: Box::new(inner.deep_clone(seen_tees)),
4004 metadata: metadata.clone(),
4005 },
4006 HydroNode::Chain {
4007 first,
4008 second,
4009 metadata,
4010 } => HydroNode::Chain {
4011 first: Box::new(first.deep_clone(seen_tees)),
4012 second: Box::new(second.deep_clone(seen_tees)),
4013 metadata: metadata.clone(),
4014 },
4015 HydroNode::MergeOrdered {
4016 first,
4017 second,
4018 metadata,
4019 } => HydroNode::MergeOrdered {
4020 first: Box::new(first.deep_clone(seen_tees)),
4021 second: Box::new(second.deep_clone(seen_tees)),
4022 metadata: metadata.clone(),
4023 },
4024 HydroNode::ChainFirst {
4025 first,
4026 second,
4027 metadata,
4028 } => HydroNode::ChainFirst {
4029 first: Box::new(first.deep_clone(seen_tees)),
4030 second: Box::new(second.deep_clone(seen_tees)),
4031 metadata: metadata.clone(),
4032 },
4033 HydroNode::CrossProduct {
4034 left,
4035 right,
4036 metadata,
4037 } => HydroNode::CrossProduct {
4038 left: Box::new(left.deep_clone(seen_tees)),
4039 right: Box::new(right.deep_clone(seen_tees)),
4040 metadata: metadata.clone(),
4041 },
4042 HydroNode::CrossSingleton {
4043 left,
4044 right,
4045 metadata,
4046 } => HydroNode::CrossSingleton {
4047 left: Box::new(left.deep_clone(seen_tees)),
4048 right: Box::new(right.deep_clone(seen_tees)),
4049 metadata: metadata.clone(),
4050 },
4051 HydroNode::Join {
4052 left,
4053 right,
4054 metadata,
4055 } => HydroNode::Join {
4056 left: Box::new(left.deep_clone(seen_tees)),
4057 right: Box::new(right.deep_clone(seen_tees)),
4058 metadata: metadata.clone(),
4059 },
4060 HydroNode::JoinHalf {
4061 left,
4062 right,
4063 metadata,
4064 } => HydroNode::JoinHalf {
4065 left: Box::new(left.deep_clone(seen_tees)),
4066 right: Box::new(right.deep_clone(seen_tees)),
4067 metadata: metadata.clone(),
4068 },
4069 HydroNode::Difference { pos, neg, metadata } => HydroNode::Difference {
4070 pos: Box::new(pos.deep_clone(seen_tees)),
4071 neg: Box::new(neg.deep_clone(seen_tees)),
4072 metadata: metadata.clone(),
4073 },
4074 HydroNode::AntiJoin { pos, neg, metadata } => HydroNode::AntiJoin {
4075 pos: Box::new(pos.deep_clone(seen_tees)),
4076 neg: Box::new(neg.deep_clone(seen_tees)),
4077 metadata: metadata.clone(),
4078 },
4079 HydroNode::ResolveFutures { input, metadata } => HydroNode::ResolveFutures {
4080 input: Box::new(input.deep_clone(seen_tees)),
4081 metadata: metadata.clone(),
4082 },
4083 HydroNode::ResolveFuturesBlocking { input, metadata } => {
4084 HydroNode::ResolveFuturesBlocking {
4085 input: Box::new(input.deep_clone(seen_tees)),
4086 metadata: metadata.clone(),
4087 }
4088 }
4089 HydroNode::ResolveFuturesOrdered { input, metadata } => {
4090 HydroNode::ResolveFuturesOrdered {
4091 input: Box::new(input.deep_clone(seen_tees)),
4092 metadata: metadata.clone(),
4093 }
4094 }
4095 HydroNode::Map { f, input, metadata } => HydroNode::Map {
4096 f: f.deep_clone(seen_tees),
4097 input: Box::new(input.deep_clone(seen_tees)),
4098 metadata: metadata.clone(),
4099 },
4100 HydroNode::FlatMap { f, input, metadata } => HydroNode::FlatMap {
4101 f: f.deep_clone(seen_tees),
4102 input: Box::new(input.deep_clone(seen_tees)),
4103 metadata: metadata.clone(),
4104 },
4105 HydroNode::FlatMapStreamBlocking { f, input, metadata } => {
4106 HydroNode::FlatMapStreamBlocking {
4107 f: f.deep_clone(seen_tees),
4108 input: Box::new(input.deep_clone(seen_tees)),
4109 metadata: metadata.clone(),
4110 }
4111 }
4112 HydroNode::Filter { f, input, metadata } => HydroNode::Filter {
4113 f: f.deep_clone(seen_tees),
4114 input: Box::new(input.deep_clone(seen_tees)),
4115 metadata: metadata.clone(),
4116 },
4117 HydroNode::FilterMap { f, input, metadata } => HydroNode::FilterMap {
4118 f: f.deep_clone(seen_tees),
4119 input: Box::new(input.deep_clone(seen_tees)),
4120 metadata: metadata.clone(),
4121 },
4122 HydroNode::DeferTick { input, metadata } => HydroNode::DeferTick {
4123 input: Box::new(input.deep_clone(seen_tees)),
4124 metadata: metadata.clone(),
4125 },
4126 HydroNode::Enumerate { input, metadata } => HydroNode::Enumerate {
4127 input: Box::new(input.deep_clone(seen_tees)),
4128 metadata: metadata.clone(),
4129 },
4130 HydroNode::Inspect { f, input, metadata } => HydroNode::Inspect {
4131 f: f.deep_clone(seen_tees),
4132 input: Box::new(input.deep_clone(seen_tees)),
4133 metadata: metadata.clone(),
4134 },
4135 HydroNode::Unique { input, metadata } => HydroNode::Unique {
4136 input: Box::new(input.deep_clone(seen_tees)),
4137 metadata: metadata.clone(),
4138 },
4139 HydroNode::Sort { input, metadata } => HydroNode::Sort {
4140 input: Box::new(input.deep_clone(seen_tees)),
4141 metadata: metadata.clone(),
4142 },
4143 HydroNode::Fold {
4144 init,
4145 acc,
4146 input,
4147 metadata,
4148 } => HydroNode::Fold {
4149 init: init.deep_clone(seen_tees),
4150 acc: acc.deep_clone(seen_tees),
4151 input: Box::new(input.deep_clone(seen_tees)),
4152 metadata: metadata.clone(),
4153 },
4154 HydroNode::Scan {
4155 init,
4156 acc,
4157 input,
4158 metadata,
4159 } => HydroNode::Scan {
4160 init: init.deep_clone(seen_tees),
4161 acc: acc.deep_clone(seen_tees),
4162 input: Box::new(input.deep_clone(seen_tees)),
4163 metadata: metadata.clone(),
4164 },
4165 HydroNode::ScanAsyncBlocking {
4166 init,
4167 acc,
4168 input,
4169 metadata,
4170 } => HydroNode::ScanAsyncBlocking {
4171 init: init.deep_clone(seen_tees),
4172 acc: acc.deep_clone(seen_tees),
4173 input: Box::new(input.deep_clone(seen_tees)),
4174 metadata: metadata.clone(),
4175 },
4176 HydroNode::FoldKeyed {
4177 init,
4178 acc,
4179 input,
4180 metadata,
4181 } => HydroNode::FoldKeyed {
4182 init: init.deep_clone(seen_tees),
4183 acc: acc.deep_clone(seen_tees),
4184 input: Box::new(input.deep_clone(seen_tees)),
4185 metadata: metadata.clone(),
4186 },
4187 HydroNode::ReduceKeyedWatermark {
4188 f,
4189 input,
4190 watermark,
4191 metadata,
4192 } => HydroNode::ReduceKeyedWatermark {
4193 f: f.deep_clone(seen_tees),
4194 input: Box::new(input.deep_clone(seen_tees)),
4195 watermark: Box::new(watermark.deep_clone(seen_tees)),
4196 metadata: metadata.clone(),
4197 },
4198 HydroNode::Reduce { f, input, metadata } => HydroNode::Reduce {
4199 f: f.deep_clone(seen_tees),
4200 input: Box::new(input.deep_clone(seen_tees)),
4201 metadata: metadata.clone(),
4202 },
4203 HydroNode::ReduceKeyed { f, input, metadata } => HydroNode::ReduceKeyed {
4204 f: f.deep_clone(seen_tees),
4205 input: Box::new(input.deep_clone(seen_tees)),
4206 metadata: metadata.clone(),
4207 },
4208 HydroNode::Network {
4209 name,
4210 networking_info,
4211 serialize,
4212 deserialize,
4213 instantiate_fn,
4214 input,
4215 metadata,
4216 } => HydroNode::Network {
4217 name: name.clone(),
4218 networking_info: networking_info.clone(),
4219 serialize: serialize.clone(),
4220 deserialize: deserialize.clone(),
4221 instantiate_fn: instantiate_fn.clone(),
4222 input: Box::new(input.deep_clone(seen_tees)),
4223 metadata: metadata.clone(),
4224 },
4225 HydroNode::ExternalInput {
4226 from_external_key,
4227 from_port_id,
4228 from_many,
4229 codec_type,
4230 port_hint,
4231 instantiate_fn,
4232 deserialize_fn,
4233 metadata,
4234 } => HydroNode::ExternalInput {
4235 from_external_key: *from_external_key,
4236 from_port_id: *from_port_id,
4237 from_many: *from_many,
4238 codec_type: codec_type.clone(),
4239 port_hint: *port_hint,
4240 instantiate_fn: instantiate_fn.clone(),
4241 deserialize_fn: deserialize_fn.clone(),
4242 metadata: metadata.clone(),
4243 },
4244 HydroNode::Counter {
4245 tag,
4246 duration,
4247 prefix,
4248 input,
4249 metadata,
4250 } => HydroNode::Counter {
4251 tag: tag.clone(),
4252 duration: duration.clone(),
4253 prefix: prefix.clone(),
4254 input: Box::new(input.deep_clone(seen_tees)),
4255 metadata: metadata.clone(),
4256 },
4257 HydroNode::VersionedNetworkFork {
4258 channel_id,
4259 channel_name,
4260 senders,
4261 metadata,
4262 } => HydroNode::VersionedNetworkFork {
4263 channel_id: *channel_id,
4264 channel_name: channel_name.clone(),
4265 senders: senders
4266 .iter()
4267 .map(|(version, sender, serialize)| {
4268 (
4269 *version,
4270 Box::new(sender.deep_clone(seen_tees)),
4271 serialize.clone(),
4272 )
4273 })
4274 .collect(),
4275 metadata: metadata.clone(),
4276 },
4277 HydroNode::VersionedNetwork {
4278 fork,
4279 version,
4280 deserialize,
4281 metadata,
4282 } => {
4283 let cloned_fork = if let Some(transformed) = seen_tees.get(&fork.as_ptr()) {
4284 SharedNode(transformed.clone())
4285 } else {
4286 let new_rc = Rc::new(RefCell::new(HydroNode::Placeholder));
4287 seen_tees.insert(fork.as_ptr(), new_rc.clone());
4288 let cloned = fork.0.borrow().deep_clone(seen_tees);
4289 *new_rc.borrow_mut() = cloned;
4290 SharedNode(new_rc)
4291 };
4292 HydroNode::VersionedNetwork {
4293 fork: cloned_fork,
4294 version: *version,
4295 deserialize: deserialize.clone(),
4296 metadata: metadata.clone(),
4297 }
4298 }
4299 }
4300 }
4301
4302 #[cfg(feature = "build")]
4303 pub fn emit_core(
4304 &mut self,
4305 builders_or_callback: &mut BuildersOrCallback<
4306 '_,
4307 impl FnMut(&mut HydroRoot, &mut crate::Counter<StmtId>),
4308 impl FnMut(&mut HydroNode, &mut crate::Counter<StmtId>),
4309 >,
4310 seen_tees: &mut SeenSharedNodes,
4311 built_tees: &mut HashMap<*const RefCell<HydroNode>, Vec<syn::Ident>>,
4312 next_stmt_id: &mut crate::Counter<StmtId>,
4313 fold_hooked_idents: &mut HashSet<String>,
4314 ) -> syn::Ident {
4315 let mut ident_stack: Vec<syn::Ident> = Vec::new();
4316
4317 self.transform_bottom_up(
4318 &mut |node: &mut HydroNode| {
4319 let out_location = node.metadata().location_id.clone();
4320 match node {
4321 HydroNode::Placeholder => {
4322 panic!()
4323 }
4324
4325 HydroNode::Cast { inner, metadata } => {
4326 let in_repr = update_feed_repr(inner.metadata());
4347 let out_repr = update_feed_repr(metadata);
4348 let cross_kind_tombstoned = in_repr == UpdateFeedRepr::Tombstoned
4349 && out_repr == UpdateFeedRepr::Tombstoned
4350 && std::mem::discriminant(&inner.metadata().collection_kind)
4351 != std::mem::discriminant(&metadata.collection_kind);
4352
4353 if in_repr == out_repr && !cross_kind_tombstoned {
4354 let _ = next_stmt_id.get_and_increment();
4357 match builders_or_callback {
4358 BuildersOrCallback::Builders(_) => {}
4359 BuildersOrCallback::Callback(_, node_callback) => {
4360 node_callback(node, next_stmt_id);
4361 }
4362 }
4363 } else {
4365 let input_ident = ident_stack.pop().unwrap();
4366
4367 let stmt_id = next_stmt_id.get_and_increment();
4368 let cast_ident =
4369 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4370
4371 match builders_or_callback {
4372 BuildersOrCallback::Builders(graph_builders) => {
4373 let cast_op: TokenStream = if cross_kind_tombstoned {
4374 match (
4375 &inner.metadata().collection_kind,
4376 &metadata.collection_kind,
4377 ) {
4378 (
4379 CollectionKind::Optional { .. },
4380 CollectionKind::KeyedSingleton { .. },
4381 ) => {
4382 let lifetime: TokenStream = graph_builders
4388 .cross_tick_state_lifetime(&out_location);
4389 let lifetime: syn::Lifetime =
4390 syn::parse2(lifetime).unwrap();
4391 quote! {
4392 scan::<#lifetime>(
4393 || ::std::option::Option::None,
4394 |previous_key, update: ::std::option::Option<(_, _)>| {
4395 let mut deltas = ::std::vec::Vec::new();
4396 match update {
4397 ::std::option::Option::Some((key, value)) => {
4398 match previous_key.take() {
4399 ::std::option::Option::Some(previous) if previous != key => {
4400 deltas.push((previous, ::std::option::Option::None));
4401 }
4402 _ => {}
4403 }
4404 *previous_key = ::std::option::Option::Some(::std::clone::Clone::clone(&key));
4405 deltas.push((key, ::std::option::Option::Some(value)));
4406 }
4407 ::std::option::Option::None => {
4408 if let ::std::option::Option::Some(previous) = previous_key.take() {
4409 deltas.push((previous, ::std::option::Option::None));
4410 }
4411 }
4412 }
4413 ::std::option::Option::Some(deltas)
4414 },
4415 ) -> flatten()
4416 }
4417 }
4418 (in_kind, out_kind) => todo!(
4419 "casting between tombstoned update feeds \
4420 {:?} -> {:?} is not yet supported",
4421 in_kind,
4422 out_kind
4423 ),
4424 }
4425 } else {
4426 match (in_repr, out_repr) {
4427 (UpdateFeedRepr::Plain, UpdateFeedRepr::Tombstoned) => {
4428 match &metadata.collection_kind {
4429 CollectionKind::Optional { .. } => {
4430 quote! { map(::std::option::Option::Some) }
4431 }
4432 CollectionKind::KeyedSingleton { .. } => {
4433 quote! { map(|(__hydro_key, __hydro_value)| (__hydro_key, ::std::option::Option::Some(__hydro_value))) }
4434 }
4435 other => panic!(
4436 "unexpected tombstoned update feed kind for cast: {:?}",
4437 other
4438 ),
4439 }
4440 }
4441 (UpdateFeedRepr::Tombstoned, UpdateFeedRepr::Plain) => {
4442 match &inner.metadata().collection_kind {
4453 CollectionKind::Optional { .. } => {
4454 quote! { filter_map(|__hydro_update| __hydro_update) }
4455 }
4456 CollectionKind::KeyedSingleton { .. } => {
4457 quote! { filter_map(|(__hydro_key, __hydro_update)| __hydro_update.map(|__hydro_value| (__hydro_key, __hydro_value))) }
4458 }
4459 other => panic!(
4460 "unexpected tombstoned update feed kind for cast: {:?}",
4461 other
4462 ),
4463 }
4464 }
4465 _ => unreachable!(),
4466 }
4467 };
4468
4469 graph_builders.add_dfir_at(
4470 &out_location,
4471 parse_quote! {
4472 #cast_ident = #input_ident -> #cast_op;
4473 },
4474 Some(&stmt_id.to_string()),
4475 );
4476 }
4477 BuildersOrCallback::Callback(_, node_callback) => {
4478 node_callback(node, next_stmt_id);
4479 }
4480 }
4481
4482 ident_stack.push(cast_ident);
4483 }
4484 }
4485
4486 HydroNode::UnboundSingleton { .. } => {
4487 let inner_ident = ident_stack.pop().unwrap();
4488
4489 let stmt_id = next_stmt_id.get_and_increment();
4490 let out_ident =
4491 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4492
4493 match builders_or_callback {
4494 BuildersOrCallback::Builders(graph_builders) => {
4495 if graph_builders.singleton_intermediates() {
4496 graph_builders.add_dfir_at(
4497 &out_location,
4498 parse_quote! {
4499 #out_ident = #inner_ident;
4500 },
4501 None,
4502 );
4503 } else {
4504 graph_builders.add_dfir_at(
4505 &out_location,
4506 parse_quote! {
4507 #out_ident = #inner_ident -> persist::<'static>();
4508 },
4509 None,
4510 );
4511 }
4512 }
4513 BuildersOrCallback::Callback(_, node_callback) => {
4514 node_callback(node, next_stmt_id);
4515 }
4516 }
4517
4518 ident_stack.push(out_ident);
4519 }
4520
4521 HydroNode::AssertIsConsistent { inner, trusted, .. } => {
4522 let inner_ident = ident_stack.pop().unwrap();
4523
4524 let stmt_id = next_stmt_id.get_and_increment();
4525 let out_ident =
4526 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4527
4528 match builders_or_callback {
4529 BuildersOrCallback::Builders(graph_builders) => {
4530 graph_builders.assert_is_consistent(
4531 *trusted,
4532 &inner.metadata().location_id,
4533 inner_ident,
4534 &out_ident,
4535 );
4536 }
4537 BuildersOrCallback::Callback(_, node_callback) => {
4538 node_callback(node, next_stmt_id);
4539 }
4540 }
4541
4542 ident_stack.push(out_ident);
4543 }
4544
4545 HydroNode::ObserveNonDet {
4546 inner,
4547 trusted,
4548 metadata,
4549 ..
4550 } => {
4551 let inner_ident = ident_stack.pop().unwrap();
4552
4553 let stmt_id = next_stmt_id.get_and_increment();
4554 let observe_ident =
4555 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4556
4557 match builders_or_callback {
4558 BuildersOrCallback::Builders(graph_builders) => {
4559 graph_builders.observe_nondet(
4560 *trusted,
4561 &inner.metadata().location_id,
4562 inner_ident,
4563 &inner.metadata().collection_kind,
4564 &observe_ident,
4565 &metadata.collection_kind,
4566 &metadata.op,
4567 );
4568 }
4569 BuildersOrCallback::Callback(_, node_callback) => {
4570 node_callback(node, next_stmt_id);
4571 }
4572 }
4573
4574 ident_stack.push(observe_ident);
4575 }
4576
4577 HydroNode::Batch {
4578 inner, metadata, ..
4579 } => {
4580 let inner_ident = ident_stack.pop().unwrap();
4581
4582 let stmt_id = next_stmt_id.get_and_increment();
4583 let batch_ident =
4584 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4585
4586 match builders_or_callback {
4587 BuildersOrCallback::Builders(graph_builders) => {
4588 graph_builders.batch(
4589 inner_ident,
4590 &inner.metadata().location_id,
4591 &inner.metadata().collection_kind,
4592 &batch_ident,
4593 &out_location,
4594 &metadata.op,
4595 fold_hooked_idents,
4596 );
4597 }
4598 BuildersOrCallback::Callback(_, node_callback) => {
4599 node_callback(node, next_stmt_id);
4600 }
4601 }
4602
4603 ident_stack.push(batch_ident);
4604 }
4605
4606 HydroNode::Snapshot {
4607 inner, metadata, ..
4608 } => {
4609 let inner_ident = ident_stack.pop().unwrap();
4610
4611 let stmt_id = next_stmt_id.get_and_increment();
4612 let snapshot_ident =
4613 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4614
4615 match builders_or_callback {
4616 BuildersOrCallback::Builders(graph_builders) => {
4617 graph_builders.snapshot(
4618 inner_ident,
4619 &inner.metadata().location_id,
4620 &inner.metadata().collection_kind,
4621 &snapshot_ident,
4622 &out_location,
4623 &metadata.op,
4624 fold_hooked_idents,
4625 );
4626 }
4627 BuildersOrCallback::Callback(_, node_callback) => {
4628 node_callback(node, next_stmt_id);
4629 }
4630 }
4631
4632 ident_stack.push(snapshot_ident);
4633 }
4634
4635 HydroNode::YieldConcat {
4636 inner, metadata, ..
4637 } => {
4638 let inner_ident = ident_stack.pop().unwrap();
4639
4640 let stmt_id = next_stmt_id.get_and_increment();
4641 let yield_ident =
4642 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4643
4644 match builders_or_callback {
4645 BuildersOrCallback::Builders(graph_builders) => {
4646 graph_builders.yield_from_tick(
4647 inner_ident,
4648 &inner.metadata().location_id,
4649 &inner.metadata().collection_kind,
4650 &yield_ident,
4651 &out_location,
4652 &metadata.collection_kind,
4653 );
4654 }
4655 BuildersOrCallback::Callback(_, node_callback) => {
4656 node_callback(node, next_stmt_id);
4657 }
4658 }
4659
4660 ident_stack.push(yield_ident);
4661 }
4662
4663 HydroNode::BeginAtomic { inner, metadata } => {
4664 let inner_ident = ident_stack.pop().unwrap();
4665
4666 let stmt_id = next_stmt_id.get_and_increment();
4667 let begin_ident =
4668 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4669
4670 match builders_or_callback {
4671 BuildersOrCallback::Builders(graph_builders) => {
4672 graph_builders.begin_atomic(
4673 inner_ident,
4674 &inner.metadata().location_id,
4675 &inner.metadata().collection_kind,
4676 &begin_ident,
4677 &out_location,
4678 &metadata.op,
4679 );
4680 }
4681 BuildersOrCallback::Callback(_, node_callback) => {
4682 node_callback(node, next_stmt_id);
4683 }
4684 }
4685
4686 ident_stack.push(begin_ident);
4687 }
4688
4689 HydroNode::EndAtomic {
4690 inner, metadata, ..
4691 } => {
4692 let inner_ident = ident_stack.pop().unwrap();
4693
4694 let stmt_id = next_stmt_id.get_and_increment();
4695 let end_ident =
4696 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4697
4698 match builders_or_callback {
4699 BuildersOrCallback::Builders(graph_builders) => {
4700 graph_builders.end_atomic(
4701 inner_ident,
4702 &inner.metadata().location_id,
4703 &inner.metadata().collection_kind,
4704 &end_ident,
4705 &metadata.collection_kind,
4706 );
4707 }
4708 BuildersOrCallback::Callback(_, node_callback) => {
4709 node_callback(node, next_stmt_id);
4710 }
4711 }
4712
4713 ident_stack.push(end_ident);
4714 }
4715
4716 HydroNode::Source {
4717 source, metadata, ..
4718 } => {
4719 if matches!(source, HydroSource::ExternalNetwork()) {
4720 ident_stack.push(syn::Ident::new("DUMMY", Span::call_site()));
4721 } else {
4722 let stmt_id = next_stmt_id.get_and_increment();
4723 let source_ident =
4724 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4725
4726 let mut tick_source_rhs: Option<TokenStream> = None;
4730
4731 let source_stmt = match source {
4732 HydroSource::Stream(expr) => {
4733 debug_assert!(metadata.location_id.is_top_level());
4734 parse_quote! {
4735 #source_ident = source_stream(#expr);
4736 }
4737 }
4738
4739 HydroSource::ExternalNetwork() => {
4740 unreachable!()
4741 }
4742
4743 HydroSource::Iter(expr) => {
4744 if metadata.location_id.is_root() {
4745 parse_quote! {
4746 #source_ident = source_iter(#expr);
4747 }
4748 } else {
4749 tick_source_rhs = Some(quote! { source_iter(#expr) });
4755 parse_quote! {
4756 #source_ident = source_iter(#expr) -> persist::<'static>();
4757 }
4758 }
4759 }
4760
4761 HydroSource::Spin() => {
4762 debug_assert!(metadata.location_id.is_top_level());
4763 parse_quote! {
4764 #source_ident = spin();
4765 }
4766 }
4767
4768 HydroSource::ClusterMembers(target_loc, state) => {
4769 debug_assert!(metadata.location_id.is_top_level());
4770
4771 let members_tee_ident = syn::Ident::new(
4772 &format!(
4773 "__cluster_members_tee_{}_{}",
4774 metadata.location_id.root().key(),
4775 target_loc.key(),
4776 ),
4777 Span::call_site(),
4778 );
4779
4780 match state {
4781 ClusterMembersState::Stream(d) => {
4782 parse_quote! {
4783 #members_tee_ident = source_stream(#d) -> tee();
4784 #source_ident = #members_tee_ident;
4785 }
4786 },
4787 ClusterMembersState::Uninit => syn::parse_quote! {
4788 #source_ident = source_stream(DUMMY);
4789 },
4790 ClusterMembersState::Tee(..) => parse_quote! {
4791 #source_ident = #members_tee_ident;
4792 },
4793 }
4794 }
4795
4796 HydroSource::Embedded(ident) => {
4797 parse_quote! {
4798 #source_ident = source_stream(#ident);
4799 }
4800 }
4801
4802 HydroSource::EmbeddedSingleton(ident) => {
4803 parse_quote! {
4804 #source_ident = source_iter([#ident]);
4805 }
4806 }
4807 };
4808
4809 match builders_or_callback {
4810 BuildersOrCallback::Builders(graph_builders) => {
4811 if let Some(source_rhs) = tick_source_rhs {
4812 graph_builders.add_tick_source(
4813 &out_location,
4814 source_rhs,
4815 &source_ident,
4816 true,
4817 Some(&stmt_id.to_string()),
4818 );
4819 } else {
4820 graph_builders.add_dfir_at(
4821 &out_location,
4822 source_stmt,
4823 Some(&stmt_id.to_string()),
4824 );
4825 }
4826 }
4827 BuildersOrCallback::Callback(_, node_callback) => {
4828 node_callback(node, next_stmt_id);
4829 }
4830 }
4831
4832 ident_stack.push(source_ident);
4833 }
4834 }
4835
4836 HydroNode::SingletonSource { value, first_tick_only, metadata } => {
4837 let stmt_id = next_stmt_id.get_and_increment();
4838 let source_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 *first_tick_only {
4844 assert!(
4845 !metadata.location_id.is_top_level(),
4846 "first_tick_only SingletonSource must be inside a tick"
4847 );
4848 graph_builders.add_tick_source(
4850 &out_location,
4851 quote! { source_iter([#value]) },
4852 &source_ident,
4853 false,
4854 Some(&stmt_id.to_string()),
4855 );
4856 } else if metadata.location_id.is_root() {
4857 if metadata.collection_kind.is_bounded() {
4858 graph_builders.add_dfir_at(
4859 &out_location,
4860 parse_quote! {
4861 #source_ident = source_iter([#value]);
4862 },
4863 Some(&stmt_id.to_string()),
4864 );
4865 } else {
4866 graph_builders.add_dfir_at(
4867 &out_location,
4868 parse_quote! {
4869 #source_ident = source_iter([#value]) -> persist::<'static>();
4870 },
4871 Some(&stmt_id.to_string()),
4872 );
4873 }
4874 } else {
4875 graph_builders.add_tick_source(
4880 &out_location,
4881 quote! { source_iter([#value]) },
4882 &source_ident,
4883 true,
4884 Some(&stmt_id.to_string()),
4885 );
4886 }
4887 }
4888 BuildersOrCallback::Callback(_, node_callback) => {
4889 node_callback(node, next_stmt_id);
4890 }
4891 }
4892
4893 ident_stack.push(source_ident);
4894 }
4895
4896 HydroNode::CycleSource { cycle_id, .. } => {
4897 let ident = cycle_id.as_ident();
4898
4899 let _ = next_stmt_id.get_and_increment();
4901
4902 match builders_or_callback {
4903 BuildersOrCallback::Builders(_) => {}
4904 BuildersOrCallback::Callback(_, node_callback) => {
4905 node_callback(node, next_stmt_id);
4906 }
4907 }
4908
4909 ident_stack.push(ident);
4910 }
4911
4912 HydroNode::Tee { inner, .. } => {
4913 let stmt_id = next_stmt_id.get_and_increment();
4916
4917 let ret_ident = if let Some(built_idents) =
4918 built_tees.get(&(std::ptr::from_ref(inner.0.as_ref())))
4919 {
4920 match builders_or_callback {
4921 BuildersOrCallback::Builders(_) => {}
4922 BuildersOrCallback::Callback(_, node_callback) => {
4923 node_callback(node, next_stmt_id);
4924 }
4925 }
4926
4927 built_idents[0].clone()
4928 } else {
4929 let inner_ident = ident_stack.pop().unwrap();
4932
4933 let tee_ident =
4934 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4935
4936 built_tees.insert(
4937 std::ptr::from_ref(inner.0.as_ref()),
4938 vec![tee_ident.clone()],
4939 );
4940
4941 match builders_or_callback {
4942 BuildersOrCallback::Builders(graph_builders) => {
4943 if fold_hooked_idents.contains(&inner_ident.to_string()) {
4955 fold_hooked_idents.insert(tee_ident.to_string());
4956 }
4957 graph_builders.add_dfir_at(
4958 &out_location,
4959 parse_quote! {
4960 #tee_ident = #inner_ident -> tee();
4961 },
4962 Some(&stmt_id.to_string()),
4963 );
4964 }
4965 BuildersOrCallback::Callback(_, node_callback) => {
4966 node_callback(node, next_stmt_id);
4967 }
4968 }
4969
4970 tee_ident
4971 };
4972
4973 ident_stack.push(ret_ident);
4974 }
4975
4976 HydroNode::Reference { inner, kind, .. } => {
4977 let stmt_id = next_stmt_id.get_and_increment();
4980
4981 let ret_ident = if let Some(built_idents) =
4982 built_tees.get(&(std::ptr::from_ref(inner.0.as_ref())))
4983 {
4984 built_idents[0].clone()
4985 } else {
4986 let inner_ident = ident_stack.pop().unwrap();
4987
4988 let ref_ident =
4989 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4990
4991 built_tees.insert(
4992 std::ptr::from_ref(inner.0.as_ref()),
4993 vec![ref_ident.clone()],
4994 );
4995
4996 match builders_or_callback {
4997 BuildersOrCallback::Builders(graph_builders) => {
4998 let op_ident = syn::Ident::new(
4999 match kind {
5000 crate::handoff_ref::HandoffRefKind::Singleton => "singleton",
5001 crate::handoff_ref::HandoffRefKind::Optional => "optional",
5002 crate::handoff_ref::HandoffRefKind::Vec => "handoff",
5003 },
5004 Span::call_site(),
5005 );
5006 graph_builders.add_dfir_at(
5007 &out_location,
5008 parse_quote! {
5009 #ref_ident = #inner_ident -> #op_ident();
5010 },
5011 Some(&stmt_id.to_string()),
5012 );
5013 }
5014 BuildersOrCallback::Callback(_, node_callback) => {
5015 node_callback(node, next_stmt_id);
5016 }
5017 }
5018
5019 ref_ident
5020 };
5021
5022 ident_stack.push(ret_ident);
5023 }
5024
5025 HydroNode::PartitionSide {
5026 inner, is_true, metadata: _,
5027 } => {
5028 let is_true = *is_true; let ptr = std::ptr::from_ref(inner.0.as_ref());
5030 let stmt_id = next_stmt_id.get_and_increment();
5031
5032 let ret_ident = if let Some(built_idents) = built_tees.get(&ptr) {
5033 match builders_or_callback {
5034 BuildersOrCallback::Builders(_) => {}
5035 BuildersOrCallback::Callback(_, node_callback) => {
5036 node_callback(node, next_stmt_id);
5037 }
5038 }
5039
5040 let idx = usize::from(!is_true);
5041 built_idents[idx].clone()
5042 } else {
5043 let partition_ident = ident_stack.pop().unwrap();
5046
5047 let true_ident = syn::Ident::new(
5048 &format!("stream_{}_true", stmt_id),
5049 Span::call_site(),
5050 );
5051 let false_ident = syn::Ident::new(
5052 &format!("stream_{}_false", stmt_id),
5053 Span::call_site(),
5054 );
5055
5056 built_tees.insert(
5057 ptr,
5058 vec![true_ident.clone(), false_ident.clone()],
5059 );
5060
5061 let stmt_id = next_stmt_id.get_and_increment();
5062 match builders_or_callback {
5063 BuildersOrCallback::Builders(graph_builders) => {
5064 graph_builders.add_dfir_at(
5065 &out_location,
5066 parse_quote! {
5067 #true_ident = #partition_ident[0];
5068 #false_ident = #partition_ident[1];
5069 },
5070 Some(&stmt_id.to_string()),
5071 );
5072 }
5073 BuildersOrCallback::Callback(_, node_callback) => {
5074 node_callback(node, next_stmt_id);
5075 }
5076 }
5077
5078 if is_true { true_ident } else { false_ident }
5079 };
5080
5081 ident_stack.push(ret_ident);
5082 }
5083
5084 HydroNode::PartitionShared { input, f, metadata } => {
5085 let inner_ident = ident_stack.pop().unwrap();
5088 let f_tokens = f.emit_tokens(&mut ident_stack);
5089
5090 let inner_ident = {
5091 maybe_observe_for_mut(
5092 f, inner_ident,
5093 &input.metadata().location_id,
5094 &input.metadata().collection_kind,
5095 &metadata.op,
5096 builders_or_callback, next_stmt_id,
5097 )
5098 };
5099
5100 let stmt_id = next_stmt_id.get_and_increment();
5101 let partition_ident = syn::Ident::new(
5102 &format!("stream_{}_partition", stmt_id),
5103 Span::call_site(),
5104 );
5105
5106 let stmt_id = next_stmt_id.get_and_increment();
5107 match builders_or_callback {
5108 BuildersOrCallback::Builders(graph_builders) => {
5109 graph_builders.add_dfir_at(
5110 &out_location,
5111 parse_quote! {
5112 #partition_ident = #inner_ident -> partition(|__item, __num_outputs| if (#f_tokens)(__item) { 0_usize } else { 1_usize });
5113 },
5114 Some(&stmt_id.to_string()),
5115 );
5116 }
5117 BuildersOrCallback::Callback(_, node_callback) => {
5118 node_callback(node, next_stmt_id);
5119 }
5120 }
5121 ident_stack.push(partition_ident);
5122 }
5123
5124 HydroNode::Chain { .. } => {
5125 let second_ident = ident_stack.pop().unwrap();
5127 let first_ident = ident_stack.pop().unwrap();
5128
5129 let stmt_id = next_stmt_id.get_and_increment();
5130 let chain_ident =
5131 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5132
5133 match builders_or_callback {
5134 BuildersOrCallback::Builders(graph_builders) => {
5135 graph_builders.add_dfir_at(
5136 &out_location,
5137 parse_quote! {
5138 #chain_ident = chain();
5139 #first_ident -> [0]#chain_ident;
5140 #second_ident -> [1]#chain_ident;
5141 },
5142 Some(&stmt_id.to_string()),
5143 );
5144 }
5145 BuildersOrCallback::Callback(_, node_callback) => {
5146 node_callback(node, next_stmt_id);
5147 }
5148 }
5149
5150 ident_stack.push(chain_ident);
5151 }
5152
5153 HydroNode::MergeOrdered { first, metadata, .. } => {
5154 let second_ident = ident_stack.pop().unwrap();
5155 let first_ident = ident_stack.pop().unwrap();
5156
5157 let stmt_id = next_stmt_id.get_and_increment();
5158 let merge_ident =
5159 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5160
5161 match builders_or_callback {
5162 BuildersOrCallback::Builders(graph_builders) => {
5163 graph_builders.merge_ordered(
5164 &first.metadata().location_id,
5165 first_ident,
5166 second_ident,
5167 &merge_ident,
5168 &first.metadata().collection_kind,
5169 &metadata.op,
5170 Some(&stmt_id.to_string()),
5171 );
5172 }
5173 BuildersOrCallback::Callback(_, node_callback) => {
5174 node_callback(node, next_stmt_id);
5175 }
5176 }
5177
5178 ident_stack.push(merge_ident);
5179 }
5180
5181 HydroNode::ChainFirst { .. } => {
5182 let second_ident = ident_stack.pop().unwrap();
5183 let first_ident = ident_stack.pop().unwrap();
5184
5185 let stmt_id = next_stmt_id.get_and_increment();
5186 let chain_ident =
5187 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5188
5189 match builders_or_callback {
5190 BuildersOrCallback::Builders(graph_builders) => {
5191 graph_builders.add_dfir_at(
5192 &out_location,
5193 parse_quote! {
5194 #chain_ident = chain_first_n(1);
5195 #first_ident -> [0]#chain_ident;
5196 #second_ident -> [1]#chain_ident;
5197 },
5198 Some(&stmt_id.to_string()),
5199 );
5200 }
5201 BuildersOrCallback::Callback(_, node_callback) => {
5202 node_callback(node, next_stmt_id);
5203 }
5204 }
5205
5206 ident_stack.push(chain_ident);
5207 }
5208
5209 HydroNode::CrossSingleton { right, .. } => {
5210 let right_ident = ident_stack.pop().unwrap();
5211 let left_ident = ident_stack.pop().unwrap();
5212
5213 let stmt_id = next_stmt_id.get_and_increment();
5214 let cross_ident =
5215 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5216
5217 match builders_or_callback {
5218 BuildersOrCallback::Builders(graph_builders) => {
5219 if right.metadata().location_id.is_top_level()
5220 && right.metadata().collection_kind.is_bounded()
5221 {
5222 let lifetime =
5223 graph_builders.cross_tick_state_lifetime(&out_location);
5224 graph_builders.add_dfir_at(
5225 &out_location,
5226 parse_quote! {
5227 #cross_ident = cross_singleton::<#lifetime>();
5228 #left_ident -> [input]#cross_ident;
5229 #right_ident -> [single]#cross_ident;
5230 },
5231 Some(&stmt_id.to_string()),
5232 );
5233 } else {
5234 graph_builders.add_dfir_at(
5235 &out_location,
5236 parse_quote! {
5237 #cross_ident = cross_singleton();
5238 #left_ident -> [input]#cross_ident;
5239 #right_ident -> [single]#cross_ident;
5240 },
5241 Some(&stmt_id.to_string()),
5242 );
5243 }
5244 }
5245 BuildersOrCallback::Callback(_, node_callback) => {
5246 node_callback(node, next_stmt_id);
5247 }
5248 }
5249
5250 ident_stack.push(cross_ident);
5251 }
5252
5253 HydroNode::CrossProduct { .. } | HydroNode::Join { .. } => {
5254 let operator: syn::Ident = if matches!(node, HydroNode::CrossProduct { .. }) {
5255 parse_quote!(cross_join_multiset)
5256 } else {
5257 parse_quote!(join_multiset)
5258 };
5259
5260 let (HydroNode::CrossProduct { left, right, .. }
5261 | HydroNode::Join { left, right, .. }) = node
5262 else {
5263 unreachable!()
5264 };
5265
5266 let is_top_level = left.metadata().location_id.is_top_level()
5267 && right.metadata().location_id.is_top_level();
5268 let left_top_level = left.metadata().location_id.is_top_level();
5269 let right_top_level = right.metadata().location_id.is_top_level();
5270
5271 let right_ident = ident_stack.pop().unwrap();
5272 let left_ident = ident_stack.pop().unwrap();
5273
5274 let stmt_id = next_stmt_id.get_and_increment();
5275 let stream_ident =
5276 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5277
5278 match builders_or_callback {
5279 BuildersOrCallback::Builders(graph_builders) => {
5280 let left_lifetime = if left_top_level {
5281 graph_builders.cross_tick_state_lifetime(&out_location)
5282 } else {
5283 graph_builders.tick_state_lifetime(&out_location)
5284 };
5285
5286 let right_lifetime = if right_top_level {
5287 graph_builders.cross_tick_state_lifetime(&out_location)
5288 } else {
5289 graph_builders.tick_state_lifetime(&out_location)
5290 };
5291
5292 graph_builders.add_dfir_at(
5293 &out_location,
5294 if is_top_level {
5295 parse_quote! {
5298 #stream_ident = #operator::<#left_lifetime, #right_lifetime>() -> multiset_delta();
5299 #left_ident -> [0]#stream_ident;
5300 #right_ident -> [1]#stream_ident;
5301 }
5302 } else {
5303 parse_quote! {
5304 #stream_ident = #operator::<#left_lifetime, #right_lifetime>();
5305 #left_ident -> [0]#stream_ident;
5306 #right_ident -> [1]#stream_ident;
5307 }
5308 },
5309 Some(&stmt_id.to_string()),
5310 );
5311 }
5312 BuildersOrCallback::Callback(_, node_callback) => {
5313 node_callback(node, next_stmt_id);
5314 }
5315 }
5316
5317 ident_stack.push(stream_ident);
5318 }
5319
5320 HydroNode::Difference { .. } | HydroNode::AntiJoin { .. } => {
5321 let operator: syn::Ident = if matches!(node, HydroNode::Difference { .. }) {
5322 parse_quote!(difference)
5323 } else {
5324 parse_quote!(anti_join)
5325 };
5326
5327 let (HydroNode::Difference { neg, .. } | HydroNode::AntiJoin { neg, .. }) =
5328 node
5329 else {
5330 unreachable!()
5331 };
5332
5333 let neg_top_level = neg.metadata().location_id.is_top_level();
5334
5335 let neg_ident = ident_stack.pop().unwrap();
5336 let pos_ident = ident_stack.pop().unwrap();
5337
5338 let stmt_id = next_stmt_id.get_and_increment();
5339 let stream_ident =
5340 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5341
5342 match builders_or_callback {
5343 BuildersOrCallback::Builders(graph_builders) => {
5344 let neg_lifetime = if neg_top_level {
5345 graph_builders.cross_tick_state_lifetime(&out_location)
5346 } else {
5347 graph_builders.tick_state_lifetime(&out_location)
5348 };
5349 let pos_lifetime =
5350 graph_builders.tick_state_lifetime(&out_location);
5351
5352 graph_builders.add_dfir_at(
5353 &out_location,
5354 parse_quote! {
5355 #stream_ident = #operator::<#pos_lifetime, #neg_lifetime>();
5356 #pos_ident -> [pos]#stream_ident;
5357 #neg_ident -> [neg]#stream_ident;
5358 },
5359 Some(&stmt_id.to_string()),
5360 );
5361 }
5362 BuildersOrCallback::Callback(_, node_callback) => {
5363 node_callback(node, next_stmt_id);
5364 }
5365 }
5366
5367 ident_stack.push(stream_ident);
5368 }
5369
5370 HydroNode::JoinHalf { .. } => {
5371 let HydroNode::JoinHalf { right, .. } = node else {
5372 unreachable!()
5373 };
5374
5375 assert!(
5376 right.metadata().collection_kind.is_bounded(),
5377 "JoinHalf requires the right (build) side to be Bounded, got {:?}",
5378 right.metadata().collection_kind
5379 );
5380
5381 let build_top_level = right.metadata().location_id.is_top_level();
5382
5383 let build_ident = ident_stack.pop().unwrap();
5384 let probe_ident = ident_stack.pop().unwrap();
5385
5386 let stmt_id = next_stmt_id.get_and_increment();
5387 let stream_ident =
5388 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5389
5390 match builders_or_callback {
5391 BuildersOrCallback::Builders(graph_builders) => {
5392 let build_lifetime = if build_top_level {
5393 graph_builders.cross_tick_state_lifetime(&out_location)
5394 } else {
5395 graph_builders.tick_state_lifetime(&out_location)
5396 };
5397 let probe_lifetime =
5398 graph_builders.tick_state_lifetime(&out_location);
5399
5400 graph_builders.add_dfir_at(
5401 &out_location,
5402 parse_quote! {
5403 #stream_ident = join_multiset_half::<#build_lifetime, #probe_lifetime>();
5404 #probe_ident -> [probe]#stream_ident;
5405 #build_ident -> [build]#stream_ident;
5406 },
5407 Some(&stmt_id.to_string()),
5408 );
5409 }
5410 BuildersOrCallback::Callback(_, node_callback) => {
5411 node_callback(node, next_stmt_id);
5412 }
5413 }
5414
5415 ident_stack.push(stream_ident);
5416 }
5417
5418 HydroNode::ResolveFutures { .. } => {
5419 let input_ident = ident_stack.pop().unwrap();
5420
5421 let stmt_id = next_stmt_id.get_and_increment();
5422 let futures_ident =
5423 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5424
5425 match builders_or_callback {
5426 BuildersOrCallback::Builders(graph_builders) => {
5427 graph_builders.add_dfir_at(
5428 &out_location,
5429 parse_quote! {
5430 #futures_ident = #input_ident -> resolve_futures();
5431 },
5432 Some(&stmt_id.to_string()),
5433 );
5434 }
5435 BuildersOrCallback::Callback(_, node_callback) => {
5436 node_callback(node, next_stmt_id);
5437 }
5438 }
5439
5440 ident_stack.push(futures_ident);
5441 }
5442
5443 HydroNode::ResolveFuturesBlocking { .. } => {
5444 let input_ident = ident_stack.pop().unwrap();
5445
5446 let stmt_id = next_stmt_id.get_and_increment();
5447 let futures_ident =
5448 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5449
5450 match builders_or_callback {
5451 BuildersOrCallback::Builders(graph_builders) => {
5452 graph_builders.add_dfir_at(
5453 &out_location,
5454 parse_quote! {
5455 #futures_ident = #input_ident -> resolve_futures_blocking();
5456 },
5457 Some(&stmt_id.to_string()),
5458 );
5459 }
5460 BuildersOrCallback::Callback(_, node_callback) => {
5461 node_callback(node, next_stmt_id);
5462 }
5463 }
5464
5465 ident_stack.push(futures_ident);
5466 }
5467
5468 HydroNode::ResolveFuturesOrdered { .. } => {
5469 let input_ident = ident_stack.pop().unwrap();
5470
5471 let stmt_id = next_stmt_id.get_and_increment();
5472 let futures_ident =
5473 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5474
5475 match builders_or_callback {
5476 BuildersOrCallback::Builders(graph_builders) => {
5477 graph_builders.add_dfir_at(
5478 &out_location,
5479 parse_quote! {
5480 #futures_ident = #input_ident -> resolve_futures_ordered();
5481 },
5482 Some(&stmt_id.to_string()),
5483 );
5484 }
5485 BuildersOrCallback::Callback(_, node_callback) => {
5486 node_callback(node, next_stmt_id);
5487 }
5488 }
5489
5490 ident_stack.push(futures_ident);
5491 }
5492
5493 HydroNode::Map {
5494 f,
5495 input,
5496 metadata,
5497 } => {
5498 let input_ident = ident_stack.pop().unwrap();
5500 let f_tokens = f.emit_tokens(&mut ident_stack);
5501
5502 let input_ident = maybe_observe_for_mut(
5503 f,
5504 input_ident,
5505 &input.metadata().location_id,
5506 &input.metadata().collection_kind,
5507 &metadata.op,
5508 builders_or_callback,
5509 next_stmt_id,
5510 );
5511
5512 let stmt_id = next_stmt_id.get_and_increment();
5513 let map_ident =
5514 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5515
5516 let map_op: TokenStream = match (
5523 update_feed_repr(input.metadata()),
5524 update_feed_repr(metadata),
5525 ) {
5526 (UpdateFeedRepr::Plain, UpdateFeedRepr::Plain) => {
5527 quote! { map(#f_tokens) }
5528 }
5529 (UpdateFeedRepr::Tombstoned, UpdateFeedRepr::Tombstoned) => {
5530 match &metadata.collection_kind {
5531 CollectionKind::Optional { .. } => {
5532 quote! {
5533 map({
5534 let mut __hydro_map_f = #f_tokens;
5535 move |__hydro_update| __hydro_update.map(&mut __hydro_map_f)
5536 })
5537 }
5538 }
5539 CollectionKind::KeyedSingleton { .. } => {
5540 quote! {
5541 map({
5542 let mut __hydro_map_f = #f_tokens;
5543 move |(__hydro_key, __hydro_update)| match __hydro_update {
5544 ::std::option::Option::Some(__hydro_value) => {
5545 let (__hydro_out_key, __hydro_out_value) =
5546 (__hydro_map_f)((__hydro_key, __hydro_value));
5547 (__hydro_out_key, ::std::option::Option::Some(__hydro_out_value))
5548 }
5549 ::std::option::Option::None => {
5550 (__hydro_key, ::std::option::Option::None)
5551 }
5552 }
5553 })
5554 }
5555 }
5556 other => panic!(
5557 "unexpected tombstoned update feed kind for `map`: {:?}",
5558 other
5559 ),
5560 }
5561 }
5562 (in_repr, out_repr) => panic!(
5563 "map may not change the update feed representation ({:?} -> {:?})",
5564 in_repr, out_repr
5565 ),
5566 };
5567
5568 match builders_or_callback {
5569 BuildersOrCallback::Builders(graph_builders) => {
5570 graph_builders.add_dfir_at(
5571 &out_location,
5572 parse_quote! {
5573 #map_ident = #input_ident -> #map_op;
5574 },
5575 Some(&stmt_id.to_string()),
5576 );
5577 }
5578 BuildersOrCallback::Callback(_, node_callback) => {
5579 node_callback(node, next_stmt_id);
5580 }
5581 }
5582
5583 ident_stack.push(map_ident);
5584 }
5585
5586 HydroNode::FlatMap { f, input, metadata } => {
5587 let input_ident = ident_stack.pop().unwrap();
5588 let f_tokens = f.emit_tokens(&mut ident_stack);
5589
5590 let input_ident = maybe_observe_for_mut(
5591 f, input_ident,
5592 &input.metadata().location_id,
5593 &input.metadata().collection_kind,
5594 &metadata.op,
5595 builders_or_callback, next_stmt_id,
5596 );
5597
5598 let stmt_id = next_stmt_id.get_and_increment();
5599 let flat_map_ident =
5600 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5601
5602 match builders_or_callback {
5603 BuildersOrCallback::Builders(graph_builders) => {
5604 graph_builders.add_dfir_at(
5605 &out_location,
5606 parse_quote! {
5607 #flat_map_ident = #input_ident -> flat_map(#f_tokens);
5608 },
5609 Some(&stmt_id.to_string()),
5610 );
5611 }
5612 BuildersOrCallback::Callback(_, node_callback) => {
5613 node_callback(node, next_stmt_id);
5614 }
5615 }
5616
5617 ident_stack.push(flat_map_ident);
5618 }
5619
5620 HydroNode::FlatMapStreamBlocking { f, input, metadata } => {
5621 let input_ident = ident_stack.pop().unwrap();
5622 let f_tokens = f.emit_tokens(&mut ident_stack);
5623
5624 let input_ident = maybe_observe_for_mut(
5625 f, input_ident,
5626 &input.metadata().location_id,
5627 &input.metadata().collection_kind,
5628 &metadata.op,
5629 builders_or_callback, next_stmt_id,
5630 );
5631
5632 let stmt_id = next_stmt_id.get_and_increment();
5633 let flat_map_stream_blocking_ident =
5634 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5635
5636 match builders_or_callback {
5637 BuildersOrCallback::Builders(graph_builders) => {
5638 graph_builders.add_dfir_at(
5639 &out_location,
5640 parse_quote! {
5641 #flat_map_stream_blocking_ident = #input_ident -> flat_map_stream_blocking(#f_tokens);
5642 },
5643 Some(&stmt_id.to_string()),
5644 );
5645 }
5646 BuildersOrCallback::Callback(_, node_callback) => {
5647 node_callback(node, next_stmt_id);
5648 }
5649 }
5650
5651 ident_stack.push(flat_map_stream_blocking_ident);
5652 }
5653
5654 HydroNode::Filter { f, input, metadata } => {
5655 let input_ident = ident_stack.pop().unwrap();
5656 let f_tokens = f.emit_tokens(&mut ident_stack);
5657
5658 let input_ident = maybe_observe_for_mut(
5659 f, input_ident,
5660 &input.metadata().location_id,
5661 &input.metadata().collection_kind,
5662 &metadata.op,
5663 builders_or_callback, next_stmt_id,
5664 );
5665
5666 let stmt_id = next_stmt_id.get_and_increment();
5667 let filter_ident =
5668 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5669
5670 let filter_op: TokenStream = match (
5679 update_feed_repr(input.metadata()),
5680 update_feed_repr(metadata),
5681 ) {
5682 (UpdateFeedRepr::Plain, UpdateFeedRepr::Plain) => {
5683 quote! { filter(#f_tokens) }
5684 }
5685 (UpdateFeedRepr::Plain, UpdateFeedRepr::Tombstoned) => {
5686 match &metadata.collection_kind {
5687 CollectionKind::Optional { .. } => {
5688 quote! {
5689 map({
5690 let mut __hydro_filter_f = #f_tokens;
5691 move |__hydro_value| {
5692 if (__hydro_filter_f)(&__hydro_value) {
5693 Some(__hydro_value)
5694 } else {
5695 None
5696 }
5697 }
5698 })
5699 }
5700 }
5701 other => panic!(
5705 "unexpected tombstoned update feed kind for `filter`: {:?}",
5706 other
5707 ),
5708 }
5709 }
5710 (UpdateFeedRepr::Tombstoned, UpdateFeedRepr::Tombstoned) => {
5711 match &metadata.collection_kind {
5712 CollectionKind::Optional { .. } => {
5713 quote! {
5714 map({
5715 let mut __hydro_filter_f = #f_tokens;
5716 move |__hydro_update: Option<_>| {
5717 __hydro_update.filter(|__hydro_value| (__hydro_filter_f)(__hydro_value))
5718 }
5719 })
5720 }
5721 }
5722 CollectionKind::KeyedSingleton { .. } => {
5723 quote! {
5726 map({
5727 let mut __hydro_filter_f = #f_tokens;
5728 move |(__hydro_key, __hydro_update)| match __hydro_update {
5729 ::std::option::Option::Some(__hydro_value) => {
5730 let __hydro_entry = (__hydro_key, __hydro_value);
5731 if (__hydro_filter_f)(&__hydro_entry) {
5732 let (__hydro_key, __hydro_value) = __hydro_entry;
5733 (__hydro_key, ::std::option::Option::Some(__hydro_value))
5734 } else {
5735 let (__hydro_key, _) = __hydro_entry;
5736 (__hydro_key, ::std::option::Option::None)
5737 }
5738 }
5739 ::std::option::Option::None => {
5740 (__hydro_key, ::std::option::Option::None)
5741 }
5742 }
5743 })
5744 }
5745 }
5746 other => panic!(
5747 "unexpected tombstoned update feed kind for `filter`: {:?}",
5748 other
5749 ),
5750 }
5751 }
5752 (in_repr, out_repr) => panic!(
5753 "filter may not un-tombstone an update feed ({:?} -> {:?})",
5754 in_repr, out_repr
5755 ),
5756 };
5757
5758 match builders_or_callback {
5759 BuildersOrCallback::Builders(graph_builders) => {
5760 graph_builders.add_dfir_at(
5761 &out_location,
5762 parse_quote! {
5763 #filter_ident = #input_ident -> #filter_op;
5764 },
5765 Some(&stmt_id.to_string()),
5766 );
5767 }
5768 BuildersOrCallback::Callback(_, node_callback) => {
5769 node_callback(node, next_stmt_id);
5770 }
5771 }
5772
5773 ident_stack.push(filter_ident);
5774 }
5775
5776 HydroNode::FilterMap { f, input, metadata } => {
5777 let input_ident = ident_stack.pop().unwrap();
5778 let f_tokens = f.emit_tokens(&mut ident_stack);
5779
5780 let input_ident = maybe_observe_for_mut(
5781 f, input_ident,
5782 &input.metadata().location_id,
5783 &input.metadata().collection_kind,
5784 &metadata.op,
5785 builders_or_callback, next_stmt_id,
5786 );
5787
5788 let stmt_id = next_stmt_id.get_and_increment();
5789 let filter_map_ident =
5790 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5791
5792 let filter_map_op: TokenStream = match (
5802 update_feed_repr(input.metadata()),
5803 update_feed_repr(metadata),
5804 ) {
5805 (UpdateFeedRepr::Plain, UpdateFeedRepr::Plain) => {
5806 quote! { filter_map(#f_tokens) }
5807 }
5808 (UpdateFeedRepr::Plain, UpdateFeedRepr::Tombstoned) => {
5809 match &metadata.collection_kind {
5810 CollectionKind::Optional { .. } => {
5811 quote! { map(#f_tokens) }
5812 }
5813 other => panic!(
5818 "unexpected tombstoned update feed kind for `filter_map`: {:?}",
5819 other
5820 ),
5821 }
5822 }
5823 (UpdateFeedRepr::Tombstoned, UpdateFeedRepr::Tombstoned) => {
5824 match &metadata.collection_kind {
5825 CollectionKind::Optional { .. } => {
5826 quote! {
5827 map({
5828 let mut __hydro_filter_map_f = #f_tokens;
5829 move |__hydro_update| {
5830 __hydro_update.and_then(&mut __hydro_filter_map_f)
5831 }
5832 })
5833 }
5834 }
5835 CollectionKind::KeyedSingleton { .. } => {
5836 quote! {
5837 map({
5838 let mut __hydro_filter_map_f = #f_tokens;
5839 move |(__hydro_key, __hydro_update)| match __hydro_update {
5840 ::std::option::Option::Some(__hydro_value) => {
5841 let __hydro_key_backup =
5842 ::std::clone::Clone::clone(&__hydro_key);
5843 match (__hydro_filter_map_f)((__hydro_key, __hydro_value)) {
5844 ::std::option::Option::Some((__hydro_out_key, __hydro_out_value)) => {
5845 (__hydro_out_key, ::std::option::Option::Some(__hydro_out_value))
5846 }
5847 ::std::option::Option::None => {
5848 (__hydro_key_backup, ::std::option::Option::None)
5849 }
5850 }
5851 }
5852 ::std::option::Option::None => {
5853 (__hydro_key, ::std::option::Option::None)
5854 }
5855 }
5856 })
5857 }
5858 }
5859 other => panic!(
5860 "unexpected tombstoned update feed kind for `filter_map`: {:?}",
5861 other
5862 ),
5863 }
5864 }
5865 (in_repr, out_repr) => panic!(
5866 "filter_map may not un-tombstone an update feed ({:?} -> {:?})",
5867 in_repr, out_repr
5868 ),
5869 };
5870
5871 match builders_or_callback {
5872 BuildersOrCallback::Builders(graph_builders) => {
5873 graph_builders.add_dfir_at(
5874 &out_location,
5875 parse_quote! {
5876 #filter_map_ident = #input_ident -> #filter_map_op;
5877 },
5878 Some(&stmt_id.to_string()),
5879 );
5880 }
5881 BuildersOrCallback::Callback(_, node_callback) => {
5882 node_callback(node, next_stmt_id);
5883 }
5884 }
5885
5886 ident_stack.push(filter_map_ident);
5887 }
5888
5889 HydroNode::Sort { .. } => {
5890 let input_ident = ident_stack.pop().unwrap();
5891
5892 let stmt_id = next_stmt_id.get_and_increment();
5893 let sort_ident =
5894 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5895
5896 match builders_or_callback {
5897 BuildersOrCallback::Builders(graph_builders) => {
5898 graph_builders.add_dfir_at(
5899 &out_location,
5900 parse_quote! {
5901 #sort_ident = #input_ident -> sort();
5902 },
5903 Some(&stmt_id.to_string()),
5904 );
5905 }
5906 BuildersOrCallback::Callback(_, node_callback) => {
5907 node_callback(node, next_stmt_id);
5908 }
5909 }
5910
5911 ident_stack.push(sort_ident);
5912 }
5913
5914 HydroNode::DeferTick { .. } => {
5915 let input_ident = ident_stack.pop().unwrap();
5916
5917 let stmt_id = next_stmt_id.get_and_increment();
5918 let defer_tick_ident =
5919 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5920
5921 match builders_or_callback {
5922 BuildersOrCallback::Builders(graph_builders) => {
5923 graph_builders.add_dfir_at(
5924 &out_location,
5925 parse_quote! {
5926 #defer_tick_ident = #input_ident -> defer_tick_lazy();
5927 },
5928 Some(&stmt_id.to_string()),
5929 );
5930 }
5931 BuildersOrCallback::Callback(_, node_callback) => {
5932 node_callback(node, next_stmt_id);
5933 }
5934 }
5935
5936 ident_stack.push(defer_tick_ident);
5937 }
5938
5939 HydroNode::Enumerate { input, .. } => {
5940 let input_ident = ident_stack.pop().unwrap();
5941
5942 let stmt_id = next_stmt_id.get_and_increment();
5943 let enumerate_ident =
5944 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5945
5946 match builders_or_callback {
5947 BuildersOrCallback::Builders(graph_builders) => {
5948 let lifetime = if input.metadata().location_id.is_top_level() {
5949 graph_builders.cross_tick_state_lifetime(&out_location)
5950 } else {
5951 graph_builders.tick_state_lifetime(&out_location)
5952 };
5953 graph_builders.add_dfir_at(
5954 &out_location,
5955 parse_quote! {
5956 #enumerate_ident = #input_ident -> enumerate::<#lifetime>();
5957 },
5958 Some(&stmt_id.to_string()),
5959 );
5960 }
5961 BuildersOrCallback::Callback(_, node_callback) => {
5962 node_callback(node, next_stmt_id);
5963 }
5964 }
5965
5966 ident_stack.push(enumerate_ident);
5967 }
5968
5969 HydroNode::Inspect { f, input, metadata } => {
5970 let input_ident = ident_stack.pop().unwrap();
5971 let f_tokens = f.emit_tokens(&mut ident_stack);
5972
5973 let input_ident = maybe_observe_for_mut(
5974 f, input_ident,
5975 &input.metadata().location_id,
5976 &input.metadata().collection_kind,
5977 &metadata.op,
5978 builders_or_callback, next_stmt_id,
5979 );
5980
5981 let stmt_id = next_stmt_id.get_and_increment();
5982 let inspect_ident =
5983 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5984
5985 if update_feed_repr(input.metadata()) == UpdateFeedRepr::Tombstoned
5993 || update_feed_repr(metadata) == UpdateFeedRepr::Tombstoned
5994 {
5995 todo!(
5996 "`inspect` on a tombstoned update feed (root unbounded \
5997 Optional/KeyedSingleton) is not yet supported"
5998 );
5999 }
6000
6001 match builders_or_callback {
6002 BuildersOrCallback::Builders(graph_builders) => {
6003 graph_builders.add_dfir_at(
6004 &out_location,
6005 parse_quote! {
6006 #inspect_ident = #input_ident -> inspect(#f_tokens);
6007 },
6008 Some(&stmt_id.to_string()),
6009 );
6010 }
6011 BuildersOrCallback::Callback(_, node_callback) => {
6012 node_callback(node, next_stmt_id);
6013 }
6014 }
6015
6016 ident_stack.push(inspect_ident);
6017 }
6018
6019 HydroNode::Unique { input, .. } => {
6020 let input_ident = ident_stack.pop().unwrap();
6021
6022 let stmt_id = next_stmt_id.get_and_increment();
6023 let unique_ident =
6024 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
6025
6026 match builders_or_callback {
6027 BuildersOrCallback::Builders(graph_builders) => {
6028 let lifetime = if input.metadata().location_id.is_top_level() {
6029 graph_builders.cross_tick_state_lifetime(&out_location)
6030 } else {
6031 graph_builders.tick_state_lifetime(&out_location)
6032 };
6033
6034 graph_builders.add_dfir_at(
6035 &out_location,
6036 parse_quote! {
6037 #unique_ident = #input_ident -> unique::<#lifetime>();
6038 },
6039 Some(&stmt_id.to_string()),
6040 );
6041 }
6042 BuildersOrCallback::Callback(_, node_callback) => {
6043 node_callback(node, next_stmt_id);
6044 }
6045 }
6046
6047 ident_stack.push(unique_ident);
6048 }
6049
6050 HydroNode::Fold { .. } | HydroNode::FoldKeyed { .. } | HydroNode::Scan { .. } | HydroNode::ScanAsyncBlocking { .. } => {
6051 let operator: syn::Ident = if let HydroNode::Fold { input, .. } = node {
6052 if input.metadata().location_id.is_root()
6053 && input.metadata().collection_kind.is_bounded()
6054 {
6055 parse_quote!(fold_no_replay)
6056 } else {
6057 parse_quote!(fold)
6058 }
6059 } else if matches!(node, HydroNode::Scan { .. }) {
6060 parse_quote!(scan)
6061 } else if matches!(node, HydroNode::ScanAsyncBlocking { .. }) {
6062 parse_quote!(scan_async_blocking)
6063 } else if let HydroNode::FoldKeyed { input, .. } = node {
6064 if input.metadata().location_id.is_root()
6065 && input.metadata().collection_kind.is_bounded()
6066 {
6067 todo!("Fold keyed on a top-level bounded collection is not yet supported")
6068 } else {
6069 parse_quote!(fold_keyed)
6070 }
6071 } else {
6072 unreachable!()
6073 };
6074
6075 let (HydroNode::Fold { input, .. }
6076 | HydroNode::FoldKeyed { input, .. }
6077 | HydroNode::Scan { input, .. }
6078 | HydroNode::ScanAsyncBlocking { input, .. }) = node
6079 else {
6080 unreachable!()
6081 };
6082
6083 let input_top_level = input.metadata().location_id.is_top_level();
6084
6085 let input_ident = ident_stack.pop().unwrap();
6086
6087 let (HydroNode::Fold { init, acc, .. }
6088 | HydroNode::FoldKeyed { init, acc, .. }
6089 | HydroNode::Scan { init, acc, .. }
6090 | HydroNode::ScanAsyncBlocking { init, acc, .. }) = &*node
6091 else {
6092 unreachable!()
6093 };
6094
6095 let acc_tokens = acc.emit_tokens(&mut ident_stack);
6096 let init_tokens = init.emit_tokens(&mut ident_stack);
6097
6098 let stmt_id = next_stmt_id.get_and_increment();
6099 let fold_ident =
6100 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
6101
6102 match builders_or_callback {
6103 BuildersOrCallback::Builders(graph_builders) => {
6104 let lifetime = if input_top_level {
6105 graph_builders.cross_tick_state_lifetime(&out_location)
6106 } else {
6107 graph_builders.tick_state_lifetime(&out_location)
6108 };
6109
6110 if matches!(node, HydroNode::Fold { .. })
6111 && node.metadata().location_id.is_root()
6112 && graph_builders.singleton_intermediates()
6113 && !node.metadata().collection_kind.is_bounded()
6114 {
6115 let HydroNode::Fold { input, .. } = &*node else { unreachable!() };
6116 let hooked_input_ident = graph_builders.emit_fold_hook(
6117 &input.metadata().location_id,
6118 &input_ident,
6119 &input.metadata().collection_kind,
6120 &node.metadata().op,
6121 );
6122
6123 let (effective_input, wrapped_acc) = if let Some(ref hooked) = hooked_input_ident {
6124 let acc: syn::Expr = parse_quote!({
6125 let mut __inner = #acc_tokens;
6126 move |__state, __batch: Vec<_>| {
6127 if __batch.is_empty() {
6128 return None;
6129 }
6130 for __value in __batch {
6131 __inner(__state, __value);
6132 }
6133 Some(__state.clone())
6134 }
6135 });
6136 (hooked, acc)
6137 } else {
6138 let acc: syn::Expr = parse_quote!({
6139 let mut __inner = #acc_tokens;
6140 move |__state, __value| {
6141 __inner(__state, __value);
6142 Some(__state.clone())
6143 }
6144 });
6145 (&input_ident, acc)
6146 };
6147
6148 graph_builders.add_dfir_at(
6149 &out_location,
6150 parse_quote! {
6151 source_iter([(#init_tokens)()]) -> [0]#fold_ident;
6152 #effective_input -> scan::<#lifetime>(#init_tokens, #wrapped_acc) -> [1]#fold_ident;
6153 #fold_ident = chain();
6154 },
6155 Some(&stmt_id.to_string()),
6156 );
6157
6158 if hooked_input_ident.is_some()
6163 && node.metadata().op.sim_hook_id.is_none()
6164 {
6165 fold_hooked_idents.insert(fold_ident.to_string());
6166 }
6167 } else if matches!(node, HydroNode::FoldKeyed { .. })
6168 && node.metadata().location_id.is_root()
6169 && graph_builders.singleton_intermediates()
6170 && !node.metadata().collection_kind.is_bounded()
6171 {
6172 let HydroNode::FoldKeyed { input, .. } = &*node else { unreachable!() };
6173 let hooked_input_ident = graph_builders.emit_fold_hook(
6174 &input.metadata().location_id,
6175 &input_ident,
6176 &input.metadata().collection_kind,
6177 &node.metadata().op,
6178 );
6179
6180 let wrapped_acc: syn::Expr = parse_quote!({
6181 let mut __init = #init_tokens;
6182 let mut __inner = #acc_tokens;
6183 move |__state, __kv: (_, _)| {
6184 let __state = __state
6186 .entry(::std::clone::Clone::clone(&__kv.0))
6187 .or_insert_with(|| (__init)());
6188 __inner(__state, __kv.1);
6189 Some((__kv.0, ::std::clone::Clone::clone(&*__state)))
6190 }
6191 });
6192
6193 if let Some(hooked_input_ident) = hooked_input_ident {
6194 graph_builders.add_dfir_at(
6195 &out_location,
6196 parse_quote! {
6197 #fold_ident = #hooked_input_ident -> flatten() -> scan::<#lifetime>(|| ::std::collections::HashMap::new(), #wrapped_acc);
6198 },
6199 Some(&stmt_id.to_string()),
6200 );
6201
6202 fold_hooked_idents.insert(fold_ident.to_string());
6203 } else {
6204 graph_builders.add_dfir_at(
6205 &out_location,
6206 parse_quote! {
6207 #fold_ident = #input_ident -> scan::<#lifetime>(|| ::std::collections::HashMap::new(), #wrapped_acc);
6208 },
6209 Some(&stmt_id.to_string()),
6210 );
6211 }
6212 } else if (matches!(node, HydroNode::Fold { .. })
6213 || matches!(node, HydroNode::FoldKeyed { .. }))
6214 && !node.metadata().location_id.is_top_level()
6215 && graph_builders.singleton_intermediates()
6216 {
6217 let input_ref = match &*node {
6218 HydroNode::Fold { input, .. } => input,
6219 HydroNode::FoldKeyed { input, .. } => input,
6220 _ => unreachable!(),
6221 };
6222 let hooked_input_ident = graph_builders.emit_fold_hook(
6223 &input_ref.metadata().location_id,
6224 &input_ident,
6225 &input_ref.metadata().collection_kind,
6226 &node.metadata().op,
6227 );
6228
6229 let actual_input = hooked_input_ident.as_ref().unwrap_or(&input_ident);
6230 graph_builders.add_dfir_at(
6231 &out_location,
6232 parse_quote! {
6233 #fold_ident = #actual_input -> #operator::<#lifetime>(#init_tokens, #acc_tokens);
6234 },
6235 Some(&stmt_id.to_string()),
6236 );
6237 } else {
6238 assert_eq!(
6244 update_feed_repr(node.metadata()),
6245 UpdateFeedRepr::Plain,
6246 "`fold`-family aggregation into a tombstoned update feed \
6247 (root unbounded Optional/KeyedSingleton) is not supported"
6248 );
6249 graph_builders.add_dfir_at(
6250 &out_location,
6251 parse_quote! {
6252 #fold_ident = #input_ident -> #operator::<#lifetime>(#init_tokens, #acc_tokens);
6253 },
6254 Some(&stmt_id.to_string()),
6255 );
6256 }
6257 }
6258 BuildersOrCallback::Callback(_, node_callback) => {
6259 node_callback(node, next_stmt_id);
6260 }
6261 }
6262
6263 ident_stack.push(fold_ident);
6264 }
6265
6266 HydroNode::Reduce { .. } | HydroNode::ReduceKeyed { .. } => {
6267 let operator: syn::Ident = if let HydroNode::Reduce { input, .. } = node {
6268 if input.metadata().location_id.is_root()
6269 && input.metadata().collection_kind.is_bounded()
6270 {
6271 parse_quote!(reduce_no_replay)
6272 } else {
6273 parse_quote!(reduce)
6274 }
6275 } else if let HydroNode::ReduceKeyed { input, .. } = node {
6276 if input.metadata().location_id.is_root()
6277 && input.metadata().collection_kind.is_bounded()
6278 {
6279 todo!(
6280 "Calling keyed reduce on a top-level bounded collection is not supported"
6281 )
6282 } else {
6283 parse_quote!(reduce_keyed)
6284 }
6285 } else {
6286 unreachable!()
6287 };
6288
6289 let (HydroNode::Reduce { input, .. } | HydroNode::ReduceKeyed { input, .. }) = node
6290 else {
6291 unreachable!()
6292 };
6293
6294 let input_top_level = input.metadata().location_id.is_top_level();
6295
6296 let input_ident = ident_stack.pop().unwrap();
6297
6298 let (HydroNode::Reduce { f, .. } | HydroNode::ReduceKeyed { f, .. }) = &*node
6299 else {
6300 unreachable!()
6301 };
6302
6303 let f_tokens = f.emit_tokens(&mut ident_stack);
6304
6305 let stmt_id = next_stmt_id.get_and_increment();
6306 let reduce_ident =
6307 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
6308
6309 match builders_or_callback {
6310 BuildersOrCallback::Builders(graph_builders) => {
6311 let lifetime = if input_top_level {
6312 graph_builders.cross_tick_state_lifetime(&out_location)
6313 } else {
6314 graph_builders.tick_state_lifetime(&out_location)
6315 };
6316
6317 if matches!(node, HydroNode::Reduce { .. })
6318 && node.metadata().location_id.is_root()
6319 && graph_builders.singleton_intermediates()
6320 && !node.metadata().collection_kind.is_bounded()
6321 {
6322 todo!(
6323 "Reduce with optional intermediates is not yet supported in simulator"
6324 );
6325 } else if matches!(node, HydroNode::ReduceKeyed { .. })
6326 && node.metadata().location_id.is_root()
6327 && graph_builders.singleton_intermediates()
6328 && !node.metadata().collection_kind.is_bounded()
6329 {
6330 todo!(
6331 "Reduce keyed with optional intermediates is not yet supported in simulator"
6332 );
6333 } else {
6334 let wrap_op: TokenStream = if matches!(
6339 node.metadata().collection_kind,
6340 CollectionKind::KeyedSingleton { .. }
6341 ) && update_feed_repr(node.metadata())
6342 == UpdateFeedRepr::Tombstoned
6343 {
6344 quote! { -> map(|(__hydro_key, __hydro_value)| (__hydro_key, ::std::option::Option::Some(__hydro_value))) }
6345 } else {
6346 quote! {}
6347 };
6348 graph_builders.add_dfir_at(
6349 &out_location,
6350 parse_quote! {
6351 #reduce_ident = #input_ident -> #operator::<#lifetime>(#f_tokens) #wrap_op;
6352 },
6353 Some(&stmt_id.to_string()),
6354 );
6355 }
6356 }
6357 BuildersOrCallback::Callback(_, node_callback) => {
6358 node_callback(node, next_stmt_id);
6359 }
6360 }
6361
6362 ident_stack.push(reduce_ident);
6363 }
6364
6365 HydroNode::ReduceKeyedWatermark {
6366 f,
6367 input,
6368 watermark,
6369 metadata,
6370 } => {
6371
6372 let watermark_ident = ident_stack.pop().unwrap();
6374 let watermark_location = watermark.metadata().location_id.clone();
6375 let input_ident = ident_stack.pop().unwrap();
6376 let f_tokens = f.emit_tokens(&mut ident_stack);
6377
6378 let stmt_id = next_stmt_id.get_and_increment();
6379 let chain_ident = syn::Ident::new(
6380 &format!("reduce_keyed_watermark_chain_{}", stmt_id),
6381 Span::call_site(),
6382 );
6383
6384 let fold_ident =
6385 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
6386
6387 let agg_operator: syn::Ident = if input.metadata().location_id.is_root()
6388 && input.metadata().collection_kind.is_bounded()
6389 {
6390 parse_quote!(fold_no_replay)
6391 } else {
6392 parse_quote!(fold)
6393 };
6394
6395 match builders_or_callback {
6396 BuildersOrCallback::Builders(graph_builders) => {
6397 let watermark_ident = graph_builders.unwindow_for_consume(
6402 watermark_ident,
6403 &watermark_location,
6404 &out_location,
6405 );
6406
6407 let lifetime = if input.metadata().location_id.is_top_level() {
6408 graph_builders.cross_tick_state_lifetime(&out_location)
6409 } else {
6410 graph_builders.tick_state_lifetime(&out_location)
6411 };
6412
6413 if metadata.location_id.is_root()
6414 && graph_builders.singleton_intermediates()
6415 && !metadata.collection_kind.is_bounded()
6416 {
6417 todo!(
6418 "Reduce keyed watermarked on a top-level bounded collection is not yet supported"
6419 )
6420 } else if update_feed_repr(metadata) == UpdateFeedRepr::Tombstoned {
6421 graph_builders.add_dfir_at(
6436 &out_location,
6437 parse_quote! {
6438 #chain_ident = chain();
6439 #input_ident
6440 -> map(|x| (Some(x), None))
6441 -> [0]#chain_ident;
6442 #watermark_ident
6443 -> map(|watermark| (None, Some(watermark)))
6444 -> [1]#chain_ident;
6445
6446 #fold_ident = #chain_ident
6447 -> scan::<#lifetime>(|| (::std::collections::HashMap::new(), None), {
6448 let __reduce_keyed_fn = #f_tokens;
6449 move |(map, opt_curr_watermark), (opt_payload, opt_watermark)| {
6450 let mut deltas = ::std::vec::Vec::new();
6451 if let Some((k, v)) = opt_payload {
6452 if let Some(curr_watermark) = *opt_curr_watermark {
6453 if k < curr_watermark {
6454 return Some(deltas);
6455 }
6456 }
6457 match map.entry(k) {
6458 ::std::collections::hash_map::Entry::Vacant(e) => {
6459 let key = ::std::clone::Clone::clone(e.key());
6460 let value = ::std::clone::Clone::clone(e.insert(v));
6461 deltas.push((key, ::std::option::Option::Some(value)));
6462 }
6463 ::std::collections::hash_map::Entry::Occupied(mut e) => {
6464 __reduce_keyed_fn(e.get_mut(), v);
6465 deltas.push((
6466 ::std::clone::Clone::clone(e.key()),
6467 ::std::option::Option::Some(::std::clone::Clone::clone(e.get())),
6468 ));
6469 }
6470 }
6471 } else {
6472 let watermark = opt_watermark.unwrap();
6473 if let Some(curr_watermark) = *opt_curr_watermark {
6474 if watermark <= curr_watermark {
6475 return Some(deltas);
6476 }
6477 }
6478 map.retain(|k, _| {
6479 if *k >= watermark {
6480 true
6481 } else {
6482 deltas.push((
6483 ::std::clone::Clone::clone(k),
6484 ::std::option::Option::None,
6485 ));
6486 false
6487 }
6488 });
6489 *opt_curr_watermark = Some(watermark);
6490 }
6491 Some(deltas)
6492 }
6493 })
6494 -> flatten();
6495 },
6496 Some(&stmt_id.to_string()),
6497 );
6498 } else {
6499 graph_builders.add_dfir_at(
6500 &out_location,
6501 parse_quote! {
6502 #chain_ident = chain();
6503 #input_ident
6504 -> map(|x| (Some(x), None))
6505 -> [0]#chain_ident;
6506 #watermark_ident
6507 -> map(|watermark| (None, Some(watermark)))
6508 -> [1]#chain_ident;
6509
6510 #fold_ident = #chain_ident
6511 -> #agg_operator::<#lifetime>(|| (::std::collections::HashMap::new(), None), {
6512 let __reduce_keyed_fn = #f_tokens;
6513 move |(map, opt_curr_watermark), (opt_payload, opt_watermark)| {
6514 if let Some((k, v)) = opt_payload {
6515 if let Some(curr_watermark) = *opt_curr_watermark {
6516 if k < curr_watermark {
6517 return;
6518 }
6519 }
6520 match map.entry(k) {
6521 ::std::collections::hash_map::Entry::Vacant(e) => {
6522 e.insert(v);
6523 }
6524 ::std::collections::hash_map::Entry::Occupied(mut e) => {
6525 __reduce_keyed_fn(e.get_mut(), v);
6526 }
6527 }
6528 } else {
6529 let watermark = opt_watermark.unwrap();
6530 if let Some(curr_watermark) = *opt_curr_watermark {
6531 if watermark <= curr_watermark {
6532 return;
6533 }
6534 }
6535 map.retain(|k, _| *k >= watermark);
6536 *opt_curr_watermark = Some(watermark);
6537 }
6538 }
6539 })
6540 -> flat_map(|(map, _curr_watermark)| map);
6541 },
6542 Some(&stmt_id.to_string()),
6543 );
6544 }
6545 }
6546 BuildersOrCallback::Callback(_, node_callback) => {
6547 node_callback(node, next_stmt_id);
6548 }
6549 }
6550
6551 ident_stack.push(fold_ident);
6552 }
6553
6554 HydroNode::Network {
6555 networking_info,
6556 serialize,
6557 deserialize,
6558 instantiate_fn,
6559 input,
6560 ..
6561 } => {
6562 let input_ident = ident_stack.pop().unwrap();
6563
6564 let stmt_id = next_stmt_id.get_and_increment();
6565 let receiver_stream_ident =
6566 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
6567
6568 let serialize_pipeline = serialize.pipeline();
6571 let deserialize_pipeline = deserialize.pipeline();
6572
6573 match builders_or_callback {
6574 BuildersOrCallback::Builders(graph_builders) => {
6575 let (sink_expr, source_expr) = match instantiate_fn {
6576 DebugInstantiate::Building => (
6577 syn::parse_quote!(DUMMY_SINK),
6578 syn::parse_quote!(DUMMY_SOURCE),
6579 ),
6580
6581 DebugInstantiate::Finalized(finalized) => {
6582 (finalized.sink.clone(), finalized.source.clone())
6583 }
6584 };
6585
6586 graph_builders.create_network(
6587 &input.metadata().location_id,
6588 &out_location,
6589 input_ident,
6590 &receiver_stream_ident,
6591 serialize_pipeline.as_ref(),
6592 sink_expr,
6593 source_expr,
6594 deserialize_pipeline.as_ref(),
6595 serialize.external_element_type(),
6596 stmt_id,
6597 networking_info,
6598 );
6599 }
6600 BuildersOrCallback::Callback(_, node_callback) => {
6601 node_callback(node, next_stmt_id);
6602 }
6603 }
6604
6605 ident_stack.push(receiver_stream_ident);
6606 }
6607
6608 HydroNode::ExternalInput {
6609 instantiate_fn,
6610 deserialize_fn: deserialize_pipeline,
6611 ..
6612 } => {
6613 let stmt_id = next_stmt_id.get_and_increment();
6614 let receiver_stream_ident =
6615 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
6616
6617 match builders_or_callback {
6618 BuildersOrCallback::Builders(graph_builders) => {
6619 let (_, source_expr) = match instantiate_fn {
6620 DebugInstantiate::Building => (
6621 syn::parse_quote!(DUMMY_SINK),
6622 syn::parse_quote!(DUMMY_SOURCE),
6623 ),
6624
6625 DebugInstantiate::Finalized(finalized) => {
6626 (finalized.sink.clone(), finalized.source.clone())
6627 }
6628 };
6629
6630 graph_builders.create_external_source(
6631 &out_location,
6632 source_expr,
6633 &receiver_stream_ident,
6634 deserialize_pipeline.as_ref(),
6635 stmt_id,
6636 );
6637 }
6638 BuildersOrCallback::Callback(_, node_callback) => {
6639 node_callback(node, next_stmt_id);
6640 }
6641 }
6642
6643 ident_stack.push(receiver_stream_ident);
6644 }
6645
6646 HydroNode::Counter {
6647 tag,
6648 duration,
6649 prefix,
6650 ..
6651 } => {
6652 let input_ident = ident_stack.pop().unwrap();
6653
6654 let stmt_id = next_stmt_id.get_and_increment();
6655 let counter_ident =
6656 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
6657
6658 match builders_or_callback {
6659 BuildersOrCallback::Builders(graph_builders) => {
6660 let arg = format!("{}({})", prefix, tag);
6661 graph_builders.add_dfir_at(
6662 &out_location,
6663 parse_quote! {
6664 #counter_ident = #input_ident -> _counter(#arg, #duration);
6665 },
6666 Some(&stmt_id.to_string()),
6667 );
6668 }
6669 BuildersOrCallback::Callback(_, node_callback) => {
6670 node_callback(node, next_stmt_id);
6671 }
6672 }
6673
6674 ident_stack.push(counter_ident);
6675 }
6676
6677 HydroNode::VersionedNetworkFork {
6678 channel_id,
6679 senders,
6680 metadata,
6681 ..
6682 } => {
6683 let split_at = ident_stack.len() - senders.len();
6685 let sender_idents = ident_stack.split_off(split_at);
6686
6687 let stmt_id = next_stmt_id.get_and_increment();
6688
6689 let external_element_type =
6692 senders.first().and_then(|(_, _, s)| s.external_element_type());
6693
6694 match builders_or_callback {
6695 BuildersOrCallback::Builders(graph_builders) => {
6696 let sender_args: Vec<(LocationId, syn::Ident, Option<DebugExpr>)> =
6697 senders
6698 .iter()
6699 .zip(sender_idents)
6700 .map(|((_version, sender, serialize), ident)| {
6701 (
6702 sender.metadata().location_id.clone(),
6703 ident,
6704 serialize.pipeline(),
6705 )
6706 })
6707 .collect();
6708 graph_builders.create_versioned_network_fork(
6709 *channel_id,
6710 &metadata.location_id,
6711 sender_args,
6712 external_element_type,
6713 stmt_id,
6714 );
6715 }
6716 BuildersOrCallback::Callback(_, node_callback) => {
6717 node_callback(node, next_stmt_id);
6718 }
6719 }
6720 }
6721
6722 HydroNode::VersionedNetwork {
6723 fork,
6724 deserialize,
6725 metadata,
6726 ..
6727 } => {
6728 let stmt_id = next_stmt_id.get_and_increment();
6729 let receiver_stream_ident =
6730 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
6731
6732 let (channel_id, source_loc) = {
6735 let fork_ref = fork.0.borrow();
6736 let HydroNode::VersionedNetworkFork {
6737 channel_id,
6738 senders,
6739 ..
6740 } = &*fork_ref
6741 else {
6742 unreachable!("VersionedNetwork.fork must be a VersionedNetworkFork");
6743 };
6744 let source_loc = senders
6745 .first()
6746 .map(|(_v, sender, _s)| sender.metadata().location_id.clone())
6747 .expect("a VersionedNetworkFork always has at least one sender");
6748 (*channel_id, source_loc)
6749 };
6750
6751 let deserialize_pipeline = deserialize.pipeline();
6752 let external_element_type = deserialize.external_element_type();
6753
6754 match builders_or_callback {
6755 BuildersOrCallback::Builders(graph_builders) => {
6756 graph_builders.create_versioned_network(
6757 channel_id,
6758 &source_loc,
6759 &metadata.location_id,
6760 &receiver_stream_ident,
6761 deserialize_pipeline.as_ref(),
6762 external_element_type,
6763 stmt_id,
6764 );
6765 }
6766 BuildersOrCallback::Callback(_, node_callback) => {
6767 node_callback(node, next_stmt_id);
6768 }
6769 }
6770
6771 ident_stack.push(receiver_stream_ident);
6772 }
6773 }
6774 },
6775 seen_tees,
6776 false,
6777 );
6778
6779 let ret = ident_stack
6780 .pop()
6781 .expect("ident_stack should have exactly one element after traversal");
6782 assert!(
6783 ident_stack.is_empty(),
6784 "ident_stack should be empty after popping the final ident, but has {} remaining element(s). \
6785 This indicates a bug in the code gen: some node pushed idents that were never consumed.",
6786 ident_stack.len()
6787 );
6788 ret
6789 }
6790
6791 pub fn visit_debug_expr(&mut self, mut transform: impl FnMut(&mut DebugExpr)) {
6792 match self {
6793 HydroNode::Placeholder => {
6794 panic!()
6795 }
6796 HydroNode::Cast { .. }
6797 | HydroNode::ObserveNonDet { .. }
6798 | HydroNode::UnboundSingleton { .. }
6799 | HydroNode::AssertIsConsistent { .. } => {}
6800 HydroNode::Source { source, .. } => match source {
6801 HydroSource::Stream(expr) | HydroSource::Iter(expr) => transform(expr),
6802 HydroSource::ExternalNetwork()
6803 | HydroSource::Spin()
6804 | HydroSource::ClusterMembers(_, _)
6805 | HydroSource::Embedded(_)
6806 | HydroSource::EmbeddedSingleton(_) => {} },
6808 HydroNode::SingletonSource { value, .. } => {
6809 transform(value);
6810 }
6811 HydroNode::CycleSource { .. }
6812 | HydroNode::Tee { .. }
6813 | HydroNode::Reference { .. }
6814 | HydroNode::YieldConcat { .. }
6815 | HydroNode::BeginAtomic { .. }
6816 | HydroNode::EndAtomic { .. }
6817 | HydroNode::Batch { .. }
6818 | HydroNode::Snapshot { .. }
6819 | HydroNode::Chain { .. }
6820 | HydroNode::MergeOrdered { .. }
6821 | HydroNode::ChainFirst { .. }
6822 | HydroNode::CrossProduct { .. }
6823 | HydroNode::CrossSingleton { .. }
6824 | HydroNode::ResolveFutures { .. }
6825 | HydroNode::ResolveFuturesBlocking { .. }
6826 | HydroNode::ResolveFuturesOrdered { .. }
6827 | HydroNode::Join { .. }
6828 | HydroNode::JoinHalf { .. }
6829 | HydroNode::Difference { .. }
6830 | HydroNode::AntiJoin { .. }
6831 | HydroNode::DeferTick { .. }
6832 | HydroNode::Enumerate { .. }
6833 | HydroNode::Unique { .. }
6834 | HydroNode::Sort { .. }
6835 | HydroNode::PartitionSide { .. }
6836 | HydroNode::VersionedNetworkFork { .. }
6837 | HydroNode::VersionedNetwork { .. } => {}
6838 HydroNode::Map { f, .. }
6839 | HydroNode::FlatMap { f, .. }
6840 | HydroNode::FlatMapStreamBlocking { f, .. }
6841 | HydroNode::Filter { f, .. }
6842 | HydroNode::FilterMap { f, .. }
6843 | HydroNode::Inspect { f, .. }
6844 | HydroNode::PartitionShared { f, .. }
6845 | HydroNode::Reduce { f, .. }
6846 | HydroNode::ReduceKeyed { f, .. }
6847 | HydroNode::ReduceKeyedWatermark { f, .. } => {
6848 transform(&mut f.expr);
6849 }
6850 HydroNode::Fold { init, acc, .. }
6851 | HydroNode::Scan { init, acc, .. }
6852 | HydroNode::ScanAsyncBlocking { init, acc, .. }
6853 | HydroNode::FoldKeyed { init, acc, .. } => {
6854 transform(&mut init.expr);
6855 transform(&mut acc.expr);
6856 }
6857 HydroNode::Network {
6858 serialize,
6859 deserialize,
6860 ..
6861 } => {
6862 if let NetworkSend::Custom {
6863 serialize_fn: Some(serialize_fn),
6864 } = serialize
6865 {
6866 transform(serialize_fn);
6867 }
6868 if let NetworkRecv::Custom {
6869 deserialize_fn: Some(deserialize_fn),
6870 } = deserialize
6871 {
6872 transform(deserialize_fn);
6873 }
6874 }
6875 HydroNode::ExternalInput { deserialize_fn, .. } => {
6876 if let Some(deserialize_fn) = deserialize_fn {
6877 transform(deserialize_fn);
6878 }
6879 }
6880 HydroNode::Counter { duration, .. } => {
6881 transform(duration);
6882 }
6883 }
6884 }
6885
6886 pub fn op_metadata(&self) -> &HydroIrOpMetadata {
6887 &self.metadata().op
6888 }
6889
6890 pub fn metadata(&self) -> &HydroIrMetadata {
6891 match self {
6892 HydroNode::Placeholder => {
6893 panic!()
6894 }
6895 HydroNode::VersionedNetworkFork { metadata, .. }
6896 | HydroNode::VersionedNetwork { metadata, .. } => metadata,
6897 HydroNode::Cast { metadata, .. }
6898 | HydroNode::ObserveNonDet { metadata, .. }
6899 | HydroNode::AssertIsConsistent { metadata, .. }
6900 | HydroNode::UnboundSingleton { metadata, .. }
6901 | HydroNode::Source { metadata, .. }
6902 | HydroNode::SingletonSource { metadata, .. }
6903 | HydroNode::CycleSource { metadata, .. }
6904 | HydroNode::Tee { metadata, .. }
6905 | HydroNode::Reference { metadata, .. }
6906 | HydroNode::PartitionSide { metadata, .. }
6907 | HydroNode::PartitionShared { metadata, .. }
6908 | HydroNode::YieldConcat { metadata, .. }
6909 | HydroNode::BeginAtomic { metadata, .. }
6910 | HydroNode::EndAtomic { metadata, .. }
6911 | HydroNode::Batch { metadata, .. }
6912 | HydroNode::Snapshot { metadata, .. }
6913 | HydroNode::Chain { metadata, .. }
6914 | HydroNode::MergeOrdered { metadata, .. }
6915 | HydroNode::ChainFirst { metadata, .. }
6916 | HydroNode::CrossProduct { metadata, .. }
6917 | HydroNode::CrossSingleton { metadata, .. }
6918 | HydroNode::Join { metadata, .. }
6919 | HydroNode::JoinHalf { metadata, .. }
6920 | HydroNode::Difference { metadata, .. }
6921 | HydroNode::AntiJoin { metadata, .. }
6922 | HydroNode::ResolveFutures { metadata, .. }
6923 | HydroNode::ResolveFuturesBlocking { metadata, .. }
6924 | HydroNode::ResolveFuturesOrdered { metadata, .. }
6925 | HydroNode::Map { metadata, .. }
6926 | HydroNode::FlatMap { metadata, .. }
6927 | HydroNode::FlatMapStreamBlocking { metadata, .. }
6928 | HydroNode::Filter { metadata, .. }
6929 | HydroNode::FilterMap { metadata, .. }
6930 | HydroNode::DeferTick { metadata, .. }
6931 | HydroNode::Enumerate { metadata, .. }
6932 | HydroNode::Inspect { metadata, .. }
6933 | HydroNode::Unique { metadata, .. }
6934 | HydroNode::Sort { metadata, .. }
6935 | HydroNode::Scan { metadata, .. }
6936 | HydroNode::ScanAsyncBlocking { metadata, .. }
6937 | HydroNode::Fold { metadata, .. }
6938 | HydroNode::FoldKeyed { metadata, .. }
6939 | HydroNode::Reduce { metadata, .. }
6940 | HydroNode::ReduceKeyed { metadata, .. }
6941 | HydroNode::ReduceKeyedWatermark { metadata, .. }
6942 | HydroNode::ExternalInput { metadata, .. }
6943 | HydroNode::Network { metadata, .. }
6944 | HydroNode::Counter { metadata, .. } => metadata,
6945 }
6946 }
6947
6948 pub fn op_metadata_mut(&mut self) -> &mut HydroIrOpMetadata {
6949 &mut self.metadata_mut().op
6950 }
6951
6952 pub fn metadata_mut(&mut self) -> &mut HydroIrMetadata {
6953 match self {
6954 HydroNode::Placeholder => {
6955 panic!()
6956 }
6957 HydroNode::VersionedNetworkFork { metadata, .. }
6958 | HydroNode::VersionedNetwork { metadata, .. } => metadata,
6959 HydroNode::Cast { metadata, .. }
6960 | HydroNode::ObserveNonDet { metadata, .. }
6961 | HydroNode::AssertIsConsistent { metadata, .. }
6962 | HydroNode::UnboundSingleton { metadata, .. }
6963 | HydroNode::Source { metadata, .. }
6964 | HydroNode::SingletonSource { metadata, .. }
6965 | HydroNode::CycleSource { metadata, .. }
6966 | HydroNode::Tee { metadata, .. }
6967 | HydroNode::Reference { metadata, .. }
6968 | HydroNode::PartitionSide { metadata, .. }
6969 | HydroNode::PartitionShared { metadata, .. }
6970 | HydroNode::YieldConcat { metadata, .. }
6971 | HydroNode::BeginAtomic { metadata, .. }
6972 | HydroNode::EndAtomic { metadata, .. }
6973 | HydroNode::Batch { metadata, .. }
6974 | HydroNode::Snapshot { metadata, .. }
6975 | HydroNode::Chain { metadata, .. }
6976 | HydroNode::MergeOrdered { metadata, .. }
6977 | HydroNode::ChainFirst { metadata, .. }
6978 | HydroNode::CrossProduct { metadata, .. }
6979 | HydroNode::CrossSingleton { metadata, .. }
6980 | HydroNode::Join { metadata, .. }
6981 | HydroNode::JoinHalf { metadata, .. }
6982 | HydroNode::Difference { metadata, .. }
6983 | HydroNode::AntiJoin { metadata, .. }
6984 | HydroNode::ResolveFutures { metadata, .. }
6985 | HydroNode::ResolveFuturesBlocking { metadata, .. }
6986 | HydroNode::ResolveFuturesOrdered { metadata, .. }
6987 | HydroNode::Map { metadata, .. }
6988 | HydroNode::FlatMap { metadata, .. }
6989 | HydroNode::FlatMapStreamBlocking { metadata, .. }
6990 | HydroNode::Filter { metadata, .. }
6991 | HydroNode::FilterMap { metadata, .. }
6992 | HydroNode::DeferTick { metadata, .. }
6993 | HydroNode::Enumerate { metadata, .. }
6994 | HydroNode::Inspect { metadata, .. }
6995 | HydroNode::Unique { metadata, .. }
6996 | HydroNode::Sort { metadata, .. }
6997 | HydroNode::Scan { metadata, .. }
6998 | HydroNode::ScanAsyncBlocking { metadata, .. }
6999 | HydroNode::Fold { metadata, .. }
7000 | HydroNode::FoldKeyed { metadata, .. }
7001 | HydroNode::Reduce { metadata, .. }
7002 | HydroNode::ReduceKeyed { metadata, .. }
7003 | HydroNode::ReduceKeyedWatermark { metadata, .. }
7004 | HydroNode::ExternalInput { metadata, .. }
7005 | HydroNode::Network { metadata, .. }
7006 | HydroNode::Counter { metadata, .. } => metadata,
7007 }
7008 }
7009
7010 pub fn input(&self) -> Vec<&HydroNode> {
7011 match self {
7012 HydroNode::Placeholder => {
7013 panic!()
7014 }
7015 HydroNode::Source { .. }
7016 | HydroNode::SingletonSource { .. }
7017 | HydroNode::ExternalInput { .. }
7018 | HydroNode::CycleSource { .. }
7019 | HydroNode::Tee { .. }
7020 | HydroNode::Reference { .. }
7021 | HydroNode::PartitionSide { .. }
7022 | HydroNode::VersionedNetwork { .. } => {
7023 vec![]
7025 }
7026 HydroNode::Cast { inner, .. }
7027 | HydroNode::ObserveNonDet { inner, .. }
7028 | HydroNode::YieldConcat { inner, .. }
7029 | HydroNode::BeginAtomic { inner, .. }
7030 | HydroNode::EndAtomic { inner, .. }
7031 | HydroNode::Batch { inner, .. }
7032 | HydroNode::Snapshot { inner, .. }
7033 | HydroNode::UnboundSingleton { inner, .. }
7034 | HydroNode::AssertIsConsistent { inner, .. } => {
7035 vec![inner]
7036 }
7037 HydroNode::Chain { first, second, .. }
7038 | HydroNode::MergeOrdered { first, second, .. }
7039 | HydroNode::ChainFirst { first, second, .. } => {
7040 vec![first, second]
7041 }
7042 HydroNode::CrossProduct { left, right, .. }
7043 | HydroNode::CrossSingleton { left, right, .. }
7044 | HydroNode::Join { left, right, .. }
7045 | HydroNode::JoinHalf { left, right, .. } => {
7046 vec![left, right]
7047 }
7048 HydroNode::Difference { pos, neg, .. } | HydroNode::AntiJoin { pos, neg, .. } => {
7049 vec![pos, neg]
7050 }
7051 HydroNode::Counter { input, .. }
7052 | HydroNode::DeferTick { input, .. }
7053 | HydroNode::Enumerate { input, .. }
7054 | HydroNode::Filter { input, .. }
7055 | HydroNode::FilterMap { input, .. }
7056 | HydroNode::FlatMap { input, .. }
7057 | HydroNode::FlatMapStreamBlocking { input, .. }
7058 | HydroNode::Fold { input, .. }
7059 | HydroNode::FoldKeyed { input, .. }
7060 | HydroNode::Inspect { input, .. }
7061 | HydroNode::Map { input, .. }
7062 | HydroNode::Network { input, .. }
7063 | HydroNode::PartitionShared { input, .. }
7064 | HydroNode::Reduce { input, .. }
7065 | HydroNode::ReduceKeyed { input, .. }
7066 | HydroNode::ResolveFutures { input, .. }
7067 | HydroNode::ResolveFuturesBlocking { input, .. }
7068 | HydroNode::ResolveFuturesOrdered { input, .. }
7069 | HydroNode::Scan { input, .. }
7070 | HydroNode::ScanAsyncBlocking { input, .. }
7071 | HydroNode::Sort { input, .. }
7072 | HydroNode::Unique { input, .. } => {
7073 vec![input]
7074 }
7075 HydroNode::ReduceKeyedWatermark {
7076 input, watermark, ..
7077 } => {
7078 vec![input, watermark]
7079 }
7080 HydroNode::VersionedNetworkFork { senders, .. } => senders
7081 .iter()
7082 .map(|(_version, sender, _serialize)| sender.as_ref())
7083 .collect(),
7084 }
7085 }
7086
7087 pub fn input_metadata(&self) -> Vec<&HydroIrMetadata> {
7088 self.input()
7089 .iter()
7090 .map(|input_node| input_node.metadata())
7091 .collect()
7092 }
7093
7094 pub fn is_shared_with_others(&self) -> bool {
7098 match self {
7099 HydroNode::Tee { inner, .. } | HydroNode::PartitionSide { inner, .. } => {
7100 Rc::strong_count(&inner.0) > 1
7101 }
7102 HydroNode::Reference { .. } => false,
7105 _ => false,
7106 }
7107 }
7108
7109 pub fn print_root(&self) -> String {
7110 match self {
7111 HydroNode::Placeholder => {
7112 panic!()
7113 }
7114 HydroNode::Cast { .. } => "Cast()".to_owned(),
7115 HydroNode::UnboundSingleton { .. } => "UnboundSingleton()".to_owned(),
7116 HydroNode::ObserveNonDet { .. } => "ObserveNonDet()".to_owned(),
7117 HydroNode::AssertIsConsistent { .. } => "AssertIsConsistent()".to_owned(),
7118 HydroNode::Source { source, .. } => format!("Source({:?})", source),
7119 HydroNode::SingletonSource {
7120 value,
7121 first_tick_only,
7122 ..
7123 } => format!(
7124 "SingletonSource({:?}, first_tick_only={})",
7125 value, first_tick_only
7126 ),
7127 HydroNode::CycleSource { cycle_id, .. } => format!("CycleSource({})", cycle_id),
7128 HydroNode::Tee { inner, .. } => {
7129 format!("Tee({})", inner.0.borrow().print_root())
7130 }
7131 HydroNode::Reference { inner, kind, .. } => {
7132 format!("Reference({:?}, {})", kind, inner.0.borrow().print_root())
7133 }
7134 HydroNode::PartitionSide { inner, is_true, .. } => {
7135 format!(
7136 "PartitionSide(is_true={}, {})",
7137 is_true,
7138 inner.0.borrow().print_root(),
7139 )
7140 }
7141 HydroNode::PartitionShared { f, .. } => format!("PartitionShared({:?})", f),
7142 HydroNode::YieldConcat { .. } => "YieldConcat()".to_owned(),
7143 HydroNode::BeginAtomic { .. } => "BeginAtomic()".to_owned(),
7144 HydroNode::EndAtomic { .. } => "EndAtomic()".to_owned(),
7145 HydroNode::Batch { .. } => "Batch()".to_owned(),
7146 HydroNode::Snapshot { .. } => "Snapshot()".to_owned(),
7147 HydroNode::Chain { first, second, .. } => {
7148 format!("Chain({}, {})", first.print_root(), second.print_root())
7149 }
7150 HydroNode::MergeOrdered { first, second, .. } => {
7151 format!(
7152 "MergeOrdered({}, {})",
7153 first.print_root(),
7154 second.print_root()
7155 )
7156 }
7157 HydroNode::ChainFirst { first, second, .. } => {
7158 format!(
7159 "ChainFirst({}, {})",
7160 first.print_root(),
7161 second.print_root()
7162 )
7163 }
7164 HydroNode::CrossProduct { left, right, .. } => {
7165 format!(
7166 "CrossProduct({}, {})",
7167 left.print_root(),
7168 right.print_root()
7169 )
7170 }
7171 HydroNode::CrossSingleton { left, right, .. } => {
7172 format!(
7173 "CrossSingleton({}, {})",
7174 left.print_root(),
7175 right.print_root()
7176 )
7177 }
7178 HydroNode::Join { left, right, .. } => {
7179 format!("Join({}, {})", left.print_root(), right.print_root())
7180 }
7181 HydroNode::JoinHalf { left, right, .. } => {
7182 format!("JoinHalf({}, {})", left.print_root(), right.print_root())
7183 }
7184 HydroNode::Difference { pos, neg, .. } => {
7185 format!("Difference({}, {})", pos.print_root(), neg.print_root())
7186 }
7187 HydroNode::AntiJoin { pos, neg, .. } => {
7188 format!("AntiJoin({}, {})", pos.print_root(), neg.print_root())
7189 }
7190 HydroNode::ResolveFutures { .. } => "ResolveFutures()".to_owned(),
7191 HydroNode::ResolveFuturesBlocking { .. } => "ResolveFuturesBlocking()".to_owned(),
7192 HydroNode::ResolveFuturesOrdered { .. } => "ResolveFuturesOrdered()".to_owned(),
7193 HydroNode::Map { f, .. } => format!("Map({:?})", f),
7194 HydroNode::FlatMap { f, .. } => format!("FlatMap({:?})", f),
7195 HydroNode::FlatMapStreamBlocking { f, .. } => format!("FlatMapStreamBlocking({:?})", f),
7196 HydroNode::Filter { f, .. } => format!("Filter({:?})", f),
7197 HydroNode::FilterMap { f, .. } => format!("FilterMap({:?})", f),
7198 HydroNode::DeferTick { .. } => "DeferTick()".to_owned(),
7199 HydroNode::Enumerate { .. } => "Enumerate()".to_owned(),
7200 HydroNode::Inspect { f, .. } => format!("Inspect({:?})", f),
7201 HydroNode::Unique { .. } => "Unique()".to_owned(),
7202 HydroNode::Sort { .. } => "Sort()".to_owned(),
7203 HydroNode::Fold { init, acc, .. } => format!("Fold({:?}, {:?})", init, acc),
7204 HydroNode::Scan { init, acc, .. } => format!("Scan({:?}, {:?})", init, acc),
7205 HydroNode::ScanAsyncBlocking { init, acc, .. } => {
7206 format!("ScanAsyncBlocking({:?}, {:?})", init, acc)
7207 }
7208 HydroNode::FoldKeyed { init, acc, .. } => format!("FoldKeyed({:?}, {:?})", init, acc),
7209 HydroNode::Reduce { f, .. } => format!("Reduce({:?})", f),
7210 HydroNode::ReduceKeyed { f, .. } => format!("ReduceKeyed({:?})", f),
7211 HydroNode::ReduceKeyedWatermark { f, .. } => format!("ReduceKeyedWatermark({:?})", f),
7212 HydroNode::Network { .. } => "Network()".to_owned(),
7213 HydroNode::ExternalInput { .. } => "ExternalInput()".to_owned(),
7214 HydroNode::Counter { tag, duration, .. } => {
7215 format!("Counter({:?}, {:?})", tag, duration)
7216 }
7217 HydroNode::VersionedNetworkFork {
7218 channel_name,
7219 senders,
7220 ..
7221 } => {
7222 let versions: Vec<u32> = senders.iter().map(|(v, _, _)| *v).collect();
7223 format!(
7224 "VersionedNetworkFork({}, senders={:?})",
7225 channel_name, versions
7226 )
7227 }
7228 HydroNode::VersionedNetwork { version, .. } => {
7229 format!("VersionedNetwork(v{})", version)
7230 }
7231 }
7232 }
7233}
7234
7235#[cfg(feature = "build")]
7236#[expect(clippy::too_many_arguments, reason = "networking codegen")]
7237fn instantiate_network<'a, D>(
7238 env: &mut D::InstantiateEnv,
7239 from_location: &LocationId,
7240 to_location: &LocationId,
7241 processes: &SparseSecondaryMap<LocationKey, D::Process>,
7242 clusters: &SparseSecondaryMap<LocationKey, D::Cluster>,
7243 name: Option<&str>,
7244 networking_info: &crate::networking::NetworkingInfo,
7245 external_types: Option<(&syn::Type, &syn::Type)>,
7246) -> (syn::Expr, syn::Expr, Box<dyn FnOnce()>)
7247where
7248 D: Deploy<'a>,
7249{
7250 if external_types.is_some() {
7251 assert!(
7252 D::SUPPORTS_EXTERNAL_SERIALIZATION,
7253 "`EMBEDDED` channels are implemented by the host of an embedded program and are \
7254 only supported by the embedded deployment backend (and the simulator). Use `TCP` \
7255 or `UDP` (with `.bincode()`) for this deployment target instead."
7256 );
7257 }
7258
7259 let ((sink, source), connect_fn) = match (from_location, to_location) {
7260 (&LocationId::Process(from), &LocationId::Process(to)) => {
7261 let from_node = processes
7262 .get(from)
7263 .unwrap_or_else(|| {
7264 panic!("A process used in the graph was not instantiated: {}", from)
7265 })
7266 .clone();
7267 let to_node = processes
7268 .get(to)
7269 .unwrap_or_else(|| {
7270 panic!("A process used in the graph was not instantiated: {}", to)
7271 })
7272 .clone();
7273
7274 let sink_port = from_node.next_port();
7275 let source_port = to_node.next_port();
7276
7277 (
7278 D::o2o_sink_source(
7279 env,
7280 &from_node,
7281 &sink_port,
7282 &to_node,
7283 &source_port,
7284 name,
7285 networking_info,
7286 external_types,
7287 ),
7288 D::o2o_connect(&from_node, &sink_port, &to_node, &source_port),
7289 )
7290 }
7291 (&LocationId::Process(from), &LocationId::Cluster(to)) => {
7292 let from_node = processes
7293 .get(from)
7294 .unwrap_or_else(|| {
7295 panic!("A process used in the graph was not instantiated: {}", from)
7296 })
7297 .clone();
7298 let to_node = clusters
7299 .get(to)
7300 .unwrap_or_else(|| {
7301 panic!("A cluster used in the graph was not instantiated: {}", to)
7302 })
7303 .clone();
7304
7305 let sink_port = from_node.next_port();
7306 let source_port = to_node.next_port();
7307
7308 (
7309 D::o2m_sink_source(
7310 env,
7311 &from_node,
7312 &sink_port,
7313 &to_node,
7314 &source_port,
7315 name,
7316 networking_info,
7317 external_types,
7318 ),
7319 D::o2m_connect(&from_node, &sink_port, &to_node, &source_port),
7320 )
7321 }
7322 (&LocationId::Cluster(from), &LocationId::Process(to)) => {
7323 let from_node = clusters
7324 .get(from)
7325 .unwrap_or_else(|| {
7326 panic!("A cluster used in the graph was not instantiated: {}", from)
7327 })
7328 .clone();
7329 let to_node = processes
7330 .get(to)
7331 .unwrap_or_else(|| {
7332 panic!("A process used in the graph was not instantiated: {}", to)
7333 })
7334 .clone();
7335
7336 let sink_port = from_node.next_port();
7337 let source_port = to_node.next_port();
7338
7339 (
7340 D::m2o_sink_source(
7341 env,
7342 &from_node,
7343 &sink_port,
7344 &to_node,
7345 &source_port,
7346 name,
7347 networking_info,
7348 external_types,
7349 ),
7350 D::m2o_connect(&from_node, &sink_port, &to_node, &source_port),
7351 )
7352 }
7353 (&LocationId::Cluster(from), &LocationId::Cluster(to)) => {
7354 let from_node = clusters
7355 .get(from)
7356 .unwrap_or_else(|| {
7357 panic!("A cluster used in the graph was not instantiated: {}", from)
7358 })
7359 .clone();
7360 let to_node = clusters
7361 .get(to)
7362 .unwrap_or_else(|| {
7363 panic!("A cluster used in the graph was not instantiated: {}", to)
7364 })
7365 .clone();
7366
7367 let sink_port = from_node.next_port();
7368 let source_port = to_node.next_port();
7369
7370 (
7371 D::m2m_sink_source(
7372 env,
7373 &from_node,
7374 &sink_port,
7375 &to_node,
7376 &source_port,
7377 name,
7378 networking_info,
7379 external_types,
7380 ),
7381 D::m2m_connect(&from_node, &sink_port, &to_node, &source_port),
7382 )
7383 }
7384 (LocationId::Tick { .. }, _) => panic!(),
7385 (_, LocationId::Tick { .. }) => panic!(),
7386 (LocationId::Atomic(_), _) => panic!(),
7387 (_, LocationId::Atomic(_)) => panic!(),
7388 };
7389 (sink, source, connect_fn)
7390}
7391
7392#[cfg(test)]
7393mod serde_test;
7394
7395#[cfg(test)]
7396mod test {
7397 use std::mem::size_of;
7398
7399 use stageleft::{QuotedWithContext, q};
7400
7401 use super::*;
7402
7403 #[test]
7404 #[cfg_attr(
7405 not(feature = "build"),
7406 ignore = "expects inclusion of feature-gated fields"
7407 )]
7408 fn hydro_node_size() {
7409 assert_eq!(size_of::<HydroNode>(), 280);
7410 }
7411
7412 #[test]
7413 #[cfg_attr(
7414 not(feature = "build"),
7415 ignore = "expects inclusion of feature-gated fields"
7416 )]
7417 fn hydro_root_size() {
7418 assert_eq!(size_of::<HydroRoot>(), 152);
7419 }
7420
7421 #[test]
7422 fn test_simplify_q_macro_basic() {
7423 let simple_expr: syn::Expr = syn::parse_str("x + y").unwrap();
7425 let result = simplify_q_macro(simple_expr.clone());
7426 assert_eq!(result, simple_expr);
7427 }
7428
7429 #[test]
7430 fn test_simplify_q_macro_actual_stageleft_call() {
7431 let stageleft_call = q!(|x: usize| x + 1).splice_fn1_ctx(&());
7433 let result = simplify_q_macro(stageleft_call);
7434 hydro_build_utils::assert_snapshot!(result.to_token_stream().to_string());
7437 }
7438
7439 #[test]
7440 fn test_closure_no_pipe_at_start() {
7441 let stageleft_call = q!({
7443 let foo = 123;
7444 move |b: usize| b + foo
7445 })
7446 .splice_fn1_ctx(&());
7447 let result = simplify_q_macro(stageleft_call);
7448 hydro_build_utils::assert_snapshot!(result.to_token_stream().to_string());
7449 }
7450}