Skip to main content

oxilean_codegen/opt_ctfe/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::lcnf::{LcnfArg, LcnfExpr, LcnfFunDecl, LcnfLetValue, LcnfLit, LcnfVarId};
6use std::collections::HashMap;
7
8use super::functions::CtfeResult;
9
10use super::functions::*;
11use std::collections::HashSet;
12
13/// CTFE abstract interpreter mode
14#[allow(dead_code)]
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum CtfeMode {
17    FullEval,
18    PartialEval,
19    FoldOnly,
20    Disabled,
21}
22/// CTFE type checker (basic type inference during evaluation)
23#[allow(dead_code)]
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub enum CtfeType {
26    Unit,
27    Bool,
28    Int,
29    Uint,
30    Float,
31    Str,
32    Tuple(Vec<CtfeType>),
33    List(Box<CtfeType>),
34    Named(String),
35    Unknown,
36}
37/// CTFE name generator
38#[allow(dead_code)]
39#[derive(Debug, Default)]
40pub struct CtfeNameGen {
41    pub(super) counter: u64,
42    pub(super) prefix: String,
43}
44#[allow(dead_code)]
45impl CtfeNameGen {
46    pub fn new(prefix: &str) -> Self {
47        Self {
48            counter: 0,
49            prefix: prefix.to_string(),
50        }
51    }
52    pub fn next(&mut self) -> String {
53        let id = self.counter;
54        self.counter += 1;
55        format!("{}{}", self.prefix, id)
56    }
57    pub fn reset(&mut self) {
58        self.counter = 0;
59    }
60}
61/// Summary statistics for a CTFE pass run.
62#[derive(Debug, Clone, Default)]
63pub struct CtfeReport {
64    /// Number of top-level functions that were fully evaluated.
65    pub functions_evaluated: usize,
66    /// Number of call sites replaced with a constant value.
67    pub calls_replaced: usize,
68    /// Number of constants propagated across function boundaries.
69    pub constants_propagated: usize,
70    /// Number of functions whose evaluation was aborted due to fuel exhaustion.
71    pub fuel_exhausted_count: usize,
72}
73/// CTFE call stack
74#[allow(dead_code)]
75#[derive(Debug, Default)]
76pub struct CtfeCallStack {
77    pub frames: Vec<CtfeCallFrame>,
78    pub max_depth: usize,
79}
80#[allow(dead_code)]
81impl CtfeCallStack {
82    pub fn new(max_depth: usize) -> Self {
83        Self {
84            frames: Vec::new(),
85            max_depth,
86        }
87    }
88    pub fn push(&mut self, func: &str, args: Vec<CtfeValueExt>) -> bool {
89        if self.frames.len() >= self.max_depth {
90            return false;
91        }
92        self.frames.push(CtfeCallFrame {
93            func_name: func.to_string(),
94            args,
95            depth: self.frames.len(),
96        });
97        true
98    }
99    pub fn pop(&mut self) -> Option<CtfeCallFrame> {
100        self.frames.pop()
101    }
102    pub fn depth(&self) -> usize {
103        self.frames.len()
104    }
105    pub fn is_recursing(&self, func: &str) -> bool {
106        self.frames.iter().any(|f| f.func_name == func)
107    }
108    pub fn stack_trace(&self) -> Vec<String> {
109        self.frames
110            .iter()
111            .rev()
112            .map(|f| format!("  at {}(...)", f.func_name))
113            .collect()
114    }
115}
116/// CTFE diagnostic severity
117#[allow(dead_code)]
118#[derive(Debug, Clone, PartialEq, Eq)]
119pub enum CtfeDiagLevel {
120    Debug,
121    Info,
122    Warning,
123    Error,
124}
125/// CTFE evaluation result
126#[allow(dead_code)]
127#[derive(Debug, Clone)]
128pub struct CtfeEvalResult {
129    pub value: Option<CtfeValueExt>,
130    pub fuel_used: u64,
131    pub steps: usize,
132    pub stack_depth_max: usize,
133    pub memo_hit: bool,
134}
135/// Arithmetic / comparison operator identifiers.
136#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
137pub enum BinOp {
138    Add,
139    Sub,
140    Mul,
141    Div,
142    Mod,
143    And,
144    Or,
145    Xor,
146    Shl,
147    Shr,
148    Eq,
149    Ne,
150    Lt,
151    Le,
152    Gt,
153    Ge,
154}
155impl BinOp {
156    /// Parse a common operator name into a `BinOp`.
157    pub fn from_name(name: &str) -> Option<BinOp> {
158        match name {
159            "add" | "+" | "Nat.add" | "Int.add" => Some(BinOp::Add),
160            "sub" | "-" | "Nat.sub" | "Int.sub" => Some(BinOp::Sub),
161            "mul" | "*" | "Nat.mul" | "Int.mul" => Some(BinOp::Mul),
162            "div" | "/" | "Nat.div" | "Int.div" => Some(BinOp::Div),
163            "mod" | "%" | "Nat.mod" | "Int.mod" => Some(BinOp::Mod),
164            "and" | "&&" | "Bool.and" => Some(BinOp::And),
165            "or" | "||" | "Bool.or" => Some(BinOp::Or),
166            "xor" | "Bool.xor" => Some(BinOp::Xor),
167            "shl" | "<<" => Some(BinOp::Shl),
168            "shr" | ">>" => Some(BinOp::Shr),
169            "eq" | "==" | "Eq" => Some(BinOp::Eq),
170            "ne" | "!=" | "Ne" => Some(BinOp::Ne),
171            "lt" | "<" | "Nat.lt" => Some(BinOp::Lt),
172            "le" | "<=" | "Nat.le" => Some(BinOp::Le),
173            "gt" | ">" | "Nat.gt" => Some(BinOp::Gt),
174            "ge" | ">=" | "Nat.ge" => Some(BinOp::Ge),
175            _ => None,
176        }
177    }
178}
179/// CTFE pass statistics (extended)
180#[allow(dead_code)]
181#[derive(Debug, Default, Clone)]
182pub struct CtfePassStatsExt {
183    pub functions_attempted: usize,
184    pub functions_evaluated: usize,
185    pub calls_replaced: usize,
186    pub constants_folded: usize,
187    pub memo_hits: u64,
188    pub memo_misses: u64,
189    pub fuel_used: u64,
190    pub fuel_exhausted_count: usize,
191    pub max_stack_depth_reached: usize,
192    pub errors: usize,
193}
194/// CTFE evaluator configuration (extended)
195#[allow(dead_code)]
196#[derive(Debug, Clone)]
197pub struct CtfeConfigExt {
198    pub fuel: u64,
199    pub max_depth: usize,
200    pub max_list_size: usize,
201    pub max_string_size: usize,
202    pub enable_memoization: bool,
203    pub enable_logging: bool,
204    pub replace_calls: bool,
205    pub propagate_constants: bool,
206    pub fold_arithmetic: bool,
207    pub fold_boolean: bool,
208    pub fold_string: bool,
209    pub fold_comparison: bool,
210}
211/// CTFE evaluation trace entry
212#[allow(dead_code)]
213#[derive(Debug, Clone)]
214pub struct CtfeTraceEntry {
215    pub depth: usize,
216    pub func: String,
217    pub args_repr: String,
218    pub result_repr: Option<String>,
219}
220/// A fully-evaluated compile-time value.
221#[derive(Debug, Clone, PartialEq)]
222pub enum CtfeValue {
223    /// Signed 64-bit integer (covers Nat for small values).
224    Int(i64),
225    /// 64-bit floating-point value.
226    Float(f64),
227    /// Boolean value.
228    Bool(bool),
229    /// String value.
230    String(String),
231    /// A heterogeneous list of values.
232    List(Vec<CtfeValue>),
233    /// A tuple of values.
234    Tuple(Vec<CtfeValue>),
235    /// An algebraic data type constructor: `name(fields...)`.
236    Constructor(String, Vec<CtfeValue>),
237    /// Undefined / not yet evaluated (bottom).
238    Undef,
239}
240impl CtfeValue {
241    /// Return the underlying integer, if any.
242    pub fn as_int(&self) -> Option<i64> {
243        match self {
244            CtfeValue::Int(n) => Some(*n),
245            _ => None,
246        }
247    }
248    /// Return the underlying bool, if any.
249    pub fn as_bool(&self) -> Option<bool> {
250        match self {
251            CtfeValue::Bool(b) => Some(*b),
252            _ => None,
253        }
254    }
255    /// Return the underlying string, if any.
256    pub fn as_str(&self) -> Option<&str> {
257        match self {
258            CtfeValue::String(s) => Some(s.as_str()),
259            _ => None,
260        }
261    }
262    /// `true` if the value is fully concrete (no `Undef` components).
263    pub fn is_concrete(&self) -> bool {
264        match self {
265            CtfeValue::Undef => false,
266            CtfeValue::List(xs) | CtfeValue::Tuple(xs) => xs.iter().all(|v| v.is_concrete()),
267            CtfeValue::Constructor(_, fields) => fields.iter().all(|v| v.is_concrete()),
268            _ => true,
269        }
270    }
271}
272/// CTFE profiler
273#[allow(dead_code)]
274#[derive(Debug, Default)]
275pub struct CtfeExtProfiler {
276    pub timings: Vec<(String, u64)>,
277}
278#[allow(dead_code)]
279impl CtfeExtProfiler {
280    pub fn new() -> Self {
281        Self::default()
282    }
283    pub fn record(&mut self, pass: &str, us: u64) {
284        self.timings.push((pass.to_string(), us));
285    }
286    pub fn total_us(&self) -> u64 {
287        self.timings.iter().map(|(_, t)| *t).sum()
288    }
289    pub fn slowest(&self) -> Option<(&str, u64)> {
290        self.timings
291            .iter()
292            .max_by_key(|(_, t)| *t)
293            .map(|(n, t)| (n.as_str(), *t))
294    }
295}
296/// CTFE value representation (extended)
297#[allow(dead_code)]
298#[derive(Debug, Clone, PartialEq)]
299pub enum CtfeValueExt {
300    Unit,
301    Bool(bool),
302    Int(i64),
303    Uint(u64),
304    Float(f64),
305    Str(String),
306    Tuple(Vec<CtfeValueExt>),
307    List(Vec<CtfeValueExt>),
308    Constructor(String, Vec<CtfeValueExt>),
309    Closure {
310        params: Vec<String>,
311        body: String,
312        env: Vec<(String, CtfeValueExt)>,
313    },
314    Opaque,
315}
316/// CTFE code stats
317#[allow(dead_code)]
318#[derive(Debug, Default, Clone)]
319pub struct CtfeCodeStats {
320    pub constants_discovered: usize,
321    pub folds_applied: usize,
322    pub calls_eliminated: usize,
323    pub loops_unrolled: usize,
324    pub conditions_resolved: usize,
325}
326/// CTFE fuel tracker
327#[allow(dead_code)]
328#[derive(Debug, Clone)]
329pub struct CtfeFuelTracker {
330    pub remaining: u64,
331    pub initial: u64,
332    pub steps_taken: u64,
333}
334#[allow(dead_code)]
335impl CtfeFuelTracker {
336    pub fn new(fuel: u64) -> Self {
337        Self {
338            remaining: fuel,
339            initial: fuel,
340            steps_taken: 0,
341        }
342    }
343    pub fn consume(&mut self, cost: u64) -> bool {
344        if self.remaining < cost {
345            false
346        } else {
347            self.remaining -= cost;
348            self.steps_taken += cost;
349            true
350        }
351    }
352    pub fn is_exhausted(&self) -> bool {
353        self.remaining == 0
354    }
355    pub fn fraction_used(&self) -> f64 {
356        if self.initial == 0 {
357            1.0
358        } else {
359            self.steps_taken as f64 / self.initial as f64
360        }
361    }
362}
363/// CTFE term simplifier
364#[allow(dead_code)]
365#[derive(Debug, Default)]
366pub struct CtfeSimplifier {
367    pub rules_applied: usize,
368    pub memo: std::collections::HashMap<String, CtfeValueExt>,
369}
370#[allow(dead_code)]
371impl CtfeSimplifier {
372    pub fn new() -> Self {
373        Self::default()
374    }
375    pub fn simplify_bool_and(a: CtfeValueExt, b: CtfeValueExt) -> CtfeValueExt {
376        match (a, b) {
377            (CtfeValueExt::Bool(false), _) | (_, CtfeValueExt::Bool(false)) => {
378                CtfeValueExt::Bool(false)
379            }
380            (CtfeValueExt::Bool(true), v) | (v, CtfeValueExt::Bool(true)) => v,
381            (av, bv) => CtfeValueExt::Constructor("And".to_string(), vec![av, bv]),
382        }
383    }
384    pub fn simplify_bool_or(a: CtfeValueExt, b: CtfeValueExt) -> CtfeValueExt {
385        match (a, b) {
386            (CtfeValueExt::Bool(true), _) | (_, CtfeValueExt::Bool(true)) => {
387                CtfeValueExt::Bool(true)
388            }
389            (CtfeValueExt::Bool(false), v) | (v, CtfeValueExt::Bool(false)) => v,
390            (av, bv) => CtfeValueExt::Constructor("Or".to_string(), vec![av, bv]),
391        }
392    }
393    pub fn simplify_add_int(a: CtfeValueExt, b: CtfeValueExt) -> CtfeValueExt {
394        match (a, b) {
395            (CtfeValueExt::Int(x), CtfeValueExt::Int(y)) => CtfeValueExt::Int(x.wrapping_add(y)),
396            (CtfeValueExt::Int(0), v) | (v, CtfeValueExt::Int(0)) => v,
397            (av, bv) => CtfeValueExt::Constructor("Add".to_string(), vec![av, bv]),
398        }
399    }
400    pub fn simplify_mul_int(a: CtfeValueExt, b: CtfeValueExt) -> CtfeValueExt {
401        match (a, b) {
402            (CtfeValueExt::Int(x), CtfeValueExt::Int(y)) => CtfeValueExt::Int(x.wrapping_mul(y)),
403            (CtfeValueExt::Int(0), _) | (_, CtfeValueExt::Int(0)) => CtfeValueExt::Int(0),
404            (CtfeValueExt::Int(1), v) | (v, CtfeValueExt::Int(1)) => v,
405            (av, bv) => CtfeValueExt::Constructor("Mul".to_string(), vec![av, bv]),
406        }
407    }
408}
409/// CTFE source buffer
410#[allow(dead_code)]
411#[derive(Debug, Default)]
412pub struct CtfeExtSourceBuffer {
413    pub content: String,
414}
415#[allow(dead_code)]
416impl CtfeExtSourceBuffer {
417    pub fn new() -> Self {
418        Self::default()
419    }
420    pub fn write(&mut self, s: &str) {
421        self.content.push_str(s);
422    }
423    pub fn writeln(&mut self, s: &str) {
424        self.content.push_str(s);
425        self.content.push('\n');
426    }
427    pub fn finish(self) -> String {
428        self.content
429    }
430}
431/// The CTFE interpreter evaluates LCNF expressions at compile time.
432pub struct CtfeInterpreter {
433    /// Memoisation cache: (function name, args) → value.
434    pub(super) memo: HashMap<(String, Vec<String>), CtfeValue>,
435    /// All function declarations available for inlining.
436    pub(super) decls: HashMap<String, LcnfFunDecl>,
437}
438impl CtfeInterpreter {
439    /// Create a new interpreter with access to a module's declarations.
440    pub fn new(decls: &[LcnfFunDecl]) -> Self {
441        let decl_map = decls.iter().map(|d| (d.name.clone(), d.clone())).collect();
442        CtfeInterpreter {
443            memo: HashMap::new(),
444            decls: decl_map,
445        }
446    }
447    /// Evaluate a literal to a `CtfeValue`.
448    pub fn eval_lit(&self, lit: &LcnfLit) -> CtfeValue {
449        match lit {
450            LcnfLit::Nat(n) => CtfeValue::Int(*n as i64),
451            LcnfLit::Str(s) => CtfeValue::String(s.clone()),
452        }
453    }
454    /// Evaluate an argument in the given context.
455    pub fn eval_arg(&self, arg: &LcnfArg, ctx: &CtfeContext) -> CtfeResult {
456        match arg {
457            LcnfArg::Lit(lit) => Ok(self.eval_lit(lit)),
458            LcnfArg::Var(id) => {
459                ctx.lookup_local(*id)
460                    .cloned()
461                    .ok_or_else(|| CtfeError::NonConstant {
462                        reason: format!("unbound variable {}", id.0),
463                    })
464            }
465            LcnfArg::Erased => Ok(CtfeValue::Undef),
466            LcnfArg::Type(_) => Ok(CtfeValue::Undef),
467        }
468    }
469    /// Evaluate a binary operation.
470    pub fn eval_binop(&self, op: BinOp, lhs: &CtfeValue, rhs: &CtfeValue) -> CtfeResult {
471        match (op, lhs, rhs) {
472            (BinOp::Add, CtfeValue::Int(a), CtfeValue::Int(b)) => a
473                .checked_add(*b)
474                .map(CtfeValue::Int)
475                .ok_or(CtfeError::Overflow {
476                    op: "add".to_string(),
477                }),
478            (BinOp::Sub, CtfeValue::Int(a), CtfeValue::Int(b)) => a
479                .checked_sub(*b)
480                .map(CtfeValue::Int)
481                .ok_or(CtfeError::Overflow {
482                    op: "sub".to_string(),
483                }),
484            (BinOp::Mul, CtfeValue::Int(a), CtfeValue::Int(b)) => a
485                .checked_mul(*b)
486                .map(CtfeValue::Int)
487                .ok_or(CtfeError::Overflow {
488                    op: "mul".to_string(),
489                }),
490            (BinOp::Div, CtfeValue::Int(_), CtfeValue::Int(0)) => Err(CtfeError::DivisionByZero),
491            (BinOp::Div, CtfeValue::Int(a), CtfeValue::Int(b)) => a
492                .checked_div(*b)
493                .map(CtfeValue::Int)
494                .ok_or(CtfeError::Overflow {
495                    op: "div".to_string(),
496                }),
497            (BinOp::Mod, CtfeValue::Int(_), CtfeValue::Int(0)) => Err(CtfeError::DivisionByZero),
498            (BinOp::Mod, CtfeValue::Int(a), CtfeValue::Int(b)) => {
499                Ok(CtfeValue::Int(a.rem_euclid(*b)))
500            }
501            (BinOp::Shl, CtfeValue::Int(a), CtfeValue::Int(b)) if *b >= 0 && *b < 64 => {
502                Ok(CtfeValue::Int(a.wrapping_shl(*b as u32)))
503            }
504            (BinOp::Shr, CtfeValue::Int(a), CtfeValue::Int(b)) if *b >= 0 && *b < 64 => {
505                Ok(CtfeValue::Int(a.wrapping_shr(*b as u32)))
506            }
507            (BinOp::And, CtfeValue::Bool(a), CtfeValue::Bool(b)) => Ok(CtfeValue::Bool(*a && *b)),
508            (BinOp::Or, CtfeValue::Bool(a), CtfeValue::Bool(b)) => Ok(CtfeValue::Bool(*a || *b)),
509            (BinOp::Xor, CtfeValue::Bool(a), CtfeValue::Bool(b)) => Ok(CtfeValue::Bool(*a ^ *b)),
510            (BinOp::Eq, CtfeValue::Int(a), CtfeValue::Int(b)) => Ok(CtfeValue::Bool(a == b)),
511            (BinOp::Ne, CtfeValue::Int(a), CtfeValue::Int(b)) => Ok(CtfeValue::Bool(a != b)),
512            (BinOp::Lt, CtfeValue::Int(a), CtfeValue::Int(b)) => Ok(CtfeValue::Bool(a < b)),
513            (BinOp::Le, CtfeValue::Int(a), CtfeValue::Int(b)) => Ok(CtfeValue::Bool(a <= b)),
514            (BinOp::Gt, CtfeValue::Int(a), CtfeValue::Int(b)) => Ok(CtfeValue::Bool(a > b)),
515            (BinOp::Ge, CtfeValue::Int(a), CtfeValue::Int(b)) => Ok(CtfeValue::Bool(a >= b)),
516            (BinOp::Eq, CtfeValue::String(a), CtfeValue::String(b)) => Ok(CtfeValue::Bool(a == b)),
517            (BinOp::Ne, CtfeValue::String(a), CtfeValue::String(b)) => Ok(CtfeValue::Bool(a != b)),
518            (BinOp::Add, CtfeValue::Float(a), CtfeValue::Float(b)) => Ok(CtfeValue::Float(a + b)),
519            (BinOp::Sub, CtfeValue::Float(a), CtfeValue::Float(b)) => Ok(CtfeValue::Float(a - b)),
520            (BinOp::Mul, CtfeValue::Float(a), CtfeValue::Float(b)) => Ok(CtfeValue::Float(a * b)),
521            (BinOp::Div, CtfeValue::Float(a), CtfeValue::Float(b)) => Ok(CtfeValue::Float(a / b)),
522            _ => Err(CtfeError::NonConstant {
523                reason: format!("unsupported binop {:?} on {:?} {:?}", op, lhs, rhs),
524            }),
525        }
526    }
527    /// Evaluate a function call to a (possibly known) function.
528    pub fn eval_call(
529        &mut self,
530        func_name: &str,
531        args: Vec<CtfeValue>,
532        ctx: &mut CtfeContext,
533    ) -> CtfeResult {
534        ctx.consume_fuel()?;
535        let cache_key = (
536            func_name.to_string(),
537            args.iter().map(|v| v.to_string()).collect::<Vec<_>>(),
538        );
539        if let Some(cached) = self.memo.get(&cache_key) {
540            return Ok(cached.clone());
541        }
542        if let Some(op) = BinOp::from_name(func_name) {
543            if args.len() == 2 {
544                let result = self.eval_binop(op, &args[0], &args[1])?;
545                self.memo.insert(cache_key, result.clone());
546                return Ok(result);
547            }
548        }
549        let decl = match self.decls.get(func_name).cloned() {
550            Some(d) => d,
551            None => {
552                return Err(CtfeError::NonConstant {
553                    reason: format!("unknown function '{}'", func_name),
554                });
555            }
556        };
557        if decl.params.len() != args.len() {
558            return Err(CtfeError::NonConstant {
559                reason: format!(
560                    "arity mismatch for '{}': expected {}, got {}",
561                    func_name,
562                    decl.params.len(),
563                    args.len()
564                ),
565            });
566        }
567        ctx.push_frame()?;
568        let mut child = ctx.child_context();
569        for (param, value) in decl.params.iter().zip(args.iter()) {
570            child.bind_local(param.id, value.clone());
571        }
572        let result = self.eval_expr(&decl.body, &mut child);
573        ctx.merge_fuel_from(&child);
574        ctx.pop_frame();
575        if let Ok(ref v) = result {
576            self.memo.insert(cache_key, v.clone());
577        }
578        result
579    }
580    /// Evaluate a full LCNF expression.
581    pub fn eval_expr(&mut self, expr: &LcnfExpr, ctx: &mut CtfeContext) -> CtfeResult {
582        ctx.consume_fuel()?;
583        match expr {
584            LcnfExpr::Return(arg) => self.eval_arg(arg, ctx),
585            LcnfExpr::Unreachable => Err(CtfeError::NonExhaustiveMatch),
586            LcnfExpr::Let {
587                id, value, body, ..
588            } => {
589                let val = self.eval_let_value(value, ctx)?;
590                ctx.bind_local(*id, val);
591                self.eval_expr(body, ctx)
592            }
593            LcnfExpr::TailCall(func, args) => {
594                let func_name = match func {
595                    LcnfArg::Var(id) => format!("__var_{}", id.0),
596                    _ => {
597                        return Err(CtfeError::NonConstant {
598                            reason: "indirect tail-call".to_string(),
599                        });
600                    }
601                };
602                let arg_vals: Result<Vec<_>, _> =
603                    args.iter().map(|a| self.eval_arg(a, ctx)).collect();
604                self.eval_call(&func_name, arg_vals?, ctx)
605            }
606            LcnfExpr::Case {
607                scrutinee,
608                alts,
609                default,
610                ..
611            } => {
612                let scr_val = ctx.lookup_local(*scrutinee).cloned().ok_or_else(|| {
613                    CtfeError::NonConstant {
614                        reason: format!("case scrutinee {} not bound", scrutinee.0),
615                    }
616                })?;
617                ctx.consume_fuel()?;
618                match &scr_val {
619                    CtfeValue::Constructor(ctor_name, fields) => {
620                        for alt in alts {
621                            if alt.ctor_name == *ctor_name {
622                                let mut child = ctx.child_context();
623                                for (param, field) in alt.params.iter().zip(fields.iter()) {
624                                    child.bind_local(param.id, field.clone());
625                                }
626                                let result = self.eval_expr(&alt.body, &mut child);
627                                ctx.merge_fuel_from(&child);
628                                return result;
629                            }
630                        }
631                        if let Some(def) = default {
632                            return self.eval_expr(def, ctx);
633                        }
634                        Err(CtfeError::NonExhaustiveMatch)
635                    }
636                    CtfeValue::Bool(b) => {
637                        let target_ctor = if *b { "true" } else { "false" };
638                        for alt in alts {
639                            if alt.ctor_name == target_ctor {
640                                return self.eval_expr(&alt.body, ctx);
641                            }
642                        }
643                        if let Some(def) = default {
644                            return self.eval_expr(def, ctx);
645                        }
646                        Err(CtfeError::NonExhaustiveMatch)
647                    }
648                    _ => {
649                        if let Some(def) = default {
650                            return self.eval_expr(def, ctx);
651                        }
652                        Err(CtfeError::NonConstant {
653                            reason: format!("cannot case-split on {:?}", scr_val),
654                        })
655                    }
656                }
657            }
658        }
659    }
660    pub(super) fn eval_let_value(
661        &mut self,
662        value: &LcnfLetValue,
663        ctx: &mut CtfeContext,
664    ) -> CtfeResult {
665        match value {
666            LcnfLetValue::Lit(lit) => Ok(self.eval_lit(lit)),
667            LcnfLetValue::Erased => Ok(CtfeValue::Undef),
668            LcnfLetValue::FVar(id) => {
669                ctx.lookup_local(*id)
670                    .cloned()
671                    .ok_or_else(|| CtfeError::NonConstant {
672                        reason: format!("free variable {}", id.0),
673                    })
674            }
675            LcnfLetValue::App(func, args) => {
676                let arg_vals: Result<Vec<_>, _> =
677                    args.iter().map(|a| self.eval_arg(a, ctx)).collect();
678                let func_name = match func {
679                    LcnfArg::Var(id) => {
680                        if let Some(v) = ctx.lookup_local(*id) {
681                            if let CtfeValue::String(name) = v.clone() {
682                                name
683                            } else {
684                                format!("__var_{}", id.0)
685                            }
686                        } else {
687                            format!("__var_{}", id.0)
688                        }
689                    }
690                    _ => {
691                        return Err(CtfeError::NonConstant {
692                            reason: "non-variable function in App".to_string(),
693                        });
694                    }
695                };
696                self.eval_call(&func_name, arg_vals?, ctx)
697            }
698            LcnfLetValue::Ctor(name, _tag, args) => {
699                let field_vals: Result<Vec<_>, _> =
700                    args.iter().map(|a| self.eval_arg(a, ctx)).collect();
701                Ok(CtfeValue::Constructor(name.clone(), field_vals?))
702            }
703            LcnfLetValue::Proj(_struct_name, idx, var) => {
704                let base =
705                    ctx.lookup_local(*var)
706                        .cloned()
707                        .ok_or_else(|| CtfeError::NonConstant {
708                            reason: format!("proj base {} not bound", var.0),
709                        })?;
710                match &base {
711                    CtfeValue::Constructor(_, fields) => fields
712                        .get(*idx as usize)
713                        .cloned()
714                        .ok_or(CtfeError::BadProjection { field: *idx }),
715                    CtfeValue::Tuple(fields) => fields
716                        .get(*idx as usize)
717                        .cloned()
718                        .ok_or(CtfeError::BadProjection { field: *idx }),
719                    _ => Err(CtfeError::BadProjection { field: *idx }),
720                }
721            }
722            LcnfLetValue::Reset(_) | LcnfLetValue::Reuse(_, _, _, _) => Ok(CtfeValue::Undef),
723        }
724    }
725}
726/// The main CTFE optimisation pass.
727pub struct CtfePass {
728    /// Configuration.
729    pub config: CtfeConfig,
730    /// Global evaluated constants available to downstream passes.
731    pub known_constants: HashMap<String, CtfeValue>,
732    /// Report accumulated during the pass.
733    pub(super) report: CtfeReport,
734}
735impl CtfePass {
736    /// Create a new pass with the given configuration.
737    pub fn new(config: CtfeConfig) -> Self {
738        CtfePass {
739            config,
740            known_constants: HashMap::new(),
741            report: CtfeReport::default(),
742        }
743    }
744    /// Create a pass with default configuration.
745    pub fn default_pass() -> Self {
746        Self::new(CtfeConfig::default())
747    }
748    /// Run the CTFE pass over all declarations.
749    ///
750    /// First all zero-argument (constant) functions are evaluated, then
751    /// call sites inside every function are replaced with their constant
752    /// folded results.
753    pub fn run(&mut self, decls: &mut [LcnfFunDecl]) {
754        self.known_constants.clear();
755        self.report = CtfeReport::default();
756        let mut interp = CtfeInterpreter::new(decls);
757        for decl in decls.iter() {
758            self.try_evaluate_decl(decl, &mut interp);
759        }
760        if self.config.replace_calls {
761            for decl in decls.iter_mut() {
762                let replaced = self.replace_calls_with_constants(decl);
763                self.report.calls_replaced += replaced;
764            }
765        }
766    }
767    /// Attempt to evaluate `decl` at compile time, storing the result in
768    /// `self.known_constants` if successful.
769    pub fn try_evaluate_decl(&mut self, decl: &LcnfFunDecl, interp: &mut CtfeInterpreter) {
770        if !decl.params.is_empty() {
771            return;
772        }
773        let mut ctx = CtfeContext::with_fuel(self.config.fuel);
774        ctx.max_depth = self.config.max_depth;
775        for (name, val) in &self.known_constants {
776            ctx.constants.insert(name.clone(), val.clone());
777        }
778        match interp.eval_expr(&decl.body, &mut ctx) {
779            Ok(value) if value.is_concrete() => {
780                self.known_constants.insert(decl.name.clone(), value);
781                self.report.functions_evaluated += 1;
782            }
783            Err(CtfeError::Timeout { .. }) => {
784                self.report.fuel_exhausted_count += 1;
785            }
786            _ => {}
787        }
788    }
789    /// Rewrite expressions in `decl` to replace calls to known constants.
790    ///
791    /// Returns the number of replacements performed.
792    pub fn replace_calls_with_constants(&mut self, decl: &mut LcnfFunDecl) -> usize {
793        let mut count = 0;
794        Self::rewrite_expr(&mut decl.body, &self.known_constants, &mut count);
795        if count > 0 {
796            self.report.constants_propagated += count;
797        }
798        count
799    }
800    /// Produce a copy of the accumulated report.
801    pub fn report(&self) -> CtfeReport {
802        self.report.clone()
803    }
804    pub(super) fn rewrite_expr(
805        expr: &mut LcnfExpr,
806        constants: &HashMap<String, CtfeValue>,
807        count: &mut usize,
808    ) {
809        match expr {
810            LcnfExpr::Let { value, body, .. } => {
811                Self::rewrite_let_value(value, constants, count);
812                Self::rewrite_expr(body, constants, count);
813            }
814            LcnfExpr::Case { alts, default, .. } => {
815                for alt in alts.iter_mut() {
816                    Self::rewrite_expr(&mut alt.body, constants, count);
817                }
818                if let Some(d) = default {
819                    Self::rewrite_expr(d, constants, count);
820                }
821            }
822            LcnfExpr::TailCall(func, _) => {
823                if let LcnfArg::Var(id) = func {
824                    let name = format!("__var_{}", id.0);
825                    if constants.contains_key(&name) {
826                        let val = constants[&name].clone();
827                        *expr = LcnfExpr::Return(ctfe_value_to_arg(&val));
828                        *count += 1;
829                    }
830                }
831            }
832            LcnfExpr::Return(_) | LcnfExpr::Unreachable => {}
833        }
834    }
835    pub(super) fn rewrite_let_value(
836        value: &mut LcnfLetValue,
837        constants: &HashMap<String, CtfeValue>,
838        count: &mut usize,
839    ) {
840        if let LcnfLetValue::App(LcnfArg::Var(id), _) = value {
841            let name = format!("__var_{}", id.0);
842            if let Some(ctfe_val) = constants.get(&name) {
843                *value = ctfe_value_to_let_value(ctfe_val.clone());
844                *count += 1;
845            }
846        }
847    }
848}
849/// CTFE id generator
850#[allow(dead_code)]
851#[derive(Debug, Default)]
852pub struct CtfeExtIdGen {
853    pub(super) counter: u64,
854}
855#[allow(dead_code)]
856impl CtfeExtIdGen {
857    pub fn new() -> Self {
858        Self::default()
859    }
860    pub fn next(&mut self, prefix: &str) -> String {
861        let id = self.counter;
862        self.counter += 1;
863        format!("ctfe_{}_{}", prefix, id)
864    }
865}
866/// CTFE environment (variable bindings during evaluation)
867#[allow(dead_code)]
868#[derive(Debug, Default, Clone)]
869pub struct CtfeEnv {
870    pub bindings: Vec<(String, CtfeValueExt)>,
871}
872#[allow(dead_code)]
873impl CtfeEnv {
874    pub fn new() -> Self {
875        Self::default()
876    }
877    pub fn bind(&mut self, name: String, val: CtfeValueExt) {
878        self.bindings.push((name, val));
879    }
880    pub fn lookup(&self, name: &str) -> Option<&CtfeValueExt> {
881        self.bindings
882            .iter()
883            .rev()
884            .find(|(n, _)| n == name)
885            .map(|(_, v)| v)
886    }
887    pub fn push_scope(&self) -> CtfeEnvScope {
888        CtfeEnvScope {
889            depth: self.bindings.len(),
890        }
891    }
892    pub fn pop_scope(&mut self, scope: CtfeEnvScope) {
893        self.bindings.truncate(scope.depth);
894    }
895}
896/// CTFE call stack frame
897#[allow(dead_code)]
898#[derive(Debug, Clone)]
899pub struct CtfeCallFrame {
900    pub func_name: String,
901    pub args: Vec<CtfeValueExt>,
902    pub depth: usize,
903}
904/// CTFE step result
905#[allow(dead_code)]
906#[derive(Debug, Clone)]
907pub enum CtfeStepResult {
908    Value(CtfeValueExt),
909    Diverge,
910    FuelExhausted,
911    Error(String),
912}
913/// CTFE result log entry
914#[allow(dead_code)]
915#[derive(Debug, Clone)]
916pub struct CtfeLogEntry {
917    pub func: String,
918    pub result: String,
919    pub fuel_used: u64,
920    pub success: bool,
921}
922/// CTFE inlining decision
923#[allow(dead_code)]
924#[derive(Debug, Clone, PartialEq, Eq)]
925pub enum CtfeInlineDecision {
926    AlwaysInline,
927    InlineIfSmall(usize),
928    NeverInline,
929    InlineForCtfe,
930}
931/// Configuration for the CTFE pass.
932#[derive(Debug, Clone)]
933pub struct CtfeConfig {
934    /// Fuel per function evaluation.
935    pub fuel: u64,
936    /// Maximum call-stack depth.
937    pub max_depth: u32,
938    /// Whether to replace call sites with evaluated constants.
939    pub replace_calls: bool,
940    /// Whether to propagate constants across module boundaries.
941    pub cross_boundary_propagation: bool,
942}
943/// CTFE memo cache (memoize pure function calls)
944#[allow(dead_code)]
945#[derive(Debug, Default)]
946pub struct CtfeMemoCache {
947    pub cache: std::collections::HashMap<(String, Vec<String>), CtfeValueExt>,
948    pub hits: u64,
949    pub misses: u64,
950}
951#[allow(dead_code)]
952impl CtfeMemoCache {
953    pub fn new() -> Self {
954        Self::default()
955    }
956    pub fn key(func: &str, args: &[CtfeValueExt]) -> (String, Vec<String>) {
957        (
958            func.to_string(),
959            args.iter().map(|a| a.to_string()).collect(),
960        )
961    }
962    pub fn get(&mut self, func: &str, args: &[CtfeValueExt]) -> Option<CtfeValueExt> {
963        let k = Self::key(func, args);
964        if let Some(v) = self.cache.get(&k) {
965            self.hits += 1;
966            Some(v.clone())
967        } else {
968            self.misses += 1;
969            None
970        }
971    }
972    pub fn insert(&mut self, func: &str, args: &[CtfeValueExt], val: CtfeValueExt) {
973        let k = Self::key(func, args);
974        self.cache.insert(k, val);
975    }
976    pub fn hit_rate(&self) -> f64 {
977        let total = self.hits + self.misses;
978        if total == 0 {
979            0.0
980        } else {
981            self.hits as f64 / total as f64
982        }
983    }
984}
985/// Evaluation context: maps names / variable IDs to their current values.
986#[derive(Debug, Clone)]
987pub struct CtfeContext {
988    /// Global constant bindings (function name → value).
989    pub constants: HashMap<String, CtfeValue>,
990    /// Local variable bindings (var ID → value).
991    pub(super) locals: HashMap<LcnfVarId, CtfeValue>,
992    /// Current recursion depth.
993    pub recursion_depth: u32,
994    /// Maximum recursion depth.
995    pub max_depth: u32,
996    /// Remaining evaluation fuel.
997    pub fuel: u64,
998    /// Total fuel consumed so far.
999    pub fuel_used: u64,
1000}
1001impl CtfeContext {
1002    /// Create a new context with default limits.
1003    pub fn new() -> Self {
1004        CtfeContext {
1005            constants: HashMap::new(),
1006            locals: HashMap::new(),
1007            recursion_depth: 0,
1008            max_depth: 256,
1009            fuel: 10_000,
1010            fuel_used: 0,
1011        }
1012    }
1013    /// Create a context with a custom fuel budget.
1014    pub fn with_fuel(fuel: u64) -> Self {
1015        CtfeContext {
1016            fuel,
1017            ..Self::new()
1018        }
1019    }
1020    /// Bind a local variable.
1021    pub fn bind_local(&mut self, id: LcnfVarId, value: CtfeValue) {
1022        self.locals.insert(id, value);
1023    }
1024    /// Look up a local variable.
1025    pub fn lookup_local(&self, id: LcnfVarId) -> Option<&CtfeValue> {
1026        self.locals.get(&id)
1027    }
1028    /// Consume one unit of fuel, returning `Err(Timeout)` if exhausted.
1029    pub fn consume_fuel(&mut self) -> Result<(), CtfeError> {
1030        if self.fuel == 0 {
1031            return Err(CtfeError::Timeout {
1032                fuel_used: self.fuel_used,
1033            });
1034        }
1035        self.fuel -= 1;
1036        self.fuel_used += 1;
1037        Ok(())
1038    }
1039    /// Push a call frame, returning `Err(StackOverflow)` if too deep.
1040    pub fn push_frame(&mut self) -> Result<(), CtfeError> {
1041        if self.recursion_depth >= self.max_depth {
1042            return Err(CtfeError::StackOverflow {
1043                depth: self.recursion_depth,
1044            });
1045        }
1046        self.recursion_depth += 1;
1047        Ok(())
1048    }
1049    /// Pop a call frame.
1050    pub fn pop_frame(&mut self) {
1051        if self.recursion_depth > 0 {
1052            self.recursion_depth -= 1;
1053        }
1054    }
1055    /// Create a child context for evaluating a sub-expression with fresh locals.
1056    pub fn child_context(&self) -> CtfeContext {
1057        CtfeContext {
1058            constants: self.constants.clone(),
1059            locals: HashMap::new(),
1060            recursion_depth: self.recursion_depth,
1061            max_depth: self.max_depth,
1062            fuel: self.fuel,
1063            fuel_used: self.fuel_used,
1064        }
1065    }
1066    /// Merge fuel consumption back from a child context.
1067    pub fn merge_fuel_from(&mut self, child: &CtfeContext) {
1068        let consumed = child.fuel_used - self.fuel_used;
1069        self.fuel = self.fuel.saturating_sub(consumed);
1070        self.fuel_used = child.fuel_used;
1071    }
1072}
1073#[allow(dead_code)]
1074pub struct CtfeEnvScope {
1075    pub(super) depth: usize,
1076}
1077/// CTFE function table
1078#[allow(dead_code)]
1079#[derive(Debug, Default)]
1080pub struct CtfeFuncTable {
1081    pub funcs: std::collections::HashMap<String, CtfeFuncEntry>,
1082}
1083#[allow(dead_code)]
1084impl CtfeFuncTable {
1085    pub fn new() -> Self {
1086        Self::default()
1087    }
1088    pub fn register(&mut self, entry: CtfeFuncEntry) {
1089        self.funcs.insert(entry.name.clone(), entry);
1090    }
1091    pub fn lookup(&self, name: &str) -> Option<&CtfeFuncEntry> {
1092        self.funcs.get(name)
1093    }
1094    pub fn lookup_mut(&mut self, name: &str) -> Option<&mut CtfeFuncEntry> {
1095        self.funcs.get_mut(name)
1096    }
1097    pub fn is_pure(&self, name: &str) -> bool {
1098        self.funcs.get(name).map(|e| e.is_pure).unwrap_or(false)
1099    }
1100    pub fn is_recursive(&self, name: &str) -> bool {
1101        self.funcs
1102            .get(name)
1103            .map(|e| e.is_recursive)
1104            .unwrap_or(false)
1105    }
1106    pub fn total_calls(&self) -> u64 {
1107        self.funcs.values().map(|e| e.call_count).sum()
1108    }
1109    pub fn hot_functions(&self, threshold: u64) -> Vec<&str> {
1110        self.funcs
1111            .values()
1112            .filter(|e| e.call_count >= threshold)
1113            .map(|e| e.name.as_str())
1114            .collect()
1115    }
1116}
1117/// CTFE pass builder
1118#[allow(dead_code)]
1119#[derive(Debug, Default)]
1120pub struct CtfePassBuilder {
1121    pub config: CtfeConfigExt,
1122    pub func_table: CtfeFuncTable,
1123    pub memo_cache: CtfeMemoCache,
1124    pub diags: CtfeDiagSink,
1125    pub stats: CtfePassStatsExt,
1126}
1127#[allow(dead_code)]
1128impl CtfePassBuilder {
1129    pub fn new() -> Self {
1130        Self::default()
1131    }
1132    pub fn with_config(mut self, cfg: CtfeConfigExt) -> Self {
1133        self.config = cfg;
1134        self
1135    }
1136    pub fn register_func(&mut self, entry: CtfeFuncEntry) {
1137        self.func_table.register(entry);
1138    }
1139    pub fn run_pass(&mut self, func: &str) -> Option<CtfeEvalResult> {
1140        if !self.config.replace_calls {
1141            return None;
1142        }
1143        if let Some(entry) = self.func_table.lookup(func) {
1144            let name = entry.name.clone();
1145            self.stats.functions_attempted += 1;
1146            if entry.is_pure {
1147                self.stats.functions_evaluated += 1;
1148                Some(CtfeEvalResult {
1149                    value: Some(CtfeValueExt::Opaque),
1150                    fuel_used: 1,
1151                    steps: 1,
1152                    stack_depth_max: 1,
1153                    memo_hit: false,
1154                })
1155            } else {
1156                self.diags.push(
1157                    CtfeDiagLevel::Info,
1158                    &format!("skipping impure function: {}", name),
1159                    Some(func),
1160                );
1161                None
1162            }
1163        } else {
1164            self.diags.push(
1165                CtfeDiagLevel::Warning,
1166                "function not found in table",
1167                Some(func),
1168            );
1169            None
1170        }
1171    }
1172    pub fn report(&self) -> String {
1173        format!("{}", self.stats)
1174    }
1175}
1176/// CTFE optimizer state
1177#[allow(dead_code)]
1178#[derive(Debug, Default)]
1179pub struct CtfeOptimizerState {
1180    pub func_list: CtfeFuncList,
1181    pub config: CtfeConfigExt,
1182    pub stats: CtfeCodeStats,
1183    pub diags: CtfeDiagSink,
1184    pub mode: Option<CtfeMode>,
1185}
1186#[allow(dead_code)]
1187impl CtfeOptimizerState {
1188    pub fn new(config: CtfeConfigExt) -> Self {
1189        Self {
1190            config,
1191            ..Default::default()
1192        }
1193    }
1194    pub fn with_mode(mut self, mode: CtfeMode) -> Self {
1195        self.mode = Some(mode);
1196        self
1197    }
1198    pub fn is_enabled(&self) -> bool {
1199        self.mode
1200            .as_ref()
1201            .map(|m| *m != CtfeMode::Disabled)
1202            .unwrap_or(true)
1203    }
1204    pub fn report(&self) -> String {
1205        format!("{}", self.stats)
1206    }
1207}
1208/// CTFE loop analysis (detect and bound loops for termination)
1209#[allow(dead_code)]
1210#[derive(Debug, Clone)]
1211pub struct CtfeLoopBound {
1212    pub loop_var: String,
1213    pub bound: i64,
1214    pub is_ascending: bool,
1215    pub confirmed: bool,
1216}
1217/// CTFE evaluation budget tracker (tracks multiple resources)
1218#[allow(dead_code)]
1219#[derive(Debug, Clone)]
1220pub struct CtfeBudget {
1221    pub fuel_remaining: u64,
1222    pub stack_remaining: usize,
1223    pub memo_size_remaining: usize,
1224    pub allocations: u64,
1225}
1226#[allow(dead_code)]
1227impl CtfeBudget {
1228    pub fn new(fuel: u64, stack: usize, memo: usize) -> Self {
1229        Self {
1230            fuel_remaining: fuel,
1231            stack_remaining: stack,
1232            memo_size_remaining: memo,
1233            allocations: 0,
1234        }
1235    }
1236    pub fn consume_fuel(&mut self, n: u64) -> bool {
1237        if self.fuel_remaining < n {
1238            false
1239        } else {
1240            self.fuel_remaining -= n;
1241            true
1242        }
1243    }
1244    pub fn push_stack(&mut self) -> bool {
1245        if self.stack_remaining == 0 {
1246            false
1247        } else {
1248            self.stack_remaining -= 1;
1249            true
1250        }
1251    }
1252    pub fn pop_stack(&mut self) {
1253        self.stack_remaining += 1;
1254    }
1255    pub fn is_exhausted(&self) -> bool {
1256        self.fuel_remaining == 0 || self.stack_remaining == 0
1257    }
1258}
1259/// CTFE partial evaluation result
1260#[allow(dead_code)]
1261#[derive(Debug, Clone)]
1262pub struct CtfePeResult {
1263    pub residual: String,
1264    pub known_values: Vec<(String, CtfeValueExt)>,
1265    pub fuel_used: u64,
1266}
1267/// CTFE whitelist / blacklist of functions
1268#[allow(dead_code)]
1269#[derive(Debug, Default, Clone)]
1270pub struct CtfeFuncList {
1271    pub names: std::collections::HashSet<String>,
1272    pub is_whitelist: bool,
1273}
1274#[allow(dead_code)]
1275impl CtfeFuncList {
1276    pub fn whitelist() -> Self {
1277        Self {
1278            names: std::collections::HashSet::new(),
1279            is_whitelist: true,
1280        }
1281    }
1282    pub fn blacklist() -> Self {
1283        Self {
1284            names: std::collections::HashSet::new(),
1285            is_whitelist: false,
1286        }
1287    }
1288    pub fn add(&mut self, name: &str) {
1289        self.names.insert(name.to_string());
1290    }
1291    pub fn should_evaluate(&self, name: &str) -> bool {
1292        if self.is_whitelist {
1293            self.names.contains(name)
1294        } else {
1295            !self.names.contains(name)
1296        }
1297    }
1298}
1299/// CTFE pass run summary
1300#[allow(dead_code)]
1301#[derive(Debug, Clone)]
1302pub struct CtfePassSummary {
1303    pub pass_name: String,
1304    pub funcs_processed: usize,
1305    pub replacements: usize,
1306    pub errors: usize,
1307    pub duration_us: u64,
1308}
1309/// CTFE reduction strategy
1310#[allow(dead_code)]
1311#[derive(Debug, Clone, PartialEq, Eq)]
1312pub enum CtfeReductionStrategy {
1313    CallByValue,
1314    CallByName,
1315    CallByNeed,
1316    Normal,
1317}
1318/// CTFE evaluation trace
1319#[allow(dead_code)]
1320#[derive(Debug, Default)]
1321pub struct CtfeTrace {
1322    pub entries: Vec<CtfeTraceEntry>,
1323    pub max_entries: usize,
1324}
1325#[allow(dead_code)]
1326impl CtfeTrace {
1327    pub fn new(max: usize) -> Self {
1328        Self {
1329            entries: Vec::new(),
1330            max_entries: max,
1331        }
1332    }
1333    pub fn push(&mut self, entry: CtfeTraceEntry) {
1334        if self.entries.len() < self.max_entries {
1335            self.entries.push(entry);
1336        }
1337    }
1338    pub fn is_full(&self) -> bool {
1339        self.entries.len() >= self.max_entries
1340    }
1341    pub fn emit(&self) -> String {
1342        self.entries
1343            .iter()
1344            .map(|e| e.to_string())
1345            .collect::<Vec<_>>()
1346            .join("\n")
1347    }
1348}
1349/// CTFE version info
1350#[allow(dead_code)]
1351#[derive(Debug, Clone)]
1352pub struct CtfeVersionInfo {
1353    pub pass_version: u32,
1354    pub min_fuel: u64,
1355    pub max_fuel: u64,
1356    pub supports_memo: bool,
1357    pub supports_partial_eval: bool,
1358}
1359/// CTFE function table entry
1360#[allow(dead_code)]
1361#[derive(Debug, Clone)]
1362pub struct CtfeFuncEntry {
1363    pub name: String,
1364    pub params: Vec<String>,
1365    pub body: String,
1366    pub is_recursive: bool,
1367    pub is_pure: bool,
1368    pub call_count: u64,
1369}
1370/// CTFE constant propagation map
1371#[allow(dead_code)]
1372#[derive(Debug, Default)]
1373pub struct CtfeConstMap {
1374    pub map: std::collections::HashMap<String, CtfeValueExt>,
1375}
1376#[allow(dead_code)]
1377impl CtfeConstMap {
1378    pub fn new() -> Self {
1379        Self::default()
1380    }
1381    pub fn insert(&mut self, var: String, val: CtfeValueExt) {
1382        self.map.insert(var, val);
1383    }
1384    pub fn lookup(&self, var: &str) -> Option<&CtfeValueExt> {
1385        self.map.get(var)
1386    }
1387    pub fn remove(&mut self, var: &str) {
1388        self.map.remove(var);
1389    }
1390    pub fn len(&self) -> usize {
1391        self.map.len()
1392    }
1393    pub fn is_empty(&self) -> bool {
1394        self.map.is_empty()
1395    }
1396    pub fn merge(&mut self, other: &CtfeConstMap) {
1397        for (k, v) in &other.map {
1398            self.map.insert(k.clone(), v.clone());
1399        }
1400    }
1401}
1402#[allow(dead_code)]
1403#[derive(Debug, Clone)]
1404pub struct CtfeDiag {
1405    pub level: CtfeDiagLevel,
1406    pub message: String,
1407    pub func: Option<String>,
1408}
1409/// CTFE numeric range (for range analysis)
1410#[allow(dead_code)]
1411#[derive(Debug, Clone)]
1412pub struct CtfeNumericRange {
1413    pub min: i64,
1414    pub max: i64,
1415    pub known_exact: bool,
1416}
1417#[allow(dead_code)]
1418impl CtfeNumericRange {
1419    pub fn exact(n: i64) -> Self {
1420        Self {
1421            min: n,
1422            max: n,
1423            known_exact: true,
1424        }
1425    }
1426    pub fn range(min: i64, max: i64) -> Self {
1427        Self {
1428            min,
1429            max,
1430            known_exact: false,
1431        }
1432    }
1433    pub fn top() -> Self {
1434        Self {
1435            min: i64::MIN,
1436            max: i64::MAX,
1437            known_exact: false,
1438        }
1439    }
1440    pub fn contains(&self, n: i64) -> bool {
1441        n >= self.min && n <= self.max
1442    }
1443    pub fn width(&self) -> u64 {
1444        (self.max as i128 - self.min as i128).unsigned_abs() as u64 + 1
1445    }
1446    pub fn join(&self, other: &CtfeNumericRange) -> CtfeNumericRange {
1447        CtfeNumericRange {
1448            min: self.min.min(other.min),
1449            max: self.max.max(other.max),
1450            known_exact: false,
1451        }
1452    }
1453}
1454/// CTFE feature flags
1455#[allow(dead_code)]
1456#[derive(Debug, Clone, Default)]
1457pub struct CtfeFeatureFlags {
1458    pub fold_arithmetic: bool,
1459    pub fold_boolean: bool,
1460    pub fold_string: bool,
1461    pub partial_eval: bool,
1462    pub memoize: bool,
1463}
1464#[allow(dead_code)]
1465#[derive(Debug, Default)]
1466pub struct CtfeDiagSink {
1467    pub diags: Vec<CtfeDiag>,
1468}
1469#[allow(dead_code)]
1470impl CtfeDiagSink {
1471    pub fn new() -> Self {
1472        Self::default()
1473    }
1474    pub fn push(&mut self, level: CtfeDiagLevel, msg: &str, func: Option<&str>) {
1475        self.diags.push(CtfeDiag {
1476            level,
1477            message: msg.to_string(),
1478            func: func.map(|s| s.to_string()),
1479        });
1480    }
1481    pub fn has_errors(&self) -> bool {
1482        self.diags.iter().any(|d| d.level == CtfeDiagLevel::Error)
1483    }
1484    pub fn error_messages(&self) -> Vec<&str> {
1485        self.diags
1486            .iter()
1487            .filter(|d| d.level == CtfeDiagLevel::Error)
1488            .map(|d| d.message.as_str())
1489            .collect()
1490    }
1491}
1492/// Errors that can occur during compile-time evaluation.
1493#[derive(Debug, Clone, PartialEq, Eq)]
1494pub enum CtfeError {
1495    /// Division (or modulo) by zero.
1496    DivisionByZero,
1497    /// An array / list index is out of bounds.
1498    IndexOutOfBounds { index: i64, length: usize },
1499    /// The recursion depth limit was hit.
1500    StackOverflow { depth: u32 },
1501    /// The expression is not constant (contains free variables or I/O).
1502    NonConstant { reason: String },
1503    /// Fuel exhausted — the evaluation took too many steps.
1504    Timeout { fuel_used: u64 },
1505    /// Integer arithmetic overflow.
1506    Overflow { op: String },
1507    /// Attempted to project out of a non-constructor value.
1508    BadProjection { field: u32 },
1509    /// Pattern match is not exhaustive at compile time.
1510    NonExhaustiveMatch,
1511}