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::LcnfString => GoType::GoString,
633            LcnfType::Erased | LcnfType::Irrelevant | LcnfType::Unit => GoType::GoUnit,
634            LcnfType::Object => GoType::GoInterface,
635            LcnfType::Var(_) => GoType::GoInterface,
636            LcnfType::Ctor(_, _) => {
637                GoType::GoPtr(Box::new(GoType::GoStruct("OxiCtor".to_string())))
638            }
639            LcnfType::Fun(params, ret) => {
640                let go_params: Vec<GoType> = params.iter().map(|p| self.compile_type(p)).collect();
641                let go_ret = self.compile_type(ret);
642                GoType::GoFunc(go_params, vec![go_ret])
643            }
644        }
645    }
646    /// Compile an LCNF expression to a sequence of Go statements.
647    ///
648    /// The last statement should be a `return` carrying the final value.
649    pub fn compile_expr(&mut self, expr: &LcnfExpr) -> Vec<GoStmt> {
650        match expr {
651            LcnfExpr::Return(arg) => {
652                let go_expr = self.compile_arg(arg);
653                vec![GoStmt::Return(vec![go_expr])]
654            }
655            LcnfExpr::Unreachable => {
656                vec![GoStmt::Panic(GoExpr::Lit(GoLit::Str(
657                    "unreachable".to_string(),
658                )))]
659            }
660            LcnfExpr::TailCall(func, args) => {
661                let go_func = self.compile_arg(func);
662                let go_args: Vec<GoExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
663                let call = GoExpr::Call(Box::new(go_func), go_args);
664                vec![GoStmt::Return(vec![call])]
665            }
666            LcnfExpr::Let {
667                id,
668                name,
669                ty: _,
670                value,
671                body,
672            } => {
673                let go_name = Self::mangle_name(name);
674                let go_name = format!("{}_{}", go_name, id.0);
675                self.var_map.insert(*id, go_name.clone());
676                let mut stmts = Vec::new();
677                let val_expr = self.compile_let_value(value);
678                stmts.push(GoStmt::ShortDecl(go_name, val_expr));
679                let cont = self.compile_expr(body);
680                stmts.extend(cont);
681                stmts
682            }
683            LcnfExpr::Case {
684                scrutinee,
685                scrutinee_ty: _,
686                alts,
687                default,
688            } => self.compile_case(*scrutinee, alts, default.as_deref()),
689        }
690    }
691    /// Compile a `LcnfLetValue` into a Go expression.
692    pub(super) fn compile_let_value(&mut self, value: &LcnfLetValue) -> GoExpr {
693        match value {
694            LcnfLetValue::Lit(lit) => self.compile_lit(lit),
695            LcnfLetValue::Erased | LcnfLetValue::Reset(_) => GoExpr::Lit(GoLit::Nil),
696            LcnfLetValue::FVar(id) => {
697                let name = self
698                    .var_map
699                    .get(id)
700                    .cloned()
701                    .unwrap_or_else(|| format!("_x{}", id.0));
702                GoExpr::Var(name)
703            }
704            LcnfLetValue::App(func, args) => {
705                let go_func = self.compile_arg(func);
706                let go_args: Vec<GoExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
707                GoExpr::Call(Box::new(go_func), go_args)
708            }
709            LcnfLetValue::Proj(_, idx, var) => {
710                let var_name = self
711                    .var_map
712                    .get(var)
713                    .cloned()
714                    .unwrap_or_else(|| format!("_x{}", var.0));
715                GoExpr::Index(
716                    Box::new(GoExpr::Field(
717                        Box::new(GoExpr::Var(var_name)),
718                        "Fields".to_string(),
719                    )),
720                    Box::new(GoExpr::Lit(GoLit::Int(*idx as i64))),
721                )
722            }
723            LcnfLetValue::Ctor(name, tag, args) => {
724                let go_name = Self::mangle_name(name);
725                let mut fields = vec![
726                    ("Tag".to_string(), GoExpr::Lit(GoLit::Int(*tag as i64))),
727                    ("_ctorName".to_string(), GoExpr::Lit(GoLit::Str(go_name))),
728                ];
729                if !args.is_empty() {
730                    let go_args: Vec<GoExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
731                    fields.push((
732                        "Fields".to_string(),
733                        GoExpr::SliceLit(GoType::GoInterface, go_args),
734                    ));
735                } else {
736                    fields.push((
737                        "Fields".to_string(),
738                        GoExpr::SliceLit(GoType::GoInterface, vec![]),
739                    ));
740                }
741                GoExpr::AddressOf(Box::new(GoExpr::Composite(
742                    GoType::GoStruct("OxiCtor".to_string()),
743                    fields,
744                )))
745            }
746            LcnfLetValue::Reuse(_slot, name, tag, args) => {
747                self.compile_let_value(&LcnfLetValue::Ctor(name.clone(), *tag, args.clone()))
748            }
749        }
750    }
751    /// Compile an `LcnfArg` into a Go expression.
752    pub fn compile_arg(&self, arg: &LcnfArg) -> GoExpr {
753        match arg {
754            LcnfArg::Var(id) => {
755                let name = self
756                    .var_map
757                    .get(id)
758                    .cloned()
759                    .unwrap_or_else(|| format!("_x{}", id.0));
760                GoExpr::Var(name)
761            }
762            LcnfArg::Lit(lit) => self.compile_lit(lit),
763            LcnfArg::Erased | LcnfArg::Type(_) => GoExpr::Lit(GoLit::Nil),
764        }
765    }
766    /// Compile an `LcnfLit` to a Go expression.
767    pub(super) fn compile_lit(&self, lit: &LcnfLit) -> GoExpr {
768        match lit {
769            LcnfLit::Nat(n) => GoExpr::Lit(GoLit::Int(*n as i64)),
770            LcnfLit::Str(s) => GoExpr::Lit(GoLit::Str(s.clone())),
771        }
772    }
773    /// Compile a `Case` expression to Go statements (switch).
774    pub(super) fn compile_case(
775        &mut self,
776        scrutinee: LcnfVarId,
777        alts: &[LcnfAlt],
778        default: Option<&LcnfExpr>,
779    ) -> Vec<GoStmt> {
780        let scr_name = self
781            .var_map
782            .get(&scrutinee)
783            .cloned()
784            .unwrap_or_else(|| format!("_x{}", scrutinee.0));
785        let tag_expr = GoExpr::Field(Box::new(GoExpr::Var(scr_name.clone())), "Tag".to_string());
786        let mut cases: Vec<GoCase> = Vec::new();
787        for alt in alts {
788            let mut alt_body: Vec<GoStmt> = Vec::new();
789            for (idx, param) in alt.params.iter().enumerate() {
790                if !param.erased {
791                    let go_param = Self::mangle_name(&param.name);
792                    let go_param = format!("{}_{}", go_param, param.id.0);
793                    self.var_map.insert(param.id, go_param.clone());
794                    let field_expr = GoExpr::Index(
795                        Box::new(GoExpr::Field(
796                            Box::new(GoExpr::Var(scr_name.clone())),
797                            "Fields".to_string(),
798                        )),
799                        Box::new(GoExpr::Lit(GoLit::Int(idx as i64))),
800                    );
801                    alt_body.push(GoStmt::ShortDecl(go_param, field_expr));
802                }
803            }
804            let cont = self.compile_expr(&alt.body);
805            alt_body.extend(cont);
806            cases.push(GoCase {
807                pattern: Some(vec![GoExpr::Lit(GoLit::Int(alt.ctor_tag as i64))]),
808                body: alt_body,
809            });
810        }
811        if let Some(def_expr) = default {
812            let def_stmts = self.compile_expr(def_expr);
813            cases.push(GoCase {
814                pattern: None,
815                body: def_stmts,
816            });
817        }
818        vec![GoStmt::Switch(Some(tag_expr), cases)]
819    }
820    /// Build the minimal OxiLean Go runtime as a list of Go functions.
821    ///
822    /// These mirror the Lean 4 / OxiLean built-in operations that compiled code may call.
823    pub(super) fn build_runtime(&self) -> Vec<GoFunc> {
824        let mut funcs = Vec::new();
825        funcs.push(self.simple_binop_func("natAdd", "+"));
826        {
827            let mut f = GoFunc::new("natSub");
828            f.add_param("a", GoType::GoInt);
829            f.add_param("b", GoType::GoInt);
830            f.add_return(GoType::GoInt);
831            f.body = vec![GoStmt::If(
832                GoExpr::BinOp(
833                    ">=".to_string(),
834                    Box::new(GoExpr::Var("a".to_string())),
835                    Box::new(GoExpr::Var("b".to_string())),
836                ),
837                vec![GoStmt::Return(vec![GoExpr::BinOp(
838                    "-".to_string(),
839                    Box::new(GoExpr::Var("a".to_string())),
840                    Box::new(GoExpr::Var("b".to_string())),
841                )])],
842                vec![GoStmt::Return(vec![GoExpr::Lit(GoLit::Int(0))])],
843            )];
844            funcs.push(f);
845        }
846        funcs.push(self.simple_binop_func("natMul", "*"));
847        {
848            let mut f = GoFunc::new("natDiv");
849            f.add_param("a", GoType::GoInt);
850            f.add_param("b", GoType::GoInt);
851            f.add_return(GoType::GoInt);
852            f.body = vec![GoStmt::If(
853                GoExpr::BinOp(
854                    "==".to_string(),
855                    Box::new(GoExpr::Var("b".to_string())),
856                    Box::new(GoExpr::Lit(GoLit::Int(0))),
857                ),
858                vec![GoStmt::Return(vec![GoExpr::Lit(GoLit::Int(0))])],
859                vec![GoStmt::Return(vec![GoExpr::BinOp(
860                    "/".to_string(),
861                    Box::new(GoExpr::Var("a".to_string())),
862                    Box::new(GoExpr::Var("b".to_string())),
863                )])],
864            )];
865            funcs.push(f);
866        }
867        {
868            let mut f = GoFunc::new("natMod");
869            f.add_param("a", GoType::GoInt);
870            f.add_param("b", GoType::GoInt);
871            f.add_return(GoType::GoInt);
872            f.body = vec![GoStmt::If(
873                GoExpr::BinOp(
874                    "==".to_string(),
875                    Box::new(GoExpr::Var("b".to_string())),
876                    Box::new(GoExpr::Lit(GoLit::Int(0))),
877                ),
878                vec![GoStmt::Return(vec![GoExpr::Lit(GoLit::Int(0))])],
879                vec![GoStmt::Return(vec![GoExpr::BinOp(
880                    "%".to_string(),
881                    Box::new(GoExpr::Var("a".to_string())),
882                    Box::new(GoExpr::Var("b".to_string())),
883                )])],
884            )];
885            funcs.push(f);
886        }
887        funcs.push(self.simple_cmp_func("natEq", "=="));
888        funcs.push(self.simple_cmp_func("natLt", "<"));
889        funcs.push(self.simple_cmp_func("natLe", "<="));
890        funcs.push(self.simple_cmp_func("natGt", ">"));
891        funcs.push(self.simple_cmp_func("natGe", ">="));
892        funcs.push(self.simple_bool_func("boolAnd", "&&"));
893        funcs.push(self.simple_bool_func("boolOr", "||"));
894        {
895            let mut f = GoFunc::new("boolNot");
896            f.add_param("a", GoType::GoBool);
897            f.add_return(GoType::GoBool);
898            f.body = vec![GoStmt::Return(vec![GoExpr::Unary(
899                "!".to_string(),
900                Box::new(GoExpr::Var("a".to_string())),
901            )])];
902            funcs.push(f);
903        }
904        {
905            let mut f = GoFunc::new("strAppend");
906            f.add_param("a", GoType::GoString);
907            f.add_param("b", GoType::GoString);
908            f.add_return(GoType::GoString);
909            f.body = vec![GoStmt::Return(vec![GoExpr::BinOp(
910                "+".to_string(),
911                Box::new(GoExpr::Var("a".to_string())),
912                Box::new(GoExpr::Var("b".to_string())),
913            )])];
914            funcs.push(f);
915        }
916        {
917            let mut f = GoFunc::new("strEq");
918            f.add_param("a", GoType::GoString);
919            f.add_param("b", GoType::GoString);
920            f.add_return(GoType::GoBool);
921            f.body = vec![GoStmt::Return(vec![GoExpr::BinOp(
922                "==".to_string(),
923                Box::new(GoExpr::Var("a".to_string())),
924                Box::new(GoExpr::Var("b".to_string())),
925            )])];
926            funcs.push(f);
927        }
928        {
929            let mut f = GoFunc::new("strLen");
930            f.add_param("s", GoType::GoString);
931            f.add_return(GoType::GoInt);
932            f.body = vec![GoStmt::Return(vec![GoExpr::Call(
933                Box::new(GoExpr::Var("int64".to_string())),
934                vec![GoExpr::Call(
935                    Box::new(GoExpr::Var("len".to_string())),
936                    vec![GoExpr::Var("s".to_string())],
937                )],
938            )])];
939            funcs.push(f);
940        }
941        {
942            let mut f = GoFunc::new("oxiPrint");
943            f.add_param("s", GoType::GoString);
944            f.body = vec![GoStmt::Expr(GoExpr::Call(
945                Box::new(GoExpr::Field(
946                    Box::new(GoExpr::Var("fmt".to_string())),
947                    "Println".to_string(),
948                )),
949                vec![GoExpr::Var("s".to_string())],
950            ))];
951            funcs.push(f);
952        }
953        {
954            let mut f = GoFunc::new("oxiPanic");
955            f.add_param("msg", GoType::GoString);
956            f.body = vec![GoStmt::Panic(GoExpr::Var("msg".to_string()))];
957            funcs.push(f);
958        }
959        funcs
960    }
961    /// Helper: build a two-arg int64 → int64 binary-op function.
962    pub(super) fn simple_binop_func(&self, name: &str, op: &str) -> GoFunc {
963        let mut f = GoFunc::new(name);
964        f.add_param("a", GoType::GoInt);
965        f.add_param("b", GoType::GoInt);
966        f.add_return(GoType::GoInt);
967        f.body = vec![GoStmt::Return(vec![GoExpr::BinOp(
968            op.to_string(),
969            Box::new(GoExpr::Var("a".to_string())),
970            Box::new(GoExpr::Var("b".to_string())),
971        )])];
972        f
973    }
974    /// Helper: build a two-arg int64 → bool comparison function.
975    pub(super) fn simple_cmp_func(&self, name: &str, op: &str) -> GoFunc {
976        let mut f = GoFunc::new(name);
977        f.add_param("a", GoType::GoInt);
978        f.add_param("b", GoType::GoInt);
979        f.add_return(GoType::GoBool);
980        f.body = vec![GoStmt::Return(vec![GoExpr::BinOp(
981            op.to_string(),
982            Box::new(GoExpr::Var("a".to_string())),
983            Box::new(GoExpr::Var("b".to_string())),
984        )])];
985        f
986    }
987    /// Helper: build a two-arg bool → bool binary-op function.
988    pub(super) fn simple_bool_func(&self, name: &str, op: &str) -> GoFunc {
989        let mut f = GoFunc::new(name);
990        f.add_param("a", GoType::GoBool);
991        f.add_param("b", GoType::GoBool);
992        f.add_return(GoType::GoBool);
993        f.body = vec![GoStmt::Return(vec![GoExpr::BinOp(
994            op.to_string(),
995            Box::new(GoExpr::Var("a".to_string())),
996            Box::new(GoExpr::Var("b".to_string())),
997        )])];
998        f
999    }
1000    /// Emit a single `GoFunc` to a string.
1001    pub fn emit_func(&self, func: &GoFunc) -> String {
1002        func.codegen()
1003    }
1004    /// Emit a `GoTypeDecl` to a string.
1005    pub fn emit_type_decl(&self, decl: &GoTypeDecl) -> String {
1006        decl.codegen()
1007    }
1008    /// Emit a full `GoModule` to a string.
1009    pub fn emit_module(&self, module: &GoModule) -> String {
1010        module.codegen()
1011    }
1012}
1013/// A case in a switch statement.
1014#[derive(Debug, Clone, PartialEq)]
1015pub struct GoCase {
1016    /// `None` means `default:`
1017    pub pattern: Option<Vec<GoExpr>>,
1018    pub body: Vec<GoStmt>,
1019}
1020/// A Go struct type declaration.
1021#[derive(Debug, Clone)]
1022pub struct GoTypeDecl {
1023    /// Type name.
1024    pub name: String,
1025    /// Fields: `[(field_name, field_type), ...]`.
1026    pub fields: Vec<(String, GoType)>,
1027    /// Whether exported.
1028    pub exported: bool,
1029}
1030impl GoTypeDecl {
1031    /// Create a new struct type declaration.
1032    pub fn new(name: impl Into<String>) -> Self {
1033        GoTypeDecl {
1034            name: name.into(),
1035            fields: Vec::new(),
1036            exported: false,
1037        }
1038    }
1039    /// Add a field.
1040    pub fn add_field(&mut self, name: impl Into<String>, ty: GoType) {
1041        self.fields.push((name.into(), ty));
1042    }
1043    /// Emit Go source for this type declaration.
1044    pub fn codegen(&self) -> String {
1045        let mut out = format!("type {} struct {{\n", self.name);
1046        for (name, ty) in &self.fields {
1047            out.push_str(&format!("    {} {}\n", name, ty));
1048        }
1049        out.push('}');
1050        out
1051    }
1052}
1053#[allow(dead_code)]
1054#[derive(Debug, Clone, Default)]
1055pub struct GoPassStats {
1056    pub total_runs: u32,
1057    pub successful_runs: u32,
1058    pub total_changes: u64,
1059    pub time_ms: u64,
1060    pub iterations_used: u32,
1061}
1062impl GoPassStats {
1063    #[allow(dead_code)]
1064    pub fn new() -> Self {
1065        Self::default()
1066    }
1067    #[allow(dead_code)]
1068    pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
1069        self.total_runs += 1;
1070        self.successful_runs += 1;
1071        self.total_changes += changes;
1072        self.time_ms += time_ms;
1073        self.iterations_used = iterations;
1074    }
1075    #[allow(dead_code)]
1076    pub fn average_changes_per_run(&self) -> f64 {
1077        if self.total_runs == 0 {
1078            return 0.0;
1079        }
1080        self.total_changes as f64 / self.total_runs as f64
1081    }
1082    #[allow(dead_code)]
1083    pub fn success_rate(&self) -> f64 {
1084        if self.total_runs == 0 {
1085            return 0.0;
1086        }
1087        self.successful_runs as f64 / self.total_runs as f64
1088    }
1089    #[allow(dead_code)]
1090    pub fn format_summary(&self) -> String {
1091        format!(
1092            "Runs: {}/{}, Changes: {}, Time: {}ms",
1093            self.successful_runs, self.total_runs, self.total_changes, self.time_ms
1094        )
1095    }
1096}
1097/// Go literal values.
1098#[derive(Debug, Clone, PartialEq)]
1099pub enum GoLit {
1100    /// Integer literal: `0`, `42`, `-7`
1101    Int(i64),
1102    /// Float literal: `3.14`
1103    Float(f64),
1104    /// Boolean literal: `true` or `false`
1105    Bool(bool),
1106    /// String literal: `"hello"`
1107    Str(String),
1108    /// `nil`
1109    Nil,
1110}
1111/// Go type representation for type-directed code generation.
1112#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1113pub enum GoType {
1114    /// `bool`
1115    GoBool,
1116    /// `int64`
1117    GoInt,
1118    /// `float64`
1119    GoFloat,
1120    /// `string`
1121    GoString,
1122    /// `[]T` — a Go slice
1123    GoSlice(Box<GoType>),
1124    /// `map[K]V` — a Go map
1125    GoMap(Box<GoType>, Box<GoType>),
1126    /// `func(params...) returns...`
1127    GoFunc(Vec<GoType>, Vec<GoType>),
1128    /// `interface{}` / `any`
1129    GoInterface,
1130    /// A named struct: `MyStruct`
1131    GoStruct(String),
1132    /// A pointer: `*T`
1133    GoPtr(Box<GoType>),
1134    /// Channel: `chan T`
1135    GoChan(Box<GoType>),
1136    /// `error` interface
1137    GoError,
1138    /// Unit (represented as an empty struct `struct{}`)
1139    GoUnit,
1140}
1141/// Go statement for code generation.
1142#[derive(Debug, Clone, PartialEq)]
1143pub enum GoStmt {
1144    /// `const name type = value`
1145    Const(String, Option<GoType>, GoExpr),
1146    /// `var name type` or `var name type = value`
1147    Var(String, GoType, Option<GoExpr>),
1148    /// Short variable declaration: `name := value`
1149    ShortDecl(String, GoExpr),
1150    /// Assignment: `target = value`
1151    Assign(GoExpr, GoExpr),
1152    /// Return statement: `return exprs...`
1153    Return(Vec<GoExpr>),
1154    /// If statement with optional else
1155    If(GoExpr, Vec<GoStmt>, Vec<GoStmt>),
1156    /// Switch statement: `switch scrutinee { case ... }`
1157    Switch(Option<GoExpr>, Vec<GoCase>),
1158    /// For loop: `for init; cond; post { body }`
1159    For(
1160        Option<Box<GoStmt>>,
1161        Option<GoExpr>,
1162        Option<Box<GoStmt>>,
1163        Vec<GoStmt>,
1164    ),
1165    /// Range-based for: `for k, v := range expr { body }`
1166    ForRange(Option<String>, Option<String>, GoExpr, Vec<GoStmt>),
1167    /// A block of statements: `{ stmts }`
1168    Block(Vec<GoStmt>),
1169    /// A bare expression statement
1170    Expr(GoExpr),
1171    /// Break statement
1172    Break,
1173    /// Continue statement
1174    Continue,
1175    /// Goto label
1176    Goto(String),
1177    /// Label statement
1178    Label(String, Box<GoStmt>),
1179    /// Defer statement
1180    Defer(GoExpr),
1181    /// Go statement (goroutine)
1182    GoRoutine(GoExpr),
1183    /// Panic
1184    Panic(GoExpr),
1185}
1186#[allow(dead_code)]
1187#[derive(Debug, Clone, PartialEq)]
1188pub enum GoPassPhase {
1189    Analysis,
1190    Transformation,
1191    Verification,
1192    Cleanup,
1193}
1194impl GoPassPhase {
1195    #[allow(dead_code)]
1196    pub fn name(&self) -> &str {
1197        match self {
1198            GoPassPhase::Analysis => "analysis",
1199            GoPassPhase::Transformation => "transformation",
1200            GoPassPhase::Verification => "verification",
1201            GoPassPhase::Cleanup => "cleanup",
1202        }
1203    }
1204    #[allow(dead_code)]
1205    pub fn is_modifying(&self) -> bool {
1206        matches!(self, GoPassPhase::Transformation | GoPassPhase::Cleanup)
1207    }
1208}
1209#[allow(dead_code)]
1210pub struct GoPassRegistry {
1211    pub(super) configs: Vec<GoPassConfig>,
1212    pub(super) stats: std::collections::HashMap<String, GoPassStats>,
1213}
1214impl GoPassRegistry {
1215    #[allow(dead_code)]
1216    pub fn new() -> Self {
1217        GoPassRegistry {
1218            configs: Vec::new(),
1219            stats: std::collections::HashMap::new(),
1220        }
1221    }
1222    #[allow(dead_code)]
1223    pub fn register(&mut self, config: GoPassConfig) {
1224        self.stats
1225            .insert(config.pass_name.clone(), GoPassStats::new());
1226        self.configs.push(config);
1227    }
1228    #[allow(dead_code)]
1229    pub fn enabled_passes(&self) -> Vec<&GoPassConfig> {
1230        self.configs.iter().filter(|c| c.enabled).collect()
1231    }
1232    #[allow(dead_code)]
1233    pub fn get_stats(&self, name: &str) -> Option<&GoPassStats> {
1234        self.stats.get(name)
1235    }
1236    #[allow(dead_code)]
1237    pub fn total_passes(&self) -> usize {
1238        self.configs.len()
1239    }
1240    #[allow(dead_code)]
1241    pub fn enabled_count(&self) -> usize {
1242        self.enabled_passes().len()
1243    }
1244    #[allow(dead_code)]
1245    pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
1246        if let Some(stats) = self.stats.get_mut(name) {
1247            stats.record_run(changes, time_ms, iter);
1248        }
1249    }
1250}
1251#[allow(dead_code)]
1252#[derive(Debug, Clone)]
1253pub struct GoCacheEntry {
1254    pub key: String,
1255    pub data: Vec<u8>,
1256    pub timestamp: u64,
1257    pub valid: bool,
1258}
1259#[allow(dead_code)]
1260#[derive(Debug, Clone)]
1261pub struct GoLivenessInfo {
1262    pub live_in: Vec<std::collections::HashSet<u32>>,
1263    pub live_out: Vec<std::collections::HashSet<u32>>,
1264    pub defs: Vec<std::collections::HashSet<u32>>,
1265    pub uses: Vec<std::collections::HashSet<u32>>,
1266}
1267impl GoLivenessInfo {
1268    #[allow(dead_code)]
1269    pub fn new(block_count: usize) -> Self {
1270        GoLivenessInfo {
1271            live_in: vec![std::collections::HashSet::new(); block_count],
1272            live_out: vec![std::collections::HashSet::new(); block_count],
1273            defs: vec![std::collections::HashSet::new(); block_count],
1274            uses: vec![std::collections::HashSet::new(); block_count],
1275        }
1276    }
1277    #[allow(dead_code)]
1278    pub fn add_def(&mut self, block: usize, var: u32) {
1279        if block < self.defs.len() {
1280            self.defs[block].insert(var);
1281        }
1282    }
1283    #[allow(dead_code)]
1284    pub fn add_use(&mut self, block: usize, var: u32) {
1285        if block < self.uses.len() {
1286            self.uses[block].insert(var);
1287        }
1288    }
1289    #[allow(dead_code)]
1290    pub fn is_live_in(&self, block: usize, var: u32) -> bool {
1291        self.live_in
1292            .get(block)
1293            .map(|s| s.contains(&var))
1294            .unwrap_or(false)
1295    }
1296    #[allow(dead_code)]
1297    pub fn is_live_out(&self, block: usize, var: u32) -> bool {
1298        self.live_out
1299            .get(block)
1300            .map(|s| s.contains(&var))
1301            .unwrap_or(false)
1302    }
1303}