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, Hash, Debug)]
2930pub enum StreamRetry {
2931 AtLeastOnce,
2932 ExactlyOnce,
2933}
2934
2935#[cfg(feature = "build")]
2936impl StreamOrder {
2937 pub fn type_tokens(&self) -> TokenStream {
2939 let root = crate::staging_util::get_this_crate();
2940 match self {
2941 StreamOrder::TotalOrder => quote! { #root::live_collections::stream::TotalOrder },
2942 StreamOrder::NoOrder => quote! { #root::live_collections::stream::NoOrder },
2943 }
2944 }
2945}
2946
2947#[cfg(feature = "build")]
2948impl StreamRetry {
2949 pub fn type_tokens(&self) -> TokenStream {
2951 let root = crate::staging_util::get_this_crate();
2952 match self {
2953 StreamRetry::ExactlyOnce => quote! { #root::live_collections::stream::ExactlyOnce },
2954 StreamRetry::AtLeastOnce => quote! { #root::live_collections::stream::AtLeastOnce },
2955 }
2956 }
2957}
2958
2959#[derive(serde::Serialize, Clone, PartialEq, Eq, Debug)]
2960pub enum KeyedSingletonBoundKind {
2961 Unbounded,
2962 MonotonicKeys,
2963 MonotonicValue,
2964 BoundedValue,
2965 Bounded,
2966}
2967
2968#[derive(serde::Serialize, Clone, PartialEq, Eq, Debug)]
2969pub enum SingletonBoundKind {
2970 Unbounded,
2971 Monotonic,
2972 Bounded,
2973}
2974
2975#[derive(Clone, PartialEq, Eq, Debug, serde::Serialize)]
2976pub enum CollectionKind {
2977 Stream {
2978 bound: BoundKind,
2979 order: StreamOrder,
2980 retry: StreamRetry,
2981 element_type: DebugType,
2982 },
2983 Singleton {
2984 bound: SingletonBoundKind,
2985 element_type: DebugType,
2986 },
2987 Optional {
2988 bound: OptionalBoundKind,
2989 element_type: DebugType,
2990 },
2991 KeyedStream {
2992 bound: BoundKind,
2993 value_order: StreamOrder,
2994 value_retry: StreamRetry,
2995 key_type: DebugType,
2996 value_type: DebugType,
2997 },
2998 KeyedSingleton {
2999 bound: KeyedSingletonBoundKind,
3000 key_type: DebugType,
3001 value_type: DebugType,
3002 },
3003}
3004
3005impl CollectionKind {
3006 pub fn is_bounded(&self) -> bool {
3007 matches!(
3008 self,
3009 CollectionKind::Stream {
3010 bound: BoundKind::Bounded,
3011 ..
3012 } | CollectionKind::Singleton {
3013 bound: SingletonBoundKind::Bounded,
3014 ..
3015 } | CollectionKind::Optional {
3016 bound: OptionalBoundKind::Bounded,
3017 ..
3018 } | CollectionKind::KeyedStream {
3019 bound: BoundKind::Bounded,
3020 ..
3021 } | CollectionKind::KeyedSingleton {
3022 bound: KeyedSingletonBoundKind::Bounded,
3023 ..
3024 }
3025 )
3026 }
3027
3028 pub fn is_strict(&self) -> bool {
3031 match self {
3032 CollectionKind::Stream { order, retry, .. } => {
3033 *order == StreamOrder::TotalOrder && *retry == StreamRetry::ExactlyOnce
3034 }
3035 CollectionKind::KeyedStream {
3036 value_order,
3037 value_retry,
3038 ..
3039 } => {
3040 *value_order == StreamOrder::TotalOrder && *value_retry == StreamRetry::ExactlyOnce
3041 }
3042 CollectionKind::Singleton { .. }
3045 | CollectionKind::Optional { .. }
3046 | CollectionKind::KeyedSingleton { .. } => true,
3047 }
3048 }
3049
3050 pub fn root_repr_is_tombstoned_update_feed(&self) -> bool {
3076 matches!(
3077 self,
3078 CollectionKind::Optional {
3079 bound: OptionalBoundKind::Unbounded,
3080 ..
3081 } | CollectionKind::KeyedSingleton {
3082 bound: KeyedSingletonBoundKind::Unbounded,
3083 ..
3084 }
3085 )
3086 }
3087
3088 pub fn strict_kind(&self) -> CollectionKind {
3090 match self {
3091 CollectionKind::Stream {
3092 bound,
3093 element_type,
3094 ..
3095 } => CollectionKind::Stream {
3096 bound: bound.clone(),
3097 order: StreamOrder::TotalOrder,
3098 retry: StreamRetry::ExactlyOnce,
3099 element_type: element_type.clone(),
3100 },
3101 CollectionKind::KeyedStream {
3102 bound,
3103 key_type,
3104 value_type,
3105 ..
3106 } => CollectionKind::KeyedStream {
3107 bound: bound.clone(),
3108 value_order: StreamOrder::TotalOrder,
3109 value_retry: StreamRetry::ExactlyOnce,
3110 key_type: key_type.clone(),
3111 value_type: value_type.clone(),
3112 },
3113 other => other.clone(),
3114 }
3115 }
3116}
3117
3118#[derive(Clone, serde::Serialize)]
3119pub struct HydroIrMetadata {
3120 pub location_id: LocationId,
3121 pub collection_kind: CollectionKind,
3122 pub consistency: Option<ClusterConsistency>,
3123 pub cardinality: Option<usize>,
3124 pub tag: Option<String>,
3125 pub op: HydroIrOpMetadata,
3126}
3127
3128impl Hash for HydroIrMetadata {
3130 fn hash<H: Hasher>(&self, _: &mut H) {}
3131}
3132
3133impl PartialEq for HydroIrMetadata {
3134 fn eq(&self, _: &Self) -> bool {
3135 true
3136 }
3137}
3138
3139impl Eq for HydroIrMetadata {}
3140
3141impl Debug for HydroIrMetadata {
3142 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3143 f.debug_struct("HydroIrMetadata")
3144 .field("location_id", &self.location_id)
3145 .field("collection_kind", &self.collection_kind)
3146 .finish()
3147 }
3148}
3149
3150#[derive(Clone, serde::Serialize)]
3153pub struct HydroIrOpMetadata {
3154 #[serde(rename = "span", serialize_with = "serialize_backtrace_as_span")]
3155 pub backtrace: Backtrace,
3156 pub cpu_usage: Option<f64>,
3157 pub network_recv_cpu_usage: Option<f64>,
3158 pub id: Option<usize>,
3159 #[serde(skip)]
3163 pub sim_hook_id: Option<usize>,
3164}
3165
3166impl HydroIrOpMetadata {
3167 #[expect(
3168 clippy::new_without_default,
3169 reason = "explicit calls to new ensure correct backtrace bounds"
3170 )]
3171 pub fn new() -> HydroIrOpMetadata {
3172 Self::new_with_skip(1)
3173 }
3174
3175 fn new_with_skip(skip_count: usize) -> HydroIrOpMetadata {
3176 HydroIrOpMetadata {
3177 backtrace: Backtrace::get_backtrace(2 + skip_count),
3178 cpu_usage: None,
3179 network_recv_cpu_usage: None,
3180 id: None,
3181 sim_hook_id: None,
3182 }
3183 }
3184}
3185
3186impl Debug for HydroIrOpMetadata {
3187 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3188 f.debug_struct("HydroIrOpMetadata").finish()
3189 }
3190}
3191
3192impl Hash for HydroIrOpMetadata {
3193 fn hash<H: Hasher>(&self, _: &mut H) {}
3194}
3195
3196#[derive(Debug, Clone, Hash, serde::Serialize)]
3202pub enum NetworkSend {
3203 Custom { serialize_fn: Option<DebugExpr> },
3206 Embedded {
3214 tag: Option<DebugType>,
3215 element_type: DebugType,
3216 },
3217}
3218
3219#[derive(Debug, Clone, Hash, serde::Serialize)]
3222pub enum NetworkRecv {
3223 Custom { deserialize_fn: Option<DebugExpr> },
3226 Embedded {
3233 tag: Option<DebugType>,
3234 element_type: DebugType,
3235 },
3236}
3237
3238#[cfg(feature = "build")]
3239impl NetworkSend {
3240 pub(crate) fn external_element_type(&self) -> Option<&syn::Type> {
3243 match self {
3244 NetworkSend::Custom { .. } => None,
3245 NetworkSend::Embedded { element_type, .. } => Some(&element_type.0),
3246 }
3247 }
3248}
3249
3250#[cfg(feature = "build")]
3251impl NetworkRecv {
3252 pub(crate) fn external_element_type(&self) -> Option<&syn::Type> {
3254 match self {
3255 NetworkRecv::Custom { .. } => None,
3256 NetworkRecv::Embedded { element_type, .. } => Some(&element_type.0),
3257 }
3258 }
3259}
3260
3261#[cfg(feature = "build")]
3262impl NetworkSend {
3263 pub(crate) fn pipeline(&self) -> Option<DebugExpr> {
3265 match self {
3266 NetworkSend::Custom { serialize_fn } => serialize_fn.clone(),
3267 NetworkSend::Embedded { tag, element_type } => {
3268 let root = crate::staging_util::get_this_crate();
3269 let element_type = &element_type.0;
3270 let expr: syn::Expr = if let Some(tag) = tag {
3271 let tag = &tag.0;
3272 parse_quote! {
3273 #root::runtime_support::stageleft::runtime_support::fn1_type_hint::<(#root::__staged::location::MemberId<#tag>, #element_type), _>(
3274 |(id, data)| (id.into_tagless(), data)
3275 )
3276 }
3277 } else {
3278 parse_quote! {
3279 #root::runtime_support::stageleft::runtime_support::fn1_type_hint::<#element_type, _>(
3280 |data| data
3281 )
3282 }
3283 };
3284 Some(expr.into())
3285 }
3286 }
3287 }
3288}
3289
3290#[cfg(feature = "build")]
3291impl NetworkRecv {
3292 pub(crate) fn pipeline(&self) -> Option<DebugExpr> {
3294 match self {
3295 NetworkRecv::Custom { deserialize_fn } => deserialize_fn.clone(),
3296 NetworkRecv::Embedded { tag, .. } => {
3301 let tag = tag.as_ref()?;
3302 let root = crate::staging_util::get_this_crate();
3303 let tag = &tag.0;
3304 let expr: syn::Expr = parse_quote! {
3305 |(id, b)| (#root::__staged::location::MemberId::<#tag>::from_tagless(id as #root::__staged::location::TaglessMemberId), b)
3306 };
3307 Some(expr.into())
3308 }
3309 }
3310 }
3311}
3312
3313#[derive(Debug, Hash, serde::Serialize)]
3316pub enum HydroNode {
3317 Placeholder,
3318
3319 Cast {
3327 inner: Box<HydroNode>,
3328 metadata: HydroIrMetadata,
3329 },
3330
3331 ObserveNonDet {
3337 inner: Box<HydroNode>,
3338 trusted: bool, metadata: HydroIrMetadata,
3340 },
3341
3342 Source {
3343 source: HydroSource,
3344 metadata: HydroIrMetadata,
3345 },
3346
3347 SingletonSource {
3348 value: DebugExpr,
3349 first_tick_only: bool,
3350 metadata: HydroIrMetadata,
3351 },
3352
3353 CycleSource {
3354 cycle_id: CycleId,
3355 metadata: HydroIrMetadata,
3356 },
3357
3358 Tee {
3359 inner: SharedNode,
3360 metadata: HydroIrMetadata,
3361 },
3362
3363 Reference {
3372 inner: SharedNode,
3373 kind: crate::handoff_ref::HandoffRefKind,
3374 access_counter: AccessCounter,
3375 metadata: HydroIrMetadata,
3376 },
3377
3378 PartitionSide {
3380 inner: SharedNode,
3381 is_true: bool,
3382 metadata: HydroIrMetadata,
3383 },
3384
3385 PartitionShared {
3387 input: Box<HydroNode>,
3388 f: ClosureExpr,
3389 metadata: HydroIrMetadata,
3390 },
3391
3392 BeginAtomic {
3393 inner: Box<HydroNode>,
3394 metadata: HydroIrMetadata,
3395 },
3396
3397 EndAtomic {
3398 inner: Box<HydroNode>,
3399 metadata: HydroIrMetadata,
3400 },
3401
3402 Batch {
3409 inner: Box<HydroNode>,
3410 metadata: HydroIrMetadata,
3411 },
3412
3413 Snapshot {
3421 inner: Box<HydroNode>,
3422 metadata: HydroIrMetadata,
3423 },
3424
3425 YieldConcat {
3426 inner: Box<HydroNode>,
3427 metadata: HydroIrMetadata,
3428 },
3429
3430 Chain {
3431 first: Box<HydroNode>,
3432 second: Box<HydroNode>,
3433 metadata: HydroIrMetadata,
3434 },
3435
3436 MergeOrdered {
3437 first: Box<HydroNode>,
3438 second: Box<HydroNode>,
3439 metadata: HydroIrMetadata,
3440 },
3441
3442 ChainFirst {
3443 first: Box<HydroNode>,
3444 second: Box<HydroNode>,
3445 metadata: HydroIrMetadata,
3446 },
3447
3448 CrossProduct {
3449 left: Box<HydroNode>,
3450 right: Box<HydroNode>,
3451 metadata: HydroIrMetadata,
3452 },
3453
3454 CrossSingleton {
3455 left: Box<HydroNode>,
3456 right: Box<HydroNode>,
3457 metadata: HydroIrMetadata,
3458 },
3459
3460 Join {
3461 left: Box<HydroNode>,
3462 right: Box<HydroNode>,
3463 metadata: HydroIrMetadata,
3464 },
3465
3466 JoinHalf {
3470 left: Box<HydroNode>,
3471 right: Box<HydroNode>,
3472 metadata: HydroIrMetadata,
3473 },
3474
3475 Difference {
3476 pos: Box<HydroNode>,
3477 neg: Box<HydroNode>,
3478 metadata: HydroIrMetadata,
3479 },
3480
3481 AntiJoin {
3482 pos: Box<HydroNode>,
3483 neg: Box<HydroNode>,
3484 metadata: HydroIrMetadata,
3485 },
3486
3487 ResolveFutures {
3488 input: Box<HydroNode>,
3489 metadata: HydroIrMetadata,
3490 },
3491 ResolveFuturesBlocking {
3492 input: Box<HydroNode>,
3493 metadata: HydroIrMetadata,
3494 },
3495 ResolveFuturesOrdered {
3496 input: Box<HydroNode>,
3497 metadata: HydroIrMetadata,
3498 },
3499
3500 Map {
3501 f: ClosureExpr,
3502 input: Box<HydroNode>,
3503 metadata: HydroIrMetadata,
3504 },
3505 FlatMap {
3506 f: ClosureExpr,
3507 input: Box<HydroNode>,
3508 metadata: HydroIrMetadata,
3509 },
3510 FlatMapStreamBlocking {
3511 f: ClosureExpr,
3512 input: Box<HydroNode>,
3513 metadata: HydroIrMetadata,
3514 },
3515 Filter {
3516 f: ClosureExpr,
3517 input: Box<HydroNode>,
3518 metadata: HydroIrMetadata,
3519 },
3520 FilterMap {
3521 f: ClosureExpr,
3522 input: Box<HydroNode>,
3523 metadata: HydroIrMetadata,
3524 },
3525
3526 DeferTick {
3527 input: Box<HydroNode>,
3528 metadata: HydroIrMetadata,
3529 },
3530 Enumerate {
3531 input: Box<HydroNode>,
3532 metadata: HydroIrMetadata,
3533 },
3534 Inspect {
3535 f: ClosureExpr,
3536 input: Box<HydroNode>,
3537 metadata: HydroIrMetadata,
3538 },
3539
3540 Unique {
3541 input: Box<HydroNode>,
3542 metadata: HydroIrMetadata,
3543 },
3544
3545 Sort {
3546 input: Box<HydroNode>,
3547 metadata: HydroIrMetadata,
3548 },
3549 Fold {
3550 init: ClosureExpr,
3551 acc: ClosureExpr,
3552 input: Box<HydroNode>,
3553 metadata: HydroIrMetadata,
3554 },
3555
3556 Scan {
3557 init: ClosureExpr,
3558 acc: ClosureExpr,
3559 input: Box<HydroNode>,
3560 metadata: HydroIrMetadata,
3561 },
3562 ScanAsyncBlocking {
3563 init: ClosureExpr,
3564 acc: ClosureExpr,
3565 input: Box<HydroNode>,
3566 metadata: HydroIrMetadata,
3567 },
3568 FoldKeyed {
3569 init: ClosureExpr,
3570 acc: ClosureExpr,
3571 input: Box<HydroNode>,
3572 metadata: HydroIrMetadata,
3573 },
3574
3575 Reduce {
3576 f: ClosureExpr,
3577 input: Box<HydroNode>,
3578 metadata: HydroIrMetadata,
3579 },
3580 ReduceKeyed {
3581 f: ClosureExpr,
3582 input: Box<HydroNode>,
3583 metadata: HydroIrMetadata,
3584 },
3585 ReduceKeyedWatermark {
3586 f: ClosureExpr,
3587 input: Box<HydroNode>,
3588 watermark: Box<HydroNode>,
3589 metadata: HydroIrMetadata,
3590 },
3591
3592 Network {
3593 name: Option<String>,
3594 networking_info: crate::networking::NetworkingInfo,
3595 serialize: NetworkSend,
3596 deserialize: NetworkRecv,
3597 instantiate_fn: DebugInstantiate,
3598 input: Box<HydroNode>,
3599 metadata: HydroIrMetadata,
3600 },
3601
3602 VersionedNetworkFork {
3603 channel_id: u32,
3604 channel_name: String,
3605 senders: Vec<(u32, Box<HydroNode>, NetworkSend)>,
3606 metadata: HydroIrMetadata,
3607 },
3608
3609 VersionedNetwork {
3610 fork: SharedNode,
3611 version: u32,
3612 deserialize: NetworkRecv,
3613 metadata: HydroIrMetadata,
3614 },
3615
3616 ExternalInput {
3617 from_external_key: LocationKey,
3618 from_port_id: ExternalPortId,
3619 from_many: bool,
3620 codec_type: DebugType,
3621 #[serde(skip)]
3622 port_hint: NetworkHint,
3623 instantiate_fn: DebugInstantiate,
3624 deserialize_fn: Option<DebugExpr>,
3625 metadata: HydroIrMetadata,
3626 },
3627
3628 Counter {
3629 tag: String,
3630 duration: DebugExpr,
3631 prefix: String,
3632 input: Box<HydroNode>,
3633 metadata: HydroIrMetadata,
3634 },
3635
3636 AssertIsConsistent {
3637 inner: Box<HydroNode>,
3638 trusted: bool,
3639 metadata: HydroIrMetadata,
3640 },
3641
3642 UnboundSingleton {
3643 inner: Box<HydroNode>,
3644 metadata: HydroIrMetadata,
3645 },
3646}
3647
3648pub type SeenSharedNodes = HashMap<*const RefCell<HydroNode>, Rc<RefCell<HydroNode>>>;
3649pub type SeenSharedNodeLocations = HashMap<*const RefCell<HydroNode>, LocationId>;
3650
3651#[cfg(feature = "build")]
3659#[derive(PartialEq, Eq, Clone, Copy, Debug)]
3660enum UpdateFeedRepr {
3661 Plain,
3663 Tombstoned,
3665}
3666
3667#[cfg(feature = "build")]
3668fn update_feed_repr(metadata: &HydroIrMetadata) -> UpdateFeedRepr {
3669 if metadata.location_id.is_root()
3670 && metadata
3671 .collection_kind
3672 .root_repr_is_tombstoned_update_feed()
3673 {
3674 UpdateFeedRepr::Tombstoned
3675 } else {
3676 UpdateFeedRepr::Plain
3677 }
3678}
3679
3680#[cfg(feature = "build")]
3684fn maybe_observe_for_mut(
3685 f: &ClosureExpr,
3686 in_ident: syn::Ident,
3687 in_location: &LocationId,
3688 in_kind: &CollectionKind,
3689 op_meta: &HydroIrOpMetadata,
3690 builders_or_callback: &mut BuildersOrCallback<
3691 '_,
3692 impl FnMut(&mut HydroRoot, &mut crate::Counter<StmtId>),
3693 impl FnMut(&mut HydroNode, &mut crate::Counter<StmtId>),
3694 >,
3695 next_stmt_id: &mut crate::Counter<StmtId>,
3696) -> syn::Ident {
3697 if f.has_mut_ref() && !in_kind.is_strict() {
3698 let observe_stmt_id = next_stmt_id.get_and_increment();
3699 let observe_ident =
3700 syn::Ident::new(&format!("stream_{}", observe_stmt_id), Span::call_site());
3701 if let BuildersOrCallback::Builders(graph_builders) = builders_or_callback {
3702 graph_builders.observe_for_mut(in_location, in_ident, in_kind, &observe_ident, op_meta);
3703 }
3704 observe_ident
3705 } else {
3706 in_ident
3707 }
3708}
3709
3710impl HydroNode {
3711 pub fn transform_bottom_up(
3712 &mut self,
3713 transform: &mut impl FnMut(&mut HydroNode),
3714 seen_tees: &mut SeenSharedNodes,
3715 check_well_formed: bool,
3716 ) {
3717 self.transform_children(
3718 |n, s| n.transform_bottom_up(transform, s, check_well_formed),
3719 seen_tees,
3720 );
3721
3722 transform(self);
3723
3724 let self_location = self.metadata().location_id.root();
3725
3726 if check_well_formed {
3727 match &*self {
3728 HydroNode::Network { .. } => {}
3729 _ => {
3730 self.input_metadata().iter().for_each(|i| {
3731 assert!(
3732 i.location_id.root() == self_location,
3733 "Mismatching IR locations, child: {:?} ({:?}) of: {:?} ({:?})",
3734 i,
3735 i.location_id.root(),
3736 self,
3737 self_location
3738 );
3739 });
3740 }
3741 }
3742 }
3743 }
3744
3745 #[inline(always)]
3746 pub fn transform_children(
3747 &mut self,
3748 mut transform: impl FnMut(&mut HydroNode, &mut SeenSharedNodes),
3749 seen_tees: &mut SeenSharedNodes,
3750 ) {
3751 match self {
3752 HydroNode::Placeholder => {
3753 panic!();
3754 }
3755
3756 HydroNode::Source { .. }
3757 | HydroNode::SingletonSource { .. }
3758 | HydroNode::CycleSource { .. }
3759 | HydroNode::ExternalInput { .. } => {}
3760
3761 HydroNode::Tee { inner, .. } | HydroNode::Reference { inner, .. } => {
3762 if let Some(transformed) = seen_tees.get(&inner.as_ptr()) {
3763 *inner = SharedNode(transformed.clone());
3764 } else {
3765 let transformed_cell = Rc::new(RefCell::new(HydroNode::Placeholder));
3766 seen_tees.insert(inner.as_ptr(), transformed_cell.clone());
3767 let mut orig = inner.0.replace(HydroNode::Placeholder);
3768 transform(&mut orig, seen_tees);
3769 *transformed_cell.borrow_mut() = orig;
3770 *inner = SharedNode(transformed_cell);
3771 }
3772 }
3773
3774 HydroNode::PartitionSide { inner, .. } => {
3775 if let Some(transformed) = seen_tees.get(&inner.as_ptr()) {
3776 *inner = SharedNode(transformed.clone());
3777 } else {
3778 let transformed_cell = Rc::new(RefCell::new(HydroNode::Placeholder));
3779 seen_tees.insert(inner.as_ptr(), transformed_cell.clone());
3780 let mut orig: HydroNode = inner.0.replace(HydroNode::Placeholder);
3781 transform(&mut orig, seen_tees);
3782 *transformed_cell.borrow_mut() = orig;
3783 *inner = SharedNode(transformed_cell);
3784 }
3785 }
3786 HydroNode::PartitionShared { input, f, .. } => {
3787 f.transform_children(&mut transform, seen_tees);
3788 transform(input.as_mut(), seen_tees);
3789 }
3790
3791 HydroNode::Cast { inner, .. }
3792 | HydroNode::ObserveNonDet { inner, .. }
3793 | HydroNode::BeginAtomic { inner, .. }
3794 | HydroNode::EndAtomic { inner, .. }
3795 | HydroNode::Batch { inner, .. }
3796 | HydroNode::Snapshot { inner, .. }
3797 | HydroNode::YieldConcat { inner, .. }
3798 | HydroNode::UnboundSingleton { inner, .. }
3799 | HydroNode::AssertIsConsistent { inner, .. } => {
3800 transform(inner.as_mut(), seen_tees);
3801 }
3802
3803 HydroNode::Chain { first, second, .. } => {
3804 transform(first.as_mut(), seen_tees);
3805 transform(second.as_mut(), seen_tees);
3806 }
3807
3808 HydroNode::MergeOrdered { first, second, .. } => {
3809 transform(first.as_mut(), seen_tees);
3810 transform(second.as_mut(), seen_tees);
3811 }
3812
3813 HydroNode::ChainFirst { first, second, .. } => {
3814 transform(first.as_mut(), seen_tees);
3815 transform(second.as_mut(), seen_tees);
3816 }
3817
3818 HydroNode::CrossSingleton { left, right, .. }
3819 | HydroNode::CrossProduct { left, right, .. }
3820 | HydroNode::Join { left, right, .. }
3821 | HydroNode::JoinHalf { left, right, .. } => {
3822 transform(left.as_mut(), seen_tees);
3823 transform(right.as_mut(), seen_tees);
3824 }
3825
3826 HydroNode::Difference { pos, neg, .. } | HydroNode::AntiJoin { pos, neg, .. } => {
3827 transform(pos.as_mut(), seen_tees);
3828 transform(neg.as_mut(), seen_tees);
3829 }
3830
3831 HydroNode::Map { f, input, .. } => {
3832 f.transform_children(&mut transform, seen_tees);
3833 transform(input.as_mut(), seen_tees);
3834 }
3835 HydroNode::FlatMap { f, input, .. }
3836 | HydroNode::FlatMapStreamBlocking { f, input, .. }
3837 | HydroNode::Filter { f, input, .. }
3838 | HydroNode::FilterMap { f, input, .. }
3839 | HydroNode::Inspect { f, input, .. }
3840 | HydroNode::Reduce { f, input, .. }
3841 | HydroNode::ReduceKeyed { f, input, .. } => {
3842 f.transform_children(&mut transform, seen_tees);
3843 transform(input.as_mut(), seen_tees);
3844 }
3845 HydroNode::ReduceKeyedWatermark {
3846 f,
3847 input,
3848 watermark,
3849 ..
3850 } => {
3851 f.transform_children(&mut transform, seen_tees);
3852 transform(input.as_mut(), seen_tees);
3853 transform(watermark.as_mut(), seen_tees);
3854 }
3855 HydroNode::Fold {
3856 init, acc, input, ..
3857 }
3858 | HydroNode::Scan {
3859 init, acc, input, ..
3860 }
3861 | HydroNode::ScanAsyncBlocking {
3862 init, acc, input, ..
3863 }
3864 | HydroNode::FoldKeyed {
3865 init, acc, input, ..
3866 } => {
3867 init.transform_children(&mut transform, seen_tees);
3868 acc.transform_children(&mut transform, seen_tees);
3869 transform(input.as_mut(), seen_tees);
3870 }
3871 HydroNode::ResolveFutures { input, .. }
3872 | HydroNode::ResolveFuturesBlocking { input, .. }
3873 | HydroNode::ResolveFuturesOrdered { input, .. }
3874 | HydroNode::Sort { input, .. }
3875 | HydroNode::DeferTick { input, .. }
3876 | HydroNode::Enumerate { input, .. }
3877 | HydroNode::Unique { input, .. }
3878 | HydroNode::Network { input, .. }
3879 | HydroNode::Counter { input, .. } => {
3880 transform(input.as_mut(), seen_tees);
3881 }
3882
3883 HydroNode::VersionedNetworkFork { senders, .. } => {
3884 for (_version, sender, _serialize) in senders.iter_mut() {
3885 transform(sender.as_mut(), seen_tees);
3886 }
3887 }
3888
3889 HydroNode::VersionedNetwork { fork, .. } => {
3890 if let Some(transformed) = seen_tees.get(&fork.as_ptr()) {
3891 *fork = SharedNode(transformed.clone());
3892 } else {
3893 let transformed_cell = Rc::new(RefCell::new(HydroNode::Placeholder));
3894 seen_tees.insert(fork.as_ptr(), transformed_cell.clone());
3895 let mut orig = fork.0.replace(HydroNode::Placeholder);
3896 transform(&mut orig, seen_tees);
3897 *transformed_cell.borrow_mut() = orig;
3898 *fork = SharedNode(transformed_cell);
3899 }
3900 }
3901 }
3902 }
3903
3904 pub fn deep_clone(&self, seen_tees: &mut SeenSharedNodes) -> HydroNode {
3905 match self {
3906 HydroNode::Placeholder => HydroNode::Placeholder,
3907 HydroNode::Cast { inner, metadata } => HydroNode::Cast {
3908 inner: Box::new(inner.deep_clone(seen_tees)),
3909 metadata: metadata.clone(),
3910 },
3911 HydroNode::UnboundSingleton { inner, metadata } => HydroNode::UnboundSingleton {
3912 inner: Box::new(inner.deep_clone(seen_tees)),
3913 metadata: metadata.clone(),
3914 },
3915 HydroNode::ObserveNonDet {
3916 inner,
3917 trusted,
3918 metadata,
3919 } => HydroNode::ObserveNonDet {
3920 inner: Box::new(inner.deep_clone(seen_tees)),
3921 trusted: *trusted,
3922 metadata: metadata.clone(),
3923 },
3924 HydroNode::AssertIsConsistent {
3925 inner,
3926 trusted,
3927 metadata,
3928 } => HydroNode::AssertIsConsistent {
3929 inner: Box::new(inner.deep_clone(seen_tees)),
3930 trusted: *trusted,
3931 metadata: metadata.clone(),
3932 },
3933 HydroNode::Source { source, metadata } => HydroNode::Source {
3934 source: source.clone(),
3935 metadata: metadata.clone(),
3936 },
3937 HydroNode::SingletonSource {
3938 value,
3939 first_tick_only,
3940 metadata,
3941 } => HydroNode::SingletonSource {
3942 value: value.clone(),
3943 first_tick_only: *first_tick_only,
3944 metadata: metadata.clone(),
3945 },
3946 HydroNode::CycleSource { cycle_id, metadata } => HydroNode::CycleSource {
3947 cycle_id: *cycle_id,
3948 metadata: metadata.clone(),
3949 },
3950 HydroNode::Tee { inner, metadata }
3951 | HydroNode::Reference {
3952 inner, metadata, ..
3953 } => {
3954 let cloned_inner = if let Some(transformed) = seen_tees.get(&inner.as_ptr()) {
3955 SharedNode(transformed.clone())
3956 } else {
3957 let new_rc = Rc::new(RefCell::new(HydroNode::Placeholder));
3958 seen_tees.insert(inner.as_ptr(), new_rc.clone());
3959 let cloned = inner.0.borrow().deep_clone(seen_tees);
3960 *new_rc.borrow_mut() = cloned;
3961 SharedNode(new_rc)
3962 };
3963 if let HydroNode::Reference {
3964 kind,
3965 access_counter,
3966 ..
3967 } = self
3968 {
3969 HydroNode::Reference {
3970 inner: cloned_inner,
3971 kind: *kind,
3972 access_counter: access_counter.freeze(),
3973 metadata: metadata.clone(),
3974 }
3975 } else {
3976 HydroNode::Tee {
3977 inner: cloned_inner,
3978 metadata: metadata.clone(),
3979 }
3980 }
3981 }
3982 HydroNode::PartitionSide {
3983 inner,
3984 is_true,
3985 metadata,
3986 } => {
3987 if let Some(transformed) = seen_tees.get(&inner.as_ptr()) {
3988 HydroNode::PartitionSide {
3989 inner: SharedNode(transformed.clone()),
3990 is_true: *is_true,
3991 metadata: metadata.clone(),
3992 }
3993 } else {
3994 let new_rc = Rc::new(RefCell::new(HydroNode::Placeholder));
3995 seen_tees.insert(inner.as_ptr(), new_rc.clone());
3996 let cloned = inner.0.borrow().deep_clone(seen_tees);
3997 *new_rc.borrow_mut() = cloned;
3998 HydroNode::PartitionSide {
3999 inner: SharedNode(new_rc),
4000 is_true: *is_true,
4001 metadata: metadata.clone(),
4002 }
4003 }
4004 }
4005 HydroNode::PartitionShared { input, f, metadata } => HydroNode::PartitionShared {
4006 input: Box::new(input.deep_clone(seen_tees)),
4007 f: f.deep_clone(seen_tees),
4008 metadata: metadata.clone(),
4009 },
4010 HydroNode::YieldConcat { inner, metadata } => HydroNode::YieldConcat {
4011 inner: Box::new(inner.deep_clone(seen_tees)),
4012 metadata: metadata.clone(),
4013 },
4014 HydroNode::BeginAtomic { inner, metadata } => HydroNode::BeginAtomic {
4015 inner: Box::new(inner.deep_clone(seen_tees)),
4016 metadata: metadata.clone(),
4017 },
4018 HydroNode::EndAtomic { inner, metadata } => HydroNode::EndAtomic {
4019 inner: Box::new(inner.deep_clone(seen_tees)),
4020 metadata: metadata.clone(),
4021 },
4022 HydroNode::Batch { inner, metadata } => HydroNode::Batch {
4023 inner: Box::new(inner.deep_clone(seen_tees)),
4024 metadata: metadata.clone(),
4025 },
4026 HydroNode::Snapshot { inner, metadata } => HydroNode::Snapshot {
4027 inner: Box::new(inner.deep_clone(seen_tees)),
4028 metadata: metadata.clone(),
4029 },
4030 HydroNode::Chain {
4031 first,
4032 second,
4033 metadata,
4034 } => HydroNode::Chain {
4035 first: Box::new(first.deep_clone(seen_tees)),
4036 second: Box::new(second.deep_clone(seen_tees)),
4037 metadata: metadata.clone(),
4038 },
4039 HydroNode::MergeOrdered {
4040 first,
4041 second,
4042 metadata,
4043 } => HydroNode::MergeOrdered {
4044 first: Box::new(first.deep_clone(seen_tees)),
4045 second: Box::new(second.deep_clone(seen_tees)),
4046 metadata: metadata.clone(),
4047 },
4048 HydroNode::ChainFirst {
4049 first,
4050 second,
4051 metadata,
4052 } => HydroNode::ChainFirst {
4053 first: Box::new(first.deep_clone(seen_tees)),
4054 second: Box::new(second.deep_clone(seen_tees)),
4055 metadata: metadata.clone(),
4056 },
4057 HydroNode::CrossProduct {
4058 left,
4059 right,
4060 metadata,
4061 } => HydroNode::CrossProduct {
4062 left: Box::new(left.deep_clone(seen_tees)),
4063 right: Box::new(right.deep_clone(seen_tees)),
4064 metadata: metadata.clone(),
4065 },
4066 HydroNode::CrossSingleton {
4067 left,
4068 right,
4069 metadata,
4070 } => HydroNode::CrossSingleton {
4071 left: Box::new(left.deep_clone(seen_tees)),
4072 right: Box::new(right.deep_clone(seen_tees)),
4073 metadata: metadata.clone(),
4074 },
4075 HydroNode::Join {
4076 left,
4077 right,
4078 metadata,
4079 } => HydroNode::Join {
4080 left: Box::new(left.deep_clone(seen_tees)),
4081 right: Box::new(right.deep_clone(seen_tees)),
4082 metadata: metadata.clone(),
4083 },
4084 HydroNode::JoinHalf {
4085 left,
4086 right,
4087 metadata,
4088 } => HydroNode::JoinHalf {
4089 left: Box::new(left.deep_clone(seen_tees)),
4090 right: Box::new(right.deep_clone(seen_tees)),
4091 metadata: metadata.clone(),
4092 },
4093 HydroNode::Difference { pos, neg, metadata } => HydroNode::Difference {
4094 pos: Box::new(pos.deep_clone(seen_tees)),
4095 neg: Box::new(neg.deep_clone(seen_tees)),
4096 metadata: metadata.clone(),
4097 },
4098 HydroNode::AntiJoin { pos, neg, metadata } => HydroNode::AntiJoin {
4099 pos: Box::new(pos.deep_clone(seen_tees)),
4100 neg: Box::new(neg.deep_clone(seen_tees)),
4101 metadata: metadata.clone(),
4102 },
4103 HydroNode::ResolveFutures { input, metadata } => HydroNode::ResolveFutures {
4104 input: Box::new(input.deep_clone(seen_tees)),
4105 metadata: metadata.clone(),
4106 },
4107 HydroNode::ResolveFuturesBlocking { input, metadata } => {
4108 HydroNode::ResolveFuturesBlocking {
4109 input: Box::new(input.deep_clone(seen_tees)),
4110 metadata: metadata.clone(),
4111 }
4112 }
4113 HydroNode::ResolveFuturesOrdered { input, metadata } => {
4114 HydroNode::ResolveFuturesOrdered {
4115 input: Box::new(input.deep_clone(seen_tees)),
4116 metadata: metadata.clone(),
4117 }
4118 }
4119 HydroNode::Map { f, input, metadata } => HydroNode::Map {
4120 f: f.deep_clone(seen_tees),
4121 input: Box::new(input.deep_clone(seen_tees)),
4122 metadata: metadata.clone(),
4123 },
4124 HydroNode::FlatMap { f, input, metadata } => HydroNode::FlatMap {
4125 f: f.deep_clone(seen_tees),
4126 input: Box::new(input.deep_clone(seen_tees)),
4127 metadata: metadata.clone(),
4128 },
4129 HydroNode::FlatMapStreamBlocking { f, input, metadata } => {
4130 HydroNode::FlatMapStreamBlocking {
4131 f: f.deep_clone(seen_tees),
4132 input: Box::new(input.deep_clone(seen_tees)),
4133 metadata: metadata.clone(),
4134 }
4135 }
4136 HydroNode::Filter { f, input, metadata } => HydroNode::Filter {
4137 f: f.deep_clone(seen_tees),
4138 input: Box::new(input.deep_clone(seen_tees)),
4139 metadata: metadata.clone(),
4140 },
4141 HydroNode::FilterMap { f, input, metadata } => HydroNode::FilterMap {
4142 f: f.deep_clone(seen_tees),
4143 input: Box::new(input.deep_clone(seen_tees)),
4144 metadata: metadata.clone(),
4145 },
4146 HydroNode::DeferTick { input, metadata } => HydroNode::DeferTick {
4147 input: Box::new(input.deep_clone(seen_tees)),
4148 metadata: metadata.clone(),
4149 },
4150 HydroNode::Enumerate { input, metadata } => HydroNode::Enumerate {
4151 input: Box::new(input.deep_clone(seen_tees)),
4152 metadata: metadata.clone(),
4153 },
4154 HydroNode::Inspect { f, input, metadata } => HydroNode::Inspect {
4155 f: f.deep_clone(seen_tees),
4156 input: Box::new(input.deep_clone(seen_tees)),
4157 metadata: metadata.clone(),
4158 },
4159 HydroNode::Unique { input, metadata } => HydroNode::Unique {
4160 input: Box::new(input.deep_clone(seen_tees)),
4161 metadata: metadata.clone(),
4162 },
4163 HydroNode::Sort { input, metadata } => HydroNode::Sort {
4164 input: Box::new(input.deep_clone(seen_tees)),
4165 metadata: metadata.clone(),
4166 },
4167 HydroNode::Fold {
4168 init,
4169 acc,
4170 input,
4171 metadata,
4172 } => HydroNode::Fold {
4173 init: init.deep_clone(seen_tees),
4174 acc: acc.deep_clone(seen_tees),
4175 input: Box::new(input.deep_clone(seen_tees)),
4176 metadata: metadata.clone(),
4177 },
4178 HydroNode::Scan {
4179 init,
4180 acc,
4181 input,
4182 metadata,
4183 } => HydroNode::Scan {
4184 init: init.deep_clone(seen_tees),
4185 acc: acc.deep_clone(seen_tees),
4186 input: Box::new(input.deep_clone(seen_tees)),
4187 metadata: metadata.clone(),
4188 },
4189 HydroNode::ScanAsyncBlocking {
4190 init,
4191 acc,
4192 input,
4193 metadata,
4194 } => HydroNode::ScanAsyncBlocking {
4195 init: init.deep_clone(seen_tees),
4196 acc: acc.deep_clone(seen_tees),
4197 input: Box::new(input.deep_clone(seen_tees)),
4198 metadata: metadata.clone(),
4199 },
4200 HydroNode::FoldKeyed {
4201 init,
4202 acc,
4203 input,
4204 metadata,
4205 } => HydroNode::FoldKeyed {
4206 init: init.deep_clone(seen_tees),
4207 acc: acc.deep_clone(seen_tees),
4208 input: Box::new(input.deep_clone(seen_tees)),
4209 metadata: metadata.clone(),
4210 },
4211 HydroNode::ReduceKeyedWatermark {
4212 f,
4213 input,
4214 watermark,
4215 metadata,
4216 } => HydroNode::ReduceKeyedWatermark {
4217 f: f.deep_clone(seen_tees),
4218 input: Box::new(input.deep_clone(seen_tees)),
4219 watermark: Box::new(watermark.deep_clone(seen_tees)),
4220 metadata: metadata.clone(),
4221 },
4222 HydroNode::Reduce { f, input, metadata } => HydroNode::Reduce {
4223 f: f.deep_clone(seen_tees),
4224 input: Box::new(input.deep_clone(seen_tees)),
4225 metadata: metadata.clone(),
4226 },
4227 HydroNode::ReduceKeyed { f, input, metadata } => HydroNode::ReduceKeyed {
4228 f: f.deep_clone(seen_tees),
4229 input: Box::new(input.deep_clone(seen_tees)),
4230 metadata: metadata.clone(),
4231 },
4232 HydroNode::Network {
4233 name,
4234 networking_info,
4235 serialize,
4236 deserialize,
4237 instantiate_fn,
4238 input,
4239 metadata,
4240 } => HydroNode::Network {
4241 name: name.clone(),
4242 networking_info: networking_info.clone(),
4243 serialize: serialize.clone(),
4244 deserialize: deserialize.clone(),
4245 instantiate_fn: instantiate_fn.clone(),
4246 input: Box::new(input.deep_clone(seen_tees)),
4247 metadata: metadata.clone(),
4248 },
4249 HydroNode::ExternalInput {
4250 from_external_key,
4251 from_port_id,
4252 from_many,
4253 codec_type,
4254 port_hint,
4255 instantiate_fn,
4256 deserialize_fn,
4257 metadata,
4258 } => HydroNode::ExternalInput {
4259 from_external_key: *from_external_key,
4260 from_port_id: *from_port_id,
4261 from_many: *from_many,
4262 codec_type: codec_type.clone(),
4263 port_hint: *port_hint,
4264 instantiate_fn: instantiate_fn.clone(),
4265 deserialize_fn: deserialize_fn.clone(),
4266 metadata: metadata.clone(),
4267 },
4268 HydroNode::Counter {
4269 tag,
4270 duration,
4271 prefix,
4272 input,
4273 metadata,
4274 } => HydroNode::Counter {
4275 tag: tag.clone(),
4276 duration: duration.clone(),
4277 prefix: prefix.clone(),
4278 input: Box::new(input.deep_clone(seen_tees)),
4279 metadata: metadata.clone(),
4280 },
4281 HydroNode::VersionedNetworkFork {
4282 channel_id,
4283 channel_name,
4284 senders,
4285 metadata,
4286 } => HydroNode::VersionedNetworkFork {
4287 channel_id: *channel_id,
4288 channel_name: channel_name.clone(),
4289 senders: senders
4290 .iter()
4291 .map(|(version, sender, serialize)| {
4292 (
4293 *version,
4294 Box::new(sender.deep_clone(seen_tees)),
4295 serialize.clone(),
4296 )
4297 })
4298 .collect(),
4299 metadata: metadata.clone(),
4300 },
4301 HydroNode::VersionedNetwork {
4302 fork,
4303 version,
4304 deserialize,
4305 metadata,
4306 } => {
4307 let cloned_fork = if let Some(transformed) = seen_tees.get(&fork.as_ptr()) {
4308 SharedNode(transformed.clone())
4309 } else {
4310 let new_rc = Rc::new(RefCell::new(HydroNode::Placeholder));
4311 seen_tees.insert(fork.as_ptr(), new_rc.clone());
4312 let cloned = fork.0.borrow().deep_clone(seen_tees);
4313 *new_rc.borrow_mut() = cloned;
4314 SharedNode(new_rc)
4315 };
4316 HydroNode::VersionedNetwork {
4317 fork: cloned_fork,
4318 version: *version,
4319 deserialize: deserialize.clone(),
4320 metadata: metadata.clone(),
4321 }
4322 }
4323 }
4324 }
4325
4326 #[cfg(feature = "build")]
4327 pub fn emit_core(
4328 &mut self,
4329 builders_or_callback: &mut BuildersOrCallback<
4330 '_,
4331 impl FnMut(&mut HydroRoot, &mut crate::Counter<StmtId>),
4332 impl FnMut(&mut HydroNode, &mut crate::Counter<StmtId>),
4333 >,
4334 seen_tees: &mut SeenSharedNodes,
4335 built_tees: &mut HashMap<*const RefCell<HydroNode>, Vec<syn::Ident>>,
4336 next_stmt_id: &mut crate::Counter<StmtId>,
4337 fold_hooked_idents: &mut HashSet<String>,
4338 ) -> syn::Ident {
4339 let mut ident_stack: Vec<syn::Ident> = Vec::new();
4340
4341 self.transform_bottom_up(
4342 &mut |node: &mut HydroNode| {
4343 let out_location = node.metadata().location_id.clone();
4344 match node {
4345 HydroNode::Placeholder => {
4346 panic!()
4347 }
4348
4349 HydroNode::Cast { inner, metadata } => {
4350 let in_repr = update_feed_repr(inner.metadata());
4371 let out_repr = update_feed_repr(metadata);
4372 let cross_kind_tombstoned = in_repr == UpdateFeedRepr::Tombstoned
4373 && out_repr == UpdateFeedRepr::Tombstoned
4374 && std::mem::discriminant(&inner.metadata().collection_kind)
4375 != std::mem::discriminant(&metadata.collection_kind);
4376
4377 if in_repr == out_repr && !cross_kind_tombstoned {
4378 let _ = next_stmt_id.get_and_increment();
4381 match builders_or_callback {
4382 BuildersOrCallback::Builders(_) => {}
4383 BuildersOrCallback::Callback(_, node_callback) => {
4384 node_callback(node, next_stmt_id);
4385 }
4386 }
4387 } else {
4389 let input_ident = ident_stack.pop().unwrap();
4390
4391 let stmt_id = next_stmt_id.get_and_increment();
4392 let cast_ident =
4393 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4394
4395 match builders_or_callback {
4396 BuildersOrCallback::Builders(graph_builders) => {
4397 let cast_op: TokenStream = if cross_kind_tombstoned {
4398 match (
4399 &inner.metadata().collection_kind,
4400 &metadata.collection_kind,
4401 ) {
4402 (
4403 CollectionKind::Optional { .. },
4404 CollectionKind::KeyedSingleton { .. },
4405 ) => {
4406 let lifetime: TokenStream = graph_builders
4412 .cross_tick_state_lifetime(&out_location);
4413 let lifetime: syn::Lifetime =
4414 syn::parse2(lifetime).unwrap();
4415 quote! {
4416 scan::<#lifetime>(
4417 || ::std::option::Option::None,
4418 |previous_key, update: ::std::option::Option<(_, _)>| {
4419 let mut deltas = ::std::vec::Vec::new();
4420 match update {
4421 ::std::option::Option::Some((key, value)) => {
4422 match previous_key.take() {
4423 ::std::option::Option::Some(previous) if previous != key => {
4424 deltas.push((previous, ::std::option::Option::None));
4425 }
4426 _ => {}
4427 }
4428 *previous_key = ::std::option::Option::Some(::std::clone::Clone::clone(&key));
4429 deltas.push((key, ::std::option::Option::Some(value)));
4430 }
4431 ::std::option::Option::None => {
4432 if let ::std::option::Option::Some(previous) = previous_key.take() {
4433 deltas.push((previous, ::std::option::Option::None));
4434 }
4435 }
4436 }
4437 ::std::option::Option::Some(deltas)
4438 },
4439 ) -> flatten()
4440 }
4441 }
4442 (in_kind, out_kind) => todo!(
4443 "casting between tombstoned update feeds \
4444 {:?} -> {:?} is not yet supported",
4445 in_kind,
4446 out_kind
4447 ),
4448 }
4449 } else {
4450 match (in_repr, out_repr) {
4451 (UpdateFeedRepr::Plain, UpdateFeedRepr::Tombstoned) => {
4452 match &metadata.collection_kind {
4453 CollectionKind::Optional { .. } => {
4454 quote! { map(::std::option::Option::Some) }
4455 }
4456 CollectionKind::KeyedSingleton { .. } => {
4457 quote! { map(|(__hydro_key, __hydro_value)| (__hydro_key, ::std::option::Option::Some(__hydro_value))) }
4458 }
4459 other => panic!(
4460 "unexpected tombstoned update feed kind for cast: {:?}",
4461 other
4462 ),
4463 }
4464 }
4465 (UpdateFeedRepr::Tombstoned, UpdateFeedRepr::Plain) => {
4466 match &inner.metadata().collection_kind {
4477 CollectionKind::Optional { .. } => {
4478 quote! { filter_map(|__hydro_update| __hydro_update) }
4479 }
4480 CollectionKind::KeyedSingleton { .. } => {
4481 quote! { filter_map(|(__hydro_key, __hydro_update)| __hydro_update.map(|__hydro_value| (__hydro_key, __hydro_value))) }
4482 }
4483 other => panic!(
4484 "unexpected tombstoned update feed kind for cast: {:?}",
4485 other
4486 ),
4487 }
4488 }
4489 _ => unreachable!(),
4490 }
4491 };
4492
4493 graph_builders.add_dfir_at(
4494 &out_location,
4495 parse_quote! {
4496 #cast_ident = #input_ident -> #cast_op;
4497 },
4498 Some(&stmt_id.to_string()),
4499 );
4500 }
4501 BuildersOrCallback::Callback(_, node_callback) => {
4502 node_callback(node, next_stmt_id);
4503 }
4504 }
4505
4506 ident_stack.push(cast_ident);
4507 }
4508 }
4509
4510 HydroNode::UnboundSingleton { .. } => {
4511 let inner_ident = ident_stack.pop().unwrap();
4512
4513 let stmt_id = next_stmt_id.get_and_increment();
4514 let out_ident =
4515 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4516
4517 match builders_or_callback {
4518 BuildersOrCallback::Builders(graph_builders) => {
4519 if graph_builders.singleton_intermediates() {
4520 graph_builders.add_dfir_at(
4521 &out_location,
4522 parse_quote! {
4523 #out_ident = #inner_ident;
4524 },
4525 None,
4526 );
4527 } else {
4528 graph_builders.add_dfir_at(
4529 &out_location,
4530 parse_quote! {
4531 #out_ident = #inner_ident -> persist::<'static>();
4532 },
4533 None,
4534 );
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::AssertIsConsistent { inner, trusted, .. } => {
4546 let inner_ident = ident_stack.pop().unwrap();
4547
4548 let stmt_id = next_stmt_id.get_and_increment();
4549 let out_ident =
4550 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4551
4552 match builders_or_callback {
4553 BuildersOrCallback::Builders(graph_builders) => {
4554 graph_builders.assert_is_consistent(
4555 *trusted,
4556 &inner.metadata().location_id,
4557 inner_ident,
4558 &out_ident,
4559 );
4560 }
4561 BuildersOrCallback::Callback(_, node_callback) => {
4562 node_callback(node, next_stmt_id);
4563 }
4564 }
4565
4566 ident_stack.push(out_ident);
4567 }
4568
4569 HydroNode::ObserveNonDet {
4570 inner,
4571 trusted,
4572 metadata,
4573 ..
4574 } => {
4575 let inner_ident = ident_stack.pop().unwrap();
4576
4577 let stmt_id = next_stmt_id.get_and_increment();
4578 let observe_ident =
4579 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4580
4581 match builders_or_callback {
4582 BuildersOrCallback::Builders(graph_builders) => {
4583 graph_builders.observe_nondet(
4584 *trusted,
4585 &inner.metadata().location_id,
4586 inner_ident,
4587 &inner.metadata().collection_kind,
4588 &observe_ident,
4589 &metadata.collection_kind,
4590 &metadata.op,
4591 );
4592 }
4593 BuildersOrCallback::Callback(_, node_callback) => {
4594 node_callback(node, next_stmt_id);
4595 }
4596 }
4597
4598 ident_stack.push(observe_ident);
4599 }
4600
4601 HydroNode::Batch {
4602 inner, metadata, ..
4603 } => {
4604 let inner_ident = ident_stack.pop().unwrap();
4605
4606 let stmt_id = next_stmt_id.get_and_increment();
4607 let batch_ident =
4608 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4609
4610 match builders_or_callback {
4611 BuildersOrCallback::Builders(graph_builders) => {
4612 graph_builders.batch(
4613 inner_ident,
4614 &inner.metadata().location_id,
4615 &inner.metadata().collection_kind,
4616 &batch_ident,
4617 &out_location,
4618 &metadata.op,
4619 fold_hooked_idents,
4620 );
4621 }
4622 BuildersOrCallback::Callback(_, node_callback) => {
4623 node_callback(node, next_stmt_id);
4624 }
4625 }
4626
4627 ident_stack.push(batch_ident);
4628 }
4629
4630 HydroNode::Snapshot {
4631 inner, metadata, ..
4632 } => {
4633 let inner_ident = ident_stack.pop().unwrap();
4634
4635 let stmt_id = next_stmt_id.get_and_increment();
4636 let snapshot_ident =
4637 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4638
4639 match builders_or_callback {
4640 BuildersOrCallback::Builders(graph_builders) => {
4641 graph_builders.snapshot(
4642 inner_ident,
4643 &inner.metadata().location_id,
4644 &inner.metadata().collection_kind,
4645 &snapshot_ident,
4646 &out_location,
4647 &metadata.op,
4648 fold_hooked_idents,
4649 );
4650 }
4651 BuildersOrCallback::Callback(_, node_callback) => {
4652 node_callback(node, next_stmt_id);
4653 }
4654 }
4655
4656 ident_stack.push(snapshot_ident);
4657 }
4658
4659 HydroNode::YieldConcat {
4660 inner, metadata, ..
4661 } => {
4662 let inner_ident = ident_stack.pop().unwrap();
4663
4664 let stmt_id = next_stmt_id.get_and_increment();
4665 let yield_ident =
4666 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4667
4668 match builders_or_callback {
4669 BuildersOrCallback::Builders(graph_builders) => {
4670 graph_builders.yield_from_tick(
4671 inner_ident,
4672 &inner.metadata().location_id,
4673 &inner.metadata().collection_kind,
4674 &yield_ident,
4675 &out_location,
4676 &metadata.collection_kind,
4677 );
4678 }
4679 BuildersOrCallback::Callback(_, node_callback) => {
4680 node_callback(node, next_stmt_id);
4681 }
4682 }
4683
4684 ident_stack.push(yield_ident);
4685 }
4686
4687 HydroNode::BeginAtomic { inner, metadata } => {
4688 let inner_ident = ident_stack.pop().unwrap();
4689
4690 let stmt_id = next_stmt_id.get_and_increment();
4691 let begin_ident =
4692 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4693
4694 match builders_or_callback {
4695 BuildersOrCallback::Builders(graph_builders) => {
4696 graph_builders.begin_atomic(
4697 inner_ident,
4698 &inner.metadata().location_id,
4699 &inner.metadata().collection_kind,
4700 &begin_ident,
4701 &out_location,
4702 &metadata.op,
4703 );
4704 }
4705 BuildersOrCallback::Callback(_, node_callback) => {
4706 node_callback(node, next_stmt_id);
4707 }
4708 }
4709
4710 ident_stack.push(begin_ident);
4711 }
4712
4713 HydroNode::EndAtomic {
4714 inner, metadata, ..
4715 } => {
4716 let inner_ident = ident_stack.pop().unwrap();
4717
4718 let stmt_id = next_stmt_id.get_and_increment();
4719 let end_ident =
4720 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4721
4722 match builders_or_callback {
4723 BuildersOrCallback::Builders(graph_builders) => {
4724 graph_builders.end_atomic(
4725 inner_ident,
4726 &inner.metadata().location_id,
4727 &inner.metadata().collection_kind,
4728 &end_ident,
4729 &metadata.collection_kind,
4730 );
4731 }
4732 BuildersOrCallback::Callback(_, node_callback) => {
4733 node_callback(node, next_stmt_id);
4734 }
4735 }
4736
4737 ident_stack.push(end_ident);
4738 }
4739
4740 HydroNode::Source {
4741 source, metadata, ..
4742 } => {
4743 if matches!(source, HydroSource::ExternalNetwork()) {
4744 ident_stack.push(syn::Ident::new("DUMMY", Span::call_site()));
4745 } else {
4746 let stmt_id = next_stmt_id.get_and_increment();
4747 let source_ident =
4748 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4749
4750 let mut tick_source_rhs: Option<TokenStream> = None;
4754
4755 let source_stmt = match source {
4756 HydroSource::Stream(expr) => {
4757 debug_assert!(metadata.location_id.is_top_level());
4758 parse_quote! {
4759 #source_ident = source_stream(#expr);
4760 }
4761 }
4762
4763 HydroSource::ExternalNetwork() => {
4764 unreachable!()
4765 }
4766
4767 HydroSource::Iter(expr) => {
4768 if metadata.location_id.is_root() {
4769 parse_quote! {
4770 #source_ident = source_iter(#expr);
4771 }
4772 } else {
4773 tick_source_rhs = Some(quote! { source_iter(#expr) });
4779 parse_quote! {
4780 #source_ident = source_iter(#expr) -> persist::<'static>();
4781 }
4782 }
4783 }
4784
4785 HydroSource::Spin() => {
4786 debug_assert!(metadata.location_id.is_top_level());
4787 parse_quote! {
4788 #source_ident = spin();
4789 }
4790 }
4791
4792 HydroSource::ClusterMembers(target_loc, state) => {
4793 debug_assert!(metadata.location_id.is_top_level());
4794
4795 let members_tee_ident = syn::Ident::new(
4796 &format!(
4797 "__cluster_members_tee_{}_{}",
4798 metadata.location_id.root().key(),
4799 target_loc.key(),
4800 ),
4801 Span::call_site(),
4802 );
4803
4804 match state {
4805 ClusterMembersState::Stream(d) => {
4806 parse_quote! {
4807 #members_tee_ident = source_stream(#d) -> tee();
4808 #source_ident = #members_tee_ident;
4809 }
4810 },
4811 ClusterMembersState::Uninit => syn::parse_quote! {
4812 #source_ident = source_stream(DUMMY);
4813 },
4814 ClusterMembersState::Tee(..) => parse_quote! {
4815 #source_ident = #members_tee_ident;
4816 },
4817 }
4818 }
4819
4820 HydroSource::Embedded(ident) => {
4821 parse_quote! {
4822 #source_ident = source_stream(#ident);
4823 }
4824 }
4825
4826 HydroSource::EmbeddedSingleton(ident) => {
4827 parse_quote! {
4828 #source_ident = source_iter([#ident]);
4829 }
4830 }
4831 };
4832
4833 match builders_or_callback {
4834 BuildersOrCallback::Builders(graph_builders) => {
4835 if let Some(source_rhs) = tick_source_rhs {
4836 graph_builders.add_tick_source(
4837 &out_location,
4838 source_rhs,
4839 &source_ident,
4840 true,
4841 Some(&stmt_id.to_string()),
4842 );
4843 } else {
4844 graph_builders.add_dfir_at(
4845 &out_location,
4846 source_stmt,
4847 Some(&stmt_id.to_string()),
4848 );
4849 }
4850 }
4851 BuildersOrCallback::Callback(_, node_callback) => {
4852 node_callback(node, next_stmt_id);
4853 }
4854 }
4855
4856 ident_stack.push(source_ident);
4857 }
4858 }
4859
4860 HydroNode::SingletonSource { value, first_tick_only, metadata } => {
4861 let stmt_id = next_stmt_id.get_and_increment();
4862 let source_ident =
4863 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4864
4865 match builders_or_callback {
4866 BuildersOrCallback::Builders(graph_builders) => {
4867 if *first_tick_only {
4868 assert!(
4869 !metadata.location_id.is_top_level(),
4870 "first_tick_only SingletonSource must be inside a tick"
4871 );
4872 graph_builders.add_tick_source(
4874 &out_location,
4875 quote! { source_iter([#value]) },
4876 &source_ident,
4877 false,
4878 Some(&stmt_id.to_string()),
4879 );
4880 } else if metadata.location_id.is_root() {
4881 if metadata.collection_kind.is_bounded() {
4882 graph_builders.add_dfir_at(
4883 &out_location,
4884 parse_quote! {
4885 #source_ident = source_iter([#value]);
4886 },
4887 Some(&stmt_id.to_string()),
4888 );
4889 } else {
4890 graph_builders.add_dfir_at(
4891 &out_location,
4892 parse_quote! {
4893 #source_ident = source_iter([#value]) -> persist::<'static>();
4894 },
4895 Some(&stmt_id.to_string()),
4896 );
4897 }
4898 } else {
4899 graph_builders.add_tick_source(
4904 &out_location,
4905 quote! { source_iter([#value]) },
4906 &source_ident,
4907 true,
4908 Some(&stmt_id.to_string()),
4909 );
4910 }
4911 }
4912 BuildersOrCallback::Callback(_, node_callback) => {
4913 node_callback(node, next_stmt_id);
4914 }
4915 }
4916
4917 ident_stack.push(source_ident);
4918 }
4919
4920 HydroNode::CycleSource { cycle_id, .. } => {
4921 let ident = cycle_id.as_ident();
4922
4923 let _ = next_stmt_id.get_and_increment();
4925
4926 match builders_or_callback {
4927 BuildersOrCallback::Builders(_) => {}
4928 BuildersOrCallback::Callback(_, node_callback) => {
4929 node_callback(node, next_stmt_id);
4930 }
4931 }
4932
4933 ident_stack.push(ident);
4934 }
4935
4936 HydroNode::Tee { inner, .. } => {
4937 let stmt_id = next_stmt_id.get_and_increment();
4940
4941 let ret_ident = if let Some(built_idents) =
4942 built_tees.get(&(std::ptr::from_ref(inner.0.as_ref())))
4943 {
4944 match builders_or_callback {
4945 BuildersOrCallback::Builders(_) => {}
4946 BuildersOrCallback::Callback(_, node_callback) => {
4947 node_callback(node, next_stmt_id);
4948 }
4949 }
4950
4951 built_idents[0].clone()
4952 } else {
4953 let inner_ident = ident_stack.pop().unwrap();
4956
4957 let tee_ident =
4958 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
4959
4960 built_tees.insert(
4961 std::ptr::from_ref(inner.0.as_ref()),
4962 vec![tee_ident.clone()],
4963 );
4964
4965 match builders_or_callback {
4966 BuildersOrCallback::Builders(graph_builders) => {
4967 if fold_hooked_idents.contains(&inner_ident.to_string()) {
4979 fold_hooked_idents.insert(tee_ident.to_string());
4980 }
4981 graph_builders.add_dfir_at(
4982 &out_location,
4983 parse_quote! {
4984 #tee_ident = #inner_ident -> tee();
4985 },
4986 Some(&stmt_id.to_string()),
4987 );
4988 }
4989 BuildersOrCallback::Callback(_, node_callback) => {
4990 node_callback(node, next_stmt_id);
4991 }
4992 }
4993
4994 tee_ident
4995 };
4996
4997 ident_stack.push(ret_ident);
4998 }
4999
5000 HydroNode::Reference { inner, kind, .. } => {
5001 let stmt_id = next_stmt_id.get_and_increment();
5004
5005 let ret_ident = if let Some(built_idents) =
5006 built_tees.get(&(std::ptr::from_ref(inner.0.as_ref())))
5007 {
5008 built_idents[0].clone()
5009 } else {
5010 let inner_ident = ident_stack.pop().unwrap();
5011
5012 let ref_ident =
5013 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5014
5015 built_tees.insert(
5016 std::ptr::from_ref(inner.0.as_ref()),
5017 vec![ref_ident.clone()],
5018 );
5019
5020 match builders_or_callback {
5021 BuildersOrCallback::Builders(graph_builders) => {
5022 let op_ident = syn::Ident::new(
5023 match kind {
5024 crate::handoff_ref::HandoffRefKind::Singleton => "singleton",
5025 crate::handoff_ref::HandoffRefKind::Optional => "optional",
5026 crate::handoff_ref::HandoffRefKind::Vec => "handoff",
5027 },
5028 Span::call_site(),
5029 );
5030 graph_builders.add_dfir_at(
5031 &out_location,
5032 parse_quote! {
5033 #ref_ident = #inner_ident -> #op_ident();
5034 },
5035 Some(&stmt_id.to_string()),
5036 );
5037 }
5038 BuildersOrCallback::Callback(_, node_callback) => {
5039 node_callback(node, next_stmt_id);
5040 }
5041 }
5042
5043 ref_ident
5044 };
5045
5046 ident_stack.push(ret_ident);
5047 }
5048
5049 HydroNode::PartitionSide {
5050 inner, is_true, metadata: _,
5051 } => {
5052 let is_true = *is_true; let ptr = std::ptr::from_ref(inner.0.as_ref());
5054 let stmt_id = next_stmt_id.get_and_increment();
5055
5056 let ret_ident = if let Some(built_idents) = built_tees.get(&ptr) {
5057 match builders_or_callback {
5058 BuildersOrCallback::Builders(_) => {}
5059 BuildersOrCallback::Callback(_, node_callback) => {
5060 node_callback(node, next_stmt_id);
5061 }
5062 }
5063
5064 let idx = usize::from(!is_true);
5065 built_idents[idx].clone()
5066 } else {
5067 let partition_ident = ident_stack.pop().unwrap();
5070
5071 let true_ident = syn::Ident::new(
5072 &format!("stream_{}_true", stmt_id),
5073 Span::call_site(),
5074 );
5075 let false_ident = syn::Ident::new(
5076 &format!("stream_{}_false", stmt_id),
5077 Span::call_site(),
5078 );
5079
5080 built_tees.insert(
5081 ptr,
5082 vec![true_ident.clone(), false_ident.clone()],
5083 );
5084
5085 let stmt_id = next_stmt_id.get_and_increment();
5086 match builders_or_callback {
5087 BuildersOrCallback::Builders(graph_builders) => {
5088 graph_builders.add_dfir_at(
5089 &out_location,
5090 parse_quote! {
5091 #true_ident = #partition_ident[0];
5092 #false_ident = #partition_ident[1];
5093 },
5094 Some(&stmt_id.to_string()),
5095 );
5096 }
5097 BuildersOrCallback::Callback(_, node_callback) => {
5098 node_callback(node, next_stmt_id);
5099 }
5100 }
5101
5102 if is_true { true_ident } else { false_ident }
5103 };
5104
5105 ident_stack.push(ret_ident);
5106 }
5107
5108 HydroNode::PartitionShared { input, f, metadata } => {
5109 let inner_ident = ident_stack.pop().unwrap();
5112 let f_tokens = f.emit_tokens(&mut ident_stack);
5113
5114 let inner_ident = {
5115 maybe_observe_for_mut(
5116 f, inner_ident,
5117 &input.metadata().location_id,
5118 &input.metadata().collection_kind,
5119 &metadata.op,
5120 builders_or_callback, next_stmt_id,
5121 )
5122 };
5123
5124 let stmt_id = next_stmt_id.get_and_increment();
5125 let partition_ident = syn::Ident::new(
5126 &format!("stream_{}_partition", stmt_id),
5127 Span::call_site(),
5128 );
5129
5130 let stmt_id = next_stmt_id.get_and_increment();
5131 match builders_or_callback {
5132 BuildersOrCallback::Builders(graph_builders) => {
5133 graph_builders.add_dfir_at(
5134 &out_location,
5135 parse_quote! {
5136 #partition_ident = #inner_ident -> partition(|__item, __num_outputs| if (#f_tokens)(__item) { 0_usize } else { 1_usize });
5137 },
5138 Some(&stmt_id.to_string()),
5139 );
5140 }
5141 BuildersOrCallback::Callback(_, node_callback) => {
5142 node_callback(node, next_stmt_id);
5143 }
5144 }
5145 ident_stack.push(partition_ident);
5146 }
5147
5148 HydroNode::Chain { .. } => {
5149 let second_ident = ident_stack.pop().unwrap();
5151 let first_ident = ident_stack.pop().unwrap();
5152
5153 let stmt_id = next_stmt_id.get_and_increment();
5154 let chain_ident =
5155 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5156
5157 match builders_or_callback {
5158 BuildersOrCallback::Builders(graph_builders) => {
5159 graph_builders.add_dfir_at(
5160 &out_location,
5161 parse_quote! {
5162 #chain_ident = chain();
5163 #first_ident -> [0]#chain_ident;
5164 #second_ident -> [1]#chain_ident;
5165 },
5166 Some(&stmt_id.to_string()),
5167 );
5168 }
5169 BuildersOrCallback::Callback(_, node_callback) => {
5170 node_callback(node, next_stmt_id);
5171 }
5172 }
5173
5174 ident_stack.push(chain_ident);
5175 }
5176
5177 HydroNode::MergeOrdered { first, metadata, .. } => {
5178 let second_ident = ident_stack.pop().unwrap();
5179 let first_ident = ident_stack.pop().unwrap();
5180
5181 let stmt_id = next_stmt_id.get_and_increment();
5182 let merge_ident =
5183 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5184
5185 match builders_or_callback {
5186 BuildersOrCallback::Builders(graph_builders) => {
5187 graph_builders.merge_ordered(
5188 &first.metadata().location_id,
5189 first_ident,
5190 second_ident,
5191 &merge_ident,
5192 &first.metadata().collection_kind,
5193 &metadata.op,
5194 Some(&stmt_id.to_string()),
5195 );
5196 }
5197 BuildersOrCallback::Callback(_, node_callback) => {
5198 node_callback(node, next_stmt_id);
5199 }
5200 }
5201
5202 ident_stack.push(merge_ident);
5203 }
5204
5205 HydroNode::ChainFirst { .. } => {
5206 let second_ident = ident_stack.pop().unwrap();
5207 let first_ident = ident_stack.pop().unwrap();
5208
5209 let stmt_id = next_stmt_id.get_and_increment();
5210 let chain_ident =
5211 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5212
5213 match builders_or_callback {
5214 BuildersOrCallback::Builders(graph_builders) => {
5215 graph_builders.add_dfir_at(
5216 &out_location,
5217 parse_quote! {
5218 #chain_ident = chain_first_n(1);
5219 #first_ident -> [0]#chain_ident;
5220 #second_ident -> [1]#chain_ident;
5221 },
5222 Some(&stmt_id.to_string()),
5223 );
5224 }
5225 BuildersOrCallback::Callback(_, node_callback) => {
5226 node_callback(node, next_stmt_id);
5227 }
5228 }
5229
5230 ident_stack.push(chain_ident);
5231 }
5232
5233 HydroNode::CrossSingleton { right, .. } => {
5234 let right_ident = ident_stack.pop().unwrap();
5235 let left_ident = ident_stack.pop().unwrap();
5236
5237 let stmt_id = next_stmt_id.get_and_increment();
5238 let cross_ident =
5239 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5240
5241 match builders_or_callback {
5242 BuildersOrCallback::Builders(graph_builders) => {
5243 if right.metadata().location_id.is_top_level()
5244 && right.metadata().collection_kind.is_bounded()
5245 {
5246 let lifetime =
5247 graph_builders.cross_tick_state_lifetime(&out_location);
5248 graph_builders.add_dfir_at(
5249 &out_location,
5250 parse_quote! {
5251 #cross_ident = cross_singleton::<#lifetime>();
5252 #left_ident -> [input]#cross_ident;
5253 #right_ident -> [single]#cross_ident;
5254 },
5255 Some(&stmt_id.to_string()),
5256 );
5257 } else {
5258 graph_builders.add_dfir_at(
5259 &out_location,
5260 parse_quote! {
5261 #cross_ident = cross_singleton();
5262 #left_ident -> [input]#cross_ident;
5263 #right_ident -> [single]#cross_ident;
5264 },
5265 Some(&stmt_id.to_string()),
5266 );
5267 }
5268 }
5269 BuildersOrCallback::Callback(_, node_callback) => {
5270 node_callback(node, next_stmt_id);
5271 }
5272 }
5273
5274 ident_stack.push(cross_ident);
5275 }
5276
5277 HydroNode::CrossProduct { .. } | HydroNode::Join { .. } => {
5278 let operator: syn::Ident = if matches!(node, HydroNode::CrossProduct { .. }) {
5279 parse_quote!(cross_join_multiset)
5280 } else {
5281 parse_quote!(join_multiset)
5282 };
5283
5284 let (HydroNode::CrossProduct { left, right, .. }
5285 | HydroNode::Join { left, right, .. }) = node
5286 else {
5287 unreachable!()
5288 };
5289
5290 let is_top_level = left.metadata().location_id.is_top_level()
5291 && right.metadata().location_id.is_top_level();
5292 let left_top_level = left.metadata().location_id.is_top_level();
5293 let right_top_level = right.metadata().location_id.is_top_level();
5294
5295 let right_ident = ident_stack.pop().unwrap();
5296 let left_ident = ident_stack.pop().unwrap();
5297
5298 let stmt_id = next_stmt_id.get_and_increment();
5299 let stream_ident =
5300 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5301
5302 match builders_or_callback {
5303 BuildersOrCallback::Builders(graph_builders) => {
5304 let left_lifetime = if left_top_level {
5305 graph_builders.cross_tick_state_lifetime(&out_location)
5306 } else {
5307 graph_builders.tick_state_lifetime(&out_location)
5308 };
5309
5310 let right_lifetime = if right_top_level {
5311 graph_builders.cross_tick_state_lifetime(&out_location)
5312 } else {
5313 graph_builders.tick_state_lifetime(&out_location)
5314 };
5315
5316 graph_builders.add_dfir_at(
5317 &out_location,
5318 if is_top_level {
5319 parse_quote! {
5322 #stream_ident = #operator::<#left_lifetime, #right_lifetime>() -> multiset_delta();
5323 #left_ident -> [0]#stream_ident;
5324 #right_ident -> [1]#stream_ident;
5325 }
5326 } else {
5327 parse_quote! {
5328 #stream_ident = #operator::<#left_lifetime, #right_lifetime>();
5329 #left_ident -> [0]#stream_ident;
5330 #right_ident -> [1]#stream_ident;
5331 }
5332 },
5333 Some(&stmt_id.to_string()),
5334 );
5335 }
5336 BuildersOrCallback::Callback(_, node_callback) => {
5337 node_callback(node, next_stmt_id);
5338 }
5339 }
5340
5341 ident_stack.push(stream_ident);
5342 }
5343
5344 HydroNode::Difference { .. } | HydroNode::AntiJoin { .. } => {
5345 let operator: syn::Ident = if matches!(node, HydroNode::Difference { .. }) {
5346 parse_quote!(difference)
5347 } else {
5348 parse_quote!(anti_join)
5349 };
5350
5351 let (HydroNode::Difference { neg, .. } | HydroNode::AntiJoin { neg, .. }) =
5352 node
5353 else {
5354 unreachable!()
5355 };
5356
5357 let neg_top_level = neg.metadata().location_id.is_top_level();
5358
5359 let neg_ident = ident_stack.pop().unwrap();
5360 let pos_ident = ident_stack.pop().unwrap();
5361
5362 let stmt_id = next_stmt_id.get_and_increment();
5363 let stream_ident =
5364 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5365
5366 match builders_or_callback {
5367 BuildersOrCallback::Builders(graph_builders) => {
5368 let neg_lifetime = if neg_top_level {
5369 graph_builders.cross_tick_state_lifetime(&out_location)
5370 } else {
5371 graph_builders.tick_state_lifetime(&out_location)
5372 };
5373 let pos_lifetime =
5374 graph_builders.tick_state_lifetime(&out_location);
5375
5376 graph_builders.add_dfir_at(
5377 &out_location,
5378 parse_quote! {
5379 #stream_ident = #operator::<#pos_lifetime, #neg_lifetime>();
5380 #pos_ident -> [pos]#stream_ident;
5381 #neg_ident -> [neg]#stream_ident;
5382 },
5383 Some(&stmt_id.to_string()),
5384 );
5385 }
5386 BuildersOrCallback::Callback(_, node_callback) => {
5387 node_callback(node, next_stmt_id);
5388 }
5389 }
5390
5391 ident_stack.push(stream_ident);
5392 }
5393
5394 HydroNode::JoinHalf { .. } => {
5395 let HydroNode::JoinHalf { right, .. } = node else {
5396 unreachable!()
5397 };
5398
5399 assert!(
5400 right.metadata().collection_kind.is_bounded(),
5401 "JoinHalf requires the right (build) side to be Bounded, got {:?}",
5402 right.metadata().collection_kind
5403 );
5404
5405 let build_top_level = right.metadata().location_id.is_top_level();
5406
5407 let build_ident = ident_stack.pop().unwrap();
5408 let probe_ident = ident_stack.pop().unwrap();
5409
5410 let stmt_id = next_stmt_id.get_and_increment();
5411 let stream_ident =
5412 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5413
5414 match builders_or_callback {
5415 BuildersOrCallback::Builders(graph_builders) => {
5416 let build_lifetime = if build_top_level {
5417 graph_builders.cross_tick_state_lifetime(&out_location)
5418 } else {
5419 graph_builders.tick_state_lifetime(&out_location)
5420 };
5421 let probe_lifetime =
5422 graph_builders.tick_state_lifetime(&out_location);
5423
5424 graph_builders.add_dfir_at(
5425 &out_location,
5426 parse_quote! {
5427 #stream_ident = join_multiset_half::<#build_lifetime, #probe_lifetime>();
5428 #probe_ident -> [probe]#stream_ident;
5429 #build_ident -> [build]#stream_ident;
5430 },
5431 Some(&stmt_id.to_string()),
5432 );
5433 }
5434 BuildersOrCallback::Callback(_, node_callback) => {
5435 node_callback(node, next_stmt_id);
5436 }
5437 }
5438
5439 ident_stack.push(stream_ident);
5440 }
5441
5442 HydroNode::ResolveFutures { .. } => {
5443 let input_ident = ident_stack.pop().unwrap();
5444
5445 let stmt_id = next_stmt_id.get_and_increment();
5446 let futures_ident =
5447 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5448
5449 match builders_or_callback {
5450 BuildersOrCallback::Builders(graph_builders) => {
5451 graph_builders.add_dfir_at(
5452 &out_location,
5453 parse_quote! {
5454 #futures_ident = #input_ident -> resolve_futures();
5455 },
5456 Some(&stmt_id.to_string()),
5457 );
5458 }
5459 BuildersOrCallback::Callback(_, node_callback) => {
5460 node_callback(node, next_stmt_id);
5461 }
5462 }
5463
5464 ident_stack.push(futures_ident);
5465 }
5466
5467 HydroNode::ResolveFuturesBlocking { .. } => {
5468 let input_ident = ident_stack.pop().unwrap();
5469
5470 let stmt_id = next_stmt_id.get_and_increment();
5471 let futures_ident =
5472 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5473
5474 match builders_or_callback {
5475 BuildersOrCallback::Builders(graph_builders) => {
5476 graph_builders.add_dfir_at(
5477 &out_location,
5478 parse_quote! {
5479 #futures_ident = #input_ident -> resolve_futures_blocking();
5480 },
5481 Some(&stmt_id.to_string()),
5482 );
5483 }
5484 BuildersOrCallback::Callback(_, node_callback) => {
5485 node_callback(node, next_stmt_id);
5486 }
5487 }
5488
5489 ident_stack.push(futures_ident);
5490 }
5491
5492 HydroNode::ResolveFuturesOrdered { .. } => {
5493 let input_ident = ident_stack.pop().unwrap();
5494
5495 let stmt_id = next_stmt_id.get_and_increment();
5496 let futures_ident =
5497 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5498
5499 match builders_or_callback {
5500 BuildersOrCallback::Builders(graph_builders) => {
5501 graph_builders.add_dfir_at(
5502 &out_location,
5503 parse_quote! {
5504 #futures_ident = #input_ident -> resolve_futures_ordered();
5505 },
5506 Some(&stmt_id.to_string()),
5507 );
5508 }
5509 BuildersOrCallback::Callback(_, node_callback) => {
5510 node_callback(node, next_stmt_id);
5511 }
5512 }
5513
5514 ident_stack.push(futures_ident);
5515 }
5516
5517 HydroNode::Map {
5518 f,
5519 input,
5520 metadata,
5521 } => {
5522 let input_ident = ident_stack.pop().unwrap();
5524 let f_tokens = f.emit_tokens(&mut ident_stack);
5525
5526 let input_ident = maybe_observe_for_mut(
5527 f,
5528 input_ident,
5529 &input.metadata().location_id,
5530 &input.metadata().collection_kind,
5531 &metadata.op,
5532 builders_or_callback,
5533 next_stmt_id,
5534 );
5535
5536 let stmt_id = next_stmt_id.get_and_increment();
5537 let map_ident =
5538 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5539
5540 let map_op: TokenStream = match (
5547 update_feed_repr(input.metadata()),
5548 update_feed_repr(metadata),
5549 ) {
5550 (UpdateFeedRepr::Plain, UpdateFeedRepr::Plain) => {
5551 quote! { map(#f_tokens) }
5552 }
5553 (UpdateFeedRepr::Tombstoned, UpdateFeedRepr::Tombstoned) => {
5554 match &metadata.collection_kind {
5555 CollectionKind::Optional { .. } => {
5556 quote! {
5557 map({
5558 let mut __hydro_map_f = #f_tokens;
5559 move |__hydro_update| __hydro_update.map(&mut __hydro_map_f)
5560 })
5561 }
5562 }
5563 CollectionKind::KeyedSingleton { .. } => {
5564 quote! {
5565 map({
5566 let mut __hydro_map_f = #f_tokens;
5567 move |(__hydro_key, __hydro_update)| match __hydro_update {
5568 ::std::option::Option::Some(__hydro_value) => {
5569 let (__hydro_out_key, __hydro_out_value) =
5570 (__hydro_map_f)((__hydro_key, __hydro_value));
5571 (__hydro_out_key, ::std::option::Option::Some(__hydro_out_value))
5572 }
5573 ::std::option::Option::None => {
5574 (__hydro_key, ::std::option::Option::None)
5575 }
5576 }
5577 })
5578 }
5579 }
5580 other => panic!(
5581 "unexpected tombstoned update feed kind for `map`: {:?}",
5582 other
5583 ),
5584 }
5585 }
5586 (in_repr, out_repr) => panic!(
5587 "map may not change the update feed representation ({:?} -> {:?})",
5588 in_repr, out_repr
5589 ),
5590 };
5591
5592 match builders_or_callback {
5593 BuildersOrCallback::Builders(graph_builders) => {
5594 graph_builders.add_dfir_at(
5595 &out_location,
5596 parse_quote! {
5597 #map_ident = #input_ident -> #map_op;
5598 },
5599 Some(&stmt_id.to_string()),
5600 );
5601 }
5602 BuildersOrCallback::Callback(_, node_callback) => {
5603 node_callback(node, next_stmt_id);
5604 }
5605 }
5606
5607 ident_stack.push(map_ident);
5608 }
5609
5610 HydroNode::FlatMap { f, input, metadata } => {
5611 let input_ident = ident_stack.pop().unwrap();
5612 let f_tokens = f.emit_tokens(&mut ident_stack);
5613
5614 let input_ident = maybe_observe_for_mut(
5615 f, input_ident,
5616 &input.metadata().location_id,
5617 &input.metadata().collection_kind,
5618 &metadata.op,
5619 builders_or_callback, next_stmt_id,
5620 );
5621
5622 let stmt_id = next_stmt_id.get_and_increment();
5623 let flat_map_ident =
5624 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5625
5626 match builders_or_callback {
5627 BuildersOrCallback::Builders(graph_builders) => {
5628 graph_builders.add_dfir_at(
5629 &out_location,
5630 parse_quote! {
5631 #flat_map_ident = #input_ident -> flat_map(#f_tokens);
5632 },
5633 Some(&stmt_id.to_string()),
5634 );
5635 }
5636 BuildersOrCallback::Callback(_, node_callback) => {
5637 node_callback(node, next_stmt_id);
5638 }
5639 }
5640
5641 ident_stack.push(flat_map_ident);
5642 }
5643
5644 HydroNode::FlatMapStreamBlocking { f, input, metadata } => {
5645 let input_ident = ident_stack.pop().unwrap();
5646 let f_tokens = f.emit_tokens(&mut ident_stack);
5647
5648 let input_ident = maybe_observe_for_mut(
5649 f, input_ident,
5650 &input.metadata().location_id,
5651 &input.metadata().collection_kind,
5652 &metadata.op,
5653 builders_or_callback, next_stmt_id,
5654 );
5655
5656 let stmt_id = next_stmt_id.get_and_increment();
5657 let flat_map_stream_blocking_ident =
5658 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5659
5660 match builders_or_callback {
5661 BuildersOrCallback::Builders(graph_builders) => {
5662 graph_builders.add_dfir_at(
5663 &out_location,
5664 parse_quote! {
5665 #flat_map_stream_blocking_ident = #input_ident -> flat_map_stream_blocking(#f_tokens);
5666 },
5667 Some(&stmt_id.to_string()),
5668 );
5669 }
5670 BuildersOrCallback::Callback(_, node_callback) => {
5671 node_callback(node, next_stmt_id);
5672 }
5673 }
5674
5675 ident_stack.push(flat_map_stream_blocking_ident);
5676 }
5677
5678 HydroNode::Filter { f, input, metadata } => {
5679 let input_ident = ident_stack.pop().unwrap();
5680 let f_tokens = f.emit_tokens(&mut ident_stack);
5681
5682 let input_ident = maybe_observe_for_mut(
5683 f, input_ident,
5684 &input.metadata().location_id,
5685 &input.metadata().collection_kind,
5686 &metadata.op,
5687 builders_or_callback, next_stmt_id,
5688 );
5689
5690 let stmt_id = next_stmt_id.get_and_increment();
5691 let filter_ident =
5692 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5693
5694 let filter_op: TokenStream = match (
5703 update_feed_repr(input.metadata()),
5704 update_feed_repr(metadata),
5705 ) {
5706 (UpdateFeedRepr::Plain, UpdateFeedRepr::Plain) => {
5707 quote! { filter(#f_tokens) }
5708 }
5709 (UpdateFeedRepr::Plain, UpdateFeedRepr::Tombstoned) => {
5710 match &metadata.collection_kind {
5711 CollectionKind::Optional { .. } => {
5712 quote! {
5713 map({
5714 let mut __hydro_filter_f = #f_tokens;
5715 move |__hydro_value| {
5716 if (__hydro_filter_f)(&__hydro_value) {
5717 Some(__hydro_value)
5718 } else {
5719 None
5720 }
5721 }
5722 })
5723 }
5724 }
5725 other => panic!(
5729 "unexpected tombstoned update feed kind for `filter`: {:?}",
5730 other
5731 ),
5732 }
5733 }
5734 (UpdateFeedRepr::Tombstoned, UpdateFeedRepr::Tombstoned) => {
5735 match &metadata.collection_kind {
5736 CollectionKind::Optional { .. } => {
5737 quote! {
5738 map({
5739 let mut __hydro_filter_f = #f_tokens;
5740 move |__hydro_update: Option<_>| {
5741 __hydro_update.filter(|__hydro_value| (__hydro_filter_f)(__hydro_value))
5742 }
5743 })
5744 }
5745 }
5746 CollectionKind::KeyedSingleton { .. } => {
5747 quote! {
5750 map({
5751 let mut __hydro_filter_f = #f_tokens;
5752 move |(__hydro_key, __hydro_update)| match __hydro_update {
5753 ::std::option::Option::Some(__hydro_value) => {
5754 let __hydro_entry = (__hydro_key, __hydro_value);
5755 if (__hydro_filter_f)(&__hydro_entry) {
5756 let (__hydro_key, __hydro_value) = __hydro_entry;
5757 (__hydro_key, ::std::option::Option::Some(__hydro_value))
5758 } else {
5759 let (__hydro_key, _) = __hydro_entry;
5760 (__hydro_key, ::std::option::Option::None)
5761 }
5762 }
5763 ::std::option::Option::None => {
5764 (__hydro_key, ::std::option::Option::None)
5765 }
5766 }
5767 })
5768 }
5769 }
5770 other => panic!(
5771 "unexpected tombstoned update feed kind for `filter`: {:?}",
5772 other
5773 ),
5774 }
5775 }
5776 (in_repr, out_repr) => panic!(
5777 "filter may not un-tombstone an update feed ({:?} -> {:?})",
5778 in_repr, out_repr
5779 ),
5780 };
5781
5782 match builders_or_callback {
5783 BuildersOrCallback::Builders(graph_builders) => {
5784 graph_builders.add_dfir_at(
5785 &out_location,
5786 parse_quote! {
5787 #filter_ident = #input_ident -> #filter_op;
5788 },
5789 Some(&stmt_id.to_string()),
5790 );
5791 }
5792 BuildersOrCallback::Callback(_, node_callback) => {
5793 node_callback(node, next_stmt_id);
5794 }
5795 }
5796
5797 ident_stack.push(filter_ident);
5798 }
5799
5800 HydroNode::FilterMap { f, input, metadata } => {
5801 let input_ident = ident_stack.pop().unwrap();
5802 let f_tokens = f.emit_tokens(&mut ident_stack);
5803
5804 let input_ident = maybe_observe_for_mut(
5805 f, input_ident,
5806 &input.metadata().location_id,
5807 &input.metadata().collection_kind,
5808 &metadata.op,
5809 builders_or_callback, next_stmt_id,
5810 );
5811
5812 let stmt_id = next_stmt_id.get_and_increment();
5813 let filter_map_ident =
5814 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5815
5816 let filter_map_op: TokenStream = match (
5826 update_feed_repr(input.metadata()),
5827 update_feed_repr(metadata),
5828 ) {
5829 (UpdateFeedRepr::Plain, UpdateFeedRepr::Plain) => {
5830 quote! { filter_map(#f_tokens) }
5831 }
5832 (UpdateFeedRepr::Plain, UpdateFeedRepr::Tombstoned) => {
5833 match &metadata.collection_kind {
5834 CollectionKind::Optional { .. } => {
5835 quote! { map(#f_tokens) }
5836 }
5837 other => panic!(
5842 "unexpected tombstoned update feed kind for `filter_map`: {:?}",
5843 other
5844 ),
5845 }
5846 }
5847 (UpdateFeedRepr::Tombstoned, UpdateFeedRepr::Tombstoned) => {
5848 match &metadata.collection_kind {
5849 CollectionKind::Optional { .. } => {
5850 quote! {
5851 map({
5852 let mut __hydro_filter_map_f = #f_tokens;
5853 move |__hydro_update| {
5854 __hydro_update.and_then(&mut __hydro_filter_map_f)
5855 }
5856 })
5857 }
5858 }
5859 CollectionKind::KeyedSingleton { .. } => {
5860 quote! {
5861 map({
5862 let mut __hydro_filter_map_f = #f_tokens;
5863 move |(__hydro_key, __hydro_update)| match __hydro_update {
5864 ::std::option::Option::Some(__hydro_value) => {
5865 let __hydro_key_backup =
5866 ::std::clone::Clone::clone(&__hydro_key);
5867 match (__hydro_filter_map_f)((__hydro_key, __hydro_value)) {
5868 ::std::option::Option::Some((__hydro_out_key, __hydro_out_value)) => {
5869 (__hydro_out_key, ::std::option::Option::Some(__hydro_out_value))
5870 }
5871 ::std::option::Option::None => {
5872 (__hydro_key_backup, ::std::option::Option::None)
5873 }
5874 }
5875 }
5876 ::std::option::Option::None => {
5877 (__hydro_key, ::std::option::Option::None)
5878 }
5879 }
5880 })
5881 }
5882 }
5883 other => panic!(
5884 "unexpected tombstoned update feed kind for `filter_map`: {:?}",
5885 other
5886 ),
5887 }
5888 }
5889 (in_repr, out_repr) => panic!(
5890 "filter_map may not un-tombstone an update feed ({:?} -> {:?})",
5891 in_repr, out_repr
5892 ),
5893 };
5894
5895 match builders_or_callback {
5896 BuildersOrCallback::Builders(graph_builders) => {
5897 graph_builders.add_dfir_at(
5898 &out_location,
5899 parse_quote! {
5900 #filter_map_ident = #input_ident -> #filter_map_op;
5901 },
5902 Some(&stmt_id.to_string()),
5903 );
5904 }
5905 BuildersOrCallback::Callback(_, node_callback) => {
5906 node_callback(node, next_stmt_id);
5907 }
5908 }
5909
5910 ident_stack.push(filter_map_ident);
5911 }
5912
5913 HydroNode::Sort { .. } => {
5914 let input_ident = ident_stack.pop().unwrap();
5915
5916 let stmt_id = next_stmt_id.get_and_increment();
5917 let sort_ident =
5918 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5919
5920 match builders_or_callback {
5921 BuildersOrCallback::Builders(graph_builders) => {
5922 graph_builders.add_dfir_at(
5923 &out_location,
5924 parse_quote! {
5925 #sort_ident = #input_ident -> sort();
5926 },
5927 Some(&stmt_id.to_string()),
5928 );
5929 }
5930 BuildersOrCallback::Callback(_, node_callback) => {
5931 node_callback(node, next_stmt_id);
5932 }
5933 }
5934
5935 ident_stack.push(sort_ident);
5936 }
5937
5938 HydroNode::DeferTick { .. } => {
5939 let input_ident = ident_stack.pop().unwrap();
5940
5941 let stmt_id = next_stmt_id.get_and_increment();
5942 let defer_tick_ident =
5943 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5944
5945 match builders_or_callback {
5946 BuildersOrCallback::Builders(graph_builders) => {
5947 graph_builders.add_dfir_at(
5948 &out_location,
5949 parse_quote! {
5950 #defer_tick_ident = #input_ident -> defer_tick_lazy();
5951 },
5952 Some(&stmt_id.to_string()),
5953 );
5954 }
5955 BuildersOrCallback::Callback(_, node_callback) => {
5956 node_callback(node, next_stmt_id);
5957 }
5958 }
5959
5960 ident_stack.push(defer_tick_ident);
5961 }
5962
5963 HydroNode::Enumerate { input, .. } => {
5964 let input_ident = ident_stack.pop().unwrap();
5965
5966 let stmt_id = next_stmt_id.get_and_increment();
5967 let enumerate_ident =
5968 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
5969
5970 match builders_or_callback {
5971 BuildersOrCallback::Builders(graph_builders) => {
5972 let lifetime = if input.metadata().location_id.is_top_level() {
5973 graph_builders.cross_tick_state_lifetime(&out_location)
5974 } else {
5975 graph_builders.tick_state_lifetime(&out_location)
5976 };
5977 graph_builders.add_dfir_at(
5978 &out_location,
5979 parse_quote! {
5980 #enumerate_ident = #input_ident -> enumerate::<#lifetime>();
5981 },
5982 Some(&stmt_id.to_string()),
5983 );
5984 }
5985 BuildersOrCallback::Callback(_, node_callback) => {
5986 node_callback(node, next_stmt_id);
5987 }
5988 }
5989
5990 ident_stack.push(enumerate_ident);
5991 }
5992
5993 HydroNode::Inspect { f, input, metadata } => {
5994 let input_ident = ident_stack.pop().unwrap();
5995 let f_tokens = f.emit_tokens(&mut ident_stack);
5996
5997 let input_ident = maybe_observe_for_mut(
5998 f, input_ident,
5999 &input.metadata().location_id,
6000 &input.metadata().collection_kind,
6001 &metadata.op,
6002 builders_or_callback, next_stmt_id,
6003 );
6004
6005 let stmt_id = next_stmt_id.get_and_increment();
6006 let inspect_ident =
6007 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
6008
6009 if update_feed_repr(input.metadata()) == UpdateFeedRepr::Tombstoned
6017 || update_feed_repr(metadata) == UpdateFeedRepr::Tombstoned
6018 {
6019 todo!(
6020 "`inspect` on a tombstoned update feed (root unbounded \
6021 Optional/KeyedSingleton) is not yet supported"
6022 );
6023 }
6024
6025 match builders_or_callback {
6026 BuildersOrCallback::Builders(graph_builders) => {
6027 graph_builders.add_dfir_at(
6028 &out_location,
6029 parse_quote! {
6030 #inspect_ident = #input_ident -> inspect(#f_tokens);
6031 },
6032 Some(&stmt_id.to_string()),
6033 );
6034 }
6035 BuildersOrCallback::Callback(_, node_callback) => {
6036 node_callback(node, next_stmt_id);
6037 }
6038 }
6039
6040 ident_stack.push(inspect_ident);
6041 }
6042
6043 HydroNode::Unique { input, .. } => {
6044 let input_ident = ident_stack.pop().unwrap();
6045
6046 let stmt_id = next_stmt_id.get_and_increment();
6047 let unique_ident =
6048 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
6049
6050 match builders_or_callback {
6051 BuildersOrCallback::Builders(graph_builders) => {
6052 let lifetime = if input.metadata().location_id.is_top_level() {
6053 graph_builders.cross_tick_state_lifetime(&out_location)
6054 } else {
6055 graph_builders.tick_state_lifetime(&out_location)
6056 };
6057
6058 graph_builders.add_dfir_at(
6059 &out_location,
6060 parse_quote! {
6061 #unique_ident = #input_ident -> unique::<#lifetime>();
6062 },
6063 Some(&stmt_id.to_string()),
6064 );
6065 }
6066 BuildersOrCallback::Callback(_, node_callback) => {
6067 node_callback(node, next_stmt_id);
6068 }
6069 }
6070
6071 ident_stack.push(unique_ident);
6072 }
6073
6074 HydroNode::Fold { .. } | HydroNode::FoldKeyed { .. } | HydroNode::Scan { .. } | HydroNode::ScanAsyncBlocking { .. } => {
6075 let operator: syn::Ident = if let HydroNode::Fold { input, .. } = node {
6076 if input.metadata().location_id.is_root()
6077 && input.metadata().collection_kind.is_bounded()
6078 {
6079 parse_quote!(fold_no_replay)
6080 } else {
6081 parse_quote!(fold)
6082 }
6083 } else if matches!(node, HydroNode::Scan { .. }) {
6084 parse_quote!(scan)
6085 } else if matches!(node, HydroNode::ScanAsyncBlocking { .. }) {
6086 parse_quote!(scan_async_blocking)
6087 } else if let HydroNode::FoldKeyed { input, .. } = node {
6088 if input.metadata().location_id.is_root()
6089 && input.metadata().collection_kind.is_bounded()
6090 {
6091 todo!("Fold keyed on a top-level bounded collection is not yet supported")
6092 } else {
6093 parse_quote!(fold_keyed)
6094 }
6095 } else {
6096 unreachable!()
6097 };
6098
6099 let (HydroNode::Fold { input, .. }
6100 | HydroNode::FoldKeyed { input, .. }
6101 | HydroNode::Scan { input, .. }
6102 | HydroNode::ScanAsyncBlocking { input, .. }) = node
6103 else {
6104 unreachable!()
6105 };
6106
6107 let input_top_level = input.metadata().location_id.is_top_level();
6108
6109 let input_ident = ident_stack.pop().unwrap();
6110
6111 let (HydroNode::Fold { init, acc, .. }
6112 | HydroNode::FoldKeyed { init, acc, .. }
6113 | HydroNode::Scan { init, acc, .. }
6114 | HydroNode::ScanAsyncBlocking { init, acc, .. }) = &*node
6115 else {
6116 unreachable!()
6117 };
6118
6119 let acc_tokens = acc.emit_tokens(&mut ident_stack);
6120 let init_tokens = init.emit_tokens(&mut ident_stack);
6121
6122 let stmt_id = next_stmt_id.get_and_increment();
6123 let fold_ident =
6124 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
6125
6126 match builders_or_callback {
6127 BuildersOrCallback::Builders(graph_builders) => {
6128 let lifetime = if input_top_level {
6129 graph_builders.cross_tick_state_lifetime(&out_location)
6130 } else {
6131 graph_builders.tick_state_lifetime(&out_location)
6132 };
6133
6134 if matches!(node, HydroNode::Fold { .. })
6135 && node.metadata().location_id.is_root()
6136 && graph_builders.singleton_intermediates()
6137 && !node.metadata().collection_kind.is_bounded()
6138 {
6139 let HydroNode::Fold { input, .. } = &*node else { unreachable!() };
6140 let hooked_input_ident = graph_builders.emit_fold_hook(
6141 &input.metadata().location_id,
6142 &input_ident,
6143 &input.metadata().collection_kind,
6144 &node.metadata().op,
6145 );
6146
6147 let (effective_input, wrapped_acc) = if let Some(ref hooked) = hooked_input_ident {
6148 let acc: syn::Expr = parse_quote!({
6149 let mut __inner = #acc_tokens;
6150 move |__state, __batch: Vec<_>| {
6151 if __batch.is_empty() {
6152 return None;
6153 }
6154 for __value in __batch {
6155 __inner(__state, __value);
6156 }
6157 Some(__state.clone())
6158 }
6159 });
6160 (hooked, acc)
6161 } else {
6162 let acc: syn::Expr = parse_quote!({
6163 let mut __inner = #acc_tokens;
6164 move |__state, __value| {
6165 __inner(__state, __value);
6166 Some(__state.clone())
6167 }
6168 });
6169 (&input_ident, acc)
6170 };
6171
6172 graph_builders.add_dfir_at(
6173 &out_location,
6174 parse_quote! {
6175 source_iter([(#init_tokens)()]) -> [0]#fold_ident;
6176 #effective_input -> scan::<#lifetime>(#init_tokens, #wrapped_acc) -> [1]#fold_ident;
6177 #fold_ident = chain();
6178 },
6179 Some(&stmt_id.to_string()),
6180 );
6181
6182 if hooked_input_ident.is_some()
6187 && node.metadata().op.sim_hook_id.is_none()
6188 {
6189 fold_hooked_idents.insert(fold_ident.to_string());
6190 }
6191 } else if matches!(node, HydroNode::FoldKeyed { .. })
6192 && node.metadata().location_id.is_root()
6193 && graph_builders.singleton_intermediates()
6194 && !node.metadata().collection_kind.is_bounded()
6195 {
6196 let HydroNode::FoldKeyed { input, .. } = &*node else { unreachable!() };
6197 let hooked_input_ident = graph_builders.emit_fold_hook(
6198 &input.metadata().location_id,
6199 &input_ident,
6200 &input.metadata().collection_kind,
6201 &node.metadata().op,
6202 );
6203
6204 let wrapped_acc: syn::Expr = parse_quote!({
6205 let mut __init = #init_tokens;
6206 let mut __inner = #acc_tokens;
6207 move |__state, __kv: (_, _)| {
6208 let __state = __state
6210 .entry(::std::clone::Clone::clone(&__kv.0))
6211 .or_insert_with(|| (__init)());
6212 __inner(__state, __kv.1);
6213 Some((__kv.0, ::std::clone::Clone::clone(&*__state)))
6214 }
6215 });
6216
6217 if let Some(hooked_input_ident) = hooked_input_ident {
6218 graph_builders.add_dfir_at(
6219 &out_location,
6220 parse_quote! {
6221 #fold_ident = #hooked_input_ident -> flatten() -> scan::<#lifetime>(|| ::std::collections::HashMap::new(), #wrapped_acc);
6222 },
6223 Some(&stmt_id.to_string()),
6224 );
6225
6226 fold_hooked_idents.insert(fold_ident.to_string());
6227 } else {
6228 graph_builders.add_dfir_at(
6229 &out_location,
6230 parse_quote! {
6231 #fold_ident = #input_ident -> scan::<#lifetime>(|| ::std::collections::HashMap::new(), #wrapped_acc);
6232 },
6233 Some(&stmt_id.to_string()),
6234 );
6235 }
6236 } else if (matches!(node, HydroNode::Fold { .. })
6237 || matches!(node, HydroNode::FoldKeyed { .. }))
6238 && !node.metadata().location_id.is_top_level()
6239 && graph_builders.singleton_intermediates()
6240 {
6241 let input_ref = match &*node {
6242 HydroNode::Fold { input, .. } => input,
6243 HydroNode::FoldKeyed { input, .. } => input,
6244 _ => unreachable!(),
6245 };
6246 let hooked_input_ident = graph_builders.emit_fold_hook(
6247 &input_ref.metadata().location_id,
6248 &input_ident,
6249 &input_ref.metadata().collection_kind,
6250 &node.metadata().op,
6251 );
6252
6253 let actual_input = hooked_input_ident.as_ref().unwrap_or(&input_ident);
6254 graph_builders.add_dfir_at(
6255 &out_location,
6256 parse_quote! {
6257 #fold_ident = #actual_input -> #operator::<#lifetime>(#init_tokens, #acc_tokens);
6258 },
6259 Some(&stmt_id.to_string()),
6260 );
6261 } else {
6262 assert_eq!(
6268 update_feed_repr(node.metadata()),
6269 UpdateFeedRepr::Plain,
6270 "`fold`-family aggregation into a tombstoned update feed \
6271 (root unbounded Optional/KeyedSingleton) is not supported"
6272 );
6273 graph_builders.add_dfir_at(
6274 &out_location,
6275 parse_quote! {
6276 #fold_ident = #input_ident -> #operator::<#lifetime>(#init_tokens, #acc_tokens);
6277 },
6278 Some(&stmt_id.to_string()),
6279 );
6280 }
6281 }
6282 BuildersOrCallback::Callback(_, node_callback) => {
6283 node_callback(node, next_stmt_id);
6284 }
6285 }
6286
6287 ident_stack.push(fold_ident);
6288 }
6289
6290 HydroNode::Reduce { .. } | HydroNode::ReduceKeyed { .. } => {
6291 let operator: syn::Ident = if let HydroNode::Reduce { input, .. } = node {
6292 if input.metadata().location_id.is_root()
6293 && input.metadata().collection_kind.is_bounded()
6294 {
6295 parse_quote!(reduce_no_replay)
6296 } else {
6297 parse_quote!(reduce)
6298 }
6299 } else if let HydroNode::ReduceKeyed { input, .. } = node {
6300 if input.metadata().location_id.is_root()
6301 && input.metadata().collection_kind.is_bounded()
6302 {
6303 todo!(
6304 "Calling keyed reduce on a top-level bounded collection is not supported"
6305 )
6306 } else {
6307 parse_quote!(reduce_keyed)
6308 }
6309 } else {
6310 unreachable!()
6311 };
6312
6313 let (HydroNode::Reduce { input, .. } | HydroNode::ReduceKeyed { input, .. }) = node
6314 else {
6315 unreachable!()
6316 };
6317
6318 let input_top_level = input.metadata().location_id.is_top_level();
6319
6320 let input_ident = ident_stack.pop().unwrap();
6321
6322 let (HydroNode::Reduce { f, .. } | HydroNode::ReduceKeyed { f, .. }) = &*node
6323 else {
6324 unreachable!()
6325 };
6326
6327 let f_tokens = f.emit_tokens(&mut ident_stack);
6328
6329 let stmt_id = next_stmt_id.get_and_increment();
6330 let reduce_ident =
6331 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
6332
6333 match builders_or_callback {
6334 BuildersOrCallback::Builders(graph_builders) => {
6335 let lifetime = if input_top_level {
6336 graph_builders.cross_tick_state_lifetime(&out_location)
6337 } else {
6338 graph_builders.tick_state_lifetime(&out_location)
6339 };
6340
6341 if matches!(node, HydroNode::Reduce { .. })
6342 && node.metadata().location_id.is_root()
6343 && graph_builders.singleton_intermediates()
6344 && !node.metadata().collection_kind.is_bounded()
6345 {
6346 todo!(
6347 "Reduce with optional intermediates is not yet supported in simulator"
6348 );
6349 } else if matches!(node, HydroNode::ReduceKeyed { .. })
6350 && node.metadata().location_id.is_root()
6351 && graph_builders.singleton_intermediates()
6352 && !node.metadata().collection_kind.is_bounded()
6353 {
6354 todo!(
6355 "Reduce keyed with optional intermediates is not yet supported in simulator"
6356 );
6357 } else {
6358 let wrap_op: TokenStream = if matches!(
6363 node.metadata().collection_kind,
6364 CollectionKind::KeyedSingleton { .. }
6365 ) && update_feed_repr(node.metadata())
6366 == UpdateFeedRepr::Tombstoned
6367 {
6368 quote! { -> map(|(__hydro_key, __hydro_value)| (__hydro_key, ::std::option::Option::Some(__hydro_value))) }
6369 } else {
6370 quote! {}
6371 };
6372 graph_builders.add_dfir_at(
6373 &out_location,
6374 parse_quote! {
6375 #reduce_ident = #input_ident -> #operator::<#lifetime>(#f_tokens) #wrap_op;
6376 },
6377 Some(&stmt_id.to_string()),
6378 );
6379 }
6380 }
6381 BuildersOrCallback::Callback(_, node_callback) => {
6382 node_callback(node, next_stmt_id);
6383 }
6384 }
6385
6386 ident_stack.push(reduce_ident);
6387 }
6388
6389 HydroNode::ReduceKeyedWatermark {
6390 f,
6391 input,
6392 watermark,
6393 metadata,
6394 } => {
6395
6396 let watermark_ident = ident_stack.pop().unwrap();
6398 let watermark_location = watermark.metadata().location_id.clone();
6399 let input_ident = ident_stack.pop().unwrap();
6400 let f_tokens = f.emit_tokens(&mut ident_stack);
6401
6402 let stmt_id = next_stmt_id.get_and_increment();
6403 let chain_ident = syn::Ident::new(
6404 &format!("reduce_keyed_watermark_chain_{}", stmt_id),
6405 Span::call_site(),
6406 );
6407
6408 let fold_ident =
6409 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
6410
6411 let agg_operator: syn::Ident = if input.metadata().location_id.is_root()
6412 && input.metadata().collection_kind.is_bounded()
6413 {
6414 parse_quote!(fold_no_replay)
6415 } else {
6416 parse_quote!(fold)
6417 };
6418
6419 match builders_or_callback {
6420 BuildersOrCallback::Builders(graph_builders) => {
6421 let watermark_ident = graph_builders.unwindow_for_consume(
6426 watermark_ident,
6427 &watermark_location,
6428 &out_location,
6429 );
6430
6431 let lifetime = if input.metadata().location_id.is_top_level() {
6432 graph_builders.cross_tick_state_lifetime(&out_location)
6433 } else {
6434 graph_builders.tick_state_lifetime(&out_location)
6435 };
6436
6437 if metadata.location_id.is_root()
6438 && graph_builders.singleton_intermediates()
6439 && !metadata.collection_kind.is_bounded()
6440 {
6441 todo!(
6442 "Reduce keyed watermarked on a top-level bounded collection is not yet supported"
6443 )
6444 } else if update_feed_repr(metadata) == UpdateFeedRepr::Tombstoned {
6445 graph_builders.add_dfir_at(
6460 &out_location,
6461 parse_quote! {
6462 #chain_ident = chain();
6463 #input_ident
6464 -> map(|x| (Some(x), None))
6465 -> [0]#chain_ident;
6466 #watermark_ident
6467 -> map(|watermark| (None, Some(watermark)))
6468 -> [1]#chain_ident;
6469
6470 #fold_ident = #chain_ident
6471 -> scan::<#lifetime>(|| (::std::collections::HashMap::new(), None), {
6472 let __reduce_keyed_fn = #f_tokens;
6473 move |(map, opt_curr_watermark), (opt_payload, opt_watermark)| {
6474 let mut deltas = ::std::vec::Vec::new();
6475 if let Some((k, v)) = opt_payload {
6476 if let Some(curr_watermark) = *opt_curr_watermark {
6477 if k < curr_watermark {
6478 return Some(deltas);
6479 }
6480 }
6481 match map.entry(k) {
6482 ::std::collections::hash_map::Entry::Vacant(e) => {
6483 let key = ::std::clone::Clone::clone(e.key());
6484 let value = ::std::clone::Clone::clone(e.insert(v));
6485 deltas.push((key, ::std::option::Option::Some(value)));
6486 }
6487 ::std::collections::hash_map::Entry::Occupied(mut e) => {
6488 __reduce_keyed_fn(e.get_mut(), v);
6489 deltas.push((
6490 ::std::clone::Clone::clone(e.key()),
6491 ::std::option::Option::Some(::std::clone::Clone::clone(e.get())),
6492 ));
6493 }
6494 }
6495 } else {
6496 let watermark = opt_watermark.unwrap();
6497 if let Some(curr_watermark) = *opt_curr_watermark {
6498 if watermark <= curr_watermark {
6499 return Some(deltas);
6500 }
6501 }
6502 map.retain(|k, _| {
6503 if *k >= watermark {
6504 true
6505 } else {
6506 deltas.push((
6507 ::std::clone::Clone::clone(k),
6508 ::std::option::Option::None,
6509 ));
6510 false
6511 }
6512 });
6513 *opt_curr_watermark = Some(watermark);
6514 }
6515 Some(deltas)
6516 }
6517 })
6518 -> flatten();
6519 },
6520 Some(&stmt_id.to_string()),
6521 );
6522 } else {
6523 graph_builders.add_dfir_at(
6524 &out_location,
6525 parse_quote! {
6526 #chain_ident = chain();
6527 #input_ident
6528 -> map(|x| (Some(x), None))
6529 -> [0]#chain_ident;
6530 #watermark_ident
6531 -> map(|watermark| (None, Some(watermark)))
6532 -> [1]#chain_ident;
6533
6534 #fold_ident = #chain_ident
6535 -> #agg_operator::<#lifetime>(|| (::std::collections::HashMap::new(), None), {
6536 let __reduce_keyed_fn = #f_tokens;
6537 move |(map, opt_curr_watermark), (opt_payload, opt_watermark)| {
6538 if let Some((k, v)) = opt_payload {
6539 if let Some(curr_watermark) = *opt_curr_watermark {
6540 if k < curr_watermark {
6541 return;
6542 }
6543 }
6544 match map.entry(k) {
6545 ::std::collections::hash_map::Entry::Vacant(e) => {
6546 e.insert(v);
6547 }
6548 ::std::collections::hash_map::Entry::Occupied(mut e) => {
6549 __reduce_keyed_fn(e.get_mut(), v);
6550 }
6551 }
6552 } else {
6553 let watermark = opt_watermark.unwrap();
6554 if let Some(curr_watermark) = *opt_curr_watermark {
6555 if watermark <= curr_watermark {
6556 return;
6557 }
6558 }
6559 map.retain(|k, _| *k >= watermark);
6560 *opt_curr_watermark = Some(watermark);
6561 }
6562 }
6563 })
6564 -> flat_map(|(map, _curr_watermark)| map);
6565 },
6566 Some(&stmt_id.to_string()),
6567 );
6568 }
6569 }
6570 BuildersOrCallback::Callback(_, node_callback) => {
6571 node_callback(node, next_stmt_id);
6572 }
6573 }
6574
6575 ident_stack.push(fold_ident);
6576 }
6577
6578 HydroNode::Network {
6579 networking_info,
6580 serialize,
6581 deserialize,
6582 instantiate_fn,
6583 input,
6584 ..
6585 } => {
6586 let input_ident = ident_stack.pop().unwrap();
6587
6588 let stmt_id = next_stmt_id.get_and_increment();
6589 let receiver_stream_ident =
6590 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
6591
6592 let serialize_pipeline = serialize.pipeline();
6595 let deserialize_pipeline = deserialize.pipeline();
6596
6597 match builders_or_callback {
6598 BuildersOrCallback::Builders(graph_builders) => {
6599 let (sink_expr, source_expr) = match instantiate_fn {
6600 DebugInstantiate::Building => (
6601 syn::parse_quote!(DUMMY_SINK),
6602 syn::parse_quote!(DUMMY_SOURCE),
6603 ),
6604
6605 DebugInstantiate::Finalized(finalized) => {
6606 (finalized.sink.clone(), finalized.source.clone())
6607 }
6608 };
6609
6610 graph_builders.create_network(
6611 &input.metadata().location_id,
6612 &out_location,
6613 input_ident,
6614 &receiver_stream_ident,
6615 serialize_pipeline.as_ref(),
6616 sink_expr,
6617 source_expr,
6618 deserialize_pipeline.as_ref(),
6619 serialize.external_element_type(),
6620 stmt_id,
6621 networking_info,
6622 );
6623 }
6624 BuildersOrCallback::Callback(_, node_callback) => {
6625 node_callback(node, next_stmt_id);
6626 }
6627 }
6628
6629 ident_stack.push(receiver_stream_ident);
6630 }
6631
6632 HydroNode::ExternalInput {
6633 instantiate_fn,
6634 deserialize_fn: deserialize_pipeline,
6635 ..
6636 } => {
6637 let stmt_id = next_stmt_id.get_and_increment();
6638 let receiver_stream_ident =
6639 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
6640
6641 match builders_or_callback {
6642 BuildersOrCallback::Builders(graph_builders) => {
6643 let (_, source_expr) = match instantiate_fn {
6644 DebugInstantiate::Building => (
6645 syn::parse_quote!(DUMMY_SINK),
6646 syn::parse_quote!(DUMMY_SOURCE),
6647 ),
6648
6649 DebugInstantiate::Finalized(finalized) => {
6650 (finalized.sink.clone(), finalized.source.clone())
6651 }
6652 };
6653
6654 graph_builders.create_external_source(
6655 &out_location,
6656 source_expr,
6657 &receiver_stream_ident,
6658 deserialize_pipeline.as_ref(),
6659 stmt_id,
6660 );
6661 }
6662 BuildersOrCallback::Callback(_, node_callback) => {
6663 node_callback(node, next_stmt_id);
6664 }
6665 }
6666
6667 ident_stack.push(receiver_stream_ident);
6668 }
6669
6670 HydroNode::Counter {
6671 tag,
6672 duration,
6673 prefix,
6674 ..
6675 } => {
6676 let input_ident = ident_stack.pop().unwrap();
6677
6678 let stmt_id = next_stmt_id.get_and_increment();
6679 let counter_ident =
6680 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
6681
6682 match builders_or_callback {
6683 BuildersOrCallback::Builders(graph_builders) => {
6684 let arg = format!("{}({})", prefix, tag);
6685 graph_builders.add_dfir_at(
6686 &out_location,
6687 parse_quote! {
6688 #counter_ident = #input_ident -> _counter(#arg, #duration);
6689 },
6690 Some(&stmt_id.to_string()),
6691 );
6692 }
6693 BuildersOrCallback::Callback(_, node_callback) => {
6694 node_callback(node, next_stmt_id);
6695 }
6696 }
6697
6698 ident_stack.push(counter_ident);
6699 }
6700
6701 HydroNode::VersionedNetworkFork {
6702 channel_id,
6703 senders,
6704 metadata,
6705 ..
6706 } => {
6707 let split_at = ident_stack.len() - senders.len();
6709 let sender_idents = ident_stack.split_off(split_at);
6710
6711 let stmt_id = next_stmt_id.get_and_increment();
6712
6713 let external_element_type =
6716 senders.first().and_then(|(_, _, s)| s.external_element_type());
6717
6718 match builders_or_callback {
6719 BuildersOrCallback::Builders(graph_builders) => {
6720 let sender_args: Vec<(LocationId, syn::Ident, Option<DebugExpr>)> =
6721 senders
6722 .iter()
6723 .zip(sender_idents)
6724 .map(|((_version, sender, serialize), ident)| {
6725 (
6726 sender.metadata().location_id.clone(),
6727 ident,
6728 serialize.pipeline(),
6729 )
6730 })
6731 .collect();
6732 graph_builders.create_versioned_network_fork(
6733 *channel_id,
6734 &metadata.location_id,
6735 sender_args,
6736 external_element_type,
6737 stmt_id,
6738 );
6739 }
6740 BuildersOrCallback::Callback(_, node_callback) => {
6741 node_callback(node, next_stmt_id);
6742 }
6743 }
6744 }
6745
6746 HydroNode::VersionedNetwork {
6747 fork,
6748 deserialize,
6749 metadata,
6750 ..
6751 } => {
6752 let stmt_id = next_stmt_id.get_and_increment();
6753 let receiver_stream_ident =
6754 syn::Ident::new(&format!("stream_{}", stmt_id), Span::call_site());
6755
6756 let (channel_id, source_loc) = {
6759 let fork_ref = fork.0.borrow();
6760 let HydroNode::VersionedNetworkFork {
6761 channel_id,
6762 senders,
6763 ..
6764 } = &*fork_ref
6765 else {
6766 unreachable!("VersionedNetwork.fork must be a VersionedNetworkFork");
6767 };
6768 let source_loc = senders
6769 .first()
6770 .map(|(_v, sender, _s)| sender.metadata().location_id.clone())
6771 .expect("a VersionedNetworkFork always has at least one sender");
6772 (*channel_id, source_loc)
6773 };
6774
6775 let deserialize_pipeline = deserialize.pipeline();
6776 let external_element_type = deserialize.external_element_type();
6777
6778 match builders_or_callback {
6779 BuildersOrCallback::Builders(graph_builders) => {
6780 graph_builders.create_versioned_network(
6781 channel_id,
6782 &source_loc,
6783 &metadata.location_id,
6784 &receiver_stream_ident,
6785 deserialize_pipeline.as_ref(),
6786 external_element_type,
6787 stmt_id,
6788 );
6789 }
6790 BuildersOrCallback::Callback(_, node_callback) => {
6791 node_callback(node, next_stmt_id);
6792 }
6793 }
6794
6795 ident_stack.push(receiver_stream_ident);
6796 }
6797 }
6798 },
6799 seen_tees,
6800 false,
6801 );
6802
6803 let ret = ident_stack
6804 .pop()
6805 .expect("ident_stack should have exactly one element after traversal");
6806 assert!(
6807 ident_stack.is_empty(),
6808 "ident_stack should be empty after popping the final ident, but has {} remaining element(s). \
6809 This indicates a bug in the code gen: some node pushed idents that were never consumed.",
6810 ident_stack.len()
6811 );
6812 ret
6813 }
6814
6815 pub fn visit_debug_expr(&mut self, mut transform: impl FnMut(&mut DebugExpr)) {
6816 match self {
6817 HydroNode::Placeholder => {
6818 panic!()
6819 }
6820 HydroNode::Cast { .. }
6821 | HydroNode::ObserveNonDet { .. }
6822 | HydroNode::UnboundSingleton { .. }
6823 | HydroNode::AssertIsConsistent { .. } => {}
6824 HydroNode::Source { source, .. } => match source {
6825 HydroSource::Stream(expr) | HydroSource::Iter(expr) => transform(expr),
6826 HydroSource::ExternalNetwork()
6827 | HydroSource::Spin()
6828 | HydroSource::ClusterMembers(_, _)
6829 | HydroSource::Embedded(_)
6830 | HydroSource::EmbeddedSingleton(_) => {} },
6832 HydroNode::SingletonSource { value, .. } => {
6833 transform(value);
6834 }
6835 HydroNode::CycleSource { .. }
6836 | HydroNode::Tee { .. }
6837 | HydroNode::Reference { .. }
6838 | HydroNode::YieldConcat { .. }
6839 | HydroNode::BeginAtomic { .. }
6840 | HydroNode::EndAtomic { .. }
6841 | HydroNode::Batch { .. }
6842 | HydroNode::Snapshot { .. }
6843 | HydroNode::Chain { .. }
6844 | HydroNode::MergeOrdered { .. }
6845 | HydroNode::ChainFirst { .. }
6846 | HydroNode::CrossProduct { .. }
6847 | HydroNode::CrossSingleton { .. }
6848 | HydroNode::ResolveFutures { .. }
6849 | HydroNode::ResolveFuturesBlocking { .. }
6850 | HydroNode::ResolveFuturesOrdered { .. }
6851 | HydroNode::Join { .. }
6852 | HydroNode::JoinHalf { .. }
6853 | HydroNode::Difference { .. }
6854 | HydroNode::AntiJoin { .. }
6855 | HydroNode::DeferTick { .. }
6856 | HydroNode::Enumerate { .. }
6857 | HydroNode::Unique { .. }
6858 | HydroNode::Sort { .. }
6859 | HydroNode::PartitionSide { .. }
6860 | HydroNode::VersionedNetworkFork { .. }
6861 | HydroNode::VersionedNetwork { .. } => {}
6862 HydroNode::Map { f, .. }
6863 | HydroNode::FlatMap { f, .. }
6864 | HydroNode::FlatMapStreamBlocking { f, .. }
6865 | HydroNode::Filter { f, .. }
6866 | HydroNode::FilterMap { f, .. }
6867 | HydroNode::Inspect { f, .. }
6868 | HydroNode::PartitionShared { f, .. }
6869 | HydroNode::Reduce { f, .. }
6870 | HydroNode::ReduceKeyed { f, .. }
6871 | HydroNode::ReduceKeyedWatermark { f, .. } => {
6872 transform(&mut f.expr);
6873 }
6874 HydroNode::Fold { init, acc, .. }
6875 | HydroNode::Scan { init, acc, .. }
6876 | HydroNode::ScanAsyncBlocking { init, acc, .. }
6877 | HydroNode::FoldKeyed { init, acc, .. } => {
6878 transform(&mut init.expr);
6879 transform(&mut acc.expr);
6880 }
6881 HydroNode::Network {
6882 serialize,
6883 deserialize,
6884 ..
6885 } => {
6886 if let NetworkSend::Custom {
6887 serialize_fn: Some(serialize_fn),
6888 } = serialize
6889 {
6890 transform(serialize_fn);
6891 }
6892 if let NetworkRecv::Custom {
6893 deserialize_fn: Some(deserialize_fn),
6894 } = deserialize
6895 {
6896 transform(deserialize_fn);
6897 }
6898 }
6899 HydroNode::ExternalInput { deserialize_fn, .. } => {
6900 if let Some(deserialize_fn) = deserialize_fn {
6901 transform(deserialize_fn);
6902 }
6903 }
6904 HydroNode::Counter { duration, .. } => {
6905 transform(duration);
6906 }
6907 }
6908 }
6909
6910 pub fn op_metadata(&self) -> &HydroIrOpMetadata {
6911 &self.metadata().op
6912 }
6913
6914 pub fn metadata(&self) -> &HydroIrMetadata {
6915 match self {
6916 HydroNode::Placeholder => {
6917 panic!()
6918 }
6919 HydroNode::VersionedNetworkFork { metadata, .. }
6920 | HydroNode::VersionedNetwork { metadata, .. } => metadata,
6921 HydroNode::Cast { metadata, .. }
6922 | HydroNode::ObserveNonDet { metadata, .. }
6923 | HydroNode::AssertIsConsistent { metadata, .. }
6924 | HydroNode::UnboundSingleton { metadata, .. }
6925 | HydroNode::Source { metadata, .. }
6926 | HydroNode::SingletonSource { metadata, .. }
6927 | HydroNode::CycleSource { metadata, .. }
6928 | HydroNode::Tee { metadata, .. }
6929 | HydroNode::Reference { metadata, .. }
6930 | HydroNode::PartitionSide { metadata, .. }
6931 | HydroNode::PartitionShared { metadata, .. }
6932 | HydroNode::YieldConcat { metadata, .. }
6933 | HydroNode::BeginAtomic { metadata, .. }
6934 | HydroNode::EndAtomic { metadata, .. }
6935 | HydroNode::Batch { metadata, .. }
6936 | HydroNode::Snapshot { metadata, .. }
6937 | HydroNode::Chain { metadata, .. }
6938 | HydroNode::MergeOrdered { metadata, .. }
6939 | HydroNode::ChainFirst { metadata, .. }
6940 | HydroNode::CrossProduct { metadata, .. }
6941 | HydroNode::CrossSingleton { metadata, .. }
6942 | HydroNode::Join { metadata, .. }
6943 | HydroNode::JoinHalf { metadata, .. }
6944 | HydroNode::Difference { metadata, .. }
6945 | HydroNode::AntiJoin { metadata, .. }
6946 | HydroNode::ResolveFutures { metadata, .. }
6947 | HydroNode::ResolveFuturesBlocking { metadata, .. }
6948 | HydroNode::ResolveFuturesOrdered { metadata, .. }
6949 | HydroNode::Map { metadata, .. }
6950 | HydroNode::FlatMap { metadata, .. }
6951 | HydroNode::FlatMapStreamBlocking { metadata, .. }
6952 | HydroNode::Filter { metadata, .. }
6953 | HydroNode::FilterMap { metadata, .. }
6954 | HydroNode::DeferTick { metadata, .. }
6955 | HydroNode::Enumerate { metadata, .. }
6956 | HydroNode::Inspect { metadata, .. }
6957 | HydroNode::Unique { metadata, .. }
6958 | HydroNode::Sort { metadata, .. }
6959 | HydroNode::Scan { metadata, .. }
6960 | HydroNode::ScanAsyncBlocking { metadata, .. }
6961 | HydroNode::Fold { metadata, .. }
6962 | HydroNode::FoldKeyed { metadata, .. }
6963 | HydroNode::Reduce { metadata, .. }
6964 | HydroNode::ReduceKeyed { metadata, .. }
6965 | HydroNode::ReduceKeyedWatermark { metadata, .. }
6966 | HydroNode::ExternalInput { metadata, .. }
6967 | HydroNode::Network { metadata, .. }
6968 | HydroNode::Counter { metadata, .. } => metadata,
6969 }
6970 }
6971
6972 pub fn op_metadata_mut(&mut self) -> &mut HydroIrOpMetadata {
6973 &mut self.metadata_mut().op
6974 }
6975
6976 pub fn metadata_mut(&mut self) -> &mut HydroIrMetadata {
6977 match self {
6978 HydroNode::Placeholder => {
6979 panic!()
6980 }
6981 HydroNode::VersionedNetworkFork { metadata, .. }
6982 | HydroNode::VersionedNetwork { metadata, .. } => metadata,
6983 HydroNode::Cast { metadata, .. }
6984 | HydroNode::ObserveNonDet { metadata, .. }
6985 | HydroNode::AssertIsConsistent { metadata, .. }
6986 | HydroNode::UnboundSingleton { metadata, .. }
6987 | HydroNode::Source { metadata, .. }
6988 | HydroNode::SingletonSource { metadata, .. }
6989 | HydroNode::CycleSource { metadata, .. }
6990 | HydroNode::Tee { metadata, .. }
6991 | HydroNode::Reference { metadata, .. }
6992 | HydroNode::PartitionSide { metadata, .. }
6993 | HydroNode::PartitionShared { metadata, .. }
6994 | HydroNode::YieldConcat { metadata, .. }
6995 | HydroNode::BeginAtomic { metadata, .. }
6996 | HydroNode::EndAtomic { metadata, .. }
6997 | HydroNode::Batch { metadata, .. }
6998 | HydroNode::Snapshot { metadata, .. }
6999 | HydroNode::Chain { metadata, .. }
7000 | HydroNode::MergeOrdered { metadata, .. }
7001 | HydroNode::ChainFirst { metadata, .. }
7002 | HydroNode::CrossProduct { metadata, .. }
7003 | HydroNode::CrossSingleton { metadata, .. }
7004 | HydroNode::Join { metadata, .. }
7005 | HydroNode::JoinHalf { metadata, .. }
7006 | HydroNode::Difference { metadata, .. }
7007 | HydroNode::AntiJoin { metadata, .. }
7008 | HydroNode::ResolveFutures { metadata, .. }
7009 | HydroNode::ResolveFuturesBlocking { metadata, .. }
7010 | HydroNode::ResolveFuturesOrdered { metadata, .. }
7011 | HydroNode::Map { metadata, .. }
7012 | HydroNode::FlatMap { metadata, .. }
7013 | HydroNode::FlatMapStreamBlocking { metadata, .. }
7014 | HydroNode::Filter { metadata, .. }
7015 | HydroNode::FilterMap { metadata, .. }
7016 | HydroNode::DeferTick { metadata, .. }
7017 | HydroNode::Enumerate { metadata, .. }
7018 | HydroNode::Inspect { metadata, .. }
7019 | HydroNode::Unique { metadata, .. }
7020 | HydroNode::Sort { metadata, .. }
7021 | HydroNode::Scan { metadata, .. }
7022 | HydroNode::ScanAsyncBlocking { metadata, .. }
7023 | HydroNode::Fold { metadata, .. }
7024 | HydroNode::FoldKeyed { metadata, .. }
7025 | HydroNode::Reduce { metadata, .. }
7026 | HydroNode::ReduceKeyed { metadata, .. }
7027 | HydroNode::ReduceKeyedWatermark { metadata, .. }
7028 | HydroNode::ExternalInput { metadata, .. }
7029 | HydroNode::Network { metadata, .. }
7030 | HydroNode::Counter { metadata, .. } => metadata,
7031 }
7032 }
7033
7034 pub fn input(&self) -> Vec<&HydroNode> {
7035 match self {
7036 HydroNode::Placeholder => {
7037 panic!()
7038 }
7039 HydroNode::Source { .. }
7040 | HydroNode::SingletonSource { .. }
7041 | HydroNode::ExternalInput { .. }
7042 | HydroNode::CycleSource { .. }
7043 | HydroNode::Tee { .. }
7044 | HydroNode::Reference { .. }
7045 | HydroNode::PartitionSide { .. }
7046 | HydroNode::VersionedNetwork { .. } => {
7047 vec![]
7049 }
7050 HydroNode::Cast { inner, .. }
7051 | HydroNode::ObserveNonDet { inner, .. }
7052 | HydroNode::YieldConcat { inner, .. }
7053 | HydroNode::BeginAtomic { inner, .. }
7054 | HydroNode::EndAtomic { inner, .. }
7055 | HydroNode::Batch { inner, .. }
7056 | HydroNode::Snapshot { inner, .. }
7057 | HydroNode::UnboundSingleton { inner, .. }
7058 | HydroNode::AssertIsConsistent { inner, .. } => {
7059 vec![inner]
7060 }
7061 HydroNode::Chain { first, second, .. }
7062 | HydroNode::MergeOrdered { first, second, .. }
7063 | HydroNode::ChainFirst { first, second, .. } => {
7064 vec![first, second]
7065 }
7066 HydroNode::CrossProduct { left, right, .. }
7067 | HydroNode::CrossSingleton { left, right, .. }
7068 | HydroNode::Join { left, right, .. }
7069 | HydroNode::JoinHalf { left, right, .. } => {
7070 vec![left, right]
7071 }
7072 HydroNode::Difference { pos, neg, .. } | HydroNode::AntiJoin { pos, neg, .. } => {
7073 vec![pos, neg]
7074 }
7075 HydroNode::Counter { input, .. }
7076 | HydroNode::DeferTick { input, .. }
7077 | HydroNode::Enumerate { input, .. }
7078 | HydroNode::Filter { input, .. }
7079 | HydroNode::FilterMap { input, .. }
7080 | HydroNode::FlatMap { input, .. }
7081 | HydroNode::FlatMapStreamBlocking { input, .. }
7082 | HydroNode::Fold { input, .. }
7083 | HydroNode::FoldKeyed { input, .. }
7084 | HydroNode::Inspect { input, .. }
7085 | HydroNode::Map { input, .. }
7086 | HydroNode::Network { input, .. }
7087 | HydroNode::PartitionShared { input, .. }
7088 | HydroNode::Reduce { input, .. }
7089 | HydroNode::ReduceKeyed { input, .. }
7090 | HydroNode::ResolveFutures { input, .. }
7091 | HydroNode::ResolveFuturesBlocking { input, .. }
7092 | HydroNode::ResolveFuturesOrdered { input, .. }
7093 | HydroNode::Scan { input, .. }
7094 | HydroNode::ScanAsyncBlocking { input, .. }
7095 | HydroNode::Sort { input, .. }
7096 | HydroNode::Unique { input, .. } => {
7097 vec![input]
7098 }
7099 HydroNode::ReduceKeyedWatermark {
7100 input, watermark, ..
7101 } => {
7102 vec![input, watermark]
7103 }
7104 HydroNode::VersionedNetworkFork { senders, .. } => senders
7105 .iter()
7106 .map(|(_version, sender, _serialize)| sender.as_ref())
7107 .collect(),
7108 }
7109 }
7110
7111 pub fn input_metadata(&self) -> Vec<&HydroIrMetadata> {
7112 self.input()
7113 .iter()
7114 .map(|input_node| input_node.metadata())
7115 .collect()
7116 }
7117
7118 pub fn is_shared_with_others(&self) -> bool {
7122 match self {
7123 HydroNode::Tee { inner, .. } | HydroNode::PartitionSide { inner, .. } => {
7124 Rc::strong_count(&inner.0) > 1
7125 }
7126 HydroNode::Reference { .. } => false,
7129 _ => false,
7130 }
7131 }
7132
7133 pub fn print_root(&self) -> String {
7134 match self {
7135 HydroNode::Placeholder => {
7136 panic!()
7137 }
7138 HydroNode::Cast { .. } => "Cast()".to_owned(),
7139 HydroNode::UnboundSingleton { .. } => "UnboundSingleton()".to_owned(),
7140 HydroNode::ObserveNonDet { .. } => "ObserveNonDet()".to_owned(),
7141 HydroNode::AssertIsConsistent { .. } => "AssertIsConsistent()".to_owned(),
7142 HydroNode::Source { source, .. } => format!("Source({:?})", source),
7143 HydroNode::SingletonSource {
7144 value,
7145 first_tick_only,
7146 ..
7147 } => format!(
7148 "SingletonSource({:?}, first_tick_only={})",
7149 value, first_tick_only
7150 ),
7151 HydroNode::CycleSource { cycle_id, .. } => format!("CycleSource({})", cycle_id),
7152 HydroNode::Tee { inner, .. } => {
7153 format!("Tee({})", inner.0.borrow().print_root())
7154 }
7155 HydroNode::Reference { inner, kind, .. } => {
7156 format!("Reference({:?}, {})", kind, inner.0.borrow().print_root())
7157 }
7158 HydroNode::PartitionSide { inner, is_true, .. } => {
7159 format!(
7160 "PartitionSide(is_true={}, {})",
7161 is_true,
7162 inner.0.borrow().print_root(),
7163 )
7164 }
7165 HydroNode::PartitionShared { f, .. } => format!("PartitionShared({:?})", f),
7166 HydroNode::YieldConcat { .. } => "YieldConcat()".to_owned(),
7167 HydroNode::BeginAtomic { .. } => "BeginAtomic()".to_owned(),
7168 HydroNode::EndAtomic { .. } => "EndAtomic()".to_owned(),
7169 HydroNode::Batch { .. } => "Batch()".to_owned(),
7170 HydroNode::Snapshot { .. } => "Snapshot()".to_owned(),
7171 HydroNode::Chain { first, second, .. } => {
7172 format!("Chain({}, {})", first.print_root(), second.print_root())
7173 }
7174 HydroNode::MergeOrdered { first, second, .. } => {
7175 format!(
7176 "MergeOrdered({}, {})",
7177 first.print_root(),
7178 second.print_root()
7179 )
7180 }
7181 HydroNode::ChainFirst { first, second, .. } => {
7182 format!(
7183 "ChainFirst({}, {})",
7184 first.print_root(),
7185 second.print_root()
7186 )
7187 }
7188 HydroNode::CrossProduct { left, right, .. } => {
7189 format!(
7190 "CrossProduct({}, {})",
7191 left.print_root(),
7192 right.print_root()
7193 )
7194 }
7195 HydroNode::CrossSingleton { left, right, .. } => {
7196 format!(
7197 "CrossSingleton({}, {})",
7198 left.print_root(),
7199 right.print_root()
7200 )
7201 }
7202 HydroNode::Join { left, right, .. } => {
7203 format!("Join({}, {})", left.print_root(), right.print_root())
7204 }
7205 HydroNode::JoinHalf { left, right, .. } => {
7206 format!("JoinHalf({}, {})", left.print_root(), right.print_root())
7207 }
7208 HydroNode::Difference { pos, neg, .. } => {
7209 format!("Difference({}, {})", pos.print_root(), neg.print_root())
7210 }
7211 HydroNode::AntiJoin { pos, neg, .. } => {
7212 format!("AntiJoin({}, {})", pos.print_root(), neg.print_root())
7213 }
7214 HydroNode::ResolveFutures { .. } => "ResolveFutures()".to_owned(),
7215 HydroNode::ResolveFuturesBlocking { .. } => "ResolveFuturesBlocking()".to_owned(),
7216 HydroNode::ResolveFuturesOrdered { .. } => "ResolveFuturesOrdered()".to_owned(),
7217 HydroNode::Map { f, .. } => format!("Map({:?})", f),
7218 HydroNode::FlatMap { f, .. } => format!("FlatMap({:?})", f),
7219 HydroNode::FlatMapStreamBlocking { f, .. } => format!("FlatMapStreamBlocking({:?})", f),
7220 HydroNode::Filter { f, .. } => format!("Filter({:?})", f),
7221 HydroNode::FilterMap { f, .. } => format!("FilterMap({:?})", f),
7222 HydroNode::DeferTick { .. } => "DeferTick()".to_owned(),
7223 HydroNode::Enumerate { .. } => "Enumerate()".to_owned(),
7224 HydroNode::Inspect { f, .. } => format!("Inspect({:?})", f),
7225 HydroNode::Unique { .. } => "Unique()".to_owned(),
7226 HydroNode::Sort { .. } => "Sort()".to_owned(),
7227 HydroNode::Fold { init, acc, .. } => format!("Fold({:?}, {:?})", init, acc),
7228 HydroNode::Scan { init, acc, .. } => format!("Scan({:?}, {:?})", init, acc),
7229 HydroNode::ScanAsyncBlocking { init, acc, .. } => {
7230 format!("ScanAsyncBlocking({:?}, {:?})", init, acc)
7231 }
7232 HydroNode::FoldKeyed { init, acc, .. } => format!("FoldKeyed({:?}, {:?})", init, acc),
7233 HydroNode::Reduce { f, .. } => format!("Reduce({:?})", f),
7234 HydroNode::ReduceKeyed { f, .. } => format!("ReduceKeyed({:?})", f),
7235 HydroNode::ReduceKeyedWatermark { f, .. } => format!("ReduceKeyedWatermark({:?})", f),
7236 HydroNode::Network { .. } => "Network()".to_owned(),
7237 HydroNode::ExternalInput { .. } => "ExternalInput()".to_owned(),
7238 HydroNode::Counter { tag, duration, .. } => {
7239 format!("Counter({:?}, {:?})", tag, duration)
7240 }
7241 HydroNode::VersionedNetworkFork {
7242 channel_name,
7243 senders,
7244 ..
7245 } => {
7246 let versions: Vec<u32> = senders.iter().map(|(v, _, _)| *v).collect();
7247 format!(
7248 "VersionedNetworkFork({}, senders={:?})",
7249 channel_name, versions
7250 )
7251 }
7252 HydroNode::VersionedNetwork { version, .. } => {
7253 format!("VersionedNetwork(v{})", version)
7254 }
7255 }
7256 }
7257}
7258
7259#[cfg(feature = "build")]
7260#[expect(clippy::too_many_arguments, reason = "networking codegen")]
7261fn instantiate_network<'a, D>(
7262 env: &mut D::InstantiateEnv,
7263 from_location: &LocationId,
7264 to_location: &LocationId,
7265 processes: &SparseSecondaryMap<LocationKey, D::Process>,
7266 clusters: &SparseSecondaryMap<LocationKey, D::Cluster>,
7267 name: Option<&str>,
7268 networking_info: &crate::networking::NetworkingInfo,
7269 external_types: Option<(&syn::Type, &syn::Type)>,
7270) -> (syn::Expr, syn::Expr, Box<dyn FnOnce()>)
7271where
7272 D: Deploy<'a>,
7273{
7274 if external_types.is_some() {
7275 assert!(
7276 D::SUPPORTS_EXTERNAL_SERIALIZATION,
7277 "`EMBEDDED` channels are implemented by the host of an embedded dataflow and are \
7278 only supported by the embedded deployment backend (and the simulator). Use `TCP` \
7279 or `UDP` (with `.bincode()`) for this deployment target instead."
7280 );
7281 }
7282
7283 let ((sink, source), connect_fn) = match (from_location, to_location) {
7284 (&LocationId::Process(from), &LocationId::Process(to)) => {
7285 let from_node = processes
7286 .get(from)
7287 .unwrap_or_else(|| {
7288 panic!("A process used in the graph was not instantiated: {}", from)
7289 })
7290 .clone();
7291 let to_node = processes
7292 .get(to)
7293 .unwrap_or_else(|| {
7294 panic!("A process used in the graph was not instantiated: {}", to)
7295 })
7296 .clone();
7297
7298 let sink_port = from_node.next_port();
7299 let source_port = to_node.next_port();
7300
7301 (
7302 D::o2o_sink_source(
7303 env,
7304 &from_node,
7305 &sink_port,
7306 &to_node,
7307 &source_port,
7308 name,
7309 networking_info,
7310 external_types,
7311 ),
7312 D::o2o_connect(&from_node, &sink_port, &to_node, &source_port),
7313 )
7314 }
7315 (&LocationId::Process(from), &LocationId::Cluster(to)) => {
7316 let from_node = processes
7317 .get(from)
7318 .unwrap_or_else(|| {
7319 panic!("A process used in the graph was not instantiated: {}", from)
7320 })
7321 .clone();
7322 let to_node = clusters
7323 .get(to)
7324 .unwrap_or_else(|| {
7325 panic!("A cluster used in the graph was not instantiated: {}", to)
7326 })
7327 .clone();
7328
7329 let sink_port = from_node.next_port();
7330 let source_port = to_node.next_port();
7331
7332 (
7333 D::o2m_sink_source(
7334 env,
7335 &from_node,
7336 &sink_port,
7337 &to_node,
7338 &source_port,
7339 name,
7340 networking_info,
7341 external_types,
7342 ),
7343 D::o2m_connect(&from_node, &sink_port, &to_node, &source_port),
7344 )
7345 }
7346 (&LocationId::Cluster(from), &LocationId::Process(to)) => {
7347 let from_node = clusters
7348 .get(from)
7349 .unwrap_or_else(|| {
7350 panic!("A cluster used in the graph was not instantiated: {}", from)
7351 })
7352 .clone();
7353 let to_node = processes
7354 .get(to)
7355 .unwrap_or_else(|| {
7356 panic!("A process used in the graph was not instantiated: {}", to)
7357 })
7358 .clone();
7359
7360 let sink_port = from_node.next_port();
7361 let source_port = to_node.next_port();
7362
7363 (
7364 D::m2o_sink_source(
7365 env,
7366 &from_node,
7367 &sink_port,
7368 &to_node,
7369 &source_port,
7370 name,
7371 networking_info,
7372 external_types,
7373 ),
7374 D::m2o_connect(&from_node, &sink_port, &to_node, &source_port),
7375 )
7376 }
7377 (&LocationId::Cluster(from), &LocationId::Cluster(to)) => {
7378 let from_node = clusters
7379 .get(from)
7380 .unwrap_or_else(|| {
7381 panic!("A cluster used in the graph was not instantiated: {}", from)
7382 })
7383 .clone();
7384 let to_node = clusters
7385 .get(to)
7386 .unwrap_or_else(|| {
7387 panic!("A cluster used in the graph was not instantiated: {}", to)
7388 })
7389 .clone();
7390
7391 let sink_port = from_node.next_port();
7392 let source_port = to_node.next_port();
7393
7394 (
7395 D::m2m_sink_source(
7396 env,
7397 &from_node,
7398 &sink_port,
7399 &to_node,
7400 &source_port,
7401 name,
7402 networking_info,
7403 external_types,
7404 ),
7405 D::m2m_connect(&from_node, &sink_port, &to_node, &source_port),
7406 )
7407 }
7408 (LocationId::Tick { .. }, _) => panic!(),
7409 (_, LocationId::Tick { .. }) => panic!(),
7410 (LocationId::Atomic(_), _) => panic!(),
7411 (_, LocationId::Atomic(_)) => panic!(),
7412 };
7413 (sink, source, connect_fn)
7414}
7415
7416#[cfg(test)]
7417mod serde_test;
7418
7419#[cfg(test)]
7420mod test {
7421 use std::mem::size_of;
7422
7423 use stageleft::{QuotedWithContext, q};
7424
7425 use super::*;
7426
7427 #[test]
7428 #[cfg_attr(
7429 not(feature = "build"),
7430 ignore = "expects inclusion of feature-gated fields"
7431 )]
7432 fn hydro_node_size() {
7433 assert_eq!(size_of::<HydroNode>(), 280);
7434 }
7435
7436 #[test]
7437 #[cfg_attr(
7438 not(feature = "build"),
7439 ignore = "expects inclusion of feature-gated fields"
7440 )]
7441 fn hydro_root_size() {
7442 assert_eq!(size_of::<HydroRoot>(), 152);
7443 }
7444
7445 #[test]
7446 fn test_simplify_q_macro_basic() {
7447 let simple_expr: syn::Expr = syn::parse_str("x + y").unwrap();
7449 let result = simplify_q_macro(simple_expr.clone());
7450 assert_eq!(result, simple_expr);
7451 }
7452
7453 #[test]
7454 fn test_simplify_q_macro_actual_stageleft_call() {
7455 let stageleft_call = q!(|x: usize| x + 1).splice_fn1_ctx(&());
7457 let result = simplify_q_macro(stageleft_call);
7458 hydro_build_utils::assert_snapshot!(result.to_token_stream().to_string());
7461 }
7462
7463 #[test]
7464 fn test_closure_no_pipe_at_start() {
7465 let stageleft_call = q!({
7467 let foo = 123;
7468 move |b: usize| b + foo
7469 })
7470 .splice_fn1_ctx(&());
7471 let result = simplify_q_macro(stageleft_call);
7472 hydro_build_utils::assert_snapshot!(result.to_token_stream().to_string());
7473 }
7474}