tidepool_codegen/datacon_env.rs
1use tidepool_repr::{CoreExpr, CoreFrame, DataConTable, TreeBuilder, VarId};
2
3/// Wrap a CoreExpr with let-bindings for all data constructors from the table.
4///
5/// For each DataCon with arity N:
6/// - arity 0: `let dc_var = Con(id, []) in ...`
7/// - arity 1: `let dc_var = \v0 -> Con(id, [v0]) in ...`
8/// - arity 2: `let dc_var = \v0 -> \v1 -> Con(id, [v0, v1]) in ...`
9/// - etc.
10///
11/// The binding VarId matches `VarId(dc.id.0)`, which is what the GHC Core translator
12/// uses to reference data constructors as function values.
13pub fn wrap_with_datacon_env(expr: &CoreExpr, table: &DataConTable) -> CoreExpr {
14 let mut b = TreeBuilder::new();
15
16 // First, push all nodes from the original expression
17 let mut src = TreeBuilder::new();
18 for node in &expr.nodes {
19 src.push(node.clone());
20 }
21 let base = b.push_tree(src);
22 let root = base + expr.nodes.len() - 1;
23
24 // Collect datacons sorted by id for deterministic output
25 let mut datacons: Vec<_> = table.iter().collect();
26 datacons.sort_by_key(|dc| dc.id.0);
27
28 let mut body = root;
29
30 for dc in &datacons {
31 let binder = VarId(dc.id.0);
32 let arity = dc.rep_arity as usize;
33
34 if arity == 0 {
35 // Con(id, [])
36 let con = b.push(CoreFrame::Con {
37 tag: dc.id,
38 fields: vec![],
39 });
40 body = b.push(CoreFrame::LetNonRec {
41 binder,
42 rhs: con,
43 body,
44 });
45 } else {
46 // Build curried lambda chain: \v0 -> \v1 -> ... -> Con(id, [v0, v1, ...])
47 // Fresh vars use a hash of the DataConId to avoid collisions
48 let fresh_base = dc
49 .id
50 .0
51 .wrapping_mul(0x517cc1b727220a95)
52 .wrapping_add(0xFFFF_0000_0000_0000);
53 let fresh_vars: Vec<VarId> = (0..arity)
54 .map(|i| VarId(fresh_base.wrapping_add(i as u64)))
55 .collect();
56
57 // Build Con(id, [v0, v1, ...]) — innermost
58 let var_indices: Vec<usize> = fresh_vars
59 .iter()
60 .map(|v| b.push(CoreFrame::Var(*v)))
61 .collect();
62 let mut inner = b.push(CoreFrame::Con {
63 tag: dc.id,
64 fields: var_indices,
65 });
66
67 // Wrap in lambdas from inside out
68 for v in fresh_vars.iter().rev() {
69 inner = b.push(CoreFrame::Lam {
70 binder: *v,
71 body: inner,
72 });
73 }
74
75 body = b.push(CoreFrame::LetNonRec {
76 binder,
77 rhs: inner,
78 body,
79 });
80 }
81 }
82
83 b.build()
84}