Skip to main content

oxilean_codegen/go_backend/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::lcnf::*;
6use std::collections::{HashMap, HashSet};
7
8use super::functions::*;
9use std::collections::VecDeque;
10
11#[allow(dead_code)]
12#[derive(Debug, Clone)]
13pub struct GoDominatorTree {
14    pub idom: Vec<Option<u32>>,
15    pub dom_children: Vec<Vec<u32>>,
16    pub dom_depth: Vec<u32>,
17}
18impl GoDominatorTree {
19    #[allow(dead_code)]
20    pub fn new(size: usize) -> Self {
21        GoDominatorTree {
22            idom: vec![None; size],
23            dom_children: vec![Vec::new(); size],
24            dom_depth: vec![0; size],
25        }
26    }
27    #[allow(dead_code)]
28    pub fn set_idom(&mut self, node: usize, idom: u32) {
29        self.idom[node] = Some(idom);
30    }
31    #[allow(dead_code)]
32    pub fn dominates(&self, a: usize, b: usize) -> bool {
33        if a == b {
34            return true;
35        }
36        let mut cur = b;
37        loop {
38            match self.idom[cur] {
39                Some(parent) if parent as usize == a => return true,
40                Some(parent) if parent as usize == cur => return false,
41                Some(parent) => cur = parent as usize,
42                None => return false,
43            }
44        }
45    }
46    #[allow(dead_code)]
47    pub fn depth(&self, node: usize) -> u32 {
48        self.dom_depth.get(node).copied().unwrap_or(0)
49    }
50}
51/// A named Go function.
52#[derive(Debug, Clone)]
53pub struct GoFunc {
54    /// Function name.
55    pub name: String,
56    /// Receiver (for methods): `(recv_name recv_type)`.
57    pub receiver: Option<(String, GoType)>,
58    /// Parameter list: `[(name, type), ...]`.
59    pub params: Vec<(String, GoType)>,
60    /// Return type list.
61    pub return_types: Vec<GoType>,
62    /// Function body statements.
63    pub body: Vec<GoStmt>,
64    /// Whether exported (capitalised).
65    pub exported: bool,
66}
67impl GoFunc {
68    /// Create a new unexported function with no receiver.
69    pub fn new(name: impl Into<String>) -> Self {
70        GoFunc {
71            name: name.into(),
72            receiver: None,
73            params: Vec::new(),
74            return_types: Vec::new(),
75            body: Vec::new(),
76            exported: false,
77        }
78    }
79    /// Add a parameter.
80    pub fn add_param(&mut self, name: impl Into<String>, ty: GoType) {
81        self.params.push((name.into(), ty));
82    }
83    /// Add a return type.
84    pub fn add_return(&mut self, ty: GoType) {
85        self.return_types.push(ty);
86    }
87    /// Emit Go source for this function.
88    pub fn codegen(&self) -> String {
89        let mut out = String::new();
90        let recv_str = self
91            .receiver
92            .as_ref()
93            .map(|(n, t)| format!("({} {}) ", n, t))
94            .unwrap_or_default();
95        let params_str = self
96            .params
97            .iter()
98            .map(|(n, t)| format!("{} {}", n, t))
99            .collect::<Vec<_>>()
100            .join(", ");
101        let ret_str = match self.return_types.len() {
102            0 => String::new(),
103            1 => format!(" {}", self.return_types[0]),
104            _ => {
105                let rs: Vec<String> = self.return_types.iter().map(|t| t.to_string()).collect();
106                format!(" ({})", rs.join(", "))
107            }
108        };
109        out.push_str(&format!(
110            "func {}{}({}){} {{\n",
111            recv_str, self.name, params_str, ret_str
112        ));
113        let body_str = format_stmts(&self.body, 1);
114        if !body_str.is_empty() {
115            out.push_str(&body_str);
116            out.push('\n');
117        }
118        out.push('}');
119        out
120    }
121}
122#[allow(dead_code)]
123#[derive(Debug, Clone)]
124pub struct GoDepGraph {
125    pub(super) nodes: Vec<u32>,
126    pub(super) edges: Vec<(u32, u32)>,
127}
128impl GoDepGraph {
129    #[allow(dead_code)]
130    pub fn new() -> Self {
131        GoDepGraph {
132            nodes: Vec::new(),
133            edges: Vec::new(),
134        }
135    }
136    #[allow(dead_code)]
137    pub fn add_node(&mut self, id: u32) {
138        if !self.nodes.contains(&id) {
139            self.nodes.push(id);
140        }
141    }
142    #[allow(dead_code)]
143    pub fn add_dep(&mut self, dep: u32, dependent: u32) {
144        self.add_node(dep);
145        self.add_node(dependent);
146        self.edges.push((dep, dependent));
147    }
148    #[allow(dead_code)]
149    pub fn dependents_of(&self, node: u32) -> Vec<u32> {
150        self.edges
151            .iter()
152            .filter(|(d, _)| *d == node)
153            .map(|(_, dep)| *dep)
154            .collect()
155    }
156    #[allow(dead_code)]
157    pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
158        self.edges
159            .iter()
160            .filter(|(_, dep)| *dep == node)
161            .map(|(d, _)| *d)
162            .collect()
163    }
164    #[allow(dead_code)]
165    pub fn topological_sort(&self) -> Vec<u32> {
166        let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
167        for &n in &self.nodes {
168            in_degree.insert(n, 0);
169        }
170        for (_, dep) in &self.edges {
171            *in_degree.entry(*dep).or_insert(0) += 1;
172        }
173        let mut queue: std::collections::VecDeque<u32> = self
174            .nodes
175            .iter()
176            .filter(|&&n| in_degree[&n] == 0)
177            .copied()
178            .collect();
179        let mut result = Vec::new();
180        while let Some(node) = queue.pop_front() {
181            result.push(node);
182            for dep in self.dependents_of(node) {
183                let cnt = in_degree.entry(dep).or_insert(0);
184                *cnt = cnt.saturating_sub(1);
185                if *cnt == 0 {
186                    queue.push_back(dep);
187                }
188            }
189        }
190        result
191    }
192    #[allow(dead_code)]
193    pub fn has_cycle(&self) -> bool {
194        self.topological_sort().len() < self.nodes.len()
195    }
196}
197/// A complete Go source file / module.
198#[derive(Debug, Clone)]
199pub struct GoModule {
200    /// Package name.
201    pub package: String,
202    /// Import paths.
203    pub imports: Vec<String>,
204    /// Type declarations.
205    pub types: Vec<GoTypeDecl>,
206    /// Function declarations.
207    pub funcs: Vec<GoFunc>,
208    /// Package-level variable declarations.
209    pub vars: Vec<GoStmt>,
210    /// Constant declarations.
211    pub consts: Vec<(String, GoType, GoExpr)>,
212}
213impl GoModule {
214    /// Create a new module with the given package name.
215    pub fn new(package: impl Into<String>) -> Self {
216        GoModule {
217            package: package.into(),
218            imports: Vec::new(),
219            types: Vec::new(),
220            funcs: Vec::new(),
221            vars: Vec::new(),
222            consts: Vec::new(),
223        }
224    }
225    /// Add an import path (e.g. `"fmt"`, `"math/big"`).
226    pub fn add_import(&mut self, path: impl Into<String>) {
227        let p = path.into();
228        if !self.imports.contains(&p) {
229            self.imports.push(p);
230        }
231    }
232    /// Emit a complete Go source file as a string.
233    pub fn codegen(&self) -> String {
234        let mut out = format!("package {}\n\n", self.package);
235        if !self.imports.is_empty() {
236            if self.imports.len() == 1 {
237                out.push_str(&format!("import \"{}\"\n\n", self.imports[0]));
238            } else {
239                out.push_str("import (\n");
240                for imp in &self.imports {
241                    out.push_str(&format!("    \"{}\"\n", imp));
242                }
243                out.push_str(")\n\n");
244            }
245        }
246        if !self.consts.is_empty() {
247            out.push_str("const (\n");
248            for (name, ty, val) in &self.consts {
249                out.push_str(&format!("    {} {} = {}\n", name, ty, val));
250            }
251            out.push_str(")\n\n");
252        }
253        for ty_decl in &self.types {
254            out.push_str(&ty_decl.codegen());
255            out.push_str("\n\n");
256        }
257        if !self.vars.is_empty() {
258            out.push_str("var (\n");
259            for v in &self.vars {
260                out.push_str(&format!("    {}\n", v));
261            }
262            out.push_str(")\n\n");
263        }
264        for func in &self.funcs {
265            out.push_str(&func.codegen());
266            out.push_str("\n\n");
267        }
268        out
269    }
270}
271#[allow(dead_code)]
272#[derive(Debug, Clone)]
273pub struct GoWorklist {
274    pub(super) items: std::collections::VecDeque<u32>,
275    pub(super) in_worklist: std::collections::HashSet<u32>,
276}
277impl GoWorklist {
278    #[allow(dead_code)]
279    pub fn new() -> Self {
280        GoWorklist {
281            items: std::collections::VecDeque::new(),
282            in_worklist: std::collections::HashSet::new(),
283        }
284    }
285    #[allow(dead_code)]
286    pub fn push(&mut self, item: u32) -> bool {
287        if self.in_worklist.insert(item) {
288            self.items.push_back(item);
289            true
290        } else {
291            false
292        }
293    }
294    #[allow(dead_code)]
295    pub fn pop(&mut self) -> Option<u32> {
296        let item = self.items.pop_front()?;
297        self.in_worklist.remove(&item);
298        Some(item)
299    }
300    #[allow(dead_code)]
301    pub fn is_empty(&self) -> bool {
302        self.items.is_empty()
303    }
304    #[allow(dead_code)]
305    pub fn len(&self) -> usize {
306        self.items.len()
307    }
308    #[allow(dead_code)]
309    pub fn contains(&self, item: u32) -> bool {
310        self.in_worklist.contains(&item)
311    }
312}
313#[allow(dead_code)]
314#[derive(Debug, Clone)]
315pub struct GoAnalysisCache {
316    pub(super) entries: std::collections::HashMap<String, GoCacheEntry>,
317    pub(super) max_size: usize,
318    pub(super) hits: u64,
319    pub(super) misses: u64,
320}
321impl GoAnalysisCache {
322    #[allow(dead_code)]
323    pub fn new(max_size: usize) -> Self {
324        GoAnalysisCache {
325            entries: std::collections::HashMap::new(),
326            max_size,
327            hits: 0,
328            misses: 0,
329        }
330    }
331    #[allow(dead_code)]
332    pub fn get(&mut self, key: &str) -> Option<&GoCacheEntry> {
333        if self.entries.contains_key(key) {
334            self.hits += 1;
335            self.entries.get(key)
336        } else {
337            self.misses += 1;
338            None
339        }
340    }
341    #[allow(dead_code)]
342    pub fn insert(&mut self, key: String, data: Vec<u8>) {
343        if self.entries.len() >= self.max_size {
344            if let Some(oldest) = self.entries.keys().next().cloned() {
345                self.entries.remove(&oldest);
346            }
347        }
348        self.entries.insert(
349            key.clone(),
350            GoCacheEntry {
351                key,
352                data,
353                timestamp: 0,
354                valid: true,
355            },
356        );
357    }
358    #[allow(dead_code)]
359    pub fn invalidate(&mut self, key: &str) {
360        if let Some(entry) = self.entries.get_mut(key) {
361            entry.valid = false;
362        }
363    }
364    #[allow(dead_code)]
365    pub fn clear(&mut self) {
366        self.entries.clear();
367    }
368    #[allow(dead_code)]
369    pub fn hit_rate(&self) -> f64 {
370        let total = self.hits + self.misses;
371        if total == 0 {
372            return 0.0;
373        }
374        self.hits as f64 / total as f64
375    }
376    #[allow(dead_code)]
377    pub fn size(&self) -> usize {
378        self.entries.len()
379    }
380}
381#[allow(dead_code)]
382pub struct GoConstantFoldingHelper;
383impl GoConstantFoldingHelper {
384    #[allow(dead_code)]
385    pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
386        a.checked_add(b)
387    }
388    #[allow(dead_code)]
389    pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
390        a.checked_sub(b)
391    }
392    #[allow(dead_code)]
393    pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
394        a.checked_mul(b)
395    }
396    #[allow(dead_code)]
397    pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
398        if b == 0 {
399            None
400        } else {
401            a.checked_div(b)
402        }
403    }
404    #[allow(dead_code)]
405    pub fn fold_add_f64(a: f64, b: f64) -> f64 {
406        a + b
407    }
408    #[allow(dead_code)]
409    pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
410        a * b
411    }
412    #[allow(dead_code)]
413    pub fn fold_neg_i64(a: i64) -> Option<i64> {
414        a.checked_neg()
415    }
416    #[allow(dead_code)]
417    pub fn fold_not_bool(a: bool) -> bool {
418        !a
419    }
420    #[allow(dead_code)]
421    pub fn fold_and_bool(a: bool, b: bool) -> bool {
422        a && b
423    }
424    #[allow(dead_code)]
425    pub fn fold_or_bool(a: bool, b: bool) -> bool {
426        a || b
427    }
428    #[allow(dead_code)]
429    pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
430        a.checked_shl(b)
431    }
432    #[allow(dead_code)]
433    pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
434        a.checked_shr(b)
435    }
436    #[allow(dead_code)]
437    pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
438        if b == 0 {
439            None
440        } else {
441            Some(a % b)
442        }
443    }
444    #[allow(dead_code)]
445    pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
446        a & b
447    }
448    #[allow(dead_code)]
449    pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
450        a | b
451    }
452    #[allow(dead_code)]
453    pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
454        a ^ b
455    }
456    #[allow(dead_code)]
457    pub fn fold_bitnot_i64(a: i64) -> i64 {
458        !a
459    }
460}
461/// Go expression for code generation.
462#[derive(Debug, Clone, PartialEq)]
463pub enum GoExpr {
464    /// A literal value: `42`, `"hello"`, `true`, `nil`
465    Lit(GoLit),
466    /// A variable identifier: `x`, `_t0`, `natAdd`
467    Var(String),
468    /// A function call: `f(a, b, c)`
469    Call(Box<GoExpr>, Vec<GoExpr>),
470    /// A binary operator expression: `a + b`, `a == b`
471    BinOp(String, Box<GoExpr>, Box<GoExpr>),
472    /// A unary operator expression: `!x`, `-n`
473    Unary(String, Box<GoExpr>),
474    /// Field access: `obj.Field`
475    Field(Box<GoExpr>, String),
476    /// Index expression: `arr[i]`
477    Index(Box<GoExpr>, Box<GoExpr>),
478    /// Type assertion: `x.(T)`
479    TypeAssert(Box<GoExpr>, GoType),
480    /// Composite literal: `MyStruct{Field: val, ...}`
481    Composite(GoType, Vec<(String, GoExpr)>),
482    /// Slice literal: `[]T{a, b, c}`
483    SliceLit(GoType, Vec<GoExpr>),
484    /// Address-of: `&expr`
485    AddressOf(Box<GoExpr>),
486    /// Dereference: `*expr`
487    Deref(Box<GoExpr>),
488    /// Anonymous function literal: `func(params) ret_ty { body }`
489    FuncLit(Vec<(String, GoType)>, Vec<GoType>, Vec<GoStmt>),
490    /// `make(T, args...)`
491    Make(GoType, Vec<GoExpr>),
492    /// `new(T)`
493    New(GoType),
494}
495#[allow(dead_code)]
496#[derive(Debug, Clone)]
497pub struct GoPassConfig {
498    pub phase: GoPassPhase,
499    pub enabled: bool,
500    pub max_iterations: u32,
501    pub debug_output: bool,
502    pub pass_name: String,
503}
504impl GoPassConfig {
505    #[allow(dead_code)]
506    pub fn new(name: impl Into<String>, phase: GoPassPhase) -> Self {
507        GoPassConfig {
508            phase,
509            enabled: true,
510            max_iterations: 10,
511            debug_output: false,
512            pass_name: name.into(),
513        }
514    }
515    #[allow(dead_code)]
516    pub fn disabled(mut self) -> Self {
517        self.enabled = false;
518        self
519    }
520    #[allow(dead_code)]
521    pub fn with_debug(mut self) -> Self {
522        self.debug_output = true;
523        self
524    }
525    #[allow(dead_code)]
526    pub fn max_iter(mut self, n: u32) -> Self {
527        self.max_iterations = n;
528        self
529    }
530}
531/// The Go code generation backend.
532///
533/// Translates LCNF declarations and expressions into Go source code.
534pub struct GoBackend {
535    /// Counter for generating fresh temporary variable names.
536    pub(super) var_counter: u64,
537    /// Map from LCNF variable IDs to Go identifiers.
538    pub(super) var_map: HashMap<LcnfVarId, String>,
539    /// Cached keyword set.
540    pub(super) keywords: HashSet<&'static str>,
541    /// Cached built-in set.
542    pub(super) builtins: HashSet<&'static str>,
543}
544impl GoBackend {
545    /// Create a new `GoBackend`.
546    pub fn new() -> Self {
547        GoBackend {
548            var_counter: 0,
549            var_map: HashMap::new(),
550            keywords: go_keywords(),
551            builtins: go_builtins(),
552        }
553    }
554    /// Generate a fresh temporary variable name.
555    pub(super) fn fresh_var(&mut self) -> String {
556        let n = self.var_counter;
557        self.var_counter += 1;
558        format!("_t{}", n)
559    }
560    /// Mangle a name so that it is a valid, non-reserved Go identifier.
561    ///
562    /// Rules:
563    /// 1. Replace `.`, `-`, `/`, ` ` with `_`.
564    /// 2. If empty → `ox_empty`.
565    /// 3. If starts with a digit → prefix with `ox_`.
566    /// 4. If a Go keyword or built-in → prefix with `ox_`.
567    pub fn mangle_name(name: &str) -> String {
568        let sanitised: String = name
569            .chars()
570            .map(|c| {
571                if c.is_alphanumeric() || c == '_' {
572                    c
573                } else {
574                    '_'
575                }
576            })
577            .collect();
578        if sanitised.is_empty() {
579            return "ox_empty".to_string();
580        }
581        if sanitised.starts_with(|c: char| c.is_ascii_digit()) {
582            return format!("ox_{}", sanitised);
583        }
584        let kw = go_keywords();
585        let builtins = go_builtins();
586        if kw.contains(sanitised.as_str()) || builtins.contains(sanitised.as_str()) {
587            return format!("ox_{}", sanitised);
588        }
589        sanitised
590    }
591    /// Emit a Go module with the OxiLean runtime preamble and compiled declarations.
592    pub fn compile_module(&mut self, decls: &[LcnfFunDecl]) -> GoModule {
593        let mut module = GoModule::new("main");
594        module.add_import("fmt");
595        let runtime_funcs = self.build_runtime();
596        for f in runtime_funcs {
597            module.funcs.push(f);
598        }
599        let mut ctor_decl = GoTypeDecl::new("OxiCtor");
600        ctor_decl.add_field("Tag", GoType::GoInt);
601        ctor_decl.add_field("Fields", GoType::GoSlice(Box::new(GoType::GoInterface)));
602        module.types.push(ctor_decl);
603        for decl in decls {
604            if let Some(func) = self.compile_decl(decl) {
605                module.funcs.push(func);
606            }
607        }
608        module
609    }
610    /// Compile a single LCNF declaration into a Go function.
611    pub fn compile_decl(&mut self, decl: &LcnfFunDecl) -> Option<GoFunc> {
612        let go_name = Self::mangle_name(&decl.name.to_string());
613        let mut func = GoFunc::new(go_name);
614        for param in &decl.params {
615            if !param.erased {
616                let go_param_name = Self::mangle_name(&param.name);
617                let go_ty = self.compile_type(&param.ty);
618                func.add_param(go_param_name.clone(), go_ty.clone());
619                self.var_map.insert(param.id, go_param_name);
620            }
621        }
622        let go_ret = self.compile_type(&decl.ret_type);
623        func.add_return(go_ret);
624        let body_stmts = self.compile_expr(&decl.body);
625        func.body = body_stmts;
626        Some(func)
627    }
628    /// Compile an LCNF type to its Go equivalent.
629    pub fn compile_type(&self, ty: &LcnfType) -> GoType {
630        match ty {
631            LcnfType::Nat => GoType::GoInt,
632            LcnfType::Int => GoType::GoInt,
633            LcnfType::LcnfString => GoType::GoString,
634            LcnfType::Erased | LcnfType::Irrelevant | LcnfType::Unit => GoType::GoUnit,
635            LcnfType::Object => GoType::GoInterface,
636            LcnfType::Var(_) => GoType::GoInterface,
637            LcnfType::Ctor(_, _) => {
638                GoType::GoPtr(Box::new(GoType::GoStruct("OxiCtor".to_string())))
639            }
640            LcnfType::Fun(params, ret) => {
641                let go_params: Vec<GoType> = params.iter().map(|p| self.compile_type(p)).collect();
642                let go_ret = self.compile_type(ret);
643                GoType::GoFunc(go_params, vec![go_ret])
644            }
645        }
646    }
647    /// Compile an LCNF expression to a sequence of Go statements.
648    ///
649    /// The last statement should be a `return` carrying the final value.
650    pub fn compile_expr(&mut self, expr: &LcnfExpr) -> Vec<GoStmt> {
651        match expr {
652            LcnfExpr::Return(arg) => {
653                let go_expr = self.compile_arg(arg);
654                vec![GoStmt::Return(vec![go_expr])]
655            }
656            LcnfExpr::Unreachable => {
657                vec![GoStmt::Panic(GoExpr::Lit(GoLit::Str(
658                    "unreachable".to_string(),
659                )))]
660            }
661            LcnfExpr::TailCall(func, args) => {
662                let go_func = self.compile_arg(func);
663                let go_args: Vec<GoExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
664                let call = GoExpr::Call(Box::new(go_func), go_args);
665                vec![GoStmt::Return(vec![call])]
666            }
667            LcnfExpr::Let {
668                id,
669                name,
670                ty: _,
671                value,
672                body,
673            } => {
674                let go_name = Self::mangle_name(name);
675                let go_name = format!("{}_{}", go_name, id.0);
676                self.var_map.insert(*id, go_name.clone());
677                let mut stmts = Vec::new();
678                let val_expr = self.compile_let_value(value);
679                stmts.push(GoStmt::ShortDecl(go_name, val_expr));
680                let cont = self.compile_expr(body);
681                stmts.extend(cont);
682                stmts
683            }
684            LcnfExpr::Case {
685                scrutinee,
686                scrutinee_ty: _,
687                alts,
688                default,
689            } => self.compile_case(*scrutinee, alts, default.as_deref()),
690        }
691    }
692    /// Compile a `LcnfLetValue` into a Go expression.
693    pub(super) fn compile_let_value(&mut self, value: &LcnfLetValue) -> GoExpr {
694        match value {
695            LcnfLetValue::Lit(lit) => self.compile_lit(lit),
696            LcnfLetValue::Erased | LcnfLetValue::Reset(_) => GoExpr::Lit(GoLit::Nil),
697            LcnfLetValue::FVar(id) => {
698                let name = self
699                    .var_map
700                    .get(id)
701                    .cloned()
702                    .unwrap_or_else(|| format!("_x{}", id.0));
703                GoExpr::Var(name)
704            }
705            LcnfLetValue::App(func, args) => {
706                let go_func = self.compile_arg(func);
707                let go_args: Vec<GoExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
708                GoExpr::Call(Box::new(go_func), go_args)
709            }
710            LcnfLetValue::Proj(_, idx, var) => {
711                let var_name = self
712                    .var_map
713                    .get(var)
714                    .cloned()
715                    .unwrap_or_else(|| format!("_x{}", var.0));
716                GoExpr::Index(
717                    Box::new(GoExpr::Field(
718                        Box::new(GoExpr::Var(var_name)),
719                        "Fields".to_string(),
720                    )),
721                    Box::new(GoExpr::Lit(GoLit::Int(*idx as i64))),
722                )
723            }
724            LcnfLetValue::Ctor(name, tag, args) => {
725                let go_name = Self::mangle_name(name);
726                let mut fields = vec![
727                    ("Tag".to_string(), GoExpr::Lit(GoLit::Int(*tag as i64))),
728                    ("_ctorName".to_string(), GoExpr::Lit(GoLit::Str(go_name))),
729                ];
730                if !args.is_empty() {
731                    let go_args: Vec<GoExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
732                    fields.push((
733                        "Fields".to_string(),
734                        GoExpr::SliceLit(GoType::GoInterface, go_args),
735                    ));
736                } else {
737                    fields.push((
738                        "Fields".to_string(),
739                        GoExpr::SliceLit(GoType::GoInterface, vec![]),
740                    ));
741                }
742                GoExpr::AddressOf(Box::new(GoExpr::Composite(
743                    GoType::GoStruct("OxiCtor".to_string()),
744                    fields,
745                )))
746            }
747            LcnfLetValue::Reuse(_slot, name, tag, args) => {
748                self.compile_let_value(&LcnfLetValue::Ctor(name.clone(), *tag, args.clone()))
749            }
750        }
751    }
752    /// Compile an `LcnfArg` into a Go expression.
753    pub fn compile_arg(&self, arg: &LcnfArg) -> GoExpr {
754        match arg {
755            LcnfArg::Var(id) => {
756                let name = self
757                    .var_map
758                    .get(id)
759                    .cloned()
760                    .unwrap_or_else(|| format!("_x{}", id.0));
761                GoExpr::Var(name)
762            }
763            LcnfArg::Lit(lit) => self.compile_lit(lit),
764            LcnfArg::Erased | LcnfArg::Type(_) => GoExpr::Lit(GoLit::Nil),
765        }
766    }
767    /// Compile an `LcnfLit` to a Go expression.
768    pub(super) fn compile_lit(&self, lit: &LcnfLit) -> GoExpr {
769        match lit {
770            LcnfLit::Nat(n) => GoExpr::Lit(GoLit::Int(*n as i64)),
771            LcnfLit::Int(i) => GoExpr::Lit(GoLit::Int(*i)),
772            LcnfLit::Str(s) => GoExpr::Lit(GoLit::Str(s.clone())),
773        }
774    }
775    /// Compile a `Case` expression to Go statements (switch).
776    pub(super) fn compile_case(
777        &mut self,
778        scrutinee: LcnfVarId,
779        alts: &[LcnfAlt],
780        default: Option<&LcnfExpr>,
781    ) -> Vec<GoStmt> {
782        let scr_name = self
783            .var_map
784            .get(&scrutinee)
785            .cloned()
786            .unwrap_or_else(|| format!("_x{}", scrutinee.0));
787        let tag_expr = GoExpr::Field(Box::new(GoExpr::Var(scr_name.clone())), "Tag".to_string());
788        let mut cases: Vec<GoCase> = Vec::new();
789        for alt in alts {
790            let mut alt_body: Vec<GoStmt> = Vec::new();
791            for (idx, param) in alt.params.iter().enumerate() {
792                if !param.erased {
793                    let go_param = Self::mangle_name(&param.name);
794                    let go_param = format!("{}_{}", go_param, param.id.0);
795                    self.var_map.insert(param.id, go_param.clone());
796                    let field_expr = GoExpr::Index(
797                        Box::new(GoExpr::Field(
798                            Box::new(GoExpr::Var(scr_name.clone())),
799                            "Fields".to_string(),
800                        )),
801                        Box::new(GoExpr::Lit(GoLit::Int(idx as i64))),
802                    );
803                    alt_body.push(GoStmt::ShortDecl(go_param, field_expr));
804                }
805            }
806            let cont = self.compile_expr(&alt.body);
807            alt_body.extend(cont);
808            cases.push(GoCase {
809                pattern: Some(vec![GoExpr::Lit(GoLit::Int(alt.ctor_tag as i64))]),
810                body: alt_body,
811            });
812        }
813        if let Some(def_expr) = default {
814            let def_stmts = self.compile_expr(def_expr);
815            cases.push(GoCase {
816                pattern: None,
817                body: def_stmts,
818            });
819        }
820        vec![GoStmt::Switch(Some(tag_expr), cases)]
821    }
822    /// Build the minimal OxiLean Go runtime as a list of Go functions.
823    ///
824    /// These mirror the Lean 4 / OxiLean built-in operations that compiled code may call.
825    pub(super) fn build_runtime(&self) -> Vec<GoFunc> {
826        let mut funcs = Vec::new();
827        funcs.push(self.simple_binop_func("natAdd", "+"));
828        {
829            let mut f = GoFunc::new("natSub");
830            f.add_param("a", GoType::GoInt);
831            f.add_param("b", GoType::GoInt);
832            f.add_return(GoType::GoInt);
833            f.body = vec![GoStmt::If(
834                GoExpr::BinOp(
835                    ">=".to_string(),
836                    Box::new(GoExpr::Var("a".to_string())),
837                    Box::new(GoExpr::Var("b".to_string())),
838                ),
839                vec![GoStmt::Return(vec![GoExpr::BinOp(
840                    "-".to_string(),
841                    Box::new(GoExpr::Var("a".to_string())),
842                    Box::new(GoExpr::Var("b".to_string())),
843                )])],
844                vec![GoStmt::Return(vec![GoExpr::Lit(GoLit::Int(0))])],
845            )];
846            funcs.push(f);
847        }
848        funcs.push(self.simple_binop_func("natMul", "*"));
849        {
850            let mut f = GoFunc::new("natDiv");
851            f.add_param("a", GoType::GoInt);
852            f.add_param("b", GoType::GoInt);
853            f.add_return(GoType::GoInt);
854            f.body = vec![GoStmt::If(
855                GoExpr::BinOp(
856                    "==".to_string(),
857                    Box::new(GoExpr::Var("b".to_string())),
858                    Box::new(GoExpr::Lit(GoLit::Int(0))),
859                ),
860                vec![GoStmt::Return(vec![GoExpr::Lit(GoLit::Int(0))])],
861                vec![GoStmt::Return(vec![GoExpr::BinOp(
862                    "/".to_string(),
863                    Box::new(GoExpr::Var("a".to_string())),
864                    Box::new(GoExpr::Var("b".to_string())),
865                )])],
866            )];
867            funcs.push(f);
868        }
869        {
870            let mut f = GoFunc::new("natMod");
871            f.add_param("a", GoType::GoInt);
872            f.add_param("b", GoType::GoInt);
873            f.add_return(GoType::GoInt);
874            f.body = vec![GoStmt::If(
875                GoExpr::BinOp(
876                    "==".to_string(),
877                    Box::new(GoExpr::Var("b".to_string())),
878                    Box::new(GoExpr::Lit(GoLit::Int(0))),
879                ),
880                vec![GoStmt::Return(vec![GoExpr::Lit(GoLit::Int(0))])],
881                vec![GoStmt::Return(vec![GoExpr::BinOp(
882                    "%".to_string(),
883                    Box::new(GoExpr::Var("a".to_string())),
884                    Box::new(GoExpr::Var("b".to_string())),
885                )])],
886            )];
887            funcs.push(f);
888        }
889        funcs.push(self.simple_cmp_func("natEq", "=="));
890        funcs.push(self.simple_cmp_func("natLt", "<"));
891        funcs.push(self.simple_cmp_func("natLe", "<="));
892        funcs.push(self.simple_cmp_func("natGt", ">"));
893        funcs.push(self.simple_cmp_func("natGe", ">="));
894        funcs.push(self.simple_bool_func("boolAnd", "&&"));
895        funcs.push(self.simple_bool_func("boolOr", "||"));
896        {
897            let mut f = GoFunc::new("boolNot");
898            f.add_param("a", GoType::GoBool);
899            f.add_return(GoType::GoBool);
900            f.body = vec![GoStmt::Return(vec![GoExpr::Unary(
901                "!".to_string(),
902                Box::new(GoExpr::Var("a".to_string())),
903            )])];
904            funcs.push(f);
905        }
906        {
907            let mut f = GoFunc::new("strAppend");
908            f.add_param("a", GoType::GoString);
909            f.add_param("b", GoType::GoString);
910            f.add_return(GoType::GoString);
911            f.body = vec![GoStmt::Return(vec![GoExpr::BinOp(
912                "+".to_string(),
913                Box::new(GoExpr::Var("a".to_string())),
914                Box::new(GoExpr::Var("b".to_string())),
915            )])];
916            funcs.push(f);
917        }
918        {
919            let mut f = GoFunc::new("strEq");
920            f.add_param("a", GoType::GoString);
921            f.add_param("b", GoType::GoString);
922            f.add_return(GoType::GoBool);
923            f.body = vec![GoStmt::Return(vec![GoExpr::BinOp(
924                "==".to_string(),
925                Box::new(GoExpr::Var("a".to_string())),
926                Box::new(GoExpr::Var("b".to_string())),
927            )])];
928            funcs.push(f);
929        }
930        {
931            let mut f = GoFunc::new("strLen");
932            f.add_param("s", GoType::GoString);
933            f.add_return(GoType::GoInt);
934            f.body = vec![GoStmt::Return(vec![GoExpr::Call(
935                Box::new(GoExpr::Var("int64".to_string())),
936                vec![GoExpr::Call(
937                    Box::new(GoExpr::Var("len".to_string())),
938                    vec![GoExpr::Var("s".to_string())],
939                )],
940            )])];
941            funcs.push(f);
942        }
943        {
944            let mut f = GoFunc::new("oxiPrint");
945            f.add_param("s", GoType::GoString);
946            f.body = vec![GoStmt::Expr(GoExpr::Call(
947                Box::new(GoExpr::Field(
948                    Box::new(GoExpr::Var("fmt".to_string())),
949                    "Println".to_string(),
950                )),
951                vec![GoExpr::Var("s".to_string())],
952            ))];
953            funcs.push(f);
954        }
955        {
956            let mut f = GoFunc::new("oxiPanic");
957            f.add_param("msg", GoType::GoString);
958            f.body = vec![GoStmt::Panic(GoExpr::Var("msg".to_string()))];
959            funcs.push(f);
960        }
961        funcs
962    }
963    /// Helper: build a two-arg int64 → int64 binary-op function.
964    pub(super) fn simple_binop_func(&self, name: &str, op: &str) -> GoFunc {
965        let mut f = GoFunc::new(name);
966        f.add_param("a", GoType::GoInt);
967        f.add_param("b", GoType::GoInt);
968        f.add_return(GoType::GoInt);
969        f.body = vec![GoStmt::Return(vec![GoExpr::BinOp(
970            op.to_string(),
971            Box::new(GoExpr::Var("a".to_string())),
972            Box::new(GoExpr::Var("b".to_string())),
973        )])];
974        f
975    }
976    /// Helper: build a two-arg int64 → bool comparison function.
977    pub(super) fn simple_cmp_func(&self, name: &str, op: &str) -> GoFunc {
978        let mut f = GoFunc::new(name);
979        f.add_param("a", GoType::GoInt);
980        f.add_param("b", GoType::GoInt);
981        f.add_return(GoType::GoBool);
982        f.body = vec![GoStmt::Return(vec![GoExpr::BinOp(
983            op.to_string(),
984            Box::new(GoExpr::Var("a".to_string())),
985            Box::new(GoExpr::Var("b".to_string())),
986        )])];
987        f
988    }
989    /// Helper: build a two-arg bool → bool binary-op function.
990    pub(super) fn simple_bool_func(&self, name: &str, op: &str) -> GoFunc {
991        let mut f = GoFunc::new(name);
992        f.add_param("a", GoType::GoBool);
993        f.add_param("b", GoType::GoBool);
994        f.add_return(GoType::GoBool);
995        f.body = vec![GoStmt::Return(vec![GoExpr::BinOp(
996            op.to_string(),
997            Box::new(GoExpr::Var("a".to_string())),
998            Box::new(GoExpr::Var("b".to_string())),
999        )])];
1000        f
1001    }
1002    /// Emit a single `GoFunc` to a string.
1003    pub fn emit_func(&self, func: &GoFunc) -> String {
1004        func.codegen()
1005    }
1006    /// Emit a `GoTypeDecl` to a string.
1007    pub fn emit_type_decl(&self, decl: &GoTypeDecl) -> String {
1008        decl.codegen()
1009    }
1010    /// Emit a full `GoModule` to a string.
1011    pub fn emit_module(&self, module: &GoModule) -> String {
1012        module.codegen()
1013    }
1014}
1015/// A case in a switch statement.
1016#[derive(Debug, Clone, PartialEq)]
1017pub struct GoCase {
1018    /// `None` means `default:`
1019    pub pattern: Option<Vec<GoExpr>>,
1020    pub body: Vec<GoStmt>,
1021}
1022/// A Go struct type declaration.
1023#[derive(Debug, Clone)]
1024pub struct GoTypeDecl {
1025    /// Type name.
1026    pub name: String,
1027    /// Fields: `[(field_name, field_type), ...]`.
1028    pub fields: Vec<(String, GoType)>,
1029    /// Whether exported.
1030    pub exported: bool,
1031}
1032impl GoTypeDecl {
1033    /// Create a new struct type declaration.
1034    pub fn new(name: impl Into<String>) -> Self {
1035        GoTypeDecl {
1036            name: name.into(),
1037            fields: Vec::new(),
1038            exported: false,
1039        }
1040    }
1041    /// Add a field.
1042    pub fn add_field(&mut self, name: impl Into<String>, ty: GoType) {
1043        self.fields.push((name.into(), ty));
1044    }
1045    /// Emit Go source for this type declaration.
1046    pub fn codegen(&self) -> String {
1047        let mut out = format!("type {} struct {{\n", self.name);
1048        for (name, ty) in &self.fields {
1049            out.push_str(&format!("    {} {}\n", name, ty));
1050        }
1051        out.push('}');
1052        out
1053    }
1054}
1055#[allow(dead_code)]
1056#[derive(Debug, Clone, Default)]
1057pub struct GoPassStats {
1058    pub total_runs: u32,
1059    pub successful_runs: u32,
1060    pub total_changes: u64,
1061    pub time_ms: u64,
1062    pub iterations_used: u32,
1063}
1064impl GoPassStats {
1065    #[allow(dead_code)]
1066    pub fn new() -> Self {
1067        Self::default()
1068    }
1069    #[allow(dead_code)]
1070    pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
1071        self.total_runs += 1;
1072        self.successful_runs += 1;
1073        self.total_changes += changes;
1074        self.time_ms += time_ms;
1075        self.iterations_used = iterations;
1076    }
1077    #[allow(dead_code)]
1078    pub fn average_changes_per_run(&self) -> f64 {
1079        if self.total_runs == 0 {
1080            return 0.0;
1081        }
1082        self.total_changes as f64 / self.total_runs as f64
1083    }
1084    #[allow(dead_code)]
1085    pub fn success_rate(&self) -> f64 {
1086        if self.total_runs == 0 {
1087            return 0.0;
1088        }
1089        self.successful_runs as f64 / self.total_runs as f64
1090    }
1091    #[allow(dead_code)]
1092    pub fn format_summary(&self) -> String {
1093        format!(
1094            "Runs: {}/{}, Changes: {}, Time: {}ms",
1095            self.successful_runs, self.total_runs, self.total_changes, self.time_ms
1096        )
1097    }
1098}
1099/// Go literal values.
1100#[derive(Debug, Clone, PartialEq)]
1101pub enum GoLit {
1102    /// Integer literal: `0`, `42`, `-7`
1103    Int(i64),
1104    /// Float literal: `3.14`
1105    Float(f64),
1106    /// Boolean literal: `true` or `false`
1107    Bool(bool),
1108    /// String literal: `"hello"`
1109    Str(String),
1110    /// `nil`
1111    Nil,
1112}
1113/// Go type representation for type-directed code generation.
1114#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1115pub enum GoType {
1116    /// `bool`
1117    GoBool,
1118    /// `int64`
1119    GoInt,
1120    /// `float64`
1121    GoFloat,
1122    /// `string`
1123    GoString,
1124    /// `[]T` — a Go slice
1125    GoSlice(Box<GoType>),
1126    /// `map[K]V` — a Go map
1127    GoMap(Box<GoType>, Box<GoType>),
1128    /// `func(params...) returns...`
1129    GoFunc(Vec<GoType>, Vec<GoType>),
1130    /// `interface{}` / `any`
1131    GoInterface,
1132    /// A named struct: `MyStruct`
1133    GoStruct(String),
1134    /// A pointer: `*T`
1135    GoPtr(Box<GoType>),
1136    /// Channel: `chan T`
1137    GoChan(Box<GoType>),
1138    /// `error` interface
1139    GoError,
1140    /// Unit (represented as an empty struct `struct{}`)
1141    GoUnit,
1142}
1143/// Go statement for code generation.
1144#[derive(Debug, Clone, PartialEq)]
1145pub enum GoStmt {
1146    /// `const name type = value`
1147    Const(String, Option<GoType>, GoExpr),
1148    /// `var name type` or `var name type = value`
1149    Var(String, GoType, Option<GoExpr>),
1150    /// Short variable declaration: `name := value`
1151    ShortDecl(String, GoExpr),
1152    /// Assignment: `target = value`
1153    Assign(GoExpr, GoExpr),
1154    /// Return statement: `return exprs...`
1155    Return(Vec<GoExpr>),
1156    /// If statement with optional else
1157    If(GoExpr, Vec<GoStmt>, Vec<GoStmt>),
1158    /// Switch statement: `switch scrutinee { case ... }`
1159    Switch(Option<GoExpr>, Vec<GoCase>),
1160    /// For loop: `for init; cond; post { body }`
1161    For(
1162        Option<Box<GoStmt>>,
1163        Option<GoExpr>,
1164        Option<Box<GoStmt>>,
1165        Vec<GoStmt>,
1166    ),
1167    /// Range-based for: `for k, v := range expr { body }`
1168    ForRange(Option<String>, Option<String>, GoExpr, Vec<GoStmt>),
1169    /// A block of statements: `{ stmts }`
1170    Block(Vec<GoStmt>),
1171    /// A bare expression statement
1172    Expr(GoExpr),
1173    /// Break statement
1174    Break,
1175    /// Continue statement
1176    Continue,
1177    /// Goto label
1178    Goto(String),
1179    /// Label statement
1180    Label(String, Box<GoStmt>),
1181    /// Defer statement
1182    Defer(GoExpr),
1183    /// Go statement (goroutine)
1184    GoRoutine(GoExpr),
1185    /// Panic
1186    Panic(GoExpr),
1187}
1188#[allow(dead_code)]
1189#[derive(Debug, Clone, PartialEq)]
1190pub enum GoPassPhase {
1191    Analysis,
1192    Transformation,
1193    Verification,
1194    Cleanup,
1195}
1196impl GoPassPhase {
1197    #[allow(dead_code)]
1198    pub fn name(&self) -> &str {
1199        match self {
1200            GoPassPhase::Analysis => "analysis",
1201            GoPassPhase::Transformation => "transformation",
1202            GoPassPhase::Verification => "verification",
1203            GoPassPhase::Cleanup => "cleanup",
1204        }
1205    }
1206    #[allow(dead_code)]
1207    pub fn is_modifying(&self) -> bool {
1208        matches!(self, GoPassPhase::Transformation | GoPassPhase::Cleanup)
1209    }
1210}
1211#[allow(dead_code)]
1212pub struct GoPassRegistry {
1213    pub(super) configs: Vec<GoPassConfig>,
1214    pub(super) stats: std::collections::HashMap<String, GoPassStats>,
1215}
1216impl GoPassRegistry {
1217    #[allow(dead_code)]
1218    pub fn new() -> Self {
1219        GoPassRegistry {
1220            configs: Vec::new(),
1221            stats: std::collections::HashMap::new(),
1222        }
1223    }
1224    #[allow(dead_code)]
1225    pub fn register(&mut self, config: GoPassConfig) {
1226        self.stats
1227            .insert(config.pass_name.clone(), GoPassStats::new());
1228        self.configs.push(config);
1229    }
1230    #[allow(dead_code)]
1231    pub fn enabled_passes(&self) -> Vec<&GoPassConfig> {
1232        self.configs.iter().filter(|c| c.enabled).collect()
1233    }
1234    #[allow(dead_code)]
1235    pub fn get_stats(&self, name: &str) -> Option<&GoPassStats> {
1236        self.stats.get(name)
1237    }
1238    #[allow(dead_code)]
1239    pub fn total_passes(&self) -> usize {
1240        self.configs.len()
1241    }
1242    #[allow(dead_code)]
1243    pub fn enabled_count(&self) -> usize {
1244        self.enabled_passes().len()
1245    }
1246    #[allow(dead_code)]
1247    pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
1248        if let Some(stats) = self.stats.get_mut(name) {
1249            stats.record_run(changes, time_ms, iter);
1250        }
1251    }
1252}
1253#[allow(dead_code)]
1254#[derive(Debug, Clone)]
1255pub struct GoCacheEntry {
1256    pub key: String,
1257    pub data: Vec<u8>,
1258    pub timestamp: u64,
1259    pub valid: bool,
1260}
1261#[allow(dead_code)]
1262#[derive(Debug, Clone)]
1263pub struct GoLivenessInfo {
1264    pub live_in: Vec<std::collections::HashSet<u32>>,
1265    pub live_out: Vec<std::collections::HashSet<u32>>,
1266    pub defs: Vec<std::collections::HashSet<u32>>,
1267    pub uses: Vec<std::collections::HashSet<u32>>,
1268}
1269impl GoLivenessInfo {
1270    #[allow(dead_code)]
1271    pub fn new(block_count: usize) -> Self {
1272        GoLivenessInfo {
1273            live_in: vec![std::collections::HashSet::new(); block_count],
1274            live_out: vec![std::collections::HashSet::new(); block_count],
1275            defs: vec![std::collections::HashSet::new(); block_count],
1276            uses: vec![std::collections::HashSet::new(); block_count],
1277        }
1278    }
1279    #[allow(dead_code)]
1280    pub fn add_def(&mut self, block: usize, var: u32) {
1281        if block < self.defs.len() {
1282            self.defs[block].insert(var);
1283        }
1284    }
1285    #[allow(dead_code)]
1286    pub fn add_use(&mut self, block: usize, var: u32) {
1287        if block < self.uses.len() {
1288            self.uses[block].insert(var);
1289        }
1290    }
1291    #[allow(dead_code)]
1292    pub fn is_live_in(&self, block: usize, var: u32) -> bool {
1293        self.live_in
1294            .get(block)
1295            .map(|s| s.contains(&var))
1296            .unwrap_or(false)
1297    }
1298    #[allow(dead_code)]
1299    pub fn is_live_out(&self, block: usize, var: u32) -> bool {
1300        self.live_out
1301            .get(block)
1302            .map(|s| s.contains(&var))
1303            .unwrap_or(false)
1304    }
1305}