Skip to main content

oxilean_codegen/dhall_backend/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::functions::*;
6use std::collections::{HashMap, HashSet, VecDeque};
7
8/// Dhall expression AST.
9#[derive(Debug, Clone, PartialEq)]
10pub enum DhallExpr {
11    /// Boolean literal: `True` / `False`
12    BoolLit(bool),
13    /// Natural number literal: `0`, `42`
14    NaturalLit(u64),
15    /// Integer literal: `+1`, `-5`
16    IntegerLit(i64),
17    /// Double literal: `3.14`
18    DoubleLit(f64),
19    /// Text literal: `"hello, world"`
20    TextLit(String),
21    /// Text interpolation: `"prefix ${expr} suffix"`
22    TextInterp(String, Box<DhallExpr>, String),
23    /// List literal: `[ v1, v2, v3 ]`
24    ListLit(Vec<DhallExpr>),
25    /// Typed empty list: `[] : List Natural`
26    EmptyList(DhallType),
27    /// `Some value` (Optional constructor)
28    Some(Box<DhallExpr>),
29    /// `None T` (empty Optional)
30    None(DhallType),
31    /// Record value: `{ field1 = v1, field2 = v2 }`
32    RecordLit(Box<DhallRecord>),
33    /// Record type: `{ field1 : T1, field2 : T2 }`
34    RecordType(Vec<(String, DhallType)>),
35    /// Union value: `< Ctor : T | ... >.Ctor value`
36    UnionLit {
37        /// Union type
38        union_type: DhallType,
39        /// Chosen constructor
40        ctor: String,
41        /// Constructor argument (None for unit constructors)
42        value: Option<Box<DhallExpr>>,
43    },
44    /// Lambda: `\(x : T) -> body`
45    Lambda(Box<DhallFunction>),
46    /// Dependent forall: `forall (x : T) -> body`
47    Forall(String, DhallType, Box<DhallExpr>),
48    /// Let binding: `let x : T = e in body`
49    Let(Vec<DhallDecl>, Box<DhallExpr>),
50    /// If-then-else: `if cond then t else f`
51    If(Box<DhallExpr>, Box<DhallExpr>, Box<DhallExpr>),
52    /// `merge handler union : T` — union elimination
53    Merge(Box<DhallExpr>, Box<DhallExpr>, Option<DhallType>),
54    /// `toMap record : List { mapKey : Text, mapValue : T }`
55    ToMap(Box<DhallExpr>, Option<DhallType>),
56    /// Equivalence assertion: `assert : a === b`
57    Assert(Box<DhallExpr>, Box<DhallExpr>),
58    /// Equivalence type: `a === b`
59    Equivalent(Box<DhallExpr>, Box<DhallExpr>),
60    /// Record update: `record // { field = value }`
61    With(Box<DhallExpr>, Vec<(String, DhallExpr)>),
62    /// Field selection: `record.field`
63    Select(Box<DhallExpr>, String),
64    /// Projection: `record.{ field1, field2 }`
65    Project(Box<DhallExpr>, Vec<String>),
66    /// Function application: `f x`
67    Application(Box<DhallExpr>, Box<DhallExpr>),
68    /// Variable: `x`, `Natural/show`, `List/map`
69    Var(String),
70    /// Import expression
71    Import(DhallImport),
72    /// Type ascription: `x : T`
73    Annot(Box<DhallExpr>, DhallType),
74    /// Boolean operators: `&&`, `||`
75    BoolOp(String, Box<DhallExpr>, Box<DhallExpr>),
76    /// Natural/Integer arithmetic: `+`, `*`
77    NaturalOp(String, Box<DhallExpr>, Box<DhallExpr>),
78    /// Text append: `x ++ y`
79    TextAppend(Box<DhallExpr>, Box<DhallExpr>),
80    /// List append: `xs # ys`
81    ListAppend(Box<DhallExpr>, Box<DhallExpr>),
82    /// Record merge (types): `T1 /\ T2`
83    RecordTypeMerge(Box<DhallExpr>, Box<DhallExpr>),
84    /// Record value merge: `r1 // r2`
85    RecordMerge(Box<DhallExpr>, Box<DhallExpr>),
86    /// `Type`, `Kind`, `Sort`
87    Universe(DhallType),
88    /// `Bool`, `Natural`, `Integer`, `Double`, `Text` as type expressions
89    BuiltinType(DhallType),
90}
91impl DhallExpr {
92    /// Emit this expression as a Dhall source string.
93    pub fn emit(&self, indent: usize) -> String {
94        let ind = " ".repeat(indent);
95        let ind2 = " ".repeat(indent + 2);
96        match self {
97            DhallExpr::BoolLit(true) => "True".into(),
98            DhallExpr::BoolLit(false) => "False".into(),
99            DhallExpr::NaturalLit(n) => n.to_string(),
100            DhallExpr::IntegerLit(n) => {
101                if *n >= 0 {
102                    format!("+{}", n)
103                } else {
104                    n.to_string()
105                }
106            }
107            DhallExpr::DoubleLit(f) => {
108                let s = format!("{}", f);
109                if s.contains('.') || s.contains('e') {
110                    s
111                } else {
112                    format!("{}.0", s)
113                }
114            }
115            DhallExpr::TextLit(s) => format!("\"{}\"", escape_dhall_string(s)),
116            DhallExpr::TextInterp(pre, expr, post) => {
117                format!(
118                    "\"{}${{{}}}{}\"",
119                    escape_dhall_string(pre),
120                    expr.emit(indent),
121                    escape_dhall_string(post)
122                )
123            }
124            DhallExpr::ListLit(items) => {
125                if items.is_empty() {
126                    return "[ ]".into();
127                }
128                let parts: Vec<String> = items.iter().map(|e| e.emit(indent + 2)).collect();
129                format!(
130                    "[\n{}{}\n{}]",
131                    ind2,
132                    parts.join(format!(",\n{}", ind2).as_str()),
133                    ind
134                )
135            }
136            DhallExpr::EmptyList(t) => format!("[] : List {}", t),
137            DhallExpr::Some(e) => format!("Some {}", e.emit(indent)),
138            DhallExpr::None(t) => format!("None {}", t),
139            DhallExpr::RecordLit(r) => r.emit(indent),
140            DhallExpr::RecordType(fields) => {
141                if fields.is_empty() {
142                    return "{}".into();
143                }
144                let parts: Vec<String> = fields
145                    .iter()
146                    .map(|(k, t)| format!("{}{} : {}", ind2, k, t))
147                    .collect();
148                format!("{{\n{}\n{}}}", parts.join(",\n"), ind)
149            }
150            DhallExpr::UnionLit {
151                union_type,
152                ctor,
153                value,
154            } => {
155                let ut = union_type.to_string();
156                match value {
157                    None => format!("({}).{}", ut, ctor),
158                    Some(v) => format!("({}).{} {}", ut, ctor, v.emit(indent)),
159                }
160            }
161            DhallExpr::Lambda(func) => func.emit(indent),
162            DhallExpr::Forall(x, t, body) => {
163                format!("forall ({} : {}) -> {}", x, t, body.emit(indent))
164            }
165            DhallExpr::Let(decls, body) => {
166                let mut out = String::new();
167                for d in decls {
168                    out.push_str(&format!("{}\n", d.emit(indent)));
169                }
170                out.push_str(&format!("in  {}", body.emit(indent)));
171                out
172            }
173            DhallExpr::If(cond, t, f) => {
174                format!(
175                    "if {}\nthen {}{}\nelse {}{}",
176                    cond.emit(indent),
177                    ind2,
178                    t.emit(indent + 2),
179                    ind2,
180                    f.emit(indent + 2),
181                )
182            }
183            DhallExpr::Merge(handler, union, ty) => match ty {
184                None => {
185                    format!("merge {} {}", handler.emit(indent), union.emit(indent))
186                }
187                Some(t) => {
188                    format!(
189                        "merge {} {} : {}",
190                        handler.emit(indent),
191                        union.emit(indent),
192                        t
193                    )
194                }
195            },
196            DhallExpr::ToMap(expr, ty) => match ty {
197                None => format!("toMap {}", expr.emit(indent)),
198                Some(t) => format!("toMap {} : {}", expr.emit(indent), t),
199            },
200            DhallExpr::Assert(lhs, rhs) => {
201                format!("assert : {} === {}", lhs.emit(indent), rhs.emit(indent))
202            }
203            DhallExpr::Equivalent(lhs, rhs) => {
204                format!("{} === {}", lhs.emit(indent), rhs.emit(indent))
205            }
206            DhallExpr::With(record, updates) => {
207                let mut s = record.emit(indent);
208                for (k, v) in updates {
209                    s = format!("({} with {} = {})", s, k, v.emit(indent));
210                }
211                s
212            }
213            DhallExpr::Select(record, field) => {
214                format!("{}.{}", record.emit(indent), field)
215            }
216            DhallExpr::Project(record, fields) => {
217                format!("{}.{{ {} }}", record.emit(indent), fields.join(", "))
218            }
219            DhallExpr::Application(func, arg) => {
220                let fs = func.emit(indent);
221                let needs_parens = matches!(
222                    arg.as_ref(),
223                    DhallExpr::Application(_, _)
224                        | DhallExpr::Lambda(_)
225                        | DhallExpr::Forall(_, _, _)
226                        | DhallExpr::Let(_, _)
227                        | DhallExpr::If(_, _, _)
228                        | DhallExpr::BoolOp(_, _, _)
229                        | DhallExpr::NaturalOp(_, _, _)
230                        | DhallExpr::TextAppend(_, _)
231                        | DhallExpr::ListAppend(_, _)
232                        | DhallExpr::RecordMerge(_, _)
233                );
234                if needs_parens {
235                    format!("{} ({})", fs, arg.emit(indent))
236                } else {
237                    format!("{} {}", fs, arg.emit(indent))
238                }
239            }
240            DhallExpr::Var(name) => name.clone(),
241            DhallExpr::Import(imp) => imp.to_string(),
242            DhallExpr::Annot(e, t) => format!("({} : {})", e.emit(indent), t),
243            DhallExpr::BoolOp(op, lhs, rhs) => {
244                format!("({} {} {})", lhs.emit(indent), op, rhs.emit(indent))
245            }
246            DhallExpr::NaturalOp(op, lhs, rhs) => {
247                format!("({} {} {})", lhs.emit(indent), op, rhs.emit(indent))
248            }
249            DhallExpr::TextAppend(lhs, rhs) => {
250                format!("({} ++ {})", lhs.emit(indent), rhs.emit(indent))
251            }
252            DhallExpr::ListAppend(lhs, rhs) => {
253                format!("({} # {})", lhs.emit(indent), rhs.emit(indent))
254            }
255            DhallExpr::RecordTypeMerge(lhs, rhs) => {
256                format!("({} /\\ {})", lhs.emit(indent), rhs.emit(indent))
257            }
258            DhallExpr::RecordMerge(lhs, rhs) => {
259                format!("({} // {})", lhs.emit(indent), rhs.emit(indent))
260            }
261            DhallExpr::Universe(u) => u.to_string(),
262            DhallExpr::BuiltinType(t) => t.to_string(),
263        }
264    }
265}
266/// Constant folding helper for DhallX2.
267#[allow(dead_code)]
268#[derive(Debug, Clone, Default)]
269pub struct DhallX2ConstFolder {
270    pub(super) folds: usize,
271    pub(super) failures: usize,
272    pub(super) enabled: bool,
273}
274impl DhallX2ConstFolder {
275    #[allow(dead_code)]
276    pub fn new() -> Self {
277        Self {
278            folds: 0,
279            failures: 0,
280            enabled: true,
281        }
282    }
283    #[allow(dead_code)]
284    pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
285        self.folds += 1;
286        a.checked_add(b)
287    }
288    #[allow(dead_code)]
289    pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
290        self.folds += 1;
291        a.checked_sub(b)
292    }
293    #[allow(dead_code)]
294    pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
295        self.folds += 1;
296        a.checked_mul(b)
297    }
298    #[allow(dead_code)]
299    pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
300        if b == 0 {
301            self.failures += 1;
302            None
303        } else {
304            self.folds += 1;
305            a.checked_div(b)
306        }
307    }
308    #[allow(dead_code)]
309    pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
310        if b == 0 {
311            self.failures += 1;
312            None
313        } else {
314            self.folds += 1;
315            a.checked_rem(b)
316        }
317    }
318    #[allow(dead_code)]
319    pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
320        self.folds += 1;
321        a.checked_neg()
322    }
323    #[allow(dead_code)]
324    pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
325        if s >= 64 {
326            self.failures += 1;
327            None
328        } else {
329            self.folds += 1;
330            a.checked_shl(s)
331        }
332    }
333    #[allow(dead_code)]
334    pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
335        if s >= 64 {
336            self.failures += 1;
337            None
338        } else {
339            self.folds += 1;
340            a.checked_shr(s)
341        }
342    }
343    #[allow(dead_code)]
344    pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
345        self.folds += 1;
346        a & b
347    }
348    #[allow(dead_code)]
349    pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
350        self.folds += 1;
351        a | b
352    }
353    #[allow(dead_code)]
354    pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
355        self.folds += 1;
356        a ^ b
357    }
358    #[allow(dead_code)]
359    pub fn not_i64(&mut self, a: i64) -> i64 {
360        self.folds += 1;
361        !a
362    }
363    #[allow(dead_code)]
364    pub fn fold_count(&self) -> usize {
365        self.folds
366    }
367    #[allow(dead_code)]
368    pub fn failure_count(&self) -> usize {
369        self.failures
370    }
371    #[allow(dead_code)]
372    pub fn enable(&mut self) {
373        self.enabled = true;
374    }
375    #[allow(dead_code)]
376    pub fn disable(&mut self) {
377        self.enabled = false;
378    }
379    #[allow(dead_code)]
380    pub fn is_enabled(&self) -> bool {
381        self.enabled
382    }
383}
384/// Pass registry for DhallX2.
385#[allow(dead_code)]
386#[derive(Debug, Default)]
387pub struct DhallX2PassRegistry {
388    pub(super) configs: Vec<DhallX2PassConfig>,
389    pub(super) stats: Vec<DhallX2PassStats>,
390}
391impl DhallX2PassRegistry {
392    #[allow(dead_code)]
393    pub fn new() -> Self {
394        Self::default()
395    }
396    #[allow(dead_code)]
397    pub fn register(&mut self, c: DhallX2PassConfig) {
398        self.stats.push(DhallX2PassStats::new());
399        self.configs.push(c);
400    }
401    #[allow(dead_code)]
402    pub fn len(&self) -> usize {
403        self.configs.len()
404    }
405    #[allow(dead_code)]
406    pub fn is_empty(&self) -> bool {
407        self.configs.is_empty()
408    }
409    #[allow(dead_code)]
410    pub fn get(&self, i: usize) -> Option<&DhallX2PassConfig> {
411        self.configs.get(i)
412    }
413    #[allow(dead_code)]
414    pub fn get_stats(&self, i: usize) -> Option<&DhallX2PassStats> {
415        self.stats.get(i)
416    }
417    #[allow(dead_code)]
418    pub fn enabled_passes(&self) -> Vec<&DhallX2PassConfig> {
419        self.configs.iter().filter(|c| c.enabled).collect()
420    }
421    #[allow(dead_code)]
422    pub fn passes_in_phase(&self, ph: &DhallX2PassPhase) -> Vec<&DhallX2PassConfig> {
423        self.configs
424            .iter()
425            .filter(|c| c.enabled && &c.phase == ph)
426            .collect()
427    }
428    #[allow(dead_code)]
429    pub fn total_nodes_visited(&self) -> usize {
430        self.stats.iter().map(|s| s.nodes_visited).sum()
431    }
432    #[allow(dead_code)]
433    pub fn any_changed(&self) -> bool {
434        self.stats.iter().any(|s| s.changed)
435    }
436}
437#[allow(dead_code)]
438#[derive(Debug, Clone)]
439pub struct DhallPassConfig {
440    pub phase: DhallPassPhase,
441    pub enabled: bool,
442    pub max_iterations: u32,
443    pub debug_output: bool,
444    pub pass_name: String,
445}
446impl DhallPassConfig {
447    #[allow(dead_code)]
448    pub fn new(name: impl Into<String>, phase: DhallPassPhase) -> Self {
449        DhallPassConfig {
450            phase,
451            enabled: true,
452            max_iterations: 10,
453            debug_output: false,
454            pass_name: name.into(),
455        }
456    }
457    #[allow(dead_code)]
458    pub fn disabled(mut self) -> Self {
459        self.enabled = false;
460        self
461    }
462    #[allow(dead_code)]
463    pub fn with_debug(mut self) -> Self {
464        self.debug_output = true;
465        self
466    }
467    #[allow(dead_code)]
468    pub fn max_iter(mut self, n: u32) -> Self {
469        self.max_iterations = n;
470        self
471    }
472}
473/// Dependency graph for DhallX2.
474#[allow(dead_code)]
475#[derive(Debug, Clone)]
476pub struct DhallX2DepGraph {
477    pub(super) n: usize,
478    pub(super) adj: Vec<Vec<usize>>,
479    pub(super) rev: Vec<Vec<usize>>,
480    pub(super) edge_count: usize,
481}
482impl DhallX2DepGraph {
483    #[allow(dead_code)]
484    pub fn new(n: usize) -> Self {
485        Self {
486            n,
487            adj: vec![Vec::new(); n],
488            rev: vec![Vec::new(); n],
489            edge_count: 0,
490        }
491    }
492    #[allow(dead_code)]
493    pub fn add_edge(&mut self, from: usize, to: usize) {
494        if from < self.n && to < self.n {
495            if !self.adj[from].contains(&to) {
496                self.adj[from].push(to);
497                self.rev[to].push(from);
498                self.edge_count += 1;
499            }
500        }
501    }
502    #[allow(dead_code)]
503    pub fn succs(&self, n: usize) -> &[usize] {
504        self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
505    }
506    #[allow(dead_code)]
507    pub fn preds(&self, n: usize) -> &[usize] {
508        self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
509    }
510    #[allow(dead_code)]
511    pub fn topo_sort(&self) -> Option<Vec<usize>> {
512        let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
513        let mut q: std::collections::VecDeque<usize> =
514            (0..self.n).filter(|&i| deg[i] == 0).collect();
515        let mut out = Vec::with_capacity(self.n);
516        while let Some(u) = q.pop_front() {
517            out.push(u);
518            for &v in &self.adj[u] {
519                deg[v] -= 1;
520                if deg[v] == 0 {
521                    q.push_back(v);
522                }
523            }
524        }
525        if out.len() == self.n {
526            Some(out)
527        } else {
528            None
529        }
530    }
531    #[allow(dead_code)]
532    pub fn has_cycle(&self) -> bool {
533        self.topo_sort().is_none()
534    }
535    #[allow(dead_code)]
536    pub fn reachable(&self, start: usize) -> Vec<usize> {
537        let mut vis = vec![false; self.n];
538        let mut stk = vec![start];
539        let mut out = Vec::new();
540        while let Some(u) = stk.pop() {
541            if u < self.n && !vis[u] {
542                vis[u] = true;
543                out.push(u);
544                for &v in &self.adj[u] {
545                    if !vis[v] {
546                        stk.push(v);
547                    }
548                }
549            }
550        }
551        out
552    }
553    #[allow(dead_code)]
554    pub fn scc(&self) -> Vec<Vec<usize>> {
555        let mut visited = vec![false; self.n];
556        let mut order = Vec::new();
557        for i in 0..self.n {
558            if !visited[i] {
559                let mut stk = vec![(i, 0usize)];
560                while let Some((u, idx)) = stk.last_mut() {
561                    if !visited[*u] {
562                        visited[*u] = true;
563                    }
564                    if *idx < self.adj[*u].len() {
565                        let v = self.adj[*u][*idx];
566                        *idx += 1;
567                        if !visited[v] {
568                            stk.push((v, 0));
569                        }
570                    } else {
571                        order.push(*u);
572                        stk.pop();
573                    }
574                }
575            }
576        }
577        let mut comp = vec![usize::MAX; self.n];
578        let mut components: Vec<Vec<usize>> = Vec::new();
579        for &start in order.iter().rev() {
580            if comp[start] == usize::MAX {
581                let cid = components.len();
582                let mut component = Vec::new();
583                let mut stk = vec![start];
584                while let Some(u) = stk.pop() {
585                    if comp[u] == usize::MAX {
586                        comp[u] = cid;
587                        component.push(u);
588                        for &v in &self.rev[u] {
589                            if comp[v] == usize::MAX {
590                                stk.push(v);
591                            }
592                        }
593                    }
594                }
595                components.push(component);
596            }
597        }
598        components
599    }
600    #[allow(dead_code)]
601    pub fn node_count(&self) -> usize {
602        self.n
603    }
604    #[allow(dead_code)]
605    pub fn edge_count(&self) -> usize {
606        self.edge_count
607    }
608}
609#[allow(dead_code)]
610pub struct DhallConstantFoldingHelper;
611impl DhallConstantFoldingHelper {
612    #[allow(dead_code)]
613    pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
614        a.checked_add(b)
615    }
616    #[allow(dead_code)]
617    pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
618        a.checked_sub(b)
619    }
620    #[allow(dead_code)]
621    pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
622        a.checked_mul(b)
623    }
624    #[allow(dead_code)]
625    pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
626        if b == 0 {
627            None
628        } else {
629            a.checked_div(b)
630        }
631    }
632    #[allow(dead_code)]
633    pub fn fold_add_f64(a: f64, b: f64) -> f64 {
634        a + b
635    }
636    #[allow(dead_code)]
637    pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
638        a * b
639    }
640    #[allow(dead_code)]
641    pub fn fold_neg_i64(a: i64) -> Option<i64> {
642        a.checked_neg()
643    }
644    #[allow(dead_code)]
645    pub fn fold_not_bool(a: bool) -> bool {
646        !a
647    }
648    #[allow(dead_code)]
649    pub fn fold_and_bool(a: bool, b: bool) -> bool {
650        a && b
651    }
652    #[allow(dead_code)]
653    pub fn fold_or_bool(a: bool, b: bool) -> bool {
654        a || b
655    }
656    #[allow(dead_code)]
657    pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
658        a.checked_shl(b)
659    }
660    #[allow(dead_code)]
661    pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
662        a.checked_shr(b)
663    }
664    #[allow(dead_code)]
665    pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
666        if b == 0 {
667            None
668        } else {
669            Some(a % b)
670        }
671    }
672    #[allow(dead_code)]
673    pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
674        a & b
675    }
676    #[allow(dead_code)]
677    pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
678        a | b
679    }
680    #[allow(dead_code)]
681    pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
682        a ^ b
683    }
684    #[allow(dead_code)]
685    pub fn fold_bitnot_i64(a: i64) -> i64 {
686        !a
687    }
688}
689#[allow(dead_code)]
690#[derive(Debug, Clone)]
691pub struct DhallCacheEntry {
692    pub key: String,
693    pub data: Vec<u8>,
694    pub timestamp: u64,
695    pub valid: bool,
696}
697/// A top-level Dhall declaration (inside a `let` chain or file).
698#[derive(Debug, Clone, PartialEq)]
699pub struct DhallDecl {
700    /// Bound name
701    pub name: String,
702    /// Optional type annotation
703    pub ty: Option<DhallType>,
704    /// Value expression
705    pub value: DhallExpr,
706}
707impl DhallDecl {
708    /// Create a declaration without a type annotation.
709    pub fn new(name: impl Into<String>, value: DhallExpr) -> Self {
710        DhallDecl {
711            name: name.into(),
712            ty: None,
713            value,
714        }
715    }
716    /// Create a declaration with a type annotation.
717    pub fn typed(name: impl Into<String>, ty: DhallType, value: DhallExpr) -> Self {
718        DhallDecl {
719            name: name.into(),
720            ty: Some(ty),
721            value,
722        }
723    }
724    pub(super) fn emit(&self, indent: usize) -> String {
725        match &self.ty {
726            None => format!("let {} = {}", self.name, self.value.emit(indent)),
727            Some(t) => format!("let {} : {} = {}", self.name, t, self.value.emit(indent)),
728        }
729    }
730}
731#[allow(dead_code)]
732pub struct DhallPassRegistry {
733    pub(super) configs: Vec<DhallPassConfig>,
734    pub(super) stats: std::collections::HashMap<String, DhallPassStats>,
735}
736impl DhallPassRegistry {
737    #[allow(dead_code)]
738    pub fn new() -> Self {
739        DhallPassRegistry {
740            configs: Vec::new(),
741            stats: std::collections::HashMap::new(),
742        }
743    }
744    #[allow(dead_code)]
745    pub fn register(&mut self, config: DhallPassConfig) {
746        self.stats
747            .insert(config.pass_name.clone(), DhallPassStats::new());
748        self.configs.push(config);
749    }
750    #[allow(dead_code)]
751    pub fn enabled_passes(&self) -> Vec<&DhallPassConfig> {
752        self.configs.iter().filter(|c| c.enabled).collect()
753    }
754    #[allow(dead_code)]
755    pub fn get_stats(&self, name: &str) -> Option<&DhallPassStats> {
756        self.stats.get(name)
757    }
758    #[allow(dead_code)]
759    pub fn total_passes(&self) -> usize {
760        self.configs.len()
761    }
762    #[allow(dead_code)]
763    pub fn enabled_count(&self) -> usize {
764        self.enabled_passes().len()
765    }
766    #[allow(dead_code)]
767    pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
768        if let Some(stats) = self.stats.get_mut(name) {
769            stats.record_run(changes, time_ms, iter);
770        }
771    }
772}
773/// Dominator tree for DhallX2.
774#[allow(dead_code)]
775#[derive(Debug, Clone)]
776pub struct DhallX2DomTree {
777    pub(super) idom: Vec<Option<usize>>,
778    pub(super) children: Vec<Vec<usize>>,
779    pub(super) depth: Vec<usize>,
780}
781impl DhallX2DomTree {
782    #[allow(dead_code)]
783    pub fn new(n: usize) -> Self {
784        Self {
785            idom: vec![None; n],
786            children: vec![Vec::new(); n],
787            depth: vec![0; n],
788        }
789    }
790    #[allow(dead_code)]
791    pub fn set_idom(&mut self, node: usize, dom: usize) {
792        if node < self.idom.len() {
793            self.idom[node] = Some(dom);
794            if dom < self.children.len() {
795                self.children[dom].push(node);
796            }
797            self.depth[node] = if dom < self.depth.len() {
798                self.depth[dom] + 1
799            } else {
800                1
801            };
802        }
803    }
804    #[allow(dead_code)]
805    pub fn dominates(&self, a: usize, mut b: usize) -> bool {
806        if a == b {
807            return true;
808        }
809        let n = self.idom.len();
810        for _ in 0..n {
811            match self.idom.get(b).copied().flatten() {
812                None => return false,
813                Some(p) if p == a => return true,
814                Some(p) if p == b => return false,
815                Some(p) => b = p,
816            }
817        }
818        false
819    }
820    #[allow(dead_code)]
821    pub fn children_of(&self, n: usize) -> &[usize] {
822        self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
823    }
824    #[allow(dead_code)]
825    pub fn depth_of(&self, n: usize) -> usize {
826        self.depth.get(n).copied().unwrap_or(0)
827    }
828    #[allow(dead_code)]
829    pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
830        let n = self.idom.len();
831        for _ in 0..(2 * n) {
832            if a == b {
833                return a;
834            }
835            if self.depth_of(a) > self.depth_of(b) {
836                a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
837            } else {
838                b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
839            }
840        }
841        0
842    }
843}
844/// Worklist for DhallX2.
845#[allow(dead_code)]
846#[derive(Debug, Clone)]
847pub struct DhallX2Worklist {
848    pub(super) items: std::collections::VecDeque<usize>,
849    pub(super) present: Vec<bool>,
850}
851impl DhallX2Worklist {
852    #[allow(dead_code)]
853    pub fn new(capacity: usize) -> Self {
854        Self {
855            items: std::collections::VecDeque::new(),
856            present: vec![false; capacity],
857        }
858    }
859    #[allow(dead_code)]
860    pub fn push(&mut self, id: usize) {
861        if id < self.present.len() && !self.present[id] {
862            self.present[id] = true;
863            self.items.push_back(id);
864        }
865    }
866    #[allow(dead_code)]
867    pub fn push_front(&mut self, id: usize) {
868        if id < self.present.len() && !self.present[id] {
869            self.present[id] = true;
870            self.items.push_front(id);
871        }
872    }
873    #[allow(dead_code)]
874    pub fn pop(&mut self) -> Option<usize> {
875        let id = self.items.pop_front()?;
876        if id < self.present.len() {
877            self.present[id] = false;
878        }
879        Some(id)
880    }
881    #[allow(dead_code)]
882    pub fn is_empty(&self) -> bool {
883        self.items.is_empty()
884    }
885    #[allow(dead_code)]
886    pub fn len(&self) -> usize {
887        self.items.len()
888    }
889    #[allow(dead_code)]
890    pub fn contains(&self, id: usize) -> bool {
891        id < self.present.len() && self.present[id]
892    }
893    #[allow(dead_code)]
894    pub fn drain_all(&mut self) -> Vec<usize> {
895        let v: Vec<usize> = self.items.drain(..).collect();
896        for &id in &v {
897            if id < self.present.len() {
898                self.present[id] = false;
899            }
900        }
901        v
902    }
903}
904#[allow(dead_code)]
905#[derive(Debug, Clone)]
906pub struct DhallAnalysisCache {
907    pub(super) entries: std::collections::HashMap<String, DhallCacheEntry>,
908    pub(super) max_size: usize,
909    pub(super) hits: u64,
910    pub(super) misses: u64,
911}
912impl DhallAnalysisCache {
913    #[allow(dead_code)]
914    pub fn new(max_size: usize) -> Self {
915        DhallAnalysisCache {
916            entries: std::collections::HashMap::new(),
917            max_size,
918            hits: 0,
919            misses: 0,
920        }
921    }
922    #[allow(dead_code)]
923    pub fn get(&mut self, key: &str) -> Option<&DhallCacheEntry> {
924        if self.entries.contains_key(key) {
925            self.hits += 1;
926            self.entries.get(key)
927        } else {
928            self.misses += 1;
929            None
930        }
931    }
932    #[allow(dead_code)]
933    pub fn insert(&mut self, key: String, data: Vec<u8>) {
934        if self.entries.len() >= self.max_size {
935            if let Some(oldest) = self.entries.keys().next().cloned() {
936                self.entries.remove(&oldest);
937            }
938        }
939        self.entries.insert(
940            key.clone(),
941            DhallCacheEntry {
942                key,
943                data,
944                timestamp: 0,
945                valid: true,
946            },
947        );
948    }
949    #[allow(dead_code)]
950    pub fn invalidate(&mut self, key: &str) {
951        if let Some(entry) = self.entries.get_mut(key) {
952            entry.valid = false;
953        }
954    }
955    #[allow(dead_code)]
956    pub fn clear(&mut self) {
957        self.entries.clear();
958    }
959    #[allow(dead_code)]
960    pub fn hit_rate(&self) -> f64 {
961        let total = self.hits + self.misses;
962        if total == 0 {
963            return 0.0;
964        }
965        self.hits as f64 / total as f64
966    }
967    #[allow(dead_code)]
968    pub fn size(&self) -> usize {
969        self.entries.len()
970    }
971}
972/// Liveness analysis for DhallX2.
973#[allow(dead_code)]
974#[derive(Debug, Clone, Default)]
975pub struct DhallX2Liveness {
976    pub live_in: Vec<Vec<usize>>,
977    pub live_out: Vec<Vec<usize>>,
978    pub defs: Vec<Vec<usize>>,
979    pub uses: Vec<Vec<usize>>,
980}
981impl DhallX2Liveness {
982    #[allow(dead_code)]
983    pub fn new(n: usize) -> Self {
984        Self {
985            live_in: vec![Vec::new(); n],
986            live_out: vec![Vec::new(); n],
987            defs: vec![Vec::new(); n],
988            uses: vec![Vec::new(); n],
989        }
990    }
991    #[allow(dead_code)]
992    pub fn live_in(&self, b: usize, v: usize) -> bool {
993        self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
994    }
995    #[allow(dead_code)]
996    pub fn live_out(&self, b: usize, v: usize) -> bool {
997        self.live_out
998            .get(b)
999            .map(|s| s.contains(&v))
1000            .unwrap_or(false)
1001    }
1002    #[allow(dead_code)]
1003    pub fn add_def(&mut self, b: usize, v: usize) {
1004        if let Some(s) = self.defs.get_mut(b) {
1005            if !s.contains(&v) {
1006                s.push(v);
1007            }
1008        }
1009    }
1010    #[allow(dead_code)]
1011    pub fn add_use(&mut self, b: usize, v: usize) {
1012        if let Some(s) = self.uses.get_mut(b) {
1013            if !s.contains(&v) {
1014                s.push(v);
1015            }
1016        }
1017    }
1018    #[allow(dead_code)]
1019    pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
1020        self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1021    }
1022    #[allow(dead_code)]
1023    pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
1024        self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1025    }
1026}
1027#[allow(dead_code)]
1028#[derive(Debug, Clone)]
1029pub struct DhallWorklist {
1030    pub(super) items: std::collections::VecDeque<u32>,
1031    pub(super) in_worklist: std::collections::HashSet<u32>,
1032}
1033impl DhallWorklist {
1034    #[allow(dead_code)]
1035    pub fn new() -> Self {
1036        DhallWorklist {
1037            items: std::collections::VecDeque::new(),
1038            in_worklist: std::collections::HashSet::new(),
1039        }
1040    }
1041    #[allow(dead_code)]
1042    pub fn push(&mut self, item: u32) -> bool {
1043        if self.in_worklist.insert(item) {
1044            self.items.push_back(item);
1045            true
1046        } else {
1047            false
1048        }
1049    }
1050    #[allow(dead_code)]
1051    pub fn pop(&mut self) -> Option<u32> {
1052        let item = self.items.pop_front()?;
1053        self.in_worklist.remove(&item);
1054        Some(item)
1055    }
1056    #[allow(dead_code)]
1057    pub fn is_empty(&self) -> bool {
1058        self.items.is_empty()
1059    }
1060    #[allow(dead_code)]
1061    pub fn len(&self) -> usize {
1062        self.items.len()
1063    }
1064    #[allow(dead_code)]
1065    pub fn contains(&self, item: u32) -> bool {
1066        self.in_worklist.contains(&item)
1067    }
1068}
1069/// Statistics for DhallExt passes.
1070#[allow(dead_code)]
1071#[derive(Debug, Clone, Default)]
1072pub struct DhallExtPassStats {
1073    pub iterations: usize,
1074    pub changed: bool,
1075    pub nodes_visited: usize,
1076    pub nodes_modified: usize,
1077    pub time_ms: u64,
1078    pub memory_bytes: usize,
1079    pub errors: usize,
1080}
1081impl DhallExtPassStats {
1082    #[allow(dead_code)]
1083    pub fn new() -> Self {
1084        Self::default()
1085    }
1086    #[allow(dead_code)]
1087    pub fn visit(&mut self) {
1088        self.nodes_visited += 1;
1089    }
1090    #[allow(dead_code)]
1091    pub fn modify(&mut self) {
1092        self.nodes_modified += 1;
1093        self.changed = true;
1094    }
1095    #[allow(dead_code)]
1096    pub fn iterate(&mut self) {
1097        self.iterations += 1;
1098    }
1099    #[allow(dead_code)]
1100    pub fn error(&mut self) {
1101        self.errors += 1;
1102    }
1103    #[allow(dead_code)]
1104    pub fn efficiency(&self) -> f64 {
1105        if self.nodes_visited == 0 {
1106            0.0
1107        } else {
1108            self.nodes_modified as f64 / self.nodes_visited as f64
1109        }
1110    }
1111    #[allow(dead_code)]
1112    pub fn merge(&mut self, o: &DhallExtPassStats) {
1113        self.iterations += o.iterations;
1114        self.changed |= o.changed;
1115        self.nodes_visited += o.nodes_visited;
1116        self.nodes_modified += o.nodes_modified;
1117        self.time_ms += o.time_ms;
1118        self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
1119        self.errors += o.errors;
1120    }
1121}
1122#[allow(dead_code)]
1123#[derive(Debug, Clone, Default)]
1124pub struct DhallPassStats {
1125    pub total_runs: u32,
1126    pub successful_runs: u32,
1127    pub total_changes: u64,
1128    pub time_ms: u64,
1129    pub iterations_used: u32,
1130}
1131impl DhallPassStats {
1132    #[allow(dead_code)]
1133    pub fn new() -> Self {
1134        Self::default()
1135    }
1136    #[allow(dead_code)]
1137    pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
1138        self.total_runs += 1;
1139        self.successful_runs += 1;
1140        self.total_changes += changes;
1141        self.time_ms += time_ms;
1142        self.iterations_used = iterations;
1143    }
1144    #[allow(dead_code)]
1145    pub fn average_changes_per_run(&self) -> f64 {
1146        if self.total_runs == 0 {
1147            return 0.0;
1148        }
1149        self.total_changes as f64 / self.total_runs as f64
1150    }
1151    #[allow(dead_code)]
1152    pub fn success_rate(&self) -> f64 {
1153        if self.total_runs == 0 {
1154            return 0.0;
1155        }
1156        self.successful_runs as f64 / self.total_runs as f64
1157    }
1158    #[allow(dead_code)]
1159    pub fn format_summary(&self) -> String {
1160        format!(
1161            "Runs: {}/{}, Changes: {}, Time: {}ms",
1162            self.successful_runs, self.total_runs, self.total_changes, self.time_ms
1163        )
1164    }
1165}
1166#[allow(dead_code)]
1167#[derive(Debug, Clone)]
1168pub struct DhallLivenessInfo {
1169    pub live_in: Vec<std::collections::HashSet<u32>>,
1170    pub live_out: Vec<std::collections::HashSet<u32>>,
1171    pub defs: Vec<std::collections::HashSet<u32>>,
1172    pub uses: Vec<std::collections::HashSet<u32>>,
1173}
1174impl DhallLivenessInfo {
1175    #[allow(dead_code)]
1176    pub fn new(block_count: usize) -> Self {
1177        DhallLivenessInfo {
1178            live_in: vec![std::collections::HashSet::new(); block_count],
1179            live_out: vec![std::collections::HashSet::new(); block_count],
1180            defs: vec![std::collections::HashSet::new(); block_count],
1181            uses: vec![std::collections::HashSet::new(); block_count],
1182        }
1183    }
1184    #[allow(dead_code)]
1185    pub fn add_def(&mut self, block: usize, var: u32) {
1186        if block < self.defs.len() {
1187            self.defs[block].insert(var);
1188        }
1189    }
1190    #[allow(dead_code)]
1191    pub fn add_use(&mut self, block: usize, var: u32) {
1192        if block < self.uses.len() {
1193            self.uses[block].insert(var);
1194        }
1195    }
1196    #[allow(dead_code)]
1197    pub fn is_live_in(&self, block: usize, var: u32) -> bool {
1198        self.live_in
1199            .get(block)
1200            .map(|s| s.contains(&var))
1201            .unwrap_or(false)
1202    }
1203    #[allow(dead_code)]
1204    pub fn is_live_out(&self, block: usize, var: u32) -> bool {
1205        self.live_out
1206            .get(block)
1207            .map(|s| s.contains(&var))
1208            .unwrap_or(false)
1209    }
1210}
1211/// Dependency graph for DhallExt.
1212#[allow(dead_code)]
1213#[derive(Debug, Clone)]
1214pub struct DhallExtDepGraph {
1215    pub(super) n: usize,
1216    pub(super) adj: Vec<Vec<usize>>,
1217    pub(super) rev: Vec<Vec<usize>>,
1218    pub(super) edge_count: usize,
1219}
1220impl DhallExtDepGraph {
1221    #[allow(dead_code)]
1222    pub fn new(n: usize) -> Self {
1223        Self {
1224            n,
1225            adj: vec![Vec::new(); n],
1226            rev: vec![Vec::new(); n],
1227            edge_count: 0,
1228        }
1229    }
1230    #[allow(dead_code)]
1231    pub fn add_edge(&mut self, from: usize, to: usize) {
1232        if from < self.n && to < self.n {
1233            if !self.adj[from].contains(&to) {
1234                self.adj[from].push(to);
1235                self.rev[to].push(from);
1236                self.edge_count += 1;
1237            }
1238        }
1239    }
1240    #[allow(dead_code)]
1241    pub fn succs(&self, n: usize) -> &[usize] {
1242        self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1243    }
1244    #[allow(dead_code)]
1245    pub fn preds(&self, n: usize) -> &[usize] {
1246        self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1247    }
1248    #[allow(dead_code)]
1249    pub fn topo_sort(&self) -> Option<Vec<usize>> {
1250        let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
1251        let mut q: std::collections::VecDeque<usize> =
1252            (0..self.n).filter(|&i| deg[i] == 0).collect();
1253        let mut out = Vec::with_capacity(self.n);
1254        while let Some(u) = q.pop_front() {
1255            out.push(u);
1256            for &v in &self.adj[u] {
1257                deg[v] -= 1;
1258                if deg[v] == 0 {
1259                    q.push_back(v);
1260                }
1261            }
1262        }
1263        if out.len() == self.n {
1264            Some(out)
1265        } else {
1266            None
1267        }
1268    }
1269    #[allow(dead_code)]
1270    pub fn has_cycle(&self) -> bool {
1271        self.topo_sort().is_none()
1272    }
1273    #[allow(dead_code)]
1274    pub fn reachable(&self, start: usize) -> Vec<usize> {
1275        let mut vis = vec![false; self.n];
1276        let mut stk = vec![start];
1277        let mut out = Vec::new();
1278        while let Some(u) = stk.pop() {
1279            if u < self.n && !vis[u] {
1280                vis[u] = true;
1281                out.push(u);
1282                for &v in &self.adj[u] {
1283                    if !vis[v] {
1284                        stk.push(v);
1285                    }
1286                }
1287            }
1288        }
1289        out
1290    }
1291    #[allow(dead_code)]
1292    pub fn scc(&self) -> Vec<Vec<usize>> {
1293        let mut visited = vec![false; self.n];
1294        let mut order = Vec::new();
1295        for i in 0..self.n {
1296            if !visited[i] {
1297                let mut stk = vec![(i, 0usize)];
1298                while let Some((u, idx)) = stk.last_mut() {
1299                    if !visited[*u] {
1300                        visited[*u] = true;
1301                    }
1302                    if *idx < self.adj[*u].len() {
1303                        let v = self.adj[*u][*idx];
1304                        *idx += 1;
1305                        if !visited[v] {
1306                            stk.push((v, 0));
1307                        }
1308                    } else {
1309                        order.push(*u);
1310                        stk.pop();
1311                    }
1312                }
1313            }
1314        }
1315        let mut comp = vec![usize::MAX; self.n];
1316        let mut components: Vec<Vec<usize>> = Vec::new();
1317        for &start in order.iter().rev() {
1318            if comp[start] == usize::MAX {
1319                let cid = components.len();
1320                let mut component = Vec::new();
1321                let mut stk = vec![start];
1322                while let Some(u) = stk.pop() {
1323                    if comp[u] == usize::MAX {
1324                        comp[u] = cid;
1325                        component.push(u);
1326                        for &v in &self.rev[u] {
1327                            if comp[v] == usize::MAX {
1328                                stk.push(v);
1329                            }
1330                        }
1331                    }
1332                }
1333                components.push(component);
1334            }
1335        }
1336        components
1337    }
1338    #[allow(dead_code)]
1339    pub fn node_count(&self) -> usize {
1340        self.n
1341    }
1342    #[allow(dead_code)]
1343    pub fn edge_count(&self) -> usize {
1344        self.edge_count
1345    }
1346}
1347/// A complete Dhall file.
1348///
1349/// Dhall files are structured as:
1350/// ```dhall
1351/// let import1 = ./lib.dhall
1352/// let decl1 : T = expr1
1353/// let decl2 = expr2
1354/// in  finalExpression
1355/// ```
1356#[derive(Debug, Clone, PartialEq)]
1357pub struct DhallFile {
1358    /// Import declarations at the top level
1359    pub imports: Vec<(String, DhallImport)>,
1360    /// Local declarations (let-bindings)
1361    pub declarations: Vec<DhallDecl>,
1362    /// The final expression the file evaluates to
1363    pub expression: DhallExpr,
1364}
1365impl DhallFile {
1366    /// Create a new Dhall file with only a final expression.
1367    pub fn new(expression: DhallExpr) -> Self {
1368        DhallFile {
1369            imports: vec![],
1370            declarations: vec![],
1371            expression,
1372        }
1373    }
1374    /// Add an import at the top.
1375    pub fn import(mut self, name: impl Into<String>, imp: DhallImport) -> Self {
1376        self.imports.push((name.into(), imp));
1377        self
1378    }
1379    /// Add a local declaration.
1380    pub fn declare(mut self, decl: DhallDecl) -> Self {
1381        self.declarations.push(decl);
1382        self
1383    }
1384    /// Emit the complete `.dhall` file contents.
1385    pub fn emit(&self) -> String {
1386        let mut out = String::from("-- Dhall configuration generated by OxiLean\n");
1387        for (name, imp) in &self.imports {
1388            out.push_str(&format!("let {} = {}\n", name, imp));
1389        }
1390        for decl in &self.declarations {
1391            out.push_str(&decl.emit(0));
1392            out.push('\n');
1393        }
1394        if !self.imports.is_empty() || !self.declarations.is_empty() {
1395            out.push_str("in  ");
1396        }
1397        out.push_str(&self.expression.emit(0));
1398        out.push('\n');
1399        out
1400    }
1401}
1402/// Constant folding helper for DhallExt.
1403#[allow(dead_code)]
1404#[derive(Debug, Clone, Default)]
1405pub struct DhallExtConstFolder {
1406    pub(super) folds: usize,
1407    pub(super) failures: usize,
1408    pub(super) enabled: bool,
1409}
1410impl DhallExtConstFolder {
1411    #[allow(dead_code)]
1412    pub fn new() -> Self {
1413        Self {
1414            folds: 0,
1415            failures: 0,
1416            enabled: true,
1417        }
1418    }
1419    #[allow(dead_code)]
1420    pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1421        self.folds += 1;
1422        a.checked_add(b)
1423    }
1424    #[allow(dead_code)]
1425    pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1426        self.folds += 1;
1427        a.checked_sub(b)
1428    }
1429    #[allow(dead_code)]
1430    pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1431        self.folds += 1;
1432        a.checked_mul(b)
1433    }
1434    #[allow(dead_code)]
1435    pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1436        if b == 0 {
1437            self.failures += 1;
1438            None
1439        } else {
1440            self.folds += 1;
1441            a.checked_div(b)
1442        }
1443    }
1444    #[allow(dead_code)]
1445    pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1446        if b == 0 {
1447            self.failures += 1;
1448            None
1449        } else {
1450            self.folds += 1;
1451            a.checked_rem(b)
1452        }
1453    }
1454    #[allow(dead_code)]
1455    pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
1456        self.folds += 1;
1457        a.checked_neg()
1458    }
1459    #[allow(dead_code)]
1460    pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
1461        if s >= 64 {
1462            self.failures += 1;
1463            None
1464        } else {
1465            self.folds += 1;
1466            a.checked_shl(s)
1467        }
1468    }
1469    #[allow(dead_code)]
1470    pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
1471        if s >= 64 {
1472            self.failures += 1;
1473            None
1474        } else {
1475            self.folds += 1;
1476            a.checked_shr(s)
1477        }
1478    }
1479    #[allow(dead_code)]
1480    pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
1481        self.folds += 1;
1482        a & b
1483    }
1484    #[allow(dead_code)]
1485    pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
1486        self.folds += 1;
1487        a | b
1488    }
1489    #[allow(dead_code)]
1490    pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
1491        self.folds += 1;
1492        a ^ b
1493    }
1494    #[allow(dead_code)]
1495    pub fn not_i64(&mut self, a: i64) -> i64 {
1496        self.folds += 1;
1497        !a
1498    }
1499    #[allow(dead_code)]
1500    pub fn fold_count(&self) -> usize {
1501        self.folds
1502    }
1503    #[allow(dead_code)]
1504    pub fn failure_count(&self) -> usize {
1505        self.failures
1506    }
1507    #[allow(dead_code)]
1508    pub fn enable(&mut self) {
1509        self.enabled = true;
1510    }
1511    #[allow(dead_code)]
1512    pub fn disable(&mut self) {
1513        self.enabled = false;
1514    }
1515    #[allow(dead_code)]
1516    pub fn is_enabled(&self) -> bool {
1517        self.enabled
1518    }
1519}
1520/// Dominator tree for DhallExt.
1521#[allow(dead_code)]
1522#[derive(Debug, Clone)]
1523pub struct DhallExtDomTree {
1524    pub(super) idom: Vec<Option<usize>>,
1525    pub(super) children: Vec<Vec<usize>>,
1526    pub(super) depth: Vec<usize>,
1527}
1528impl DhallExtDomTree {
1529    #[allow(dead_code)]
1530    pub fn new(n: usize) -> Self {
1531        Self {
1532            idom: vec![None; n],
1533            children: vec![Vec::new(); n],
1534            depth: vec![0; n],
1535        }
1536    }
1537    #[allow(dead_code)]
1538    pub fn set_idom(&mut self, node: usize, dom: usize) {
1539        if node < self.idom.len() {
1540            self.idom[node] = Some(dom);
1541            if dom < self.children.len() {
1542                self.children[dom].push(node);
1543            }
1544            self.depth[node] = if dom < self.depth.len() {
1545                self.depth[dom] + 1
1546            } else {
1547                1
1548            };
1549        }
1550    }
1551    #[allow(dead_code)]
1552    pub fn dominates(&self, a: usize, mut b: usize) -> bool {
1553        if a == b {
1554            return true;
1555        }
1556        let n = self.idom.len();
1557        for _ in 0..n {
1558            match self.idom.get(b).copied().flatten() {
1559                None => return false,
1560                Some(p) if p == a => return true,
1561                Some(p) if p == b => return false,
1562                Some(p) => b = p,
1563            }
1564        }
1565        false
1566    }
1567    #[allow(dead_code)]
1568    pub fn children_of(&self, n: usize) -> &[usize] {
1569        self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1570    }
1571    #[allow(dead_code)]
1572    pub fn depth_of(&self, n: usize) -> usize {
1573        self.depth.get(n).copied().unwrap_or(0)
1574    }
1575    #[allow(dead_code)]
1576    pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
1577        let n = self.idom.len();
1578        for _ in 0..(2 * n) {
1579            if a == b {
1580                return a;
1581            }
1582            if self.depth_of(a) > self.depth_of(b) {
1583                a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
1584            } else {
1585                b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
1586            }
1587        }
1588        0
1589    }
1590}
1591/// A Dhall import statement.
1592#[derive(Debug, Clone, PartialEq)]
1593pub enum DhallImport {
1594    /// Local file import: `./foo.dhall`
1595    Local(String),
1596    /// Remote URL import: `https://example.com/foo.dhall`
1597    Remote(String),
1598    /// Environment variable import: `env:HOME`
1599    Env(String),
1600    /// Missing import (always fails): `missing`
1601    Missing,
1602    /// Import with hash: `./foo.dhall sha256:abc123`
1603    Hashed(Box<DhallImport>, String),
1604    /// Import with fallback: `./foo.dhall ? ./bar.dhall`
1605    Fallback(Box<DhallImport>, Box<DhallImport>),
1606}
1607#[allow(dead_code)]
1608#[derive(Debug, Clone)]
1609pub struct DhallDepGraph {
1610    pub(super) nodes: Vec<u32>,
1611    pub(super) edges: Vec<(u32, u32)>,
1612}
1613impl DhallDepGraph {
1614    #[allow(dead_code)]
1615    pub fn new() -> Self {
1616        DhallDepGraph {
1617            nodes: Vec::new(),
1618            edges: Vec::new(),
1619        }
1620    }
1621    #[allow(dead_code)]
1622    pub fn add_node(&mut self, id: u32) {
1623        if !self.nodes.contains(&id) {
1624            self.nodes.push(id);
1625        }
1626    }
1627    #[allow(dead_code)]
1628    pub fn add_dep(&mut self, dep: u32, dependent: u32) {
1629        self.add_node(dep);
1630        self.add_node(dependent);
1631        self.edges.push((dep, dependent));
1632    }
1633    #[allow(dead_code)]
1634    pub fn dependents_of(&self, node: u32) -> Vec<u32> {
1635        self.edges
1636            .iter()
1637            .filter(|(d, _)| *d == node)
1638            .map(|(_, dep)| *dep)
1639            .collect()
1640    }
1641    #[allow(dead_code)]
1642    pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
1643        self.edges
1644            .iter()
1645            .filter(|(_, dep)| *dep == node)
1646            .map(|(d, _)| *d)
1647            .collect()
1648    }
1649    #[allow(dead_code)]
1650    pub fn topological_sort(&self) -> Vec<u32> {
1651        let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
1652        for &n in &self.nodes {
1653            in_degree.insert(n, 0);
1654        }
1655        for (_, dep) in &self.edges {
1656            *in_degree.entry(*dep).or_insert(0) += 1;
1657        }
1658        let mut queue: std::collections::VecDeque<u32> = self
1659            .nodes
1660            .iter()
1661            .filter(|&&n| in_degree[&n] == 0)
1662            .copied()
1663            .collect();
1664        let mut result = Vec::new();
1665        while let Some(node) = queue.pop_front() {
1666            result.push(node);
1667            for dep in self.dependents_of(node) {
1668                let cnt = in_degree.entry(dep).or_insert(0);
1669                *cnt = cnt.saturating_sub(1);
1670                if *cnt == 0 {
1671                    queue.push_back(dep);
1672                }
1673            }
1674        }
1675        result
1676    }
1677    #[allow(dead_code)]
1678    pub fn has_cycle(&self) -> bool {
1679        self.topological_sort().len() < self.nodes.len()
1680    }
1681}
1682/// Worklist for DhallExt.
1683#[allow(dead_code)]
1684#[derive(Debug, Clone)]
1685pub struct DhallExtWorklist {
1686    pub(super) items: std::collections::VecDeque<usize>,
1687    pub(super) present: Vec<bool>,
1688}
1689impl DhallExtWorklist {
1690    #[allow(dead_code)]
1691    pub fn new(capacity: usize) -> Self {
1692        Self {
1693            items: std::collections::VecDeque::new(),
1694            present: vec![false; capacity],
1695        }
1696    }
1697    #[allow(dead_code)]
1698    pub fn push(&mut self, id: usize) {
1699        if id < self.present.len() && !self.present[id] {
1700            self.present[id] = true;
1701            self.items.push_back(id);
1702        }
1703    }
1704    #[allow(dead_code)]
1705    pub fn push_front(&mut self, id: usize) {
1706        if id < self.present.len() && !self.present[id] {
1707            self.present[id] = true;
1708            self.items.push_front(id);
1709        }
1710    }
1711    #[allow(dead_code)]
1712    pub fn pop(&mut self) -> Option<usize> {
1713        let id = self.items.pop_front()?;
1714        if id < self.present.len() {
1715            self.present[id] = false;
1716        }
1717        Some(id)
1718    }
1719    #[allow(dead_code)]
1720    pub fn is_empty(&self) -> bool {
1721        self.items.is_empty()
1722    }
1723    #[allow(dead_code)]
1724    pub fn len(&self) -> usize {
1725        self.items.len()
1726    }
1727    #[allow(dead_code)]
1728    pub fn contains(&self, id: usize) -> bool {
1729        id < self.present.len() && self.present[id]
1730    }
1731    #[allow(dead_code)]
1732    pub fn drain_all(&mut self) -> Vec<usize> {
1733        let v: Vec<usize> = self.items.drain(..).collect();
1734        for &id in &v {
1735            if id < self.present.len() {
1736                self.present[id] = false;
1737            }
1738        }
1739        v
1740    }
1741}
1742/// Pass execution phase for DhallExt.
1743#[allow(dead_code)]
1744#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1745pub enum DhallExtPassPhase {
1746    Early,
1747    Middle,
1748    Late,
1749    Finalize,
1750}
1751impl DhallExtPassPhase {
1752    #[allow(dead_code)]
1753    pub fn is_early(&self) -> bool {
1754        matches!(self, Self::Early)
1755    }
1756    #[allow(dead_code)]
1757    pub fn is_middle(&self) -> bool {
1758        matches!(self, Self::Middle)
1759    }
1760    #[allow(dead_code)]
1761    pub fn is_late(&self) -> bool {
1762        matches!(self, Self::Late)
1763    }
1764    #[allow(dead_code)]
1765    pub fn is_finalize(&self) -> bool {
1766        matches!(self, Self::Finalize)
1767    }
1768    #[allow(dead_code)]
1769    pub fn order(&self) -> u32 {
1770        match self {
1771            Self::Early => 0,
1772            Self::Middle => 1,
1773            Self::Late => 2,
1774            Self::Finalize => 3,
1775        }
1776    }
1777    #[allow(dead_code)]
1778    pub fn from_order(n: u32) -> Option<Self> {
1779        match n {
1780            0 => Some(Self::Early),
1781            1 => Some(Self::Middle),
1782            2 => Some(Self::Late),
1783            3 => Some(Self::Finalize),
1784            _ => None,
1785        }
1786    }
1787}
1788/// Statistics for DhallX2 passes.
1789#[allow(dead_code)]
1790#[derive(Debug, Clone, Default)]
1791pub struct DhallX2PassStats {
1792    pub iterations: usize,
1793    pub changed: bool,
1794    pub nodes_visited: usize,
1795    pub nodes_modified: usize,
1796    pub time_ms: u64,
1797    pub memory_bytes: usize,
1798    pub errors: usize,
1799}
1800impl DhallX2PassStats {
1801    #[allow(dead_code)]
1802    pub fn new() -> Self {
1803        Self::default()
1804    }
1805    #[allow(dead_code)]
1806    pub fn visit(&mut self) {
1807        self.nodes_visited += 1;
1808    }
1809    #[allow(dead_code)]
1810    pub fn modify(&mut self) {
1811        self.nodes_modified += 1;
1812        self.changed = true;
1813    }
1814    #[allow(dead_code)]
1815    pub fn iterate(&mut self) {
1816        self.iterations += 1;
1817    }
1818    #[allow(dead_code)]
1819    pub fn error(&mut self) {
1820        self.errors += 1;
1821    }
1822    #[allow(dead_code)]
1823    pub fn efficiency(&self) -> f64 {
1824        if self.nodes_visited == 0 {
1825            0.0
1826        } else {
1827            self.nodes_modified as f64 / self.nodes_visited as f64
1828        }
1829    }
1830    #[allow(dead_code)]
1831    pub fn merge(&mut self, o: &DhallX2PassStats) {
1832        self.iterations += o.iterations;
1833        self.changed |= o.changed;
1834        self.nodes_visited += o.nodes_visited;
1835        self.nodes_modified += o.nodes_modified;
1836        self.time_ms += o.time_ms;
1837        self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
1838        self.errors += o.errors;
1839    }
1840}
1841/// A Dhall record value: `{ field1 = v1, field2 = v2 }`
1842#[derive(Debug, Clone, PartialEq)]
1843pub struct DhallRecord {
1844    /// Ordered list of (field name, value) pairs
1845    pub fields: Vec<(String, DhallExpr)>,
1846}
1847impl DhallRecord {
1848    /// Create a new empty record.
1849    pub fn new() -> Self {
1850        DhallRecord { fields: vec![] }
1851    }
1852    /// Add a field.
1853    pub fn field(mut self, name: impl Into<String>, value: DhallExpr) -> Self {
1854        self.fields.push((name.into(), value));
1855        self
1856    }
1857    pub(super) fn emit(&self, indent: usize) -> String {
1858        if self.fields.is_empty() {
1859            return "{=}".into();
1860        }
1861        let ind2 = " ".repeat(indent + 2);
1862        let ind = " ".repeat(indent);
1863        let parts: Vec<String> = self
1864            .fields
1865            .iter()
1866            .map(|(k, v)| format!("{}{} = {}", ind2, k, v.emit(indent + 2)))
1867            .collect();
1868        format!("{{\n{}\n{}}}", parts.join(",\n"), ind)
1869    }
1870}
1871#[allow(dead_code)]
1872#[derive(Debug, Clone, PartialEq)]
1873pub enum DhallPassPhase {
1874    Analysis,
1875    Transformation,
1876    Verification,
1877    Cleanup,
1878}
1879impl DhallPassPhase {
1880    #[allow(dead_code)]
1881    pub fn name(&self) -> &str {
1882        match self {
1883            DhallPassPhase::Analysis => "analysis",
1884            DhallPassPhase::Transformation => "transformation",
1885            DhallPassPhase::Verification => "verification",
1886            DhallPassPhase::Cleanup => "cleanup",
1887        }
1888    }
1889    #[allow(dead_code)]
1890    pub fn is_modifying(&self) -> bool {
1891        matches!(
1892            self,
1893            DhallPassPhase::Transformation | DhallPassPhase::Cleanup
1894        )
1895    }
1896}
1897/// Analysis cache for DhallX2.
1898#[allow(dead_code)]
1899#[derive(Debug)]
1900pub struct DhallX2Cache {
1901    pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
1902    pub(super) cap: usize,
1903    pub(super) total_hits: u64,
1904    pub(super) total_misses: u64,
1905}
1906impl DhallX2Cache {
1907    #[allow(dead_code)]
1908    pub fn new(cap: usize) -> Self {
1909        Self {
1910            entries: Vec::new(),
1911            cap,
1912            total_hits: 0,
1913            total_misses: 0,
1914        }
1915    }
1916    #[allow(dead_code)]
1917    pub fn get(&mut self, key: u64) -> Option<&[u8]> {
1918        for e in self.entries.iter_mut() {
1919            if e.0 == key && e.2 {
1920                e.3 += 1;
1921                self.total_hits += 1;
1922                return Some(&e.1);
1923            }
1924        }
1925        self.total_misses += 1;
1926        None
1927    }
1928    #[allow(dead_code)]
1929    pub fn put(&mut self, key: u64, data: Vec<u8>) {
1930        if self.entries.len() >= self.cap {
1931            self.entries.retain(|e| e.2);
1932            if self.entries.len() >= self.cap {
1933                self.entries.remove(0);
1934            }
1935        }
1936        self.entries.push((key, data, true, 0));
1937    }
1938    #[allow(dead_code)]
1939    pub fn invalidate(&mut self) {
1940        for e in self.entries.iter_mut() {
1941            e.2 = false;
1942        }
1943    }
1944    #[allow(dead_code)]
1945    pub fn hit_rate(&self) -> f64 {
1946        let t = self.total_hits + self.total_misses;
1947        if t == 0 {
1948            0.0
1949        } else {
1950            self.total_hits as f64 / t as f64
1951        }
1952    }
1953    #[allow(dead_code)]
1954    pub fn live_count(&self) -> usize {
1955        self.entries.iter().filter(|e| e.2).count()
1956    }
1957}
1958/// The Dhall code generation backend.
1959///
1960/// Converts OxiLean IR constructs into Dhall configuration language output.
1961pub struct DhallBackend {
1962    /// Whether to emit types with full annotation (default true)
1963    pub emit_annotations: bool,
1964}
1965impl DhallBackend {
1966    /// Create a new DhallBackend with default settings.
1967    pub fn new() -> Self {
1968        DhallBackend {
1969            emit_annotations: true,
1970        }
1971    }
1972    /// Emit a DhallExpr to string.
1973    pub fn emit_expr(&self, expr: &DhallExpr, indent: usize) -> String {
1974        expr.emit(indent)
1975    }
1976    /// Emit a complete DhallFile to string.
1977    pub fn emit_file(&self, file: &DhallFile) -> String {
1978        file.emit()
1979    }
1980    /// Emit a DhallRecord to string.
1981    pub fn emit_record(&self, record: &DhallRecord, indent: usize) -> String {
1982        record.emit(indent)
1983    }
1984    /// Emit a DhallFunction to string.
1985    pub fn emit_function(&self, func: &DhallFunction, indent: usize) -> String {
1986        func.emit(indent)
1987    }
1988    /// Build a Dhall record schema (record type) from a field map.
1989    pub fn make_schema(&self, fields: Vec<(&str, DhallType)>) -> DhallExpr {
1990        DhallExpr::RecordType(
1991            fields
1992                .into_iter()
1993                .map(|(k, t)| (k.to_string(), t))
1994                .collect(),
1995        )
1996    }
1997    /// Build a `List/map` application.
1998    pub fn make_list_map(
1999        &self,
2000        input_type: DhallType,
2001        output_type: DhallType,
2002        func: DhallExpr,
2003        list: DhallExpr,
2004    ) -> DhallExpr {
2005        DhallExpr::Application(
2006            Box::new(DhallExpr::Application(
2007                Box::new(DhallExpr::Application(
2008                    Box::new(DhallExpr::Application(
2009                        Box::new(DhallExpr::Var("List/map".into())),
2010                        Box::new(DhallExpr::BuiltinType(input_type)),
2011                    )),
2012                    Box::new(DhallExpr::BuiltinType(output_type)),
2013                )),
2014                Box::new(func),
2015            )),
2016            Box::new(list),
2017        )
2018    }
2019    /// Build a `Natural/fold`-style loop body.
2020    #[allow(clippy::too_many_arguments)]
2021    pub fn make_natural_fold(
2022        &self,
2023        n: DhallExpr,
2024        result_type: DhallType,
2025        succ: DhallExpr,
2026        zero: DhallExpr,
2027    ) -> DhallExpr {
2028        DhallExpr::Application(
2029            Box::new(DhallExpr::Application(
2030                Box::new(DhallExpr::Application(
2031                    Box::new(DhallExpr::Application(
2032                        Box::new(DhallExpr::Var("Natural/fold".into())),
2033                        Box::new(n),
2034                    )),
2035                    Box::new(DhallExpr::BuiltinType(result_type)),
2036                )),
2037                Box::new(succ),
2038            )),
2039            Box::new(zero),
2040        )
2041    }
2042    /// Build a configuration record for a service-like schema.
2043    #[allow(clippy::too_many_arguments)]
2044    pub fn make_service_config(
2045        &self,
2046        enable: bool,
2047        name: &str,
2048        port: u64,
2049        extra_fields: Vec<(String, DhallExpr)>,
2050    ) -> DhallExpr {
2051        let mut fields = vec![
2052            ("enable".to_string(), DhallExpr::BoolLit(enable)),
2053            ("name".to_string(), DhallExpr::TextLit(name.to_string())),
2054            ("port".to_string(), DhallExpr::NaturalLit(port)),
2055        ];
2056        fields.extend(extra_fields);
2057        DhallExpr::RecordLit(Box::new(DhallRecord { fields }))
2058    }
2059    /// Build a union type for an enumeration.
2060    pub fn make_enum(&self, variants: Vec<&str>) -> DhallType {
2061        DhallType::Union(
2062            variants
2063                .into_iter()
2064                .map(|v| (v.to_string(), None))
2065                .collect(),
2066        )
2067    }
2068    /// Build an Optional handling pattern with `merge`.
2069    pub fn make_optional_merge(
2070        &self,
2071        optional_value: DhallExpr,
2072        some_handler: DhallExpr,
2073        none_value: DhallExpr,
2074        result_type: DhallType,
2075    ) -> DhallExpr {
2076        let handler = DhallExpr::RecordLit(Box::new(DhallRecord {
2077            fields: vec![
2078                ("Some".to_string(), some_handler),
2079                ("None".to_string(), none_value),
2080            ],
2081        }));
2082        DhallExpr::Merge(
2083            Box::new(handler),
2084            Box::new(optional_value),
2085            Some(result_type),
2086        )
2087    }
2088    /// Generate a Dhall prelude-style package.dhall skeleton.
2089    pub fn make_package(&self, _module_name: &str, exports: Vec<(&str, DhallExpr)>) -> DhallFile {
2090        let record = DhallExpr::RecordLit(Box::new(DhallRecord {
2091            fields: exports
2092                .into_iter()
2093                .map(|(k, v)| (k.to_string(), v))
2094                .collect(),
2095        }));
2096        DhallFile::new(record).declare(DhallDecl::new(
2097            "version",
2098            DhallExpr::TextLit("1.0.0".into()),
2099        ))
2100    }
2101}
2102/// Pass execution phase for DhallX2.
2103#[allow(dead_code)]
2104#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2105pub enum DhallX2PassPhase {
2106    Early,
2107    Middle,
2108    Late,
2109    Finalize,
2110}
2111impl DhallX2PassPhase {
2112    #[allow(dead_code)]
2113    pub fn is_early(&self) -> bool {
2114        matches!(self, Self::Early)
2115    }
2116    #[allow(dead_code)]
2117    pub fn is_middle(&self) -> bool {
2118        matches!(self, Self::Middle)
2119    }
2120    #[allow(dead_code)]
2121    pub fn is_late(&self) -> bool {
2122        matches!(self, Self::Late)
2123    }
2124    #[allow(dead_code)]
2125    pub fn is_finalize(&self) -> bool {
2126        matches!(self, Self::Finalize)
2127    }
2128    #[allow(dead_code)]
2129    pub fn order(&self) -> u32 {
2130        match self {
2131            Self::Early => 0,
2132            Self::Middle => 1,
2133            Self::Late => 2,
2134            Self::Finalize => 3,
2135        }
2136    }
2137    #[allow(dead_code)]
2138    pub fn from_order(n: u32) -> Option<Self> {
2139        match n {
2140            0 => Some(Self::Early),
2141            1 => Some(Self::Middle),
2142            2 => Some(Self::Late),
2143            3 => Some(Self::Finalize),
2144            _ => None,
2145        }
2146    }
2147}
2148#[allow(dead_code)]
2149#[derive(Debug, Clone)]
2150pub struct DhallDominatorTree {
2151    pub idom: Vec<Option<u32>>,
2152    pub dom_children: Vec<Vec<u32>>,
2153    pub dom_depth: Vec<u32>,
2154}
2155impl DhallDominatorTree {
2156    #[allow(dead_code)]
2157    pub fn new(size: usize) -> Self {
2158        DhallDominatorTree {
2159            idom: vec![None; size],
2160            dom_children: vec![Vec::new(); size],
2161            dom_depth: vec![0; size],
2162        }
2163    }
2164    #[allow(dead_code)]
2165    pub fn set_idom(&mut self, node: usize, idom: u32) {
2166        self.idom[node] = Some(idom);
2167    }
2168    #[allow(dead_code)]
2169    pub fn dominates(&self, a: usize, b: usize) -> bool {
2170        if a == b {
2171            return true;
2172        }
2173        let mut cur = b;
2174        loop {
2175            match self.idom[cur] {
2176                Some(parent) if parent as usize == a => return true,
2177                Some(parent) if parent as usize == cur => return false,
2178                Some(parent) => cur = parent as usize,
2179                None => return false,
2180            }
2181        }
2182    }
2183    #[allow(dead_code)]
2184    pub fn depth(&self, node: usize) -> u32 {
2185        self.dom_depth.get(node).copied().unwrap_or(0)
2186    }
2187}
2188/// Analysis cache for DhallExt.
2189#[allow(dead_code)]
2190#[derive(Debug)]
2191pub struct DhallExtCache {
2192    pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
2193    pub(super) cap: usize,
2194    pub(super) total_hits: u64,
2195    pub(super) total_misses: u64,
2196}
2197impl DhallExtCache {
2198    #[allow(dead_code)]
2199    pub fn new(cap: usize) -> Self {
2200        Self {
2201            entries: Vec::new(),
2202            cap,
2203            total_hits: 0,
2204            total_misses: 0,
2205        }
2206    }
2207    #[allow(dead_code)]
2208    pub fn get(&mut self, key: u64) -> Option<&[u8]> {
2209        for e in self.entries.iter_mut() {
2210            if e.0 == key && e.2 {
2211                e.3 += 1;
2212                self.total_hits += 1;
2213                return Some(&e.1);
2214            }
2215        }
2216        self.total_misses += 1;
2217        None
2218    }
2219    #[allow(dead_code)]
2220    pub fn put(&mut self, key: u64, data: Vec<u8>) {
2221        if self.entries.len() >= self.cap {
2222            self.entries.retain(|e| e.2);
2223            if self.entries.len() >= self.cap {
2224                self.entries.remove(0);
2225            }
2226        }
2227        self.entries.push((key, data, true, 0));
2228    }
2229    #[allow(dead_code)]
2230    pub fn invalidate(&mut self) {
2231        for e in self.entries.iter_mut() {
2232            e.2 = false;
2233        }
2234    }
2235    #[allow(dead_code)]
2236    pub fn hit_rate(&self) -> f64 {
2237        let t = self.total_hits + self.total_misses;
2238        if t == 0 {
2239            0.0
2240        } else {
2241            self.total_hits as f64 / t as f64
2242        }
2243    }
2244    #[allow(dead_code)]
2245    pub fn live_count(&self) -> usize {
2246        self.entries.iter().filter(|e| e.2).count()
2247    }
2248}
2249/// Dhall type-level expressions (a subset of DhallExpr, named for clarity).
2250#[derive(Debug, Clone, PartialEq)]
2251pub enum DhallType {
2252    /// `Bool`
2253    Bool,
2254    /// `Natural`
2255    Natural,
2256    /// `Integer`
2257    Integer,
2258    /// `Double`
2259    Double,
2260    /// `Text`
2261    Text,
2262    /// `List T`
2263    List(Box<DhallType>),
2264    /// `Optional T`
2265    Optional(Box<DhallType>),
2266    /// Record type: `{ field1 : T1, field2 : T2 }`
2267    Record(Vec<(String, DhallType)>),
2268    /// Union type: `< Ctor1 : T1 | Ctor2 | Ctor3 : T3 >`
2269    Union(Vec<(String, Option<DhallType>)>),
2270    /// Function type: `T1 -> T2`
2271    Function(Box<DhallType>, Box<DhallType>),
2272    /// Dependent function type: `forall (x : T1) -> T2`
2273    Forall(String, Box<DhallType>, Box<DhallType>),
2274    /// `Type` universe
2275    Type,
2276    /// `Kind` universe (type of types)
2277    Kind,
2278    /// `Sort` universe (type of kinds)
2279    Sort,
2280    /// Named type reference: `Natural/show`, `MyRecord`
2281    Named(String),
2282}
2283/// Configuration for DhallExt passes.
2284#[allow(dead_code)]
2285#[derive(Debug, Clone)]
2286pub struct DhallExtPassConfig {
2287    pub name: String,
2288    pub phase: DhallExtPassPhase,
2289    pub enabled: bool,
2290    pub max_iterations: usize,
2291    pub debug: u32,
2292    pub timeout_ms: Option<u64>,
2293}
2294impl DhallExtPassConfig {
2295    #[allow(dead_code)]
2296    pub fn new(name: impl Into<String>) -> Self {
2297        Self {
2298            name: name.into(),
2299            phase: DhallExtPassPhase::Middle,
2300            enabled: true,
2301            max_iterations: 100,
2302            debug: 0,
2303            timeout_ms: None,
2304        }
2305    }
2306    #[allow(dead_code)]
2307    pub fn with_phase(mut self, phase: DhallExtPassPhase) -> Self {
2308        self.phase = phase;
2309        self
2310    }
2311    #[allow(dead_code)]
2312    pub fn with_max_iter(mut self, n: usize) -> Self {
2313        self.max_iterations = n;
2314        self
2315    }
2316    #[allow(dead_code)]
2317    pub fn with_debug(mut self, d: u32) -> Self {
2318        self.debug = d;
2319        self
2320    }
2321    #[allow(dead_code)]
2322    pub fn disabled(mut self) -> Self {
2323        self.enabled = false;
2324        self
2325    }
2326    #[allow(dead_code)]
2327    pub fn with_timeout(mut self, ms: u64) -> Self {
2328        self.timeout_ms = Some(ms);
2329        self
2330    }
2331    #[allow(dead_code)]
2332    pub fn is_debug_enabled(&self) -> bool {
2333        self.debug > 0
2334    }
2335}
2336/// Configuration for DhallX2 passes.
2337#[allow(dead_code)]
2338#[derive(Debug, Clone)]
2339pub struct DhallX2PassConfig {
2340    pub name: String,
2341    pub phase: DhallX2PassPhase,
2342    pub enabled: bool,
2343    pub max_iterations: usize,
2344    pub debug: u32,
2345    pub timeout_ms: Option<u64>,
2346}
2347impl DhallX2PassConfig {
2348    #[allow(dead_code)]
2349    pub fn new(name: impl Into<String>) -> Self {
2350        Self {
2351            name: name.into(),
2352            phase: DhallX2PassPhase::Middle,
2353            enabled: true,
2354            max_iterations: 100,
2355            debug: 0,
2356            timeout_ms: None,
2357        }
2358    }
2359    #[allow(dead_code)]
2360    pub fn with_phase(mut self, phase: DhallX2PassPhase) -> Self {
2361        self.phase = phase;
2362        self
2363    }
2364    #[allow(dead_code)]
2365    pub fn with_max_iter(mut self, n: usize) -> Self {
2366        self.max_iterations = n;
2367        self
2368    }
2369    #[allow(dead_code)]
2370    pub fn with_debug(mut self, d: u32) -> Self {
2371        self.debug = d;
2372        self
2373    }
2374    #[allow(dead_code)]
2375    pub fn disabled(mut self) -> Self {
2376        self.enabled = false;
2377        self
2378    }
2379    #[allow(dead_code)]
2380    pub fn with_timeout(mut self, ms: u64) -> Self {
2381        self.timeout_ms = Some(ms);
2382        self
2383    }
2384    #[allow(dead_code)]
2385    pub fn is_debug_enabled(&self) -> bool {
2386        self.debug > 0
2387    }
2388}
2389/// Liveness analysis for DhallExt.
2390#[allow(dead_code)]
2391#[derive(Debug, Clone, Default)]
2392pub struct DhallExtLiveness {
2393    pub live_in: Vec<Vec<usize>>,
2394    pub live_out: Vec<Vec<usize>>,
2395    pub defs: Vec<Vec<usize>>,
2396    pub uses: Vec<Vec<usize>>,
2397}
2398impl DhallExtLiveness {
2399    #[allow(dead_code)]
2400    pub fn new(n: usize) -> Self {
2401        Self {
2402            live_in: vec![Vec::new(); n],
2403            live_out: vec![Vec::new(); n],
2404            defs: vec![Vec::new(); n],
2405            uses: vec![Vec::new(); n],
2406        }
2407    }
2408    #[allow(dead_code)]
2409    pub fn live_in(&self, b: usize, v: usize) -> bool {
2410        self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
2411    }
2412    #[allow(dead_code)]
2413    pub fn live_out(&self, b: usize, v: usize) -> bool {
2414        self.live_out
2415            .get(b)
2416            .map(|s| s.contains(&v))
2417            .unwrap_or(false)
2418    }
2419    #[allow(dead_code)]
2420    pub fn add_def(&mut self, b: usize, v: usize) {
2421        if let Some(s) = self.defs.get_mut(b) {
2422            if !s.contains(&v) {
2423                s.push(v);
2424            }
2425        }
2426    }
2427    #[allow(dead_code)]
2428    pub fn add_use(&mut self, b: usize, v: usize) {
2429        if let Some(s) = self.uses.get_mut(b) {
2430            if !s.contains(&v) {
2431                s.push(v);
2432            }
2433        }
2434    }
2435    #[allow(dead_code)]
2436    pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
2437        self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
2438    }
2439    #[allow(dead_code)]
2440    pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
2441        self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
2442    }
2443}
2444/// Pass registry for DhallExt.
2445#[allow(dead_code)]
2446#[derive(Debug, Default)]
2447pub struct DhallExtPassRegistry {
2448    pub(super) configs: Vec<DhallExtPassConfig>,
2449    pub(super) stats: Vec<DhallExtPassStats>,
2450}
2451impl DhallExtPassRegistry {
2452    #[allow(dead_code)]
2453    pub fn new() -> Self {
2454        Self::default()
2455    }
2456    #[allow(dead_code)]
2457    pub fn register(&mut self, c: DhallExtPassConfig) {
2458        self.stats.push(DhallExtPassStats::new());
2459        self.configs.push(c);
2460    }
2461    #[allow(dead_code)]
2462    pub fn len(&self) -> usize {
2463        self.configs.len()
2464    }
2465    #[allow(dead_code)]
2466    pub fn is_empty(&self) -> bool {
2467        self.configs.is_empty()
2468    }
2469    #[allow(dead_code)]
2470    pub fn get(&self, i: usize) -> Option<&DhallExtPassConfig> {
2471        self.configs.get(i)
2472    }
2473    #[allow(dead_code)]
2474    pub fn get_stats(&self, i: usize) -> Option<&DhallExtPassStats> {
2475        self.stats.get(i)
2476    }
2477    #[allow(dead_code)]
2478    pub fn enabled_passes(&self) -> Vec<&DhallExtPassConfig> {
2479        self.configs.iter().filter(|c| c.enabled).collect()
2480    }
2481    #[allow(dead_code)]
2482    pub fn passes_in_phase(&self, ph: &DhallExtPassPhase) -> Vec<&DhallExtPassConfig> {
2483        self.configs
2484            .iter()
2485            .filter(|c| c.enabled && &c.phase == ph)
2486            .collect()
2487    }
2488    #[allow(dead_code)]
2489    pub fn total_nodes_visited(&self) -> usize {
2490        self.stats.iter().map(|s| s.nodes_visited).sum()
2491    }
2492    #[allow(dead_code)]
2493    pub fn any_changed(&self) -> bool {
2494        self.stats.iter().any(|s| s.changed)
2495    }
2496}
2497/// A Dhall function (lambda): `\(label : annotation) -> body`
2498#[derive(Debug, Clone, PartialEq)]
2499pub struct DhallFunction {
2500    /// Parameter label
2501    pub label: String,
2502    /// Parameter type annotation
2503    pub annotation: DhallType,
2504    /// Function body
2505    pub body: Box<DhallExpr>,
2506}
2507impl DhallFunction {
2508    /// Create a new function.
2509    pub fn new(label: impl Into<String>, annotation: DhallType, body: DhallExpr) -> Self {
2510        DhallFunction {
2511            label: label.into(),
2512            annotation,
2513            body: Box::new(body),
2514        }
2515    }
2516    pub(super) fn emit(&self, indent: usize) -> String {
2517        format!(
2518            r"\({} : {}) -> {}",
2519            self.label,
2520            self.annotation,
2521            self.body.emit(indent)
2522        )
2523    }
2524}