1use 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#[allow(dead_code)]
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum CtfeMode {
17 FullEval,
18 PartialEval,
19 FoldOnly,
20 Disabled,
21}
22#[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#[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#[derive(Debug, Clone, Default)]
63pub struct CtfeReport {
64 pub functions_evaluated: usize,
66 pub calls_replaced: usize,
68 pub constants_propagated: usize,
70 pub fuel_exhausted_count: usize,
72}
73#[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#[allow(dead_code)]
118#[derive(Debug, Clone, PartialEq, Eq)]
119pub enum CtfeDiagLevel {
120 Debug,
121 Info,
122 Warning,
123 Error,
124}
125#[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#[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 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#[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#[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#[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#[derive(Debug, Clone, PartialEq)]
222pub enum CtfeValue {
223 Int(i64),
225 Float(f64),
227 Bool(bool),
229 String(String),
231 List(Vec<CtfeValue>),
233 Tuple(Vec<CtfeValue>),
235 Constructor(String, Vec<CtfeValue>),
237 Undef,
239}
240impl CtfeValue {
241 pub fn as_int(&self) -> Option<i64> {
243 match self {
244 CtfeValue::Int(n) => Some(*n),
245 _ => None,
246 }
247 }
248 pub fn as_bool(&self) -> Option<bool> {
250 match self {
251 CtfeValue::Bool(b) => Some(*b),
252 _ => None,
253 }
254 }
255 pub fn as_str(&self) -> Option<&str> {
257 match self {
258 CtfeValue::String(s) => Some(s.as_str()),
259 _ => None,
260 }
261 }
262 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#[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#[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#[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#[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#[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#[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}
431pub struct CtfeInterpreter {
433 pub(super) memo: HashMap<(String, Vec<String>), CtfeValue>,
435 pub(super) decls: HashMap<String, LcnfFunDecl>,
437}
438impl CtfeInterpreter {
439 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 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 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 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 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 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}
726pub struct CtfePass {
728 pub config: CtfeConfig,
730 pub known_constants: HashMap<String, CtfeValue>,
732 pub(super) report: CtfeReport,
734}
735impl CtfePass {
736 pub fn new(config: CtfeConfig) -> Self {
738 CtfePass {
739 config,
740 known_constants: HashMap::new(),
741 report: CtfeReport::default(),
742 }
743 }
744 pub fn default_pass() -> Self {
746 Self::new(CtfeConfig::default())
747 }
748 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 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 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 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#[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#[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#[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#[allow(dead_code)]
906#[derive(Debug, Clone)]
907pub enum CtfeStepResult {
908 Value(CtfeValueExt),
909 Diverge,
910 FuelExhausted,
911 Error(String),
912}
913#[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#[allow(dead_code)]
924#[derive(Debug, Clone, PartialEq, Eq)]
925pub enum CtfeInlineDecision {
926 AlwaysInline,
927 InlineIfSmall(usize),
928 NeverInline,
929 InlineForCtfe,
930}
931#[derive(Debug, Clone)]
933pub struct CtfeConfig {
934 pub fuel: u64,
936 pub max_depth: u32,
938 pub replace_calls: bool,
940 pub cross_boundary_propagation: bool,
942}
943#[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#[derive(Debug, Clone)]
987pub struct CtfeContext {
988 pub constants: HashMap<String, CtfeValue>,
990 pub(super) locals: HashMap<LcnfVarId, CtfeValue>,
992 pub recursion_depth: u32,
994 pub max_depth: u32,
996 pub fuel: u64,
998 pub fuel_used: u64,
1000}
1001impl CtfeContext {
1002 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 pub fn with_fuel(fuel: u64) -> Self {
1015 CtfeContext {
1016 fuel,
1017 ..Self::new()
1018 }
1019 }
1020 pub fn bind_local(&mut self, id: LcnfVarId, value: CtfeValue) {
1022 self.locals.insert(id, value);
1023 }
1024 pub fn lookup_local(&self, id: LcnfVarId) -> Option<&CtfeValue> {
1026 self.locals.get(&id)
1027 }
1028 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 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 pub fn pop_frame(&mut self) {
1051 if self.recursion_depth > 0 {
1052 self.recursion_depth -= 1;
1053 }
1054 }
1055 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 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#[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#[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#[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#[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#[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#[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#[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#[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#[allow(dead_code)]
1311#[derive(Debug, Clone, PartialEq, Eq)]
1312pub enum CtfeReductionStrategy {
1313 CallByValue,
1314 CallByName,
1315 CallByNeed,
1316 Normal,
1317}
1318#[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#[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#[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#[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#[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#[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#[derive(Debug, Clone, PartialEq, Eq)]
1494pub enum CtfeError {
1495 DivisionByZero,
1497 IndexOutOfBounds { index: i64, length: usize },
1499 StackOverflow { depth: u32 },
1501 NonConstant { reason: String },
1503 Timeout { fuel_used: u64 },
1505 Overflow { op: String },
1507 BadProjection { field: u32 },
1509 NonExhaustiveMatch,
1511}