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::Int(i) => CtfeValue::Int(*i),
452            LcnfLit::Str(s) => CtfeValue::String(s.clone()),
453        }
454    }
455    /// Evaluate an argument in the given context.
456    pub fn eval_arg(&self, arg: &LcnfArg, ctx: &CtfeContext) -> CtfeResult {
457        match arg {
458            LcnfArg::Lit(lit) => Ok(self.eval_lit(lit)),
459            LcnfArg::Var(id) => {
460                ctx.lookup_local(*id)
461                    .cloned()
462                    .ok_or_else(|| CtfeError::NonConstant {
463                        reason: format!("unbound variable {}", id.0),
464                    })
465            }
466            LcnfArg::Erased => Ok(CtfeValue::Undef),
467            LcnfArg::Type(_) => Ok(CtfeValue::Undef),
468        }
469    }
470    /// Evaluate a binary operation.
471    pub fn eval_binop(&self, op: BinOp, lhs: &CtfeValue, rhs: &CtfeValue) -> CtfeResult {
472        match (op, lhs, rhs) {
473            (BinOp::Add, CtfeValue::Int(a), CtfeValue::Int(b)) => a
474                .checked_add(*b)
475                .map(CtfeValue::Int)
476                .ok_or(CtfeError::Overflow {
477                    op: "add".to_string(),
478                }),
479            (BinOp::Sub, CtfeValue::Int(a), CtfeValue::Int(b)) => a
480                .checked_sub(*b)
481                .map(CtfeValue::Int)
482                .ok_or(CtfeError::Overflow {
483                    op: "sub".to_string(),
484                }),
485            (BinOp::Mul, CtfeValue::Int(a), CtfeValue::Int(b)) => a
486                .checked_mul(*b)
487                .map(CtfeValue::Int)
488                .ok_or(CtfeError::Overflow {
489                    op: "mul".to_string(),
490                }),
491            (BinOp::Div, CtfeValue::Int(_), CtfeValue::Int(0)) => Err(CtfeError::DivisionByZero),
492            (BinOp::Div, CtfeValue::Int(a), CtfeValue::Int(b)) => a
493                .checked_div(*b)
494                .map(CtfeValue::Int)
495                .ok_or(CtfeError::Overflow {
496                    op: "div".to_string(),
497                }),
498            (BinOp::Mod, CtfeValue::Int(_), CtfeValue::Int(0)) => Err(CtfeError::DivisionByZero),
499            (BinOp::Mod, CtfeValue::Int(a), CtfeValue::Int(b)) => {
500                Ok(CtfeValue::Int(a.rem_euclid(*b)))
501            }
502            (BinOp::Shl, CtfeValue::Int(a), CtfeValue::Int(b)) if *b >= 0 && *b < 64 => {
503                Ok(CtfeValue::Int(a.wrapping_shl(*b as u32)))
504            }
505            (BinOp::Shr, CtfeValue::Int(a), CtfeValue::Int(b)) if *b >= 0 && *b < 64 => {
506                Ok(CtfeValue::Int(a.wrapping_shr(*b as u32)))
507            }
508            (BinOp::And, CtfeValue::Bool(a), CtfeValue::Bool(b)) => Ok(CtfeValue::Bool(*a && *b)),
509            (BinOp::Or, CtfeValue::Bool(a), CtfeValue::Bool(b)) => Ok(CtfeValue::Bool(*a || *b)),
510            (BinOp::Xor, CtfeValue::Bool(a), CtfeValue::Bool(b)) => Ok(CtfeValue::Bool(*a ^ *b)),
511            (BinOp::Eq, CtfeValue::Int(a), CtfeValue::Int(b)) => Ok(CtfeValue::Bool(a == b)),
512            (BinOp::Ne, CtfeValue::Int(a), CtfeValue::Int(b)) => Ok(CtfeValue::Bool(a != b)),
513            (BinOp::Lt, CtfeValue::Int(a), CtfeValue::Int(b)) => Ok(CtfeValue::Bool(a < b)),
514            (BinOp::Le, CtfeValue::Int(a), CtfeValue::Int(b)) => Ok(CtfeValue::Bool(a <= b)),
515            (BinOp::Gt, CtfeValue::Int(a), CtfeValue::Int(b)) => Ok(CtfeValue::Bool(a > b)),
516            (BinOp::Ge, CtfeValue::Int(a), CtfeValue::Int(b)) => Ok(CtfeValue::Bool(a >= b)),
517            (BinOp::Eq, CtfeValue::String(a), CtfeValue::String(b)) => Ok(CtfeValue::Bool(a == b)),
518            (BinOp::Ne, CtfeValue::String(a), CtfeValue::String(b)) => Ok(CtfeValue::Bool(a != b)),
519            (BinOp::Add, CtfeValue::Float(a), CtfeValue::Float(b)) => Ok(CtfeValue::Float(a + b)),
520            (BinOp::Sub, CtfeValue::Float(a), CtfeValue::Float(b)) => Ok(CtfeValue::Float(a - b)),
521            (BinOp::Mul, CtfeValue::Float(a), CtfeValue::Float(b)) => Ok(CtfeValue::Float(a * b)),
522            (BinOp::Div, CtfeValue::Float(a), CtfeValue::Float(b)) => Ok(CtfeValue::Float(a / b)),
523            _ => Err(CtfeError::NonConstant {
524                reason: format!("unsupported binop {:?} on {:?} {:?}", op, lhs, rhs),
525            }),
526        }
527    }
528    /// Evaluate a function call to a (possibly known) function.
529    pub fn eval_call(
530        &mut self,
531        func_name: &str,
532        args: Vec<CtfeValue>,
533        ctx: &mut CtfeContext,
534    ) -> CtfeResult {
535        ctx.consume_fuel()?;
536        let cache_key = (
537            func_name.to_string(),
538            args.iter().map(|v| v.to_string()).collect::<Vec<_>>(),
539        );
540        if let Some(cached) = self.memo.get(&cache_key) {
541            return Ok(cached.clone());
542        }
543        if let Some(op) = BinOp::from_name(func_name) {
544            if args.len() == 2 {
545                let result = self.eval_binop(op, &args[0], &args[1])?;
546                self.memo.insert(cache_key, result.clone());
547                return Ok(result);
548            }
549        }
550        let decl = match self.decls.get(func_name).cloned() {
551            Some(d) => d,
552            None => {
553                return Err(CtfeError::NonConstant {
554                    reason: format!("unknown function '{}'", func_name),
555                });
556            }
557        };
558        if decl.params.len() != args.len() {
559            return Err(CtfeError::NonConstant {
560                reason: format!(
561                    "arity mismatch for '{}': expected {}, got {}",
562                    func_name,
563                    decl.params.len(),
564                    args.len()
565                ),
566            });
567        }
568        ctx.push_frame()?;
569        let mut child = ctx.child_context();
570        for (param, value) in decl.params.iter().zip(args.iter()) {
571            child.bind_local(param.id, value.clone());
572        }
573        let result = self.eval_expr(&decl.body, &mut child);
574        ctx.merge_fuel_from(&child);
575        ctx.pop_frame();
576        if let Ok(ref v) = result {
577            self.memo.insert(cache_key, v.clone());
578        }
579        result
580    }
581    /// Evaluate a full LCNF expression.
582    pub fn eval_expr(&mut self, expr: &LcnfExpr, ctx: &mut CtfeContext) -> CtfeResult {
583        ctx.consume_fuel()?;
584        match expr {
585            LcnfExpr::Return(arg) => self.eval_arg(arg, ctx),
586            LcnfExpr::Unreachable => Err(CtfeError::NonExhaustiveMatch),
587            LcnfExpr::Let {
588                id, value, body, ..
589            } => {
590                let val = self.eval_let_value(value, ctx)?;
591                ctx.bind_local(*id, val);
592                self.eval_expr(body, ctx)
593            }
594            LcnfExpr::TailCall(func, args) => {
595                let func_name = match func {
596                    LcnfArg::Var(id) => format!("__var_{}", id.0),
597                    _ => {
598                        return Err(CtfeError::NonConstant {
599                            reason: "indirect tail-call".to_string(),
600                        });
601                    }
602                };
603                let arg_vals: Result<Vec<_>, _> =
604                    args.iter().map(|a| self.eval_arg(a, ctx)).collect();
605                self.eval_call(&func_name, arg_vals?, ctx)
606            }
607            LcnfExpr::Case {
608                scrutinee,
609                alts,
610                default,
611                ..
612            } => {
613                let scr_val = ctx.lookup_local(*scrutinee).cloned().ok_or_else(|| {
614                    CtfeError::NonConstant {
615                        reason: format!("case scrutinee {} not bound", scrutinee.0),
616                    }
617                })?;
618                ctx.consume_fuel()?;
619                match &scr_val {
620                    CtfeValue::Constructor(ctor_name, fields) => {
621                        for alt in alts {
622                            if alt.ctor_name == *ctor_name {
623                                let mut child = ctx.child_context();
624                                for (param, field) in alt.params.iter().zip(fields.iter()) {
625                                    child.bind_local(param.id, field.clone());
626                                }
627                                let result = self.eval_expr(&alt.body, &mut child);
628                                ctx.merge_fuel_from(&child);
629                                return result;
630                            }
631                        }
632                        if let Some(def) = default {
633                            return self.eval_expr(def, ctx);
634                        }
635                        Err(CtfeError::NonExhaustiveMatch)
636                    }
637                    CtfeValue::Bool(b) => {
638                        let target_ctor = if *b { "true" } else { "false" };
639                        for alt in alts {
640                            if alt.ctor_name == target_ctor {
641                                return self.eval_expr(&alt.body, ctx);
642                            }
643                        }
644                        if let Some(def) = default {
645                            return self.eval_expr(def, ctx);
646                        }
647                        Err(CtfeError::NonExhaustiveMatch)
648                    }
649                    _ => {
650                        if let Some(def) = default {
651                            return self.eval_expr(def, ctx);
652                        }
653                        Err(CtfeError::NonConstant {
654                            reason: format!("cannot case-split on {:?}", scr_val),
655                        })
656                    }
657                }
658            }
659        }
660    }
661    pub(super) fn eval_let_value(
662        &mut self,
663        value: &LcnfLetValue,
664        ctx: &mut CtfeContext,
665    ) -> CtfeResult {
666        match value {
667            LcnfLetValue::Lit(lit) => Ok(self.eval_lit(lit)),
668            LcnfLetValue::Erased => Ok(CtfeValue::Undef),
669            LcnfLetValue::FVar(id) => {
670                ctx.lookup_local(*id)
671                    .cloned()
672                    .ok_or_else(|| CtfeError::NonConstant {
673                        reason: format!("free variable {}", id.0),
674                    })
675            }
676            LcnfLetValue::App(func, args) => {
677                let arg_vals: Result<Vec<_>, _> =
678                    args.iter().map(|a| self.eval_arg(a, ctx)).collect();
679                let func_name = match func {
680                    LcnfArg::Var(id) => {
681                        if let Some(v) = ctx.lookup_local(*id) {
682                            if let CtfeValue::String(name) = v.clone() {
683                                name
684                            } else {
685                                format!("__var_{}", id.0)
686                            }
687                        } else {
688                            format!("__var_{}", id.0)
689                        }
690                    }
691                    _ => {
692                        return Err(CtfeError::NonConstant {
693                            reason: "non-variable function in App".to_string(),
694                        });
695                    }
696                };
697                self.eval_call(&func_name, arg_vals?, ctx)
698            }
699            LcnfLetValue::Ctor(name, _tag, args) => {
700                let field_vals: Result<Vec<_>, _> =
701                    args.iter().map(|a| self.eval_arg(a, ctx)).collect();
702                Ok(CtfeValue::Constructor(name.clone(), field_vals?))
703            }
704            LcnfLetValue::Proj(_struct_name, idx, var) => {
705                let base =
706                    ctx.lookup_local(*var)
707                        .cloned()
708                        .ok_or_else(|| CtfeError::NonConstant {
709                            reason: format!("proj base {} not bound", var.0),
710                        })?;
711                match &base {
712                    CtfeValue::Constructor(_, fields) => fields
713                        .get(*idx as usize)
714                        .cloned()
715                        .ok_or(CtfeError::BadProjection { field: *idx }),
716                    CtfeValue::Tuple(fields) => fields
717                        .get(*idx as usize)
718                        .cloned()
719                        .ok_or(CtfeError::BadProjection { field: *idx }),
720                    _ => Err(CtfeError::BadProjection { field: *idx }),
721                }
722            }
723            LcnfLetValue::Reset(_) | LcnfLetValue::Reuse(_, _, _, _) => Ok(CtfeValue::Undef),
724        }
725    }
726}
727/// The main CTFE optimisation pass.
728pub struct CtfePass {
729    /// Configuration.
730    pub config: CtfeConfig,
731    /// Global evaluated constants available to downstream passes.
732    pub known_constants: HashMap<String, CtfeValue>,
733    /// Report accumulated during the pass.
734    pub(super) report: CtfeReport,
735}
736impl CtfePass {
737    /// Create a new pass with the given configuration.
738    pub fn new(config: CtfeConfig) -> Self {
739        CtfePass {
740            config,
741            known_constants: HashMap::new(),
742            report: CtfeReport::default(),
743        }
744    }
745    /// Create a pass with default configuration.
746    pub fn default_pass() -> Self {
747        Self::new(CtfeConfig::default())
748    }
749    /// Run the CTFE pass over all declarations.
750    ///
751    /// First all zero-argument (constant) functions are evaluated, then
752    /// call sites inside every function are replaced with their constant
753    /// folded results.
754    pub fn run(&mut self, decls: &mut [LcnfFunDecl]) {
755        self.known_constants.clear();
756        self.report = CtfeReport::default();
757        let mut interp = CtfeInterpreter::new(decls);
758        for decl in decls.iter() {
759            self.try_evaluate_decl(decl, &mut interp);
760        }
761        if self.config.replace_calls {
762            for decl in decls.iter_mut() {
763                let replaced = self.replace_calls_with_constants(decl);
764                self.report.calls_replaced += replaced;
765            }
766        }
767    }
768    /// Attempt to evaluate `decl` at compile time, storing the result in
769    /// `self.known_constants` if successful.
770    pub fn try_evaluate_decl(&mut self, decl: &LcnfFunDecl, interp: &mut CtfeInterpreter) {
771        if !decl.params.is_empty() {
772            return;
773        }
774        let mut ctx = CtfeContext::with_fuel(self.config.fuel);
775        ctx.max_depth = self.config.max_depth;
776        for (name, val) in &self.known_constants {
777            ctx.constants.insert(name.clone(), val.clone());
778        }
779        match interp.eval_expr(&decl.body, &mut ctx) {
780            Ok(value) if value.is_concrete() => {
781                self.known_constants.insert(decl.name.clone(), value);
782                self.report.functions_evaluated += 1;
783            }
784            Err(CtfeError::Timeout { .. }) => {
785                self.report.fuel_exhausted_count += 1;
786            }
787            _ => {}
788        }
789    }
790    /// Rewrite expressions in `decl` to replace calls to known constants.
791    ///
792    /// Returns the number of replacements performed.
793    pub fn replace_calls_with_constants(&mut self, decl: &mut LcnfFunDecl) -> usize {
794        let mut count = 0;
795        Self::rewrite_expr(&mut decl.body, &self.known_constants, &mut count);
796        if count > 0 {
797            self.report.constants_propagated += count;
798        }
799        count
800    }
801    /// Produce a copy of the accumulated report.
802    pub fn report(&self) -> CtfeReport {
803        self.report.clone()
804    }
805    pub(super) fn rewrite_expr(
806        expr: &mut LcnfExpr,
807        constants: &HashMap<String, CtfeValue>,
808        count: &mut usize,
809    ) {
810        match expr {
811            LcnfExpr::Let { value, body, .. } => {
812                Self::rewrite_let_value(value, constants, count);
813                Self::rewrite_expr(body, constants, count);
814            }
815            LcnfExpr::Case { alts, default, .. } => {
816                for alt in alts.iter_mut() {
817                    Self::rewrite_expr(&mut alt.body, constants, count);
818                }
819                if let Some(d) = default {
820                    Self::rewrite_expr(d, constants, count);
821                }
822            }
823            LcnfExpr::TailCall(func, _) => {
824                if let LcnfArg::Var(id) = func {
825                    let name = format!("__var_{}", id.0);
826                    if constants.contains_key(&name) {
827                        let val = constants[&name].clone();
828                        *expr = LcnfExpr::Return(ctfe_value_to_arg(&val));
829                        *count += 1;
830                    }
831                }
832            }
833            LcnfExpr::Return(_) | LcnfExpr::Unreachable => {}
834        }
835    }
836    pub(super) fn rewrite_let_value(
837        value: &mut LcnfLetValue,
838        constants: &HashMap<String, CtfeValue>,
839        count: &mut usize,
840    ) {
841        if let LcnfLetValue::App(LcnfArg::Var(id), _) = value {
842            let name = format!("__var_{}", id.0);
843            if let Some(ctfe_val) = constants.get(&name) {
844                *value = ctfe_value_to_let_value(ctfe_val.clone());
845                *count += 1;
846            }
847        }
848    }
849}
850/// CTFE id generator
851#[allow(dead_code)]
852#[derive(Debug, Default)]
853pub struct CtfeExtIdGen {
854    pub(super) counter: u64,
855}
856#[allow(dead_code)]
857impl CtfeExtIdGen {
858    pub fn new() -> Self {
859        Self::default()
860    }
861    pub fn next(&mut self, prefix: &str) -> String {
862        let id = self.counter;
863        self.counter += 1;
864        format!("ctfe_{}_{}", prefix, id)
865    }
866}
867/// CTFE environment (variable bindings during evaluation)
868#[allow(dead_code)]
869#[derive(Debug, Default, Clone)]
870pub struct CtfeEnv {
871    pub bindings: Vec<(String, CtfeValueExt)>,
872}
873#[allow(dead_code)]
874impl CtfeEnv {
875    pub fn new() -> Self {
876        Self::default()
877    }
878    pub fn bind(&mut self, name: String, val: CtfeValueExt) {
879        self.bindings.push((name, val));
880    }
881    pub fn lookup(&self, name: &str) -> Option<&CtfeValueExt> {
882        self.bindings
883            .iter()
884            .rev()
885            .find(|(n, _)| n == name)
886            .map(|(_, v)| v)
887    }
888    pub fn push_scope(&self) -> CtfeEnvScope {
889        CtfeEnvScope {
890            depth: self.bindings.len(),
891        }
892    }
893    pub fn pop_scope(&mut self, scope: CtfeEnvScope) {
894        self.bindings.truncate(scope.depth);
895    }
896}
897/// CTFE call stack frame
898#[allow(dead_code)]
899#[derive(Debug, Clone)]
900pub struct CtfeCallFrame {
901    pub func_name: String,
902    pub args: Vec<CtfeValueExt>,
903    pub depth: usize,
904}
905/// CTFE step result
906#[allow(dead_code)]
907#[derive(Debug, Clone)]
908pub enum CtfeStepResult {
909    Value(CtfeValueExt),
910    Diverge,
911    FuelExhausted,
912    Error(String),
913}
914/// CTFE result log entry
915#[allow(dead_code)]
916#[derive(Debug, Clone)]
917pub struct CtfeLogEntry {
918    pub func: String,
919    pub result: String,
920    pub fuel_used: u64,
921    pub success: bool,
922}
923/// CTFE inlining decision
924#[allow(dead_code)]
925#[derive(Debug, Clone, PartialEq, Eq)]
926pub enum CtfeInlineDecision {
927    AlwaysInline,
928    InlineIfSmall(usize),
929    NeverInline,
930    InlineForCtfe,
931}
932/// Configuration for the CTFE pass.
933#[derive(Debug, Clone)]
934pub struct CtfeConfig {
935    /// Fuel per function evaluation.
936    pub fuel: u64,
937    /// Maximum call-stack depth.
938    pub max_depth: u32,
939    /// Whether to replace call sites with evaluated constants.
940    pub replace_calls: bool,
941    /// Whether to propagate constants across module boundaries.
942    pub cross_boundary_propagation: bool,
943}
944/// CTFE memo cache (memoize pure function calls)
945#[allow(dead_code)]
946#[derive(Debug, Default)]
947pub struct CtfeMemoCache {
948    pub cache: std::collections::HashMap<(String, Vec<String>), CtfeValueExt>,
949    pub hits: u64,
950    pub misses: u64,
951}
952#[allow(dead_code)]
953impl CtfeMemoCache {
954    pub fn new() -> Self {
955        Self::default()
956    }
957    pub fn key(func: &str, args: &[CtfeValueExt]) -> (String, Vec<String>) {
958        (
959            func.to_string(),
960            args.iter().map(|a| a.to_string()).collect(),
961        )
962    }
963    pub fn get(&mut self, func: &str, args: &[CtfeValueExt]) -> Option<CtfeValueExt> {
964        let k = Self::key(func, args);
965        if let Some(v) = self.cache.get(&k) {
966            self.hits += 1;
967            Some(v.clone())
968        } else {
969            self.misses += 1;
970            None
971        }
972    }
973    pub fn insert(&mut self, func: &str, args: &[CtfeValueExt], val: CtfeValueExt) {
974        let k = Self::key(func, args);
975        self.cache.insert(k, val);
976    }
977    pub fn hit_rate(&self) -> f64 {
978        let total = self.hits + self.misses;
979        if total == 0 {
980            0.0
981        } else {
982            self.hits as f64 / total as f64
983        }
984    }
985}
986/// Evaluation context: maps names / variable IDs to their current values.
987#[derive(Debug, Clone)]
988pub struct CtfeContext {
989    /// Global constant bindings (function name → value).
990    pub constants: HashMap<String, CtfeValue>,
991    /// Local variable bindings (var ID → value).
992    pub(super) locals: HashMap<LcnfVarId, CtfeValue>,
993    /// Current recursion depth.
994    pub recursion_depth: u32,
995    /// Maximum recursion depth.
996    pub max_depth: u32,
997    /// Remaining evaluation fuel.
998    pub fuel: u64,
999    /// Total fuel consumed so far.
1000    pub fuel_used: u64,
1001}
1002impl CtfeContext {
1003    /// Create a new context with default limits.
1004    pub fn new() -> Self {
1005        CtfeContext {
1006            constants: HashMap::new(),
1007            locals: HashMap::new(),
1008            recursion_depth: 0,
1009            max_depth: 256,
1010            fuel: 10_000,
1011            fuel_used: 0,
1012        }
1013    }
1014    /// Create a context with a custom fuel budget.
1015    pub fn with_fuel(fuel: u64) -> Self {
1016        CtfeContext {
1017            fuel,
1018            ..Self::new()
1019        }
1020    }
1021    /// Bind a local variable.
1022    pub fn bind_local(&mut self, id: LcnfVarId, value: CtfeValue) {
1023        self.locals.insert(id, value);
1024    }
1025    /// Look up a local variable.
1026    pub fn lookup_local(&self, id: LcnfVarId) -> Option<&CtfeValue> {
1027        self.locals.get(&id)
1028    }
1029    /// Consume one unit of fuel, returning `Err(Timeout)` if exhausted.
1030    pub fn consume_fuel(&mut self) -> Result<(), CtfeError> {
1031        if self.fuel == 0 {
1032            return Err(CtfeError::Timeout {
1033                fuel_used: self.fuel_used,
1034            });
1035        }
1036        self.fuel -= 1;
1037        self.fuel_used += 1;
1038        Ok(())
1039    }
1040    /// Push a call frame, returning `Err(StackOverflow)` if too deep.
1041    pub fn push_frame(&mut self) -> Result<(), CtfeError> {
1042        if self.recursion_depth >= self.max_depth {
1043            return Err(CtfeError::StackOverflow {
1044                depth: self.recursion_depth,
1045            });
1046        }
1047        self.recursion_depth += 1;
1048        Ok(())
1049    }
1050    /// Pop a call frame.
1051    pub fn pop_frame(&mut self) {
1052        if self.recursion_depth > 0 {
1053            self.recursion_depth -= 1;
1054        }
1055    }
1056    /// Create a child context for evaluating a sub-expression with fresh locals.
1057    pub fn child_context(&self) -> CtfeContext {
1058        CtfeContext {
1059            constants: self.constants.clone(),
1060            locals: HashMap::new(),
1061            recursion_depth: self.recursion_depth,
1062            max_depth: self.max_depth,
1063            fuel: self.fuel,
1064            fuel_used: self.fuel_used,
1065        }
1066    }
1067    /// Merge fuel consumption back from a child context.
1068    pub fn merge_fuel_from(&mut self, child: &CtfeContext) {
1069        let consumed = child.fuel_used - self.fuel_used;
1070        self.fuel = self.fuel.saturating_sub(consumed);
1071        self.fuel_used = child.fuel_used;
1072    }
1073}
1074#[allow(dead_code)]
1075pub struct CtfeEnvScope {
1076    pub(super) depth: usize,
1077}
1078/// CTFE function table
1079#[allow(dead_code)]
1080#[derive(Debug, Default)]
1081pub struct CtfeFuncTable {
1082    pub funcs: std::collections::HashMap<String, CtfeFuncEntry>,
1083}
1084#[allow(dead_code)]
1085impl CtfeFuncTable {
1086    pub fn new() -> Self {
1087        Self::default()
1088    }
1089    pub fn register(&mut self, entry: CtfeFuncEntry) {
1090        self.funcs.insert(entry.name.clone(), entry);
1091    }
1092    pub fn lookup(&self, name: &str) -> Option<&CtfeFuncEntry> {
1093        self.funcs.get(name)
1094    }
1095    pub fn lookup_mut(&mut self, name: &str) -> Option<&mut CtfeFuncEntry> {
1096        self.funcs.get_mut(name)
1097    }
1098    pub fn is_pure(&self, name: &str) -> bool {
1099        self.funcs.get(name).map(|e| e.is_pure).unwrap_or(false)
1100    }
1101    pub fn is_recursive(&self, name: &str) -> bool {
1102        self.funcs
1103            .get(name)
1104            .map(|e| e.is_recursive)
1105            .unwrap_or(false)
1106    }
1107    pub fn total_calls(&self) -> u64 {
1108        self.funcs.values().map(|e| e.call_count).sum()
1109    }
1110    pub fn hot_functions(&self, threshold: u64) -> Vec<&str> {
1111        self.funcs
1112            .values()
1113            .filter(|e| e.call_count >= threshold)
1114            .map(|e| e.name.as_str())
1115            .collect()
1116    }
1117}
1118/// CTFE pass builder
1119#[allow(dead_code)]
1120#[derive(Debug, Default)]
1121pub struct CtfePassBuilder {
1122    pub config: CtfeConfigExt,
1123    pub func_table: CtfeFuncTable,
1124    pub memo_cache: CtfeMemoCache,
1125    pub diags: CtfeDiagSink,
1126    pub stats: CtfePassStatsExt,
1127}
1128#[allow(dead_code)]
1129impl CtfePassBuilder {
1130    pub fn new() -> Self {
1131        Self::default()
1132    }
1133    pub fn with_config(mut self, cfg: CtfeConfigExt) -> Self {
1134        self.config = cfg;
1135        self
1136    }
1137    pub fn register_func(&mut self, entry: CtfeFuncEntry) {
1138        self.func_table.register(entry);
1139    }
1140    pub fn run_pass(&mut self, func: &str) -> Option<CtfeEvalResult> {
1141        if !self.config.replace_calls {
1142            return None;
1143        }
1144        if let Some(entry) = self.func_table.lookup(func) {
1145            let name = entry.name.clone();
1146            self.stats.functions_attempted += 1;
1147            if entry.is_pure {
1148                self.stats.functions_evaluated += 1;
1149                Some(CtfeEvalResult {
1150                    value: Some(CtfeValueExt::Opaque),
1151                    fuel_used: 1,
1152                    steps: 1,
1153                    stack_depth_max: 1,
1154                    memo_hit: false,
1155                })
1156            } else {
1157                self.diags.push(
1158                    CtfeDiagLevel::Info,
1159                    &format!("skipping impure function: {}", name),
1160                    Some(func),
1161                );
1162                None
1163            }
1164        } else {
1165            self.diags.push(
1166                CtfeDiagLevel::Warning,
1167                "function not found in table",
1168                Some(func),
1169            );
1170            None
1171        }
1172    }
1173    pub fn report(&self) -> String {
1174        format!("{}", self.stats)
1175    }
1176}
1177/// CTFE optimizer state
1178#[allow(dead_code)]
1179#[derive(Debug, Default)]
1180pub struct CtfeOptimizerState {
1181    pub func_list: CtfeFuncList,
1182    pub config: CtfeConfigExt,
1183    pub stats: CtfeCodeStats,
1184    pub diags: CtfeDiagSink,
1185    pub mode: Option<CtfeMode>,
1186}
1187#[allow(dead_code)]
1188impl CtfeOptimizerState {
1189    pub fn new(config: CtfeConfigExt) -> Self {
1190        Self {
1191            config,
1192            ..Default::default()
1193        }
1194    }
1195    pub fn with_mode(mut self, mode: CtfeMode) -> Self {
1196        self.mode = Some(mode);
1197        self
1198    }
1199    pub fn is_enabled(&self) -> bool {
1200        self.mode
1201            .as_ref()
1202            .map(|m| *m != CtfeMode::Disabled)
1203            .unwrap_or(true)
1204    }
1205    pub fn report(&self) -> String {
1206        format!("{}", self.stats)
1207    }
1208}
1209/// CTFE loop analysis (detect and bound loops for termination)
1210#[allow(dead_code)]
1211#[derive(Debug, Clone)]
1212pub struct CtfeLoopBound {
1213    pub loop_var: String,
1214    pub bound: i64,
1215    pub is_ascending: bool,
1216    pub confirmed: bool,
1217}
1218/// CTFE evaluation budget tracker (tracks multiple resources)
1219#[allow(dead_code)]
1220#[derive(Debug, Clone)]
1221pub struct CtfeBudget {
1222    pub fuel_remaining: u64,
1223    pub stack_remaining: usize,
1224    pub memo_size_remaining: usize,
1225    pub allocations: u64,
1226}
1227#[allow(dead_code)]
1228impl CtfeBudget {
1229    pub fn new(fuel: u64, stack: usize, memo: usize) -> Self {
1230        Self {
1231            fuel_remaining: fuel,
1232            stack_remaining: stack,
1233            memo_size_remaining: memo,
1234            allocations: 0,
1235        }
1236    }
1237    pub fn consume_fuel(&mut self, n: u64) -> bool {
1238        if self.fuel_remaining < n {
1239            false
1240        } else {
1241            self.fuel_remaining -= n;
1242            true
1243        }
1244    }
1245    pub fn push_stack(&mut self) -> bool {
1246        if self.stack_remaining == 0 {
1247            false
1248        } else {
1249            self.stack_remaining -= 1;
1250            true
1251        }
1252    }
1253    pub fn pop_stack(&mut self) {
1254        self.stack_remaining += 1;
1255    }
1256    pub fn is_exhausted(&self) -> bool {
1257        self.fuel_remaining == 0 || self.stack_remaining == 0
1258    }
1259}
1260/// CTFE partial evaluation result
1261#[allow(dead_code)]
1262#[derive(Debug, Clone)]
1263pub struct CtfePeResult {
1264    pub residual: String,
1265    pub known_values: Vec<(String, CtfeValueExt)>,
1266    pub fuel_used: u64,
1267}
1268/// CTFE whitelist / blacklist of functions
1269#[allow(dead_code)]
1270#[derive(Debug, Default, Clone)]
1271pub struct CtfeFuncList {
1272    pub names: std::collections::HashSet<String>,
1273    pub is_whitelist: bool,
1274}
1275#[allow(dead_code)]
1276impl CtfeFuncList {
1277    pub fn whitelist() -> Self {
1278        Self {
1279            names: std::collections::HashSet::new(),
1280            is_whitelist: true,
1281        }
1282    }
1283    pub fn blacklist() -> Self {
1284        Self {
1285            names: std::collections::HashSet::new(),
1286            is_whitelist: false,
1287        }
1288    }
1289    pub fn add(&mut self, name: &str) {
1290        self.names.insert(name.to_string());
1291    }
1292    pub fn should_evaluate(&self, name: &str) -> bool {
1293        if self.is_whitelist {
1294            self.names.contains(name)
1295        } else {
1296            !self.names.contains(name)
1297        }
1298    }
1299}
1300/// CTFE pass run summary
1301#[allow(dead_code)]
1302#[derive(Debug, Clone)]
1303pub struct CtfePassSummary {
1304    pub pass_name: String,
1305    pub funcs_processed: usize,
1306    pub replacements: usize,
1307    pub errors: usize,
1308    pub duration_us: u64,
1309}
1310/// CTFE reduction strategy
1311#[allow(dead_code)]
1312#[derive(Debug, Clone, PartialEq, Eq)]
1313pub enum CtfeReductionStrategy {
1314    CallByValue,
1315    CallByName,
1316    CallByNeed,
1317    Normal,
1318}
1319/// CTFE evaluation trace
1320#[allow(dead_code)]
1321#[derive(Debug, Default)]
1322pub struct CtfeTrace {
1323    pub entries: Vec<CtfeTraceEntry>,
1324    pub max_entries: usize,
1325}
1326#[allow(dead_code)]
1327impl CtfeTrace {
1328    pub fn new(max: usize) -> Self {
1329        Self {
1330            entries: Vec::new(),
1331            max_entries: max,
1332        }
1333    }
1334    pub fn push(&mut self, entry: CtfeTraceEntry) {
1335        if self.entries.len() < self.max_entries {
1336            self.entries.push(entry);
1337        }
1338    }
1339    pub fn is_full(&self) -> bool {
1340        self.entries.len() >= self.max_entries
1341    }
1342    pub fn emit(&self) -> String {
1343        self.entries
1344            .iter()
1345            .map(|e| e.to_string())
1346            .collect::<Vec<_>>()
1347            .join("\n")
1348    }
1349}
1350/// CTFE version info
1351#[allow(dead_code)]
1352#[derive(Debug, Clone)]
1353pub struct CtfeVersionInfo {
1354    pub pass_version: u32,
1355    pub min_fuel: u64,
1356    pub max_fuel: u64,
1357    pub supports_memo: bool,
1358    pub supports_partial_eval: bool,
1359}
1360/// CTFE function table entry
1361#[allow(dead_code)]
1362#[derive(Debug, Clone)]
1363pub struct CtfeFuncEntry {
1364    pub name: String,
1365    pub params: Vec<String>,
1366    pub body: String,
1367    pub is_recursive: bool,
1368    pub is_pure: bool,
1369    pub call_count: u64,
1370}
1371/// CTFE constant propagation map
1372#[allow(dead_code)]
1373#[derive(Debug, Default)]
1374pub struct CtfeConstMap {
1375    pub map: std::collections::HashMap<String, CtfeValueExt>,
1376}
1377#[allow(dead_code)]
1378impl CtfeConstMap {
1379    pub fn new() -> Self {
1380        Self::default()
1381    }
1382    pub fn insert(&mut self, var: String, val: CtfeValueExt) {
1383        self.map.insert(var, val);
1384    }
1385    pub fn lookup(&self, var: &str) -> Option<&CtfeValueExt> {
1386        self.map.get(var)
1387    }
1388    pub fn remove(&mut self, var: &str) {
1389        self.map.remove(var);
1390    }
1391    pub fn len(&self) -> usize {
1392        self.map.len()
1393    }
1394    pub fn is_empty(&self) -> bool {
1395        self.map.is_empty()
1396    }
1397    pub fn merge(&mut self, other: &CtfeConstMap) {
1398        for (k, v) in &other.map {
1399            self.map.insert(k.clone(), v.clone());
1400        }
1401    }
1402}
1403#[allow(dead_code)]
1404#[derive(Debug, Clone)]
1405pub struct CtfeDiag {
1406    pub level: CtfeDiagLevel,
1407    pub message: String,
1408    pub func: Option<String>,
1409}
1410/// CTFE numeric range (for range analysis)
1411#[allow(dead_code)]
1412#[derive(Debug, Clone)]
1413pub struct CtfeNumericRange {
1414    pub min: i64,
1415    pub max: i64,
1416    pub known_exact: bool,
1417}
1418#[allow(dead_code)]
1419impl CtfeNumericRange {
1420    pub fn exact(n: i64) -> Self {
1421        Self {
1422            min: n,
1423            max: n,
1424            known_exact: true,
1425        }
1426    }
1427    pub fn range(min: i64, max: i64) -> Self {
1428        Self {
1429            min,
1430            max,
1431            known_exact: false,
1432        }
1433    }
1434    pub fn top() -> Self {
1435        Self {
1436            min: i64::MIN,
1437            max: i64::MAX,
1438            known_exact: false,
1439        }
1440    }
1441    pub fn contains(&self, n: i64) -> bool {
1442        n >= self.min && n <= self.max
1443    }
1444    pub fn width(&self) -> u64 {
1445        (self.max as i128 - self.min as i128).unsigned_abs() as u64 + 1
1446    }
1447    pub fn join(&self, other: &CtfeNumericRange) -> CtfeNumericRange {
1448        CtfeNumericRange {
1449            min: self.min.min(other.min),
1450            max: self.max.max(other.max),
1451            known_exact: false,
1452        }
1453    }
1454}
1455/// CTFE feature flags
1456#[allow(dead_code)]
1457#[derive(Debug, Clone, Default)]
1458pub struct CtfeFeatureFlags {
1459    pub fold_arithmetic: bool,
1460    pub fold_boolean: bool,
1461    pub fold_string: bool,
1462    pub partial_eval: bool,
1463    pub memoize: bool,
1464}
1465#[allow(dead_code)]
1466#[derive(Debug, Default)]
1467pub struct CtfeDiagSink {
1468    pub diags: Vec<CtfeDiag>,
1469}
1470#[allow(dead_code)]
1471impl CtfeDiagSink {
1472    pub fn new() -> Self {
1473        Self::default()
1474    }
1475    pub fn push(&mut self, level: CtfeDiagLevel, msg: &str, func: Option<&str>) {
1476        self.diags.push(CtfeDiag {
1477            level,
1478            message: msg.to_string(),
1479            func: func.map(|s| s.to_string()),
1480        });
1481    }
1482    pub fn has_errors(&self) -> bool {
1483        self.diags.iter().any(|d| d.level == CtfeDiagLevel::Error)
1484    }
1485    pub fn error_messages(&self) -> Vec<&str> {
1486        self.diags
1487            .iter()
1488            .filter(|d| d.level == CtfeDiagLevel::Error)
1489            .map(|d| d.message.as_str())
1490            .collect()
1491    }
1492}
1493/// Errors that can occur during compile-time evaluation.
1494#[derive(Debug, Clone, PartialEq, Eq)]
1495pub enum CtfeError {
1496    /// Division (or modulo) by zero.
1497    DivisionByZero,
1498    /// An array / list index is out of bounds.
1499    IndexOutOfBounds { index: i64, length: usize },
1500    /// The recursion depth limit was hit.
1501    StackOverflow { depth: u32 },
1502    /// The expression is not constant (contains free variables or I/O).
1503    NonConstant { reason: String },
1504    /// Fuel exhausted — the evaluation took too many steps.
1505    Timeout { fuel_used: u64 },
1506    /// Integer arithmetic overflow.
1507    Overflow { op: String },
1508    /// Attempted to project out of a non-constructor value.
1509    BadProjection { field: u32 },
1510    /// Pattern match is not exhaustive at compile time.
1511    NonExhaustiveMatch,
1512}