dfir_lang/graph/ops/lattice_fold.rs
1use syn::parse_quote_spanned;
2
3use super::{
4 DelayType, OperatorCategory, OperatorConstraints, WriteContextArgs,
5 RANGE_0, RANGE_1,
6};
7
8/// > 1 input stream, 1 output stream
9///
10/// A specialized operator for merging lattices together into a accumulated value. Like [`fold()`](#fold)
11/// but specialized for lattice types. `lattice_fold(MyLattice::default)` is equivalent to `fold(MyLattice::default, dfir_rs::lattices::Merge::merge)`.
12///
13/// `lattice_fold` can also be provided with one generic lifetime persistence argument, either
14/// `'tick` or `'static`, to specify how data persists. With `'tick`, values will only be collected
15/// within the same tick. With `'static`, values will be remembered across ticks and will be
16/// aggregated with pairs arriving in later ticks. When not explicitly specified persistence
17/// defaults to `'tick`.
18///
19/// `lattice_fold` is differentiated from `lattice_reduce` in that `lattice_fold` can accumulate into a different type from its input.
20/// But it also means that the accumulating type must have a sensible default value
21///
22/// ```dfir
23/// use dfir_rs::lattices::set_union::SetUnionSingletonSet;
24/// use dfir_rs::lattices::set_union::SetUnionHashSet;
25///
26/// source_iter([SetUnionSingletonSet::new_from(7)])
27/// -> lattice_fold(SetUnionHashSet::<usize>::default)
28/// -> assert_eq([SetUnionHashSet::new_from([7])]);
29/// ```
30pub const LATTICE_FOLD: OperatorConstraints = OperatorConstraints {
31 name: "lattice_fold",
32 categories: &[OperatorCategory::LatticeFold],
33 hard_range_inn: RANGE_1,
34 soft_range_inn: RANGE_1,
35 hard_range_out: RANGE_1,
36 soft_range_out: RANGE_1,
37 num_args: 1,
38 persistence_args: &(0..=1),
39 type_args: RANGE_0,
40 is_external_input: false,
41 has_singleton_output: false,
42 flo_type: None,
43 ports_inn: None,
44 ports_out: None,
45 input_delaytype_fn: |_| Some(DelayType::MonotoneAccum),
46 write_fn: |wc @ &WriteContextArgs {
47 root,
48 is_pull,
49 op_span,
50 arguments,
51 ..
52 },
53 diagnostics| {
54 assert!(is_pull);
55
56 let first_arg = &arguments[0];
57
58 let arguments = &parse_quote_spanned! {op_span=>
59 #first_arg,
60 |acc, item| { #root::lattices::Merge::<_>::merge(acc, item); }
61 };
62
63 let wc = WriteContextArgs {
64 arguments,
65 ..wc.clone()
66 };
67
68 // Can't do better type checking here because we need heavy type inference
69 // to support different accumulator and input types.
70 (super::fold::FOLD.write_fn)(&wc, diagnostics)
71 },
72};