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::Int(i) => CtfeValue::Int(*i),
452 LcnfLit::Str(s) => CtfeValue::String(s.clone()),
453 }
454 }
455 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 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 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 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}
727pub struct CtfePass {
729 pub config: CtfeConfig,
731 pub known_constants: HashMap<String, CtfeValue>,
733 pub(super) report: CtfeReport,
735}
736impl CtfePass {
737 pub fn new(config: CtfeConfig) -> Self {
739 CtfePass {
740 config,
741 known_constants: HashMap::new(),
742 report: CtfeReport::default(),
743 }
744 }
745 pub fn default_pass() -> Self {
747 Self::new(CtfeConfig::default())
748 }
749 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 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 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 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#[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#[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#[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#[allow(dead_code)]
907#[derive(Debug, Clone)]
908pub enum CtfeStepResult {
909 Value(CtfeValueExt),
910 Diverge,
911 FuelExhausted,
912 Error(String),
913}
914#[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#[allow(dead_code)]
925#[derive(Debug, Clone, PartialEq, Eq)]
926pub enum CtfeInlineDecision {
927 AlwaysInline,
928 InlineIfSmall(usize),
929 NeverInline,
930 InlineForCtfe,
931}
932#[derive(Debug, Clone)]
934pub struct CtfeConfig {
935 pub fuel: u64,
937 pub max_depth: u32,
939 pub replace_calls: bool,
941 pub cross_boundary_propagation: bool,
943}
944#[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#[derive(Debug, Clone)]
988pub struct CtfeContext {
989 pub constants: HashMap<String, CtfeValue>,
991 pub(super) locals: HashMap<LcnfVarId, CtfeValue>,
993 pub recursion_depth: u32,
995 pub max_depth: u32,
997 pub fuel: u64,
999 pub fuel_used: u64,
1001}
1002impl CtfeContext {
1003 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 pub fn with_fuel(fuel: u64) -> Self {
1016 CtfeContext {
1017 fuel,
1018 ..Self::new()
1019 }
1020 }
1021 pub fn bind_local(&mut self, id: LcnfVarId, value: CtfeValue) {
1023 self.locals.insert(id, value);
1024 }
1025 pub fn lookup_local(&self, id: LcnfVarId) -> Option<&CtfeValue> {
1027 self.locals.get(&id)
1028 }
1029 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 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 pub fn pop_frame(&mut self) {
1052 if self.recursion_depth > 0 {
1053 self.recursion_depth -= 1;
1054 }
1055 }
1056 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 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#[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#[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#[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#[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#[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#[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#[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#[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#[allow(dead_code)]
1312#[derive(Debug, Clone, PartialEq, Eq)]
1313pub enum CtfeReductionStrategy {
1314 CallByValue,
1315 CallByName,
1316 CallByNeed,
1317 Normal,
1318}
1319#[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#[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#[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#[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#[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#[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#[derive(Debug, Clone, PartialEq, Eq)]
1495pub enum CtfeError {
1496 DivisionByZero,
1498 IndexOutOfBounds { index: i64, length: usize },
1500 StackOverflow { depth: u32 },
1502 NonConstant { reason: String },
1504 Timeout { fuel_used: u64 },
1506 Overflow { op: String },
1508 BadProjection { field: u32 },
1510 NonExhaustiveMatch,
1512}