Skip to main content

oxilean_kernel/match_compile/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::functions::*;
6use crate::{Expr, Name};
7use std::collections::HashMap;
8
9/// A decision tree node.
10#[derive(Debug, Clone)]
11pub enum DecisionTree {
12    /// Leaf: execute this arm's RHS
13    Leaf {
14        /// Index of the original arm
15        arm_idx: usize,
16        /// Bindings from pattern variables
17        bindings: Vec<(Name, Expr)>,
18    },
19    /// Switch on a variable by constructor
20    Switch {
21        /// The scrutinee variable
22        scrutinee: Expr,
23        /// One branch per constructor
24        branches: Vec<(Name, Vec<Name>, DecisionTree)>,
25        /// Default branch (if not all constructors are listed)
26        default: Option<Box<DecisionTree>>,
27    },
28    /// Failure: no match (should not occur in well-typed code)
29    Failure,
30}
31/// A pool of reusable string buffers.
32#[allow(dead_code)]
33pub struct StringPool {
34    free: Vec<String>,
35}
36#[allow(dead_code)]
37impl StringPool {
38    /// Creates a new empty string pool.
39    pub fn new() -> Self {
40        Self { free: Vec::new() }
41    }
42    /// Takes a string from the pool (may be empty).
43    pub fn take(&mut self) -> String {
44        self.free.pop().unwrap_or_default()
45    }
46    /// Returns a string to the pool.
47    pub fn give(&mut self, mut s: String) {
48        s.clear();
49        self.free.push(s);
50    }
51    /// Returns the number of free strings in the pool.
52    pub fn free_count(&self) -> usize {
53        self.free.len()
54    }
55}
56/// Statistics about a compiled match expression.
57#[derive(Debug, Clone)]
58pub struct MatchStats {
59    /// Number of arms in the original match.
60    pub num_arms: usize,
61    /// Indices of arms that are actually reachable.
62    pub reachable_arms: Vec<usize>,
63    /// Indices of arms that are unreachable (redundant).
64    pub unreachable_arms: Vec<usize>,
65    /// Whether the match is exhaustive (covers all cases).
66    pub is_exhaustive: bool,
67    /// Depth of the generated decision tree.
68    pub tree_depth: usize,
69}
70impl MatchStats {
71    /// Returns true if all arms are reachable.
72    pub fn is_exhaustive(&self) -> bool {
73        self.is_exhaustive
74    }
75    /// Returns true if there are any redundant (unreachable) arms.
76    pub fn has_redundant_arms(&self) -> bool {
77        !self.unreachable_arms.is_empty()
78    }
79}
80/// A tagged union for representing a simple two-case discriminated union.
81#[allow(dead_code)]
82pub enum Either2<A, B> {
83    /// The first alternative.
84    First(A),
85    /// The second alternative.
86    Second(B),
87}
88#[allow(dead_code)]
89impl<A, B> Either2<A, B> {
90    /// Returns `true` if this is the first alternative.
91    pub fn is_first(&self) -> bool {
92        matches!(self, Either2::First(_))
93    }
94    /// Returns `true` if this is the second alternative.
95    pub fn is_second(&self) -> bool {
96        matches!(self, Either2::Second(_))
97    }
98    /// Returns the first value if present.
99    pub fn first(self) -> Option<A> {
100        match self {
101            Either2::First(a) => Some(a),
102            _ => None,
103        }
104    }
105    /// Returns the second value if present.
106    pub fn second(self) -> Option<B> {
107        match self {
108            Either2::Second(b) => Some(b),
109            _ => None,
110        }
111    }
112    /// Maps over the first alternative.
113    pub fn map_first<C, F: FnOnce(A) -> C>(self, f: F) -> Either2<C, B> {
114        match self {
115            Either2::First(a) => Either2::First(f(a)),
116            Either2::Second(b) => Either2::Second(b),
117        }
118    }
119}
120/// A label set for a graph node.
121#[allow(dead_code)]
122pub struct LabelSet {
123    labels: Vec<String>,
124}
125#[allow(dead_code)]
126impl LabelSet {
127    /// Creates a new empty label set.
128    pub fn new() -> Self {
129        Self { labels: Vec::new() }
130    }
131    /// Adds a label (deduplicates).
132    pub fn add(&mut self, label: impl Into<String>) {
133        let s = label.into();
134        if !self.labels.contains(&s) {
135            self.labels.push(s);
136        }
137    }
138    /// Returns `true` if `label` is present.
139    pub fn has(&self, label: &str) -> bool {
140        self.labels.iter().any(|l| l == label)
141    }
142    /// Returns the count of labels.
143    pub fn count(&self) -> usize {
144        self.labels.len()
145    }
146    /// Returns all labels.
147    pub fn all(&self) -> &[String] {
148        &self.labels
149    }
150}
151/// A dependency closure builder (transitive closure via BFS).
152#[allow(dead_code)]
153pub struct TransitiveClosure {
154    adj: Vec<Vec<usize>>,
155    n: usize,
156}
157#[allow(dead_code)]
158impl TransitiveClosure {
159    /// Creates a transitive closure builder for `n` nodes.
160    pub fn new(n: usize) -> Self {
161        Self {
162            adj: vec![Vec::new(); n],
163            n,
164        }
165    }
166    /// Adds a direct edge.
167    pub fn add_edge(&mut self, from: usize, to: usize) {
168        if from < self.n {
169            self.adj[from].push(to);
170        }
171    }
172    /// Computes all nodes reachable from `start` (including `start`).
173    pub fn reachable_from(&self, start: usize) -> Vec<usize> {
174        let mut visited = vec![false; self.n];
175        let mut queue = std::collections::VecDeque::new();
176        queue.push_back(start);
177        while let Some(node) = queue.pop_front() {
178            if node >= self.n || visited[node] {
179                continue;
180            }
181            visited[node] = true;
182            for &next in &self.adj[node] {
183                queue.push_back(next);
184            }
185        }
186        (0..self.n).filter(|&i| visited[i]).collect()
187    }
188    /// Returns `true` if `from` can transitively reach `to`.
189    pub fn can_reach(&self, from: usize, to: usize) -> bool {
190        self.reachable_from(from).contains(&to)
191    }
192}
193/// A fixed-size sliding window that computes a running sum.
194#[allow(dead_code)]
195pub struct SlidingSum {
196    window: Vec<f64>,
197    capacity: usize,
198    pos: usize,
199    sum: f64,
200    count: usize,
201}
202#[allow(dead_code)]
203impl SlidingSum {
204    /// Creates a sliding sum with the given window size.
205    pub fn new(capacity: usize) -> Self {
206        Self {
207            window: vec![0.0; capacity],
208            capacity,
209            pos: 0,
210            sum: 0.0,
211            count: 0,
212        }
213    }
214    /// Adds a value to the window, removing the oldest if full.
215    pub fn push(&mut self, val: f64) {
216        let oldest = self.window[self.pos];
217        self.sum -= oldest;
218        self.sum += val;
219        self.window[self.pos] = val;
220        self.pos = (self.pos + 1) % self.capacity;
221        if self.count < self.capacity {
222            self.count += 1;
223        }
224    }
225    /// Returns the current window sum.
226    pub fn sum(&self) -> f64 {
227        self.sum
228    }
229    /// Returns the window mean, or `None` if empty.
230    pub fn mean(&self) -> Option<f64> {
231        if self.count == 0 {
232            None
233        } else {
234            Some(self.sum / self.count as f64)
235        }
236    }
237    /// Returns the current window size (number of valid elements).
238    pub fn count(&self) -> usize {
239        self.count
240    }
241}
242/// A match arm: pattern(s) → body.
243#[derive(Debug, Clone)]
244pub struct MatchArm {
245    /// Patterns (one per scrutinee in multi-way match)
246    pub patterns: Vec<Pattern>,
247    /// Right-hand side
248    pub rhs: Expr,
249    /// Guard condition (if any)
250    pub guard: Option<Expr>,
251}
252/// Result of compilation.
253#[derive(Debug, Clone)]
254pub struct CompileResult {
255    /// The decision tree
256    pub tree: DecisionTree,
257    /// Indices of unreachable arms (redundancy)
258    pub unreachable_arms: Vec<usize>,
259    /// Missing constructor patterns (for exhaustiveness errors)
260    pub missing_patterns: Vec<Vec<Pattern>>,
261}
262/// A simple key-value store backed by a sorted Vec for small maps.
263#[allow(dead_code)]
264pub struct SmallMap<K: Ord + Clone, V: Clone> {
265    entries: Vec<(K, V)>,
266}
267#[allow(dead_code)]
268impl<K: Ord + Clone, V: Clone> SmallMap<K, V> {
269    /// Creates a new empty small map.
270    pub fn new() -> Self {
271        Self {
272            entries: Vec::new(),
273        }
274    }
275    /// Inserts or replaces the value for `key`.
276    pub fn insert(&mut self, key: K, val: V) {
277        match self.entries.binary_search_by_key(&&key, |(k, _)| k) {
278            Ok(i) => self.entries[i].1 = val,
279            Err(i) => self.entries.insert(i, (key, val)),
280        }
281    }
282    /// Returns the value for `key`, or `None`.
283    pub fn get(&self, key: &K) -> Option<&V> {
284        self.entries
285            .binary_search_by_key(&key, |(k, _)| k)
286            .ok()
287            .map(|i| &self.entries[i].1)
288    }
289    /// Returns the number of entries.
290    pub fn len(&self) -> usize {
291        self.entries.len()
292    }
293    /// Returns `true` if empty.
294    pub fn is_empty(&self) -> bool {
295        self.entries.is_empty()
296    }
297    /// Returns all keys.
298    pub fn keys(&self) -> Vec<&K> {
299        self.entries.iter().map(|(k, _)| k).collect()
300    }
301    /// Returns all values.
302    pub fn values(&self) -> Vec<&V> {
303        self.entries.iter().map(|(_, v)| v).collect()
304    }
305}
306/// A simple directed acyclic graph.
307#[allow(dead_code)]
308pub struct SimpleDag {
309    /// `edges[i]` is the list of direct successors of node `i`.
310    edges: Vec<Vec<usize>>,
311}
312#[allow(dead_code)]
313impl SimpleDag {
314    /// Creates a DAG with `n` nodes and no edges.
315    pub fn new(n: usize) -> Self {
316        Self {
317            edges: vec![Vec::new(); n],
318        }
319    }
320    /// Adds an edge from `from` to `to`.
321    pub fn add_edge(&mut self, from: usize, to: usize) {
322        if from < self.edges.len() {
323            self.edges[from].push(to);
324        }
325    }
326    /// Returns the successors of `node`.
327    pub fn successors(&self, node: usize) -> &[usize] {
328        self.edges.get(node).map(|v| v.as_slice()).unwrap_or(&[])
329    }
330    /// Returns `true` if `from` can reach `to` via DFS.
331    pub fn can_reach(&self, from: usize, to: usize) -> bool {
332        let mut visited = vec![false; self.edges.len()];
333        self.dfs(from, to, &mut visited)
334    }
335    fn dfs(&self, cur: usize, target: usize, visited: &mut Vec<bool>) -> bool {
336        if cur == target {
337            return true;
338        }
339        if cur >= visited.len() || visited[cur] {
340            return false;
341        }
342        visited[cur] = true;
343        for &next in self.successors(cur) {
344            if self.dfs(next, target, visited) {
345                return true;
346            }
347        }
348        false
349    }
350    /// Returns the topological order of nodes, or `None` if a cycle is detected.
351    pub fn topological_sort(&self) -> Option<Vec<usize>> {
352        let n = self.edges.len();
353        let mut in_degree = vec![0usize; n];
354        for succs in &self.edges {
355            for &s in succs {
356                if s < n {
357                    in_degree[s] += 1;
358                }
359            }
360        }
361        let mut queue: std::collections::VecDeque<usize> =
362            (0..n).filter(|&i| in_degree[i] == 0).collect();
363        let mut order = Vec::new();
364        while let Some(node) = queue.pop_front() {
365            order.push(node);
366            for &s in self.successors(node) {
367                if s < n {
368                    in_degree[s] -= 1;
369                    if in_degree[s] == 0 {
370                        queue.push_back(s);
371                    }
372                }
373            }
374        }
375        if order.len() == n {
376            Some(order)
377        } else {
378            None
379        }
380    }
381    /// Returns the number of nodes.
382    pub fn num_nodes(&self) -> usize {
383        self.edges.len()
384    }
385}
386/// A non-empty list (at least one element guaranteed).
387#[allow(dead_code)]
388pub struct NonEmptyVec<T> {
389    head: T,
390    tail: Vec<T>,
391}
392#[allow(dead_code)]
393impl<T> NonEmptyVec<T> {
394    /// Creates a non-empty vec with a single element.
395    pub fn singleton(val: T) -> Self {
396        Self {
397            head: val,
398            tail: Vec::new(),
399        }
400    }
401    /// Pushes an element.
402    pub fn push(&mut self, val: T) {
403        self.tail.push(val);
404    }
405    /// Returns a reference to the first element.
406    pub fn first(&self) -> &T {
407        &self.head
408    }
409    /// Returns a reference to the last element.
410    pub fn last(&self) -> &T {
411        self.tail.last().unwrap_or(&self.head)
412    }
413    /// Returns the number of elements.
414    pub fn len(&self) -> usize {
415        1 + self.tail.len()
416    }
417    /// Returns whether the collection is empty.
418    pub fn is_empty(&self) -> bool {
419        self.len() == 0
420    }
421    /// Returns all elements as a Vec.
422    pub fn to_vec(&self) -> Vec<&T> {
423        let mut v = vec![&self.head];
424        v.extend(self.tail.iter());
425        v
426    }
427}
428/// A reusable scratch buffer for path computations.
429#[allow(dead_code)]
430pub struct PathBuf {
431    components: Vec<String>,
432}
433#[allow(dead_code)]
434impl PathBuf {
435    /// Creates a new empty path buffer.
436    pub fn new() -> Self {
437        Self {
438            components: Vec::new(),
439        }
440    }
441    /// Pushes a component.
442    pub fn push(&mut self, comp: impl Into<String>) {
443        self.components.push(comp.into());
444    }
445    /// Pops the last component.
446    pub fn pop(&mut self) {
447        self.components.pop();
448    }
449    /// Returns the current path as a `/`-separated string.
450    pub fn as_str(&self) -> String {
451        self.components.join("/")
452    }
453    /// Returns the depth of the path.
454    pub fn depth(&self) -> usize {
455        self.components.len()
456    }
457    /// Clears the path.
458    pub fn clear(&mut self) {
459        self.components.clear();
460    }
461}
462/// A flat list of substitution pairs `(from, to)`.
463#[allow(dead_code)]
464pub struct FlatSubstitution {
465    pairs: Vec<(String, String)>,
466}
467#[allow(dead_code)]
468impl FlatSubstitution {
469    /// Creates an empty substitution.
470    pub fn new() -> Self {
471        Self { pairs: Vec::new() }
472    }
473    /// Adds a pair.
474    pub fn add(&mut self, from: impl Into<String>, to: impl Into<String>) {
475        self.pairs.push((from.into(), to.into()));
476    }
477    /// Applies all substitutions to `s` (leftmost-first order).
478    pub fn apply(&self, s: &str) -> String {
479        let mut result = s.to_string();
480        for (from, to) in &self.pairs {
481            result = result.replace(from.as_str(), to.as_str());
482        }
483        result
484    }
485    /// Returns the number of pairs.
486    pub fn len(&self) -> usize {
487        self.pairs.len()
488    }
489    /// Returns `true` if empty.
490    pub fn is_empty(&self) -> bool {
491        self.pairs.is_empty()
492    }
493}
494/// A counter that can measure elapsed time between snapshots.
495#[allow(dead_code)]
496pub struct Stopwatch {
497    start: std::time::Instant,
498    splits: Vec<f64>,
499}
500#[allow(dead_code)]
501impl Stopwatch {
502    /// Creates and starts a new stopwatch.
503    pub fn start() -> Self {
504        Self {
505            start: std::time::Instant::now(),
506            splits: Vec::new(),
507        }
508    }
509    /// Records a split time (elapsed since start).
510    pub fn split(&mut self) {
511        self.splits.push(self.elapsed_ms());
512    }
513    /// Returns total elapsed milliseconds since start.
514    pub fn elapsed_ms(&self) -> f64 {
515        self.start.elapsed().as_secs_f64() * 1000.0
516    }
517    /// Returns all recorded split times.
518    pub fn splits(&self) -> &[f64] {
519        &self.splits
520    }
521    /// Returns the number of splits.
522    pub fn num_splits(&self) -> usize {
523        self.splits.len()
524    }
525}
526/// A write-once cell.
527#[allow(dead_code)]
528pub struct WriteOnce<T> {
529    value: std::cell::Cell<Option<T>>,
530}
531#[allow(dead_code)]
532impl<T: Copy> WriteOnce<T> {
533    /// Creates an empty write-once cell.
534    pub fn new() -> Self {
535        Self {
536            value: std::cell::Cell::new(None),
537        }
538    }
539    /// Writes a value.  Returns `false` if already written.
540    pub fn write(&self, val: T) -> bool {
541        if self.value.get().is_some() {
542            return false;
543        }
544        self.value.set(Some(val));
545        true
546    }
547    /// Returns the value if written.
548    pub fn read(&self) -> Option<T> {
549        self.value.get()
550    }
551    /// Returns `true` if the value has been written.
552    pub fn is_written(&self) -> bool {
553        self.value.get().is_some()
554    }
555}
556/// A window iterator that yields overlapping windows of size `n`.
557#[allow(dead_code)]
558pub struct WindowIterator<'a, T> {
559    pub(super) data: &'a [T],
560    pub(super) pos: usize,
561    pub(super) window: usize,
562}
563#[allow(dead_code)]
564impl<'a, T> WindowIterator<'a, T> {
565    /// Creates a new window iterator.
566    pub fn new(data: &'a [T], window: usize) -> Self {
567        Self {
568            data,
569            pos: 0,
570            window,
571        }
572    }
573}
574/// Represents a rewrite rule `lhs → rhs`.
575#[allow(dead_code)]
576#[allow(missing_docs)]
577pub struct RewriteRule {
578    /// The name of the rule.
579    pub name: String,
580    /// A string representation of the LHS pattern.
581    pub lhs: String,
582    /// A string representation of the RHS.
583    pub rhs: String,
584    /// Whether this is a conditional rule (has side conditions).
585    pub conditional: bool,
586}
587#[allow(dead_code)]
588impl RewriteRule {
589    /// Creates an unconditional rewrite rule.
590    pub fn unconditional(
591        name: impl Into<String>,
592        lhs: impl Into<String>,
593        rhs: impl Into<String>,
594    ) -> Self {
595        Self {
596            name: name.into(),
597            lhs: lhs.into(),
598            rhs: rhs.into(),
599            conditional: false,
600        }
601    }
602    /// Creates a conditional rewrite rule.
603    pub fn conditional(
604        name: impl Into<String>,
605        lhs: impl Into<String>,
606        rhs: impl Into<String>,
607    ) -> Self {
608        Self {
609            name: name.into(),
610            lhs: lhs.into(),
611            rhs: rhs.into(),
612            conditional: true,
613        }
614    }
615    /// Returns a textual representation.
616    pub fn display(&self) -> String {
617        format!("{}: {} → {}", self.name, self.lhs, self.rhs)
618    }
619}
620/// A simple stack-based calculator for arithmetic expressions.
621#[allow(dead_code)]
622pub struct StackCalc {
623    stack: Vec<i64>,
624}
625#[allow(dead_code)]
626impl StackCalc {
627    /// Creates a new empty calculator.
628    pub fn new() -> Self {
629        Self { stack: Vec::new() }
630    }
631    /// Pushes an integer literal.
632    pub fn push(&mut self, n: i64) {
633        self.stack.push(n);
634    }
635    /// Adds the top two values.  Panics if fewer than two values.
636    pub fn add(&mut self) {
637        let b = self
638            .stack
639            .pop()
640            .expect("stack must have at least two values for add");
641        let a = self
642            .stack
643            .pop()
644            .expect("stack must have at least two values for add");
645        self.stack.push(a + b);
646    }
647    /// Subtracts top from second.
648    pub fn sub(&mut self) {
649        let b = self
650            .stack
651            .pop()
652            .expect("stack must have at least two values for sub");
653        let a = self
654            .stack
655            .pop()
656            .expect("stack must have at least two values for sub");
657        self.stack.push(a - b);
658    }
659    /// Multiplies the top two values.
660    pub fn mul(&mut self) {
661        let b = self
662            .stack
663            .pop()
664            .expect("stack must have at least two values for mul");
665        let a = self
666            .stack
667            .pop()
668            .expect("stack must have at least two values for mul");
669        self.stack.push(a * b);
670    }
671    /// Peeks the top value.
672    pub fn peek(&self) -> Option<i64> {
673        self.stack.last().copied()
674    }
675    /// Returns the stack depth.
676    pub fn depth(&self) -> usize {
677        self.stack.len()
678    }
679}
680/// A token bucket rate limiter.
681#[allow(dead_code)]
682pub struct TokenBucket {
683    capacity: u64,
684    tokens: u64,
685    refill_per_ms: u64,
686    last_refill: std::time::Instant,
687}
688#[allow(dead_code)]
689impl TokenBucket {
690    /// Creates a new token bucket.
691    pub fn new(capacity: u64, refill_per_ms: u64) -> Self {
692        Self {
693            capacity,
694            tokens: capacity,
695            refill_per_ms,
696            last_refill: std::time::Instant::now(),
697        }
698    }
699    /// Attempts to consume `n` tokens.  Returns `true` on success.
700    pub fn try_consume(&mut self, n: u64) -> bool {
701        self.refill();
702        if self.tokens >= n {
703            self.tokens -= n;
704            true
705        } else {
706            false
707        }
708    }
709    fn refill(&mut self) {
710        let now = std::time::Instant::now();
711        let elapsed_ms = now.duration_since(self.last_refill).as_millis() as u64;
712        if elapsed_ms > 0 {
713            let new_tokens = elapsed_ms * self.refill_per_ms;
714            self.tokens = (self.tokens + new_tokens).min(self.capacity);
715            self.last_refill = now;
716        }
717    }
718    /// Returns the number of currently available tokens.
719    pub fn available(&self) -> u64 {
720        self.tokens
721    }
722    /// Returns the bucket capacity.
723    pub fn capacity(&self) -> u64 {
724        self.capacity
725    }
726}
727/// A pattern in a match expression.
728#[derive(Debug, Clone, PartialEq)]
729pub enum Pattern {
730    /// Wildcard: matches anything
731    Wildcard,
732    /// Variable binding: matches anything, binds the value
733    Var(Name),
734    /// Constructor application: `C p1 p2 ...`
735    Constructor(Name, Vec<Pattern>),
736    /// Literal: matches a specific literal
737    Literal(crate::Literal),
738    /// As-pattern: `x @ p` (bind and match)
739    As(Name, Box<Pattern>),
740    /// Or-pattern: `p1 | p2`
741    Or(Vec<Pattern>),
742    /// Inaccessible pattern: `.( expr )`
743    Inaccessible(Expr),
744}
745/// A set of rewrite rules.
746#[allow(dead_code)]
747pub struct RewriteRuleSet {
748    rules: Vec<RewriteRule>,
749}
750#[allow(dead_code)]
751impl RewriteRuleSet {
752    /// Creates an empty rule set.
753    pub fn new() -> Self {
754        Self { rules: Vec::new() }
755    }
756    /// Adds a rule.
757    pub fn add(&mut self, rule: RewriteRule) {
758        self.rules.push(rule);
759    }
760    /// Returns the number of rules.
761    pub fn len(&self) -> usize {
762        self.rules.len()
763    }
764    /// Returns `true` if the set is empty.
765    pub fn is_empty(&self) -> bool {
766        self.rules.is_empty()
767    }
768    /// Returns all conditional rules.
769    pub fn conditional_rules(&self) -> Vec<&RewriteRule> {
770        self.rules.iter().filter(|r| r.conditional).collect()
771    }
772    /// Returns all unconditional rules.
773    pub fn unconditional_rules(&self) -> Vec<&RewriteRule> {
774        self.rules.iter().filter(|r| !r.conditional).collect()
775    }
776    /// Looks up a rule by name.
777    pub fn get(&self, name: &str) -> Option<&RewriteRule> {
778        self.rules.iter().find(|r| r.name == name)
779    }
780}
781/// A mutable reference stack for tracking the current "focus" in a tree traversal.
782#[allow(dead_code)]
783pub struct FocusStack<T> {
784    items: Vec<T>,
785}
786#[allow(dead_code)]
787impl<T> FocusStack<T> {
788    /// Creates an empty focus stack.
789    pub fn new() -> Self {
790        Self { items: Vec::new() }
791    }
792    /// Focuses on `item`.
793    pub fn focus(&mut self, item: T) {
794        self.items.push(item);
795    }
796    /// Blurs (pops) the current focus.
797    pub fn blur(&mut self) -> Option<T> {
798        self.items.pop()
799    }
800    /// Returns the current focus, or `None`.
801    pub fn current(&self) -> Option<&T> {
802        self.items.last()
803    }
804    /// Returns the focus depth.
805    pub fn depth(&self) -> usize {
806        self.items.len()
807    }
808    /// Returns `true` if there is no current focus.
809    pub fn is_empty(&self) -> bool {
810        self.items.is_empty()
811    }
812}
813/// A type-erased function pointer with arity tracking.
814#[allow(dead_code)]
815pub struct RawFnPtr {
816    /// The raw function pointer (stored as usize for type erasure).
817    ptr: usize,
818    arity: usize,
819    name: String,
820}
821#[allow(dead_code)]
822impl RawFnPtr {
823    /// Creates a new raw function pointer descriptor.
824    pub fn new(ptr: usize, arity: usize, name: impl Into<String>) -> Self {
825        Self {
826            ptr,
827            arity,
828            name: name.into(),
829        }
830    }
831    /// Returns the arity.
832    pub fn arity(&self) -> usize {
833        self.arity
834    }
835    /// Returns the name.
836    pub fn name(&self) -> &str {
837        &self.name
838    }
839    /// Returns the raw pointer value.
840    pub fn raw(&self) -> usize {
841        self.ptr
842    }
843}
844/// A simple decision tree node for rule dispatching.
845#[allow(dead_code)]
846#[allow(missing_docs)]
847pub enum DecisionNode {
848    /// A leaf with an action string.
849    Leaf(String),
850    /// An interior node: check `key` equals `val` → `yes_branch`, else `no_branch`.
851    Branch {
852        key: String,
853        val: String,
854        yes_branch: Box<DecisionNode>,
855        no_branch: Box<DecisionNode>,
856    },
857}
858#[allow(dead_code)]
859impl DecisionNode {
860    /// Evaluates the decision tree with the given context.
861    pub fn evaluate(&self, ctx: &std::collections::HashMap<String, String>) -> &str {
862        match self {
863            DecisionNode::Leaf(action) => action.as_str(),
864            DecisionNode::Branch {
865                key,
866                val,
867                yes_branch,
868                no_branch,
869            } => {
870                let actual = ctx.get(key).map(|s| s.as_str()).unwrap_or("");
871                if actual == val.as_str() {
872                    yes_branch.evaluate(ctx)
873                } else {
874                    no_branch.evaluate(ctx)
875                }
876            }
877        }
878    }
879    /// Returns the depth of the decision tree.
880    pub fn depth(&self) -> usize {
881        match self {
882            DecisionNode::Leaf(_) => 0,
883            DecisionNode::Branch {
884                yes_branch,
885                no_branch,
886                ..
887            } => 1 + yes_branch.depth().max(no_branch.depth()),
888        }
889    }
890}
891/// A generic counter that tracks min/max/sum for statistical summaries.
892#[allow(dead_code)]
893pub struct StatSummary {
894    count: u64,
895    sum: f64,
896    min: f64,
897    max: f64,
898}
899#[allow(dead_code)]
900impl StatSummary {
901    /// Creates an empty summary.
902    pub fn new() -> Self {
903        Self {
904            count: 0,
905            sum: 0.0,
906            min: f64::INFINITY,
907            max: f64::NEG_INFINITY,
908        }
909    }
910    /// Records a sample.
911    pub fn record(&mut self, val: f64) {
912        self.count += 1;
913        self.sum += val;
914        if val < self.min {
915            self.min = val;
916        }
917        if val > self.max {
918            self.max = val;
919        }
920    }
921    /// Returns the mean, or `None` if no samples.
922    pub fn mean(&self) -> Option<f64> {
923        if self.count == 0 {
924            None
925        } else {
926            Some(self.sum / self.count as f64)
927        }
928    }
929    /// Returns the minimum, or `None` if no samples.
930    pub fn min(&self) -> Option<f64> {
931        if self.count == 0 {
932            None
933        } else {
934            Some(self.min)
935        }
936    }
937    /// Returns the maximum, or `None` if no samples.
938    pub fn max(&self) -> Option<f64> {
939        if self.count == 0 {
940            None
941        } else {
942            Some(self.max)
943        }
944    }
945    /// Returns the count of recorded samples.
946    pub fn count(&self) -> u64 {
947        self.count
948    }
949}
950/// A sparse vector: stores only non-default elements.
951#[allow(dead_code)]
952pub struct SparseVec<T: Default + Clone + PartialEq> {
953    entries: std::collections::HashMap<usize, T>,
954    default_: T,
955    logical_len: usize,
956}
957#[allow(dead_code)]
958impl<T: Default + Clone + PartialEq> SparseVec<T> {
959    /// Creates a new sparse vector with logical length `len`.
960    pub fn new(len: usize) -> Self {
961        Self {
962            entries: std::collections::HashMap::new(),
963            default_: T::default(),
964            logical_len: len,
965        }
966    }
967    /// Sets element at `idx`.
968    pub fn set(&mut self, idx: usize, val: T) {
969        if val == self.default_ {
970            self.entries.remove(&idx);
971        } else {
972            self.entries.insert(idx, val);
973        }
974    }
975    /// Gets element at `idx`.
976    pub fn get(&self, idx: usize) -> &T {
977        self.entries.get(&idx).unwrap_or(&self.default_)
978    }
979    /// Returns the logical length.
980    pub fn len(&self) -> usize {
981        self.logical_len
982    }
983    /// Returns whether the collection is empty.
984    pub fn is_empty(&self) -> bool {
985        self.len() == 0
986    }
987    /// Returns the number of non-default elements.
988    pub fn nnz(&self) -> usize {
989        self.entries.len()
990    }
991}
992/// A versioned record that stores a history of values.
993#[allow(dead_code)]
994pub struct VersionedRecord<T: Clone> {
995    history: Vec<T>,
996}
997#[allow(dead_code)]
998impl<T: Clone> VersionedRecord<T> {
999    /// Creates a new record with an initial value.
1000    pub fn new(initial: T) -> Self {
1001        Self {
1002            history: vec![initial],
1003        }
1004    }
1005    /// Updates the record with a new version.
1006    pub fn update(&mut self, val: T) {
1007        self.history.push(val);
1008    }
1009    /// Returns the current (latest) value.
1010    pub fn current(&self) -> &T {
1011        self.history
1012            .last()
1013            .expect("VersionedRecord history is always non-empty after construction")
1014    }
1015    /// Returns the value at version `n` (0-indexed), or `None`.
1016    pub fn at_version(&self, n: usize) -> Option<&T> {
1017        self.history.get(n)
1018    }
1019    /// Returns the version number of the current value.
1020    pub fn version(&self) -> usize {
1021        self.history.len() - 1
1022    }
1023    /// Returns `true` if more than one version exists.
1024    pub fn has_history(&self) -> bool {
1025        self.history.len() > 1
1026    }
1027}
1028/// Constructor info needed for exhaustiveness checking.
1029#[derive(Debug, Clone)]
1030pub struct ConstructorInfo {
1031    /// Constructor name
1032    pub name: Name,
1033    /// Number of fields
1034    pub num_fields: u32,
1035    /// Parent inductive type
1036    pub inductive: Name,
1037}
1038/// Statistics about the compiled pattern match.
1039#[derive(Clone, Debug, Default)]
1040pub struct PatternStats {
1041    /// Total number of patterns in the match.
1042    pub total_patterns: usize,
1043    /// Number of wildcard patterns.
1044    pub wildcards: usize,
1045    /// Number of constructor patterns.
1046    pub constructors: usize,
1047    /// Number of literal patterns.
1048    pub literals: usize,
1049    /// Number of or-patterns.
1050    pub or_patterns: usize,
1051    /// Number of redundant arms detected.
1052    pub redundant_arms: usize,
1053}
1054impl PatternStats {
1055    /// Create zeroed stats.
1056    pub fn new() -> Self {
1057        Self::default()
1058    }
1059    /// Collect stats from a list of patterns.
1060    pub fn from_patterns(patterns: &[Pattern]) -> Self {
1061        let mut stats = Self::new();
1062        for p in patterns {
1063            stats.total_patterns += 1;
1064            stats.count_pattern(p);
1065        }
1066        stats
1067    }
1068    fn count_pattern(&mut self, pattern: &Pattern) {
1069        match pattern {
1070            Pattern::Wildcard => self.wildcards += 1,
1071            Pattern::Var(_) => self.wildcards += 1,
1072            Pattern::Constructor(_, sub) => {
1073                self.constructors += 1;
1074                for p in sub {
1075                    self.count_pattern(p);
1076                }
1077            }
1078            Pattern::Literal(_) => self.literals += 1,
1079            Pattern::As(_, inner) => self.count_pattern(inner),
1080            Pattern::Or(pats) => {
1081                self.or_patterns += 1;
1082                for p in pats {
1083                    self.count_pattern(p);
1084                }
1085            }
1086            Pattern::Inaccessible(_) => {}
1087        }
1088    }
1089}
1090/// A pair of `StatSummary` values tracking before/after a transformation.
1091#[allow(dead_code)]
1092pub struct TransformStat {
1093    before: StatSummary,
1094    after: StatSummary,
1095}
1096#[allow(dead_code)]
1097impl TransformStat {
1098    /// Creates a new transform stat recorder.
1099    pub fn new() -> Self {
1100        Self {
1101            before: StatSummary::new(),
1102            after: StatSummary::new(),
1103        }
1104    }
1105    /// Records a before value.
1106    pub fn record_before(&mut self, v: f64) {
1107        self.before.record(v);
1108    }
1109    /// Records an after value.
1110    pub fn record_after(&mut self, v: f64) {
1111        self.after.record(v);
1112    }
1113    /// Returns the mean reduction ratio (after/before).
1114    pub fn mean_ratio(&self) -> Option<f64> {
1115        let b = self.before.mean()?;
1116        let a = self.after.mean()?;
1117        if b.abs() < f64::EPSILON {
1118            return None;
1119        }
1120        Some(a / b)
1121    }
1122}
1123/// A hierarchical configuration tree.
1124#[allow(dead_code)]
1125pub struct ConfigNode {
1126    key: String,
1127    value: Option<String>,
1128    children: Vec<ConfigNode>,
1129}
1130#[allow(dead_code)]
1131impl ConfigNode {
1132    /// Creates a leaf config node with a value.
1133    pub fn leaf(key: impl Into<String>, value: impl Into<String>) -> Self {
1134        Self {
1135            key: key.into(),
1136            value: Some(value.into()),
1137            children: Vec::new(),
1138        }
1139    }
1140    /// Creates a section node with children.
1141    pub fn section(key: impl Into<String>) -> Self {
1142        Self {
1143            key: key.into(),
1144            value: None,
1145            children: Vec::new(),
1146        }
1147    }
1148    /// Adds a child node.
1149    pub fn add_child(&mut self, child: ConfigNode) {
1150        self.children.push(child);
1151    }
1152    /// Returns the key.
1153    pub fn key(&self) -> &str {
1154        &self.key
1155    }
1156    /// Returns the value, or `None` for section nodes.
1157    pub fn value(&self) -> Option<&str> {
1158        self.value.as_deref()
1159    }
1160    /// Returns the number of children.
1161    pub fn num_children(&self) -> usize {
1162        self.children.len()
1163    }
1164    /// Looks up a dot-separated path.
1165    pub fn lookup(&self, path: &str) -> Option<&str> {
1166        let mut parts = path.splitn(2, '.');
1167        let head = parts.next()?;
1168        let tail = parts.next();
1169        if head != self.key {
1170            return None;
1171        }
1172        match tail {
1173            None => self.value.as_deref(),
1174            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
1175        }
1176    }
1177    fn lookup_relative(&self, path: &str) -> Option<&str> {
1178        let mut parts = path.splitn(2, '.');
1179        let head = parts.next()?;
1180        let tail = parts.next();
1181        if head != self.key {
1182            return None;
1183        }
1184        match tail {
1185            None => self.value.as_deref(),
1186            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
1187        }
1188    }
1189}
1190/// Pattern match compiler.
1191pub struct MatchCompiler {
1192    /// Counter for generating fresh variables
1193    next_var: u32,
1194    /// Known constructors for each inductive type
1195    constructors: HashMap<Name, Vec<ConstructorInfo>>,
1196}
1197impl MatchCompiler {
1198    /// Create a new match compiler.
1199    pub fn new() -> Self {
1200        Self {
1201            next_var: 0,
1202            constructors: HashMap::new(),
1203        }
1204    }
1205    /// Generate a fresh variable name.
1206    pub fn fresh_var(&mut self) -> Name {
1207        let n = self.next_var;
1208        self.next_var += 1;
1209        Name::str(format!("_match{}", n))
1210    }
1211    /// Register constructors for an inductive type.
1212    pub fn register_constructors(&mut self, ind_name: Name, ctors: Vec<ConstructorInfo>) {
1213        self.constructors.insert(ind_name, ctors);
1214    }
1215    /// Get constructors for an inductive type.
1216    pub fn get_constructors(&self, ind_name: &Name) -> Option<&Vec<ConstructorInfo>> {
1217        self.constructors.get(ind_name)
1218    }
1219    /// Compile a match expression.
1220    ///
1221    /// Takes a list of scrutinees and match arms, produces a decision tree.
1222    pub fn compile_match(
1223        &mut self,
1224        scrutinees: &[Expr],
1225        arms: &[MatchArm],
1226    ) -> Result<CompileResult, String> {
1227        if arms.is_empty() {
1228            return Err("Match expression has no arms".to_string());
1229        }
1230        for (i, arm) in arms.iter().enumerate() {
1231            if arm.patterns.len() != scrutinees.len() {
1232                return Err(format!(
1233                    "Arm {} has {} patterns but expected {}",
1234                    i,
1235                    arm.patterns.len(),
1236                    scrutinees.len()
1237                ));
1238            }
1239        }
1240        let matrix: Vec<(Vec<&Pattern>, usize)> = arms
1241            .iter()
1242            .enumerate()
1243            .map(|(i, arm)| (arm.patterns.iter().collect(), i))
1244            .collect();
1245        let tree = self.compile_matrix(scrutinees, &matrix)?;
1246        let mut reachable = vec![false; arms.len()];
1247        self.mark_reachable(&tree, &mut reachable);
1248        let unreachable_arms: Vec<usize> = reachable
1249            .iter()
1250            .enumerate()
1251            .filter(|(_, &r)| !r)
1252            .map(|(i, _)| i)
1253            .collect();
1254        let missing_patterns = self.check_exhaustiveness_tree(&tree);
1255        Ok(CompileResult {
1256            tree,
1257            unreachable_arms,
1258            missing_patterns,
1259        })
1260    }
1261    /// Compile a pattern matrix into a decision tree.
1262    fn compile_matrix(
1263        &mut self,
1264        scrutinees: &[Expr],
1265        matrix: &[(Vec<&Pattern>, usize)],
1266    ) -> Result<DecisionTree, String> {
1267        if scrutinees.is_empty() {
1268            if let Some((_, arm_idx)) = matrix.first() {
1269                return Ok(DecisionTree::Leaf {
1270                    arm_idx: *arm_idx,
1271                    bindings: Vec::new(),
1272                });
1273            }
1274            return Ok(DecisionTree::Failure);
1275        }
1276        if matrix.is_empty() {
1277            return Ok(DecisionTree::Failure);
1278        }
1279        let col = self.find_best_column(matrix);
1280        let all_wildcards = matrix
1281            .iter()
1282            .all(|(pats, _)| matches!(pats[col], Pattern::Wildcard | Pattern::Var(_)));
1283        if all_wildcards {
1284            let (_, arm_idx) = &matrix[0];
1285            return Ok(DecisionTree::Leaf {
1286                arm_idx: *arm_idx,
1287                bindings: Vec::new(),
1288            });
1289        }
1290        let ctors_used: Vec<Name> = matrix
1291            .iter()
1292            .filter_map(|(pats, _)| {
1293                if let Pattern::Constructor(name, _) = &pats[col] {
1294                    Some(name.clone())
1295                } else {
1296                    None
1297                }
1298            })
1299            .collect::<std::collections::HashSet<_>>()
1300            .into_iter()
1301            .collect();
1302        if ctors_used.is_empty() {
1303            let has_literals = matrix
1304                .iter()
1305                .any(|(pats, _)| matches!(pats[col], Pattern::Literal(_)));
1306            if has_literals {
1307                return self.compile_literal_column(scrutinees, matrix, col);
1308            }
1309            let (_, arm_idx) = &matrix[0];
1310            return Ok(DecisionTree::Leaf {
1311                arm_idx: *arm_idx,
1312                bindings: Vec::new(),
1313            });
1314        }
1315        let mut branches = Vec::new();
1316        let mut has_default = false;
1317        for ctor_name in &ctors_used {
1318            let ctor_arity = self.get_ctor_arity(ctor_name);
1319            let field_names: Vec<Name> = (0..ctor_arity).map(|_| self.fresh_var()).collect();
1320            let sub_matrix: Vec<(Vec<&Pattern>, usize)> = matrix
1321                .iter()
1322                .filter_map(|(pats, arm_idx)| match &pats[col] {
1323                    Pattern::Constructor(name, sub_pats) if name == ctor_name => {
1324                        let mut new_pats = Vec::new();
1325                        for (i, p) in pats.iter().enumerate() {
1326                            if i == col {
1327                                for sp in sub_pats {
1328                                    new_pats.push(sp);
1329                                }
1330                            } else {
1331                                new_pats.push(p);
1332                            }
1333                        }
1334                        Some((new_pats, *arm_idx))
1335                    }
1336                    Pattern::Wildcard | Pattern::Var(_) => {
1337                        let mut new_pats = Vec::new();
1338                        for (i, p) in pats.iter().enumerate() {
1339                            if i == col {
1340                                for _ in 0..ctor_arity {
1341                                    new_pats.push(&Pattern::Wildcard);
1342                                }
1343                            } else {
1344                                new_pats.push(p);
1345                            }
1346                        }
1347                        Some((new_pats, *arm_idx))
1348                    }
1349                    _ => None,
1350                })
1351                .collect();
1352            let mut sub_scrutinees = Vec::new();
1353            for (i, s) in scrutinees.iter().enumerate() {
1354                if i == col {
1355                    for (j, fname) in field_names.iter().enumerate() {
1356                        sub_scrutinees.push(Expr::Proj(
1357                            ctor_name.clone(),
1358                            j as u32,
1359                            Box::new(s.clone()),
1360                        ));
1361                        let _ = fname;
1362                    }
1363                } else {
1364                    sub_scrutinees.push(s.clone());
1365                }
1366            }
1367            let sub_tree = self.compile_matrix(&sub_scrutinees, &sub_matrix)?;
1368            branches.push((ctor_name.clone(), field_names, sub_tree));
1369        }
1370        let default_matrix: Vec<(Vec<&Pattern>, usize)> = matrix
1371            .iter()
1372            .filter_map(|(pats, arm_idx)| match &pats[col] {
1373                Pattern::Wildcard | Pattern::Var(_) => {
1374                    let mut new_pats: Vec<&Pattern> = pats
1375                        .iter()
1376                        .enumerate()
1377                        .filter(|(i, _)| *i != col)
1378                        .map(|(_, p)| *p)
1379                        .collect();
1380                    if new_pats.is_empty() && !pats.is_empty() {
1381                        new_pats.push(&Pattern::Wildcard);
1382                    }
1383                    Some((new_pats, *arm_idx))
1384                }
1385                _ => None,
1386            })
1387            .collect();
1388        let default = if !default_matrix.is_empty() {
1389            has_default = true;
1390            let remaining: Vec<Expr> = scrutinees
1391                .iter()
1392                .enumerate()
1393                .filter(|(i, _)| *i != col)
1394                .map(|(_, s)| s.clone())
1395                .collect();
1396            Some(Box::new(self.compile_matrix(&remaining, &default_matrix)?))
1397        } else {
1398            None
1399        };
1400        let _ = has_default;
1401        Ok(DecisionTree::Switch {
1402            scrutinee: scrutinees[col].clone(),
1403            branches,
1404            default,
1405        })
1406    }
1407    /// Find the best column to split on.
1408    fn find_best_column(&self, matrix: &[(Vec<&Pattern>, usize)]) -> usize {
1409        if matrix.is_empty() || matrix[0].0.is_empty() {
1410            return 0;
1411        }
1412        let ncols = matrix[0].0.len();
1413        let mut best_col = 0;
1414        let mut best_score = 0;
1415        for col in 0..ncols {
1416            let mut score = 0;
1417            for (pats, _) in matrix {
1418                match pats[col] {
1419                    Pattern::Constructor(_, _) => score += 2,
1420                    Pattern::Literal(_) => score += 1,
1421                    _ => {}
1422                }
1423            }
1424            if score > best_score {
1425                best_score = score;
1426                best_col = col;
1427            }
1428        }
1429        best_col
1430    }
1431    /// Get constructor arity from registered info.
1432    fn get_ctor_arity(&self, ctor_name: &Name) -> u32 {
1433        for ctors in self.constructors.values() {
1434            for ctor in ctors {
1435                if &ctor.name == ctor_name {
1436                    return ctor.num_fields;
1437                }
1438            }
1439        }
1440        0
1441    }
1442    /// Compile a column with literal patterns.
1443    ///
1444    /// Builds a `Switch` node where each branch corresponds to a unique literal
1445    /// value found in `col`. Rows with wildcard/var patterns in `col` contribute
1446    /// to the default branch.
1447    fn compile_literal_column(
1448        &mut self,
1449        scrutinees: &[Expr],
1450        matrix: &[(Vec<&Pattern>, usize)],
1451        col: usize,
1452    ) -> Result<DecisionTree, String> {
1453        if matrix.is_empty() {
1454            return Ok(DecisionTree::Failure);
1455        }
1456        let mut seen_lits: Vec<crate::Literal> = Vec::new();
1457        for (pats, _) in matrix {
1458            if let Pattern::Literal(lit) = &pats[col] {
1459                if !seen_lits.contains(lit) {
1460                    seen_lits.push(lit.clone());
1461                }
1462            }
1463        }
1464        let sub_scrutinees: Vec<Expr> = scrutinees
1465            .iter()
1466            .enumerate()
1467            .filter(|(i, _)| *i != col)
1468            .map(|(_, s)| s.clone())
1469            .collect();
1470        let mut branches = Vec::new();
1471        for lit in &seen_lits {
1472            let sub_matrix: Vec<(Vec<&Pattern>, usize)> = matrix
1473                .iter()
1474                .filter_map(|(pats, arm_idx)| match &pats[col] {
1475                    Pattern::Literal(l) if l == lit => {
1476                        let new_pats: Vec<&Pattern> = pats
1477                            .iter()
1478                            .enumerate()
1479                            .filter(|(i, _)| *i != col)
1480                            .map(|(_, p)| *p)
1481                            .collect();
1482                        Some((new_pats, *arm_idx))
1483                    }
1484                    Pattern::Wildcard | Pattern::Var(_) => {
1485                        let new_pats: Vec<&Pattern> = pats
1486                            .iter()
1487                            .enumerate()
1488                            .filter(|(i, _)| *i != col)
1489                            .map(|(_, p)| *p)
1490                            .collect();
1491                        Some((new_pats, *arm_idx))
1492                    }
1493                    _ => None,
1494                })
1495                .collect();
1496            let sub_tree = self.compile_matrix(&sub_scrutinees, &sub_matrix)?;
1497            let lit_name = Name::str(format!("{:?}", lit));
1498            branches.push((lit_name, Vec::new(), sub_tree));
1499        }
1500        let default_matrix: Vec<(Vec<&Pattern>, usize)> = matrix
1501            .iter()
1502            .filter_map(|(pats, arm_idx)| match &pats[col] {
1503                Pattern::Wildcard | Pattern::Var(_) => {
1504                    let new_pats: Vec<&Pattern> = pats
1505                        .iter()
1506                        .enumerate()
1507                        .filter(|(i, _)| *i != col)
1508                        .map(|(_, p)| *p)
1509                        .collect();
1510                    Some((new_pats, *arm_idx))
1511                }
1512                _ => None,
1513            })
1514            .collect();
1515        let default = if !default_matrix.is_empty() {
1516            Some(Box::new(
1517                self.compile_matrix(&sub_scrutinees, &default_matrix)?,
1518            ))
1519        } else {
1520            None
1521        };
1522        Ok(DecisionTree::Switch {
1523            scrutinee: scrutinees[col].clone(),
1524            branches,
1525            default,
1526        })
1527    }
1528    /// Mark reachable arms in a decision tree.
1529    fn mark_reachable(&self, tree: &DecisionTree, reachable: &mut [bool]) {
1530        match tree {
1531            DecisionTree::Leaf { arm_idx, .. } => {
1532                if *arm_idx < reachable.len() {
1533                    reachable[*arm_idx] = true;
1534                }
1535            }
1536            DecisionTree::Switch {
1537                branches, default, ..
1538            } => {
1539                for (_, _, sub_tree) in branches {
1540                    self.mark_reachable(sub_tree, reachable);
1541                }
1542                if let Some(d) = default {
1543                    self.mark_reachable(d, reachable);
1544                }
1545            }
1546            DecisionTree::Failure => {}
1547        }
1548    }
1549    /// Check exhaustiveness from the decision tree.
1550    fn check_exhaustiveness_tree(&self, tree: &DecisionTree) -> Vec<Vec<Pattern>> {
1551        match tree {
1552            DecisionTree::Failure => vec![vec![Pattern::Wildcard]],
1553            DecisionTree::Leaf { .. } => Vec::new(),
1554            DecisionTree::Switch {
1555                branches, default, ..
1556            } => {
1557                let mut missing = Vec::new();
1558                for (_, _, sub_tree) in branches {
1559                    missing.extend(self.check_exhaustiveness_tree(sub_tree));
1560                }
1561                if let Some(d) = default {
1562                    missing.extend(self.check_exhaustiveness_tree(d));
1563                }
1564                missing
1565            }
1566        }
1567    }
1568    /// Check if patterns are exhaustive.
1569    ///
1570    /// Handles Or-patterns (flattened), As-patterns (inner checked),
1571    /// Var/Wildcard (irrefutable), and constructor coverage.
1572    pub fn check_exhaustive(&self, patterns: &[Pattern], ind_name: &Name) -> Result<(), String> {
1573        let mut flat: Vec<&Pattern> = Vec::new();
1574        for pat in patterns {
1575            Self::collect_top_patterns(pat, &mut flat);
1576        }
1577        if flat.iter().any(|p| Self::is_irrefutable(p)) {
1578            return Ok(());
1579        }
1580        if let Some(ctors) = self.constructors.get(ind_name) {
1581            let mut covered: std::collections::HashSet<Name> = std::collections::HashSet::new();
1582            for pat in &flat {
1583                if let Pattern::Constructor(name, _) = pat {
1584                    covered.insert(name.clone());
1585                }
1586            }
1587            let missing: Vec<&Name> = ctors
1588                .iter()
1589                .filter(|c| !covered.contains(&c.name))
1590                .map(|c| &c.name)
1591                .collect();
1592            if missing.is_empty() {
1593                Ok(())
1594            } else {
1595                Err(format!(
1596                    "Non-exhaustive patterns: missing constructors {:?}",
1597                    missing
1598                ))
1599            }
1600        } else {
1601            Ok(())
1602        }
1603    }
1604    /// Check if any patterns are redundant.
1605    ///
1606    /// A pattern is redundant if it is preceded by a wildcard/var, or if
1607    /// a constructor or literal it matches was already covered earlier
1608    /// (including via Or-patterns).
1609    pub fn check_redundant(&self, patterns: &[Pattern]) -> Vec<usize> {
1610        let mut seen_wildcard = false;
1611        let mut seen_ctors: std::collections::HashSet<Name> = std::collections::HashSet::new();
1612        let mut seen_lits: std::collections::HashSet<String> = std::collections::HashSet::new();
1613        let mut redundant = Vec::new();
1614        for (i, pat) in patterns.iter().enumerate() {
1615            if seen_wildcard {
1616                redundant.push(i);
1617                continue;
1618            }
1619            if Self::is_covered_by(pat, &seen_ctors, &seen_lits) {
1620                redundant.push(i);
1621            }
1622            Self::record_pattern(pat, &mut seen_wildcard, &mut seen_ctors, &mut seen_lits);
1623        }
1624        redundant
1625    }
1626    /// Flatten Or/As wrappers, collecting concrete patterns.
1627    fn collect_top_patterns<'a>(pat: &'a Pattern, out: &mut Vec<&'a Pattern>) {
1628        match pat {
1629            Pattern::Or(pats) => {
1630                for p in pats {
1631                    Self::collect_top_patterns(p, out);
1632                }
1633            }
1634            Pattern::As(_, inner) => Self::collect_top_patterns(inner, out),
1635            _ => out.push(pat),
1636        }
1637    }
1638    /// Return `true` if the pattern is irrefutable (matches everything).
1639    fn is_irrefutable(pat: &Pattern) -> bool {
1640        match pat {
1641            Pattern::Wildcard | Pattern::Var(_) => true,
1642            Pattern::As(_, inner) => Self::is_irrefutable(inner),
1643            Pattern::Or(pats) => pats.iter().any(Self::is_irrefutable),
1644            _ => false,
1645        }
1646    }
1647    /// Check if `pat` is completely covered by what we've seen so far.
1648    fn is_covered_by(
1649        pat: &Pattern,
1650        seen_ctors: &std::collections::HashSet<Name>,
1651        seen_lits: &std::collections::HashSet<String>,
1652    ) -> bool {
1653        match pat {
1654            Pattern::Constructor(name, _) => seen_ctors.contains(name),
1655            Pattern::Literal(lit) => seen_lits.contains(&format!("{:?}", lit)),
1656            Pattern::As(_, inner) => Self::is_covered_by(inner, seen_ctors, seen_lits),
1657            Pattern::Or(pats) => pats
1658                .iter()
1659                .all(|p| Self::is_covered_by(p, seen_ctors, seen_lits)),
1660            Pattern::Wildcard | Pattern::Var(_) | Pattern::Inaccessible(_) => false,
1661        }
1662    }
1663    /// Record the patterns introduced by `pat` into the tracking sets.
1664    fn record_pattern(
1665        pat: &Pattern,
1666        seen_wildcard: &mut bool,
1667        seen_ctors: &mut std::collections::HashSet<Name>,
1668        seen_lits: &mut std::collections::HashSet<String>,
1669    ) {
1670        match pat {
1671            Pattern::Wildcard | Pattern::Var(_) => *seen_wildcard = true,
1672            Pattern::As(_, inner) => {
1673                Self::record_pattern(inner, seen_wildcard, seen_ctors, seen_lits)
1674            }
1675            Pattern::Constructor(name, _) => {
1676                seen_ctors.insert(name.clone());
1677            }
1678            Pattern::Literal(lit) => {
1679                seen_lits.insert(format!("{:?}", lit));
1680            }
1681            Pattern::Or(pats) => {
1682                for p in pats {
1683                    Self::record_pattern(p, seen_wildcard, seen_ctors, seen_lits);
1684                }
1685            }
1686            Pattern::Inaccessible(_) => {}
1687        }
1688    }
1689}