Skip to main content

logicaffeine_compile/
codegen.rs

1//! Code generation from LOGOS AST to Rust source code.
2//!
3//! This module transforms the parsed and type-checked LOGOS program into
4//! idiomatic Rust code. The generated code uses `logicaffeine_data` types
5//! for runtime values and integrates with the kernel for proof verification.
6//!
7//! # Pipeline Position
8//!
9//! ```text
10//! ┌─────────────────────────────────────────────────────────┐
11//! │  LOGOS Source → Lexer → Parser → AST → Analysis → HERE │
12//! └─────────────────────────────────────────────────────────┘
13//!                                                      ↓
14//!                                               Rust Source
15//! ```
16//!
17//! # Code Generation Rules
18//!
19//! | LOGOS Statement | Rust Output |
20//! |-----------------|-------------|
21//! | `Let x be 5.` | `let x = 5;` |
22//! | `Set x to 10.` | `x = 10;` |
23//! | `Give x to y.` | `let y = x;` (move) |
24//! | `Show x to show.` | `println!("{}", x);` |
25//! | `If x > 0 then...` | `if x > 0 { ... }` |
26//! | `Repeat while x > 0...` | `while x > 0 { ... }` |
27//! | `Zone "name"...` | `{ /* zone scope */ }` |
28//! | `Mount x at "path".` | `x.mount(vfs, "path").await;` |
29//! | `Sync x on "topic".` | `x.subscribe("topic").await;` |
30//!
31//! # Key Features
32//!
33//! - **Refinement Types**: Generates `debug_assert!` for type predicates
34//! - **Policy Enforcement**: Emits capability checks for access control
35//! - **Zone Safety**: Translates memory zones to Rust scopes
36//! - **CRDT Mutability**: Uses `.set()` for LWWRegister/MVRegister fields
37//! - **Async Detection**: Adds `#[tokio::main]` when async operations are present
38//! - **VFS Detection**: Injects `NativeVfs::new()` for file operations
39//! - **Mount+Sync Detection**: Uses `Distributed<T>` for combined persistence/sync
40//!
41//! # Refinement Context
42//!
43//! The [`RefinementContext`] tracks type predicates across scopes:
44//!
45//! ```text
46//! Let x: { it: Int | it > 0 } be 5.   ←  Register constraint
47//!      ↓
48//! debug_assert!(5 > 0);               ←  Check at definition
49//!      ↓
50//! Set x to 10.                        ←  Re-check on mutation
51//!      ↓
52//! debug_assert!(10 > 0);              ←  Re-emit assertion
53//! ```
54//!
55//! - When a variable with a refinement type is defined, its constraint is registered
56//! - When that variable is mutated, the assertion is re-emitted
57//! - Variable types are tracked for capability resolution
58//!
59//! # Entry Point
60//!
61//! The main entry point is [`codegen_program`], which generates a complete Rust
62//! program from a list of statements.
63
64use std::collections::{HashMap, HashSet};
65use std::fmt::Write;
66
67use crate::analysis::registry::{FieldDef, FieldType, TypeDef, TypeRegistry, VariantDef};
68use crate::analysis::policy::{PolicyRegistry, PredicateDef, CapabilityDef, PolicyCondition};
69use crate::ast::logic::{LogicExpr, NumberKind, Term};
70use crate::ast::stmt::{BinaryOpKind, Expr, Literal, ReadSource, Stmt, TypeExpr};
71use crate::formatter::RustFormatter;
72use crate::intern::{Interner, Symbol};
73use crate::registry::SymbolRegistry;
74
75// =============================================================================
76// Refinement Type Enforcement
77// =============================================================================
78
79/// Tracks refinement type constraints across scopes for mutation enforcement.
80///
81/// When a variable with a refinement type is defined, its constraint is registered
82/// in the current scope. When that variable is mutated via `Set`, the assertion is
83/// re-emitted to ensure the invariant is preserved.
84///
85/// # Scope Management
86///
87/// The context maintains a stack of scopes to handle nested blocks:
88///
89/// ```text
90/// ┌─────────────────────────────┐
91/// │ Global Scope               │ ← x: { it > 0 }
92/// │  ┌──────────────────────┐  │
93/// │  │ Zone Scope           │  │ ← y: { it < 100 }
94/// │  │  ┌────────────────┐  │  │
95/// │  │  │ If Block Scope │  │  │ ← z: { it != 0 }
96/// │  │  └────────────────┘  │  │
97/// │  └──────────────────────┘  │
98/// └─────────────────────────────┘
99/// ```
100///
101/// # Variable Type Tracking
102///
103/// The context also tracks variable types for capability resolution. This allows
104/// statements like `Check that user can publish the document` to resolve "the document"
105/// to a variable named `doc` of type `Document`.
106pub struct RefinementContext<'a> {
107    /// Stack of scopes. Each scope maps variable Symbol to (bound_var, predicate).
108    scopes: Vec<HashMap<Symbol, (Symbol, &'a LogicExpr<'a>)>>,
109
110    /// Maps variable name Symbol to type name (for capability resolution).
111    ///
112    /// Example: `doc` → `"Document"` allows resolving "the document" to `&doc`.
113    variable_types: HashMap<Symbol, String>,
114
115    /// Stack of scopes tracking which bindings came from boxed enum fields.
116    /// When these are used in expressions, they need to be dereferenced with `*`.
117    boxed_binding_scopes: Vec<HashSet<Symbol>>,
118
119    /// Tracks variables that are known to be String type.
120    /// Used for proper string concatenation codegen (format! vs +).
121    string_vars: HashSet<Symbol>,
122}
123
124impl<'a> RefinementContext<'a> {
125    pub fn new() -> Self {
126        Self {
127            scopes: vec![HashMap::new()],
128            variable_types: HashMap::new(),
129            boxed_binding_scopes: vec![HashSet::new()],
130            string_vars: HashSet::new(),
131        }
132    }
133
134    fn push_scope(&mut self) {
135        self.scopes.push(HashMap::new());
136        self.boxed_binding_scopes.push(HashSet::new());
137    }
138
139    fn pop_scope(&mut self) {
140        self.scopes.pop();
141        self.boxed_binding_scopes.pop();
142    }
143
144    /// Register a binding that came from a boxed enum field.
145    /// These need `*` dereferencing when used in expressions.
146    fn register_boxed_binding(&mut self, var: Symbol) {
147        if let Some(scope) = self.boxed_binding_scopes.last_mut() {
148            scope.insert(var);
149        }
150    }
151
152    /// Check if a variable is a boxed binding (needs dereferencing).
153    fn is_boxed_binding(&self, var: Symbol) -> bool {
154        for scope in self.boxed_binding_scopes.iter().rev() {
155            if scope.contains(&var) {
156                return true;
157            }
158        }
159        false
160    }
161
162    /// Register a variable as having String type.
163    /// Used for proper string concatenation codegen.
164    fn register_string_var(&mut self, var: Symbol) {
165        self.string_vars.insert(var);
166    }
167
168    /// Check if a variable is known to be a String.
169    fn is_string_var(&self, var: Symbol) -> bool {
170        self.string_vars.contains(&var)
171    }
172
173    /// Get a reference to the string_vars set for expression codegen.
174    fn get_string_vars(&self) -> &HashSet<Symbol> {
175        &self.string_vars
176    }
177
178    fn register(&mut self, var: Symbol, bound_var: Symbol, predicate: &'a LogicExpr<'a>) {
179        if let Some(scope) = self.scopes.last_mut() {
180            scope.insert(var, (bound_var, predicate));
181        }
182    }
183
184    fn get_constraint(&self, var: Symbol) -> Option<(Symbol, &'a LogicExpr<'a>)> {
185        for scope in self.scopes.iter().rev() {
186            if let Some(entry) = scope.get(&var) {
187                return Some(*entry);
188            }
189        }
190        None
191    }
192
193    /// Phase 50: Register a variable with its type for capability resolution
194    fn register_variable_type(&mut self, var: Symbol, type_name: String) {
195        self.variable_types.insert(var, type_name);
196    }
197
198    /// Get variable type map for expression codegen optimization.
199    fn get_variable_types(&self) -> &HashMap<Symbol, String> {
200        &self.variable_types
201    }
202
203    /// Phase 50: Find a variable name by its type (for resolving "the document" to "doc")
204    fn find_variable_by_type(&self, type_name: &str, interner: &Interner) -> Option<String> {
205        let type_lower = type_name.to_lowercase();
206        for (var_sym, var_type) in &self.variable_types {
207            if var_type.to_lowercase() == type_lower {
208                return Some(interner.resolve(*var_sym).to_string());
209            }
210        }
211        None
212    }
213}
214
215/// Emits a debug_assert for a refinement predicate, substituting the bound variable.
216fn emit_refinement_check(
217    var_name: &str,
218    bound_var: Symbol,
219    predicate: &LogicExpr,
220    interner: &Interner,
221    indent_str: &str,
222    output: &mut String,
223) {
224    let assertion = codegen_assertion(predicate, interner);
225    let bound = interner.resolve(bound_var);
226    let check = if bound == var_name {
227        assertion
228    } else {
229        replace_word(&assertion, bound, var_name)
230    };
231    writeln!(output, "{}debug_assert!({});", indent_str, check).unwrap();
232}
233
234/// Word-boundary replacement to substitute bound variable with actual variable.
235fn replace_word(text: &str, from: &str, to: &str) -> String {
236    let mut result = String::with_capacity(text.len());
237    let mut word = String::new();
238    for c in text.chars() {
239        if c.is_alphanumeric() || c == '_' {
240            word.push(c);
241        } else {
242            if !word.is_empty() {
243                result.push_str(if word == from { to } else { &word });
244                word.clear();
245            }
246            result.push(c);
247        }
248    }
249    if !word.is_empty() {
250        result.push_str(if word == from { to } else { &word });
251    }
252    result
253}
254
255// =============================================================================
256// Mount+Sync Detection for Distributed<T>
257// =============================================================================
258
259/// Tracks which variables have Mount and/or Sync statements.
260///
261/// This is used to detect when a variable needs `Distributed<T>` instead of
262/// separate persistence and synchronization wrappers. A variable that is both
263/// mounted and synced can use the unified `Distributed<T>` type.
264///
265/// # Detection Flow
266///
267/// ```text
268/// Pre-scan all statements
269///       ↓
270/// Found "Mount x at path"  →  x.mounted = true, x.mount_path = Some(path)
271/// Found "Sync x on topic"  →  x.synced = true, x.sync_topic = Some(topic)
272///       ↓
273/// If x.mounted && x.synced  →  Use Distributed<T> with both
274/// ```
275#[derive(Debug, Default)]
276pub struct VariableCapabilities {
277    /// Variable has a Mount statement (persistence).
278    mounted: bool,
279    /// Variable has a Sync statement (network synchronization).
280    synced: bool,
281    /// Path expression for Mount (as generated code string).
282    mount_path: Option<String>,
283    /// Topic expression for Sync (as generated code string).
284    sync_topic: Option<String>,
285}
286
287/// Helper to create an empty VariableCapabilities map (for tests).
288pub fn empty_var_caps() -> HashMap<Symbol, VariableCapabilities> {
289    HashMap::new()
290}
291
292/// Pre-scan statements to detect variables that have both Mount and Sync.
293/// Returns a map from variable Symbol to its capabilities.
294fn analyze_variable_capabilities<'a>(
295    stmts: &[Stmt<'a>],
296    interner: &Interner,
297) -> HashMap<Symbol, VariableCapabilities> {
298    let mut caps: HashMap<Symbol, VariableCapabilities> = HashMap::new();
299    let empty_synced = HashSet::new();
300
301    for stmt in stmts {
302        match stmt {
303            Stmt::Mount { var, path } => {
304                let entry = caps.entry(*var).or_default();
305                entry.mounted = true;
306                entry.mount_path = Some(codegen_expr(path, interner, &empty_synced));
307            }
308            Stmt::Sync { var, topic } => {
309                let entry = caps.entry(*var).or_default();
310                entry.synced = true;
311                entry.sync_topic = Some(codegen_expr(topic, interner, &empty_synced));
312            }
313            // Recursively check nested blocks (Block<'a> is &[Stmt<'a>])
314            Stmt::If { then_block, else_block, .. } => {
315                let nested = analyze_variable_capabilities(then_block, interner);
316                for (var, cap) in nested {
317                    let entry = caps.entry(var).or_default();
318                    if cap.mounted { entry.mounted = true; entry.mount_path = cap.mount_path; }
319                    if cap.synced { entry.synced = true; entry.sync_topic = cap.sync_topic; }
320                }
321                if let Some(else_b) = else_block {
322                    let nested = analyze_variable_capabilities(else_b, interner);
323                    for (var, cap) in nested {
324                        let entry = caps.entry(var).or_default();
325                        if cap.mounted { entry.mounted = true; entry.mount_path = cap.mount_path; }
326                        if cap.synced { entry.synced = true; entry.sync_topic = cap.sync_topic; }
327                    }
328                }
329            }
330            Stmt::While { body, .. } | Stmt::Repeat { body, .. } => {
331                let nested = analyze_variable_capabilities(body, interner);
332                for (var, cap) in nested {
333                    let entry = caps.entry(var).or_default();
334                    if cap.mounted { entry.mounted = true; entry.mount_path = cap.mount_path; }
335                    if cap.synced { entry.synced = true; entry.sync_topic = cap.sync_topic; }
336                }
337            }
338            _ => {}
339        }
340    }
341
342    caps
343}
344
345/// FFI: Detect if any function is exported for WASM.
346/// Used to emit `use wasm_bindgen::prelude::*;` preamble.
347fn has_wasm_exports(stmts: &[Stmt], interner: &Interner) -> bool {
348    stmts.iter().any(|stmt| {
349        if let Stmt::FunctionDef { is_exported: true, export_target: Some(target), .. } = stmt {
350            interner.resolve(*target).eq_ignore_ascii_case("wasm")
351        } else {
352            false
353        }
354    })
355}
356
357/// FFI: Detect if any function is exported for C ABI.
358/// Used to emit the LogosStatus runtime preamble and CStr/CString imports.
359fn has_c_exports(stmts: &[Stmt], interner: &Interner) -> bool {
360    stmts.iter().any(|stmt| {
361        if let Stmt::FunctionDef { is_exported: true, export_target, .. } = stmt {
362            match export_target {
363                None => true,
364                Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
365            }
366        } else {
367            false
368        }
369    })
370}
371
372/// FFI: Detect if any C-exported function uses Text (String) types.
373/// Used to emit `use std::ffi::{CStr, CString};` preamble.
374fn has_c_exports_with_text(stmts: &[Stmt], interner: &Interner) -> bool {
375    stmts.iter().any(|stmt| {
376        if let Stmt::FunctionDef { is_exported: true, export_target, params, return_type, .. } = stmt {
377            let is_c = match export_target {
378                None => true,
379                Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
380            };
381            if !is_c { return false; }
382            let has_text_param = params.iter().any(|(_, ty)| is_text_type(ty, interner));
383            let has_text_return = return_type.as_ref().map_or(false, |ty| is_text_type(ty, interner));
384            has_text_param || has_text_return
385        } else {
386            false
387        }
388    })
389}
390
391// =============================================================================
392// Universal ABI: Status-Code Error Runtime
393// =============================================================================
394
395/// Classification of a LOGOS type for C ABI boundary crossing.
396///
397/// Value types are passed directly as `#[repr(C)]` values.
398/// Reference types are passed as opaque handles with accessor functions.
399#[derive(Debug, Clone, Copy, PartialEq)]
400pub enum CAbiClass {
401    /// Passed directly by value (primitives, Text, small flat structs).
402    ValueType,
403    /// Passed as opaque `logos_handle_t` with generated accessors and free function.
404    ReferenceType,
405}
406
407/// Classify a LOGOS TypeExpr for C ABI boundary crossing.
408///
409/// Value types: Int, Nat, Real, Bool, Byte, Char, Text, small user structs (all value-type fields).
410/// Reference types: Seq, Map, Set, Option of reference, Result, large/recursive user types.
411fn classify_type_for_c_abi(ty: &TypeExpr, interner: &Interner, registry: &TypeRegistry) -> CAbiClass {
412    match ty {
413        TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
414            let name = interner.resolve(*sym);
415            match name {
416                "Int" | "Nat" | "Real" | "Float" | "Bool" | "Boolean"
417                | "Byte" | "Char" | "Unit" => CAbiClass::ValueType,
418                "Text" | "String" => CAbiClass::ValueType,
419                _ => {
420                    // Check registry for user-defined types
421                    if let Some(type_def) = registry.get(*sym) {
422                        match type_def {
423                            TypeDef::Struct { fields, .. } => {
424                                // Small struct with all value-type fields → ValueType
425                                let all_value = fields.iter().all(|f| {
426                                    is_value_type_field(&f.ty, interner)
427                                });
428                                if all_value && fields.len() <= 4 {
429                                    CAbiClass::ValueType
430                                } else {
431                                    CAbiClass::ReferenceType
432                                }
433                            }
434                            TypeDef::Enum { .. } => CAbiClass::ReferenceType,
435                            TypeDef::Primitive => CAbiClass::ValueType,
436                            TypeDef::Generic { .. } => CAbiClass::ReferenceType,
437                            TypeDef::Alias { .. } => CAbiClass::ValueType,
438                        }
439                    } else {
440                        CAbiClass::ValueType // Unknown → pass through
441                    }
442                }
443            }
444        }
445        TypeExpr::Refinement { base, .. } => classify_type_for_c_abi(base, interner, registry),
446        TypeExpr::Generic { base, .. } => {
447            let base_name = interner.resolve(*base);
448            match base_name {
449                "Option" | "Maybe" => {
450                    // Option of value type → value type (struct { present, value })
451                    // Option of reference type → reference type
452                    // For simplicity, treat all Options as reference types for now
453                    CAbiClass::ReferenceType
454                }
455                "Result" | "Seq" | "List" | "Vec" | "Map" | "HashMap"
456                | "Set" | "HashSet" => CAbiClass::ReferenceType,
457                _ => CAbiClass::ReferenceType,
458            }
459        }
460        TypeExpr::Function { .. } => CAbiClass::ReferenceType,
461        TypeExpr::Persistent { .. } => CAbiClass::ReferenceType,
462    }
463}
464
465/// Check if a field type is a C ABI value type (for struct classification).
466fn is_value_type_field(ft: &FieldType, interner: &Interner) -> bool {
467    match ft {
468        FieldType::Primitive(sym) | FieldType::Named(sym) => {
469            let name = interner.resolve(*sym);
470            matches!(name, "Int" | "Nat" | "Real" | "Float" | "Bool" | "Boolean"
471                | "Byte" | "Char" | "Unit")
472            // Text/String excluded — String cannot cross C ABI by value
473        }
474        FieldType::Generic { .. } => false, // Generic fields are reference types
475        FieldType::TypeParam(_) => false,
476    }
477}
478
479/// Check if a name is a Rust keyword (needs `r#` escaping in generated code).
480fn is_rust_keyword(name: &str) -> bool {
481    matches!(name,
482        "as" | "async" | "await" | "break" | "const" | "continue" | "crate" |
483        "dyn" | "else" | "enum" | "extern" | "false" | "fn" | "for" | "if" |
484        "impl" | "in" | "let" | "loop" | "match" | "mod" | "move" | "mut" |
485        "pub" | "ref" | "return" | "self" | "Self" | "static" | "struct" |
486        "super" | "trait" | "true" | "type" | "unsafe" | "use" | "where" |
487        "while" | "abstract" | "become" | "box" | "do" | "final" | "macro" |
488        "override" | "priv" | "try" | "typeof" | "unsized" | "virtual" | "yield"
489    )
490}
491
492/// Escape a name as a Rust raw identifier if it's a keyword.
493/// e.g., "move" → "r#move", "add" → "add"
494fn escape_rust_ident(name: &str) -> String {
495    if is_rust_keyword(name) {
496        format!("r#{}", name)
497    } else {
498        name.to_string()
499    }
500}
501
502/// Mangle a Rust type string into a C-safe identifier component.
503/// e.g., "i64" → "i64", "Vec<i64>" → "seq_i64", "HashMap<String, i64>" → "map_string_i64"
504fn mangle_type_for_c(ty: &TypeExpr, interner: &Interner) -> String {
505    match ty {
506        TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
507            let name = interner.resolve(*sym);
508            match name {
509                "Int" => "i64".to_string(),
510                "Nat" => "u64".to_string(),
511                "Real" | "Float" => "f64".to_string(),
512                "Bool" | "Boolean" => "bool".to_string(),
513                "Byte" => "u8".to_string(),
514                "Char" => "char".to_string(),
515                "Text" | "String" => "string".to_string(),
516                other => other.to_lowercase(),
517            }
518        }
519        TypeExpr::Refinement { base, .. } => mangle_type_for_c(base, interner),
520        TypeExpr::Generic { base, params } => {
521            let base_name = interner.resolve(*base);
522            let param_strs: Vec<String> = params.iter()
523                .map(|p| mangle_type_for_c(p, interner))
524                .collect();
525            match base_name {
526                "Seq" | "List" | "Vec" => format!("seq_{}", param_strs.join("_")),
527                "Map" | "HashMap" => format!("map_{}", param_strs.join("_")),
528                "Set" | "HashSet" => format!("set_{}", param_strs.join("_")),
529                "Option" | "Maybe" => format!("option_{}", param_strs.join("_")),
530                "Result" => format!("result_{}", param_strs.join("_")),
531                other => format!("{}_{}", other.to_lowercase(), param_strs.join("_")),
532            }
533        }
534        TypeExpr::Function { .. } => "fn".to_string(),
535        TypeExpr::Persistent { inner } => mangle_type_for_c(inner, interner),
536    }
537}
538
539/// Generate the LogosStatus runtime preamble for C ABI exports.
540///
541/// Emits:
542/// - `LogosStatus` repr(C) enum
543/// - Thread-local error storage
544/// - `logos_get_last_error()` and `logos_clear_error()` extern C functions
545/// - `logos_free_string()` for freeing allocated CStrings
546fn codegen_logos_runtime_preamble() -> String {
547    let mut out = String::new();
548
549    writeln!(out, "// ═══ LogicAffeine Universal ABI Runtime ═══\n").unwrap();
550
551    // LogosStatus enum
552    writeln!(out, "#[repr(C)]").unwrap();
553    writeln!(out, "#[derive(Debug, Clone, Copy, PartialEq)]").unwrap();
554    writeln!(out, "pub enum LogosStatus {{").unwrap();
555    writeln!(out, "    Ok = 0,").unwrap();
556    writeln!(out, "    Error = 1,").unwrap();
557    writeln!(out, "    RefinementViolation = 2,").unwrap();
558    writeln!(out, "    NullPointer = 3,").unwrap();
559    writeln!(out, "    OutOfBounds = 4,").unwrap();
560    writeln!(out, "    DeserializationFailed = 5,").unwrap();
561    writeln!(out, "    InvalidHandle = 6,").unwrap();
562    writeln!(out, "    ContainsNullByte = 7,").unwrap();
563    writeln!(out, "    ThreadPanic = 8,").unwrap();
564    writeln!(out, "    MemoryExhausted = 9,").unwrap();
565    writeln!(out, "}}\n").unwrap();
566
567    // Opaque handle type alias
568    writeln!(out, "pub type LogosHandle = *mut std::ffi::c_void;\n").unwrap();
569
570    // Thread-safe error storage (keyed by ThreadId)
571    writeln!(out, "fn logos_error_store() -> &'static std::sync::Mutex<std::collections::HashMap<std::thread::ThreadId, String>> {{").unwrap();
572    writeln!(out, "    use std::sync::OnceLock;").unwrap();
573    writeln!(out, "    static STORE: OnceLock<std::sync::Mutex<std::collections::HashMap<std::thread::ThreadId, String>>> = OnceLock::new();").unwrap();
574    writeln!(out, "    STORE.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()))").unwrap();
575    writeln!(out, "}}\n").unwrap();
576
577    // set_last_error helper (not exported, internal only)
578    writeln!(out, "fn logos_set_last_error(msg: String) {{").unwrap();
579    writeln!(out, "    let mut store = logos_error_store().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
580    writeln!(out, "    store.insert(std::thread::current().id(), msg);").unwrap();
581    writeln!(out, "}}\n").unwrap();
582
583    // logos_last_error (exported) — canonical name
584    // Uses a thread-local CString cache to avoid dangling pointers.
585    // The returned pointer is valid until the next call to logos_last_error on the same thread.
586    writeln!(out, "thread_local! {{").unwrap();
587    writeln!(out, "    static LOGOS_ERROR_CACHE: std::cell::RefCell<Option<std::ffi::CString>> = std::cell::RefCell::new(None);").unwrap();
588    writeln!(out, "}}\n").unwrap();
589
590    writeln!(out, "#[no_mangle]").unwrap();
591    writeln!(out, "pub extern \"C\" fn logos_last_error() -> *const std::os::raw::c_char {{").unwrap();
592    writeln!(out, "    let msg = logos_error_store().lock().unwrap_or_else(|e| e.into_inner())").unwrap();
593    writeln!(out, "        .get(&std::thread::current().id()).cloned();").unwrap();
594    writeln!(out, "    match msg {{").unwrap();
595    writeln!(out, "        Some(s) => match std::ffi::CString::new(s) {{").unwrap();
596    writeln!(out, "            Ok(cstr) => {{").unwrap();
597    writeln!(out, "                let ptr = cstr.as_ptr();").unwrap();
598    writeln!(out, "                LOGOS_ERROR_CACHE.with(|cache| {{ cache.borrow_mut().replace(cstr); }});").unwrap();
599    writeln!(out, "                LOGOS_ERROR_CACHE.with(|cache| {{").unwrap();
600    writeln!(out, "                    cache.borrow().as_ref().map_or(std::ptr::null(), |c| c.as_ptr())").unwrap();
601    writeln!(out, "                }})").unwrap();
602    writeln!(out, "            }}").unwrap();
603    writeln!(out, "            Err(_) => std::ptr::null(),").unwrap();
604    writeln!(out, "        }}").unwrap();
605    writeln!(out, "        None => std::ptr::null(),").unwrap();
606    writeln!(out, "    }}").unwrap();
607    writeln!(out, "}}\n").unwrap();
608
609    // logos_get_last_error (exported) — backwards-compatible alias
610    writeln!(out, "#[no_mangle]").unwrap();
611    writeln!(out, "pub extern \"C\" fn logos_get_last_error() -> *const std::os::raw::c_char {{").unwrap();
612    writeln!(out, "    logos_last_error()").unwrap();
613    writeln!(out, "}}\n").unwrap();
614
615    // logos_clear_error (exported)
616    writeln!(out, "#[no_mangle]").unwrap();
617    writeln!(out, "pub extern \"C\" fn logos_clear_error() {{").unwrap();
618    writeln!(out, "    let mut store = logos_error_store().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
619    writeln!(out, "    store.remove(&std::thread::current().id());").unwrap();
620    writeln!(out, "}}\n").unwrap();
621
622    // logos_free_string (exported) — for freeing CStrings returned by accessors/JSON helpers
623    writeln!(out, "#[no_mangle]").unwrap();
624    writeln!(out, "pub extern \"C\" fn logos_free_string(ptr: *mut std::os::raw::c_char) {{").unwrap();
625    writeln!(out, "    if !ptr.is_null() {{").unwrap();
626    writeln!(out, "        unsafe {{ drop(std::ffi::CString::from_raw(ptr)); }}").unwrap();
627    writeln!(out, "    }}").unwrap();
628    writeln!(out, "}}\n").unwrap();
629
630    // ABI version constant and introspection functions
631    writeln!(out, "pub const LOGOS_ABI_VERSION: u32 = 1;\n").unwrap();
632
633    writeln!(out, "#[no_mangle]").unwrap();
634    writeln!(out, "pub extern \"C\" fn logos_version() -> *const std::os::raw::c_char {{").unwrap();
635    writeln!(out, "    concat!(env!(\"CARGO_PKG_VERSION\"), \"\\0\").as_ptr() as *const std::os::raw::c_char").unwrap();
636    writeln!(out, "}}\n").unwrap();
637
638    writeln!(out, "#[no_mangle]").unwrap();
639    writeln!(out, "pub extern \"C\" fn logos_abi_version() -> u32 {{").unwrap();
640    writeln!(out, "    LOGOS_ABI_VERSION").unwrap();
641    writeln!(out, "}}\n").unwrap();
642
643    // Handle registry with generation counters for use-after-free protection
644    writeln!(out, "struct HandleEntry {{").unwrap();
645    writeln!(out, "    data: usize,").unwrap();
646    writeln!(out, "    generation: u64,").unwrap();
647    writeln!(out, "}}\n").unwrap();
648
649    writeln!(out, "struct HandleRegistry {{").unwrap();
650    writeln!(out, "    entries: std::collections::HashMap<u64, HandleEntry>,").unwrap();
651    writeln!(out, "    counter: u64,").unwrap();
652    writeln!(out, "}}\n").unwrap();
653
654    writeln!(out, "impl HandleRegistry {{").unwrap();
655    writeln!(out, "    fn new() -> Self {{").unwrap();
656    writeln!(out, "        HandleRegistry {{ entries: std::collections::HashMap::new(), counter: 0 }}").unwrap();
657    writeln!(out, "    }}").unwrap();
658    writeln!(out, "    fn register(&mut self, ptr: usize) -> (u64, u64) {{").unwrap();
659    writeln!(out, "        self.counter += 1;").unwrap();
660    writeln!(out, "        let id = self.counter;").unwrap();
661    writeln!(out, "        let generation = id;").unwrap();
662    writeln!(out, "        self.entries.insert(id, HandleEntry {{ data: ptr, generation }});").unwrap();
663    writeln!(out, "        (id, generation)").unwrap();
664    writeln!(out, "    }}").unwrap();
665    writeln!(out, "    fn validate_handle(&self, id: u64, generation: u64) -> bool {{").unwrap();
666    writeln!(out, "        self.entries.get(&id).map_or(false, |e| e.generation == generation)").unwrap();
667    writeln!(out, "    }}").unwrap();
668    writeln!(out, "    fn deref(&self, id: u64) -> Option<usize> {{").unwrap();
669    writeln!(out, "        self.entries.get(&id).map(|e| e.data)").unwrap();
670    writeln!(out, "    }}").unwrap();
671    writeln!(out, "    fn free(&mut self, id: u64) -> Result<usize, ()> {{").unwrap();
672    writeln!(out, "        if let Some(entry) = self.entries.remove(&id) {{ Ok(entry.data) }} else {{ Err(()) }}").unwrap();
673    writeln!(out, "    }}").unwrap();
674    writeln!(out, "}}\n").unwrap();
675
676    writeln!(out, "fn logos_handle_registry() -> &'static std::sync::Mutex<HandleRegistry> {{").unwrap();
677    writeln!(out, "    use std::sync::OnceLock;").unwrap();
678    writeln!(out, "    static REGISTRY: OnceLock<std::sync::Mutex<HandleRegistry>> = OnceLock::new();").unwrap();
679    writeln!(out, "    REGISTRY.get_or_init(|| std::sync::Mutex::new(HandleRegistry::new()))").unwrap();
680    writeln!(out, "}}\n").unwrap();
681
682    out
683}
684
685/// Emit the opening of a catch_unwind panic boundary for an accessor function body.
686fn emit_catch_unwind_open(out: &mut String) {
687    writeln!(out, "    match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {{").unwrap();
688}
689
690/// Emit the closing of a catch_unwind panic boundary for an accessor.
691/// `default_expr` is the fallback value on panic (e.g., "0", "std::ptr::null_mut()").
692fn emit_catch_unwind_close(out: &mut String, default_expr: &str) {
693    writeln!(out, "    }})) {{").unwrap();
694    writeln!(out, "        Ok(__v) => __v,").unwrap();
695    writeln!(out, "        Err(__panic) => {{").unwrap();
696    writeln!(out, "            let __msg = if let Some(s) = __panic.downcast_ref::<String>() {{ s.clone() }} else if let Some(s) = __panic.downcast_ref::<&str>() {{ s.to_string() }} else {{ \"Unknown panic\".to_string() }};").unwrap();
697    writeln!(out, "            logos_set_last_error(__msg);").unwrap();
698    writeln!(out, "            {}", default_expr).unwrap();
699    writeln!(out, "        }}").unwrap();
700    writeln!(out, "    }}").unwrap();
701}
702
703/// Emit a null handle check with early return. Used at the start of every accessor/free body.
704fn emit_null_handle_check(out: &mut String, default_expr: &str) {
705    writeln!(out, "        if handle.is_null() {{ logos_set_last_error(\"NullPointer: handle is null\".to_string()); return {}; }}", default_expr).unwrap();
706}
707
708/// Emit a null out-parameter check with early return. Used before every `*out = ...` write.
709fn emit_null_out_check(out: &mut String, default_expr: &str) {
710    writeln!(out, "        if out.is_null() {{ logos_set_last_error(\"NullPointer: output parameter is null\".to_string()); return {}; }}", default_expr).unwrap();
711}
712
713/// Emit registry handle lookup for an accessor. Returns the pointer or early-returns with error.
714fn emit_registry_deref(out: &mut String, default_expr: &str) {
715    writeln!(out, "        let __id = handle as u64;").unwrap();
716    writeln!(out, "        let __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
717    writeln!(out, "        let __ptr = match __reg.deref(__id) {{").unwrap();
718    writeln!(out, "            Some(p) => p,").unwrap();
719    writeln!(out, "            None => {{ logos_set_last_error(\"InvalidHandle: handle not found in registry\".to_string()); return {}; }}", default_expr).unwrap();
720    writeln!(out, "        }};").unwrap();
721    writeln!(out, "        drop(__reg);").unwrap();
722}
723
724/// Emit a _create body that registers the handle in the registry.
725/// `alloc_expr` is like `Vec::<i64>::new()`. `rust_type` is like `Vec<i64>`.
726fn emit_registry_create(out: &mut String, alloc_expr: &str, _rust_type: &str) {
727    writeln!(out, "        let __data = {};", alloc_expr).unwrap();
728    writeln!(out, "        let __ptr = Box::into_raw(Box::new(__data)) as usize;").unwrap();
729    writeln!(out, "        let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
730    writeln!(out, "        let (__id, _) = __reg.register(__ptr);").unwrap();
731    writeln!(out, "        __id as LogosHandle").unwrap();
732}
733
734/// Emit a _free body that deregisters and drops the handle.
735/// `rust_type` is like `Vec<i64>`.
736fn emit_registry_free(out: &mut String, rust_type: &str) {
737    writeln!(out, "        if handle.is_null() {{ return; }}").unwrap();
738    writeln!(out, "        let __id = handle as u64;").unwrap();
739    writeln!(out, "        let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
740    writeln!(out, "        match __reg.free(__id) {{").unwrap();
741    writeln!(out, "            Ok(__ptr) => {{ unsafe {{ drop(Box::from_raw(__ptr as *mut {})); }} }}", rust_type).unwrap();
742    writeln!(out, "            Err(()) => {{ logos_set_last_error(\"InvalidHandle: handle already freed or not found\".to_string()); }}").unwrap();
743    writeln!(out, "        }}").unwrap();
744}
745
746/// Generate accessor functions for a reference type (Seq, Map, Set, user structs).
747/// Returns the Rust source for the accessor/free functions.
748fn codegen_c_accessors(ty: &TypeExpr, interner: &Interner, registry: &TypeRegistry) -> String {
749    let mut out = String::new();
750    let mangled = mangle_type_for_c(ty, interner);
751
752    match ty {
753        TypeExpr::Generic { base, params } => {
754            let base_name = interner.resolve(*base);
755            match base_name {
756                "Seq" | "List" | "Vec" if !params.is_empty() => {
757                    let inner_rust_type = codegen_type_expr(&params[0], interner);
758                    let _inner_mangled = mangle_type_for_c(&params[0], interner);
759                    let is_inner_text = is_text_type(&params[0], interner);
760                    let vec_type = format!("Vec<{}>", inner_rust_type);
761
762                    // len
763                    writeln!(out, "#[no_mangle]").unwrap();
764                    writeln!(out, "pub extern \"C\" fn logos_{}_len(handle: LogosHandle) -> usize {{", mangled).unwrap();
765                    emit_catch_unwind_open(&mut out);
766                    emit_null_handle_check(&mut out, "0");
767                    emit_registry_deref(&mut out, "0");
768                    writeln!(out, "        let seq = unsafe {{ &*(__ptr as *const {}) }};", vec_type).unwrap();
769                    writeln!(out, "        seq.len()").unwrap();
770                    emit_catch_unwind_close(&mut out, "0");
771                    writeln!(out, "}}\n").unwrap();
772
773                    // at (index access)
774                    if is_inner_text {
775                        writeln!(out, "#[no_mangle]").unwrap();
776                        writeln!(out, "pub extern \"C\" fn logos_{}_at(handle: LogosHandle, index: usize) -> *mut std::os::raw::c_char {{", mangled).unwrap();
777                        emit_catch_unwind_open(&mut out);
778                        emit_null_handle_check(&mut out, "std::ptr::null_mut()");
779                        emit_registry_deref(&mut out, "std::ptr::null_mut()");
780                        writeln!(out, "        let seq = unsafe {{ &*(__ptr as *const {}) }};", vec_type).unwrap();
781                        writeln!(out, "        if index >= seq.len() {{").unwrap();
782                        writeln!(out, "            logos_set_last_error(format!(\"Index {{}} out of bounds (len {{}})\", index, seq.len()));").unwrap();
783                        writeln!(out, "            return std::ptr::null_mut();").unwrap();
784                        writeln!(out, "        }}").unwrap();
785                        writeln!(out, "        match std::ffi::CString::new(seq[index].clone()) {{").unwrap();
786                        writeln!(out, "            Ok(cstr) => cstr.into_raw(),").unwrap();
787                        writeln!(out, "            Err(_) => {{ logos_set_last_error(\"String contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
788                        writeln!(out, "        }}").unwrap();
789                        emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
790                        writeln!(out, "}}\n").unwrap();
791                    } else {
792                        writeln!(out, "#[no_mangle]").unwrap();
793                        writeln!(out, "pub extern \"C\" fn logos_{}_at(handle: LogosHandle, index: usize, out: *mut {}) -> LogosStatus {{", mangled, inner_rust_type).unwrap();
794                        emit_catch_unwind_open(&mut out);
795                        emit_null_handle_check(&mut out, "LogosStatus::NullPointer");
796                        emit_null_out_check(&mut out, "LogosStatus::NullPointer");
797                        emit_registry_deref(&mut out, "LogosStatus::InvalidHandle");
798                        writeln!(out, "        let seq = unsafe {{ &*(__ptr as *const {}) }};", vec_type).unwrap();
799                        writeln!(out, "        if index >= seq.len() {{").unwrap();
800                        writeln!(out, "            logos_set_last_error(format!(\"Index {{}} out of bounds (len {{}})\", index, seq.len()));").unwrap();
801                        writeln!(out, "            return LogosStatus::OutOfBounds;").unwrap();
802                        writeln!(out, "        }}").unwrap();
803                        writeln!(out, "        unsafe {{ *out = seq[index].clone(); }}").unwrap();
804                        writeln!(out, "        LogosStatus::Ok").unwrap();
805                        emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
806                        writeln!(out, "}}\n").unwrap();
807                    }
808
809                    // create
810                    writeln!(out, "#[no_mangle]").unwrap();
811                    writeln!(out, "pub extern \"C\" fn logos_{}_create() -> LogosHandle {{", mangled).unwrap();
812                    emit_catch_unwind_open(&mut out);
813                    emit_registry_create(&mut out, &format!("Vec::<{}>::new()", inner_rust_type), &vec_type);
814                    emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
815                    writeln!(out, "}}\n").unwrap();
816
817                    // push
818                    if is_inner_text {
819                        writeln!(out, "#[no_mangle]").unwrap();
820                        writeln!(out, "pub extern \"C\" fn logos_{}_push(handle: LogosHandle, value: *const std::os::raw::c_char) {{", mangled).unwrap();
821                        emit_catch_unwind_open(&mut out);
822                        emit_null_handle_check(&mut out, "()");
823                        emit_registry_deref(&mut out, "()");
824                        writeln!(out, "        let seq = unsafe {{ &mut *(__ptr as *mut {}) }};", vec_type).unwrap();
825                        writeln!(out, "        let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
826                        writeln!(out, "        seq.push(val_str);").unwrap();
827                        emit_catch_unwind_close(&mut out, "()");
828                        writeln!(out, "}}\n").unwrap();
829                    } else {
830                        writeln!(out, "#[no_mangle]").unwrap();
831                        writeln!(out, "pub extern \"C\" fn logos_{}_push(handle: LogosHandle, value: {}) {{", mangled, inner_rust_type).unwrap();
832                        emit_catch_unwind_open(&mut out);
833                        emit_null_handle_check(&mut out, "()");
834                        emit_registry_deref(&mut out, "()");
835                        writeln!(out, "        let seq = unsafe {{ &mut *(__ptr as *mut {}) }};", vec_type).unwrap();
836                        writeln!(out, "        seq.push(value);").unwrap();
837                        emit_catch_unwind_close(&mut out, "()");
838                        writeln!(out, "}}\n").unwrap();
839                    }
840
841                    // pop
842                    if is_inner_text {
843                        writeln!(out, "#[no_mangle]").unwrap();
844                        writeln!(out, "pub extern \"C\" fn logos_{}_pop(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled).unwrap();
845                        emit_catch_unwind_open(&mut out);
846                        emit_null_handle_check(&mut out, "std::ptr::null_mut()");
847                        emit_registry_deref(&mut out, "std::ptr::null_mut()");
848                        writeln!(out, "        let seq = unsafe {{ &mut *(__ptr as *mut {}) }};", vec_type).unwrap();
849                        writeln!(out, "        match seq.pop() {{").unwrap();
850                        writeln!(out, "            Some(val) => match std::ffi::CString::new(val) {{").unwrap();
851                        writeln!(out, "                Ok(cstr) => cstr.into_raw(),").unwrap();
852                        writeln!(out, "                Err(_) => {{ logos_set_last_error(\"Value contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
853                        writeln!(out, "            }},").unwrap();
854                        writeln!(out, "            None => {{ logos_set_last_error(\"Pop from empty sequence\".to_string()); std::ptr::null_mut() }}").unwrap();
855                        writeln!(out, "        }}").unwrap();
856                        emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
857                        writeln!(out, "}}\n").unwrap();
858                    } else {
859                        writeln!(out, "#[no_mangle]").unwrap();
860                        writeln!(out, "pub extern \"C\" fn logos_{}_pop(handle: LogosHandle, out: *mut {}) -> LogosStatus {{", mangled, inner_rust_type).unwrap();
861                        emit_catch_unwind_open(&mut out);
862                        emit_null_handle_check(&mut out, "LogosStatus::NullPointer");
863                        emit_null_out_check(&mut out, "LogosStatus::NullPointer");
864                        emit_registry_deref(&mut out, "LogosStatus::InvalidHandle");
865                        writeln!(out, "        let seq = unsafe {{ &mut *(__ptr as *mut {}) }};", vec_type).unwrap();
866                        writeln!(out, "        match seq.pop() {{").unwrap();
867                        writeln!(out, "            Some(val) => {{ unsafe {{ *out = val; }} LogosStatus::Ok }}").unwrap();
868                        writeln!(out, "            None => {{ logos_set_last_error(\"Pop from empty sequence\".to_string()); LogosStatus::Error }}").unwrap();
869                        writeln!(out, "        }}").unwrap();
870                        emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
871                        writeln!(out, "}}\n").unwrap();
872                    }
873
874                    // to_json
875                    writeln!(out, "#[no_mangle]").unwrap();
876                    writeln!(out, "pub extern \"C\" fn logos_{}_to_json(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled).unwrap();
877                    emit_catch_unwind_open(&mut out);
878                    emit_null_handle_check(&mut out, "std::ptr::null_mut()");
879                    emit_registry_deref(&mut out, "std::ptr::null_mut()");
880                    writeln!(out, "        let seq = unsafe {{ &*(__ptr as *const {}) }};", vec_type).unwrap();
881                    writeln!(out, "        match serde_json::to_string(seq) {{").unwrap();
882                    writeln!(out, "            Ok(json) => match std::ffi::CString::new(json) {{").unwrap();
883                    writeln!(out, "                Ok(cstr) => cstr.into_raw(),").unwrap();
884                    writeln!(out, "                Err(_) => {{ logos_set_last_error(\"JSON contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
885                    writeln!(out, "            }},").unwrap();
886                    writeln!(out, "            Err(e) => {{ logos_set_last_error(e.to_string()); std::ptr::null_mut() }}").unwrap();
887                    writeln!(out, "        }}").unwrap();
888                    emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
889                    writeln!(out, "}}\n").unwrap();
890
891                    // from_json
892                    writeln!(out, "#[no_mangle]").unwrap();
893                    writeln!(out, "pub extern \"C\" fn logos_{}_from_json(json: *const std::os::raw::c_char, out: *mut LogosHandle) -> LogosStatus {{", mangled).unwrap();
894                    emit_catch_unwind_open(&mut out);
895                    writeln!(out, "        if json.is_null() {{ logos_set_last_error(\"Null JSON pointer\".to_string()); return LogosStatus::NullPointer; }}").unwrap();
896                    emit_null_out_check(&mut out, "LogosStatus::NullPointer");
897                    writeln!(out, "        let json_str = unsafe {{ std::ffi::CStr::from_ptr(json).to_string_lossy() }};").unwrap();
898                    writeln!(out, "        match serde_json::from_str::<{}>(&json_str) {{", vec_type).unwrap();
899                    writeln!(out, "            Ok(val) => {{").unwrap();
900                    writeln!(out, "                let __ptr = Box::into_raw(Box::new(val)) as usize;").unwrap();
901                    writeln!(out, "                let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
902                    writeln!(out, "                let (__id, _) = __reg.register(__ptr);").unwrap();
903                    writeln!(out, "                unsafe {{ *out = __id as LogosHandle; }}").unwrap();
904                    writeln!(out, "                LogosStatus::Ok").unwrap();
905                    writeln!(out, "            }}").unwrap();
906                    writeln!(out, "            Err(e) => {{ logos_set_last_error(e.to_string()); LogosStatus::DeserializationFailed }}").unwrap();
907                    writeln!(out, "        }}").unwrap();
908                    emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
909                    writeln!(out, "}}\n").unwrap();
910
911                    // free
912                    writeln!(out, "#[no_mangle]").unwrap();
913                    writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled).unwrap();
914                    emit_catch_unwind_open(&mut out);
915                    emit_registry_free(&mut out, &vec_type);
916                    emit_catch_unwind_close(&mut out, "()");
917                    writeln!(out, "}}\n").unwrap();
918                }
919
920                "Map" | "HashMap" if params.len() >= 2 => {
921                    let key_rust = codegen_type_expr(&params[0], interner);
922                    let val_rust = codegen_type_expr(&params[1], interner);
923                    let is_key_text = is_text_type(&params[0], interner);
924                    let is_val_text = is_text_type(&params[1], interner);
925                    let map_type = format!("std::collections::HashMap<{}, {}>", key_rust, val_rust);
926
927                    // len
928                    writeln!(out, "#[no_mangle]").unwrap();
929                    writeln!(out, "pub extern \"C\" fn logos_{}_len(handle: LogosHandle) -> usize {{", mangled).unwrap();
930                    emit_catch_unwind_open(&mut out);
931                    emit_null_handle_check(&mut out, "0");
932                    emit_registry_deref(&mut out, "0");
933                    writeln!(out, "        let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
934                    writeln!(out, "        map.len()").unwrap();
935                    emit_catch_unwind_close(&mut out, "0");
936                    writeln!(out, "}}\n").unwrap();
937
938                    // get
939                    if is_key_text {
940                        if is_val_text {
941                            writeln!(out, "#[no_mangle]").unwrap();
942                            writeln!(out, "pub extern \"C\" fn logos_{}_get(handle: LogosHandle, key: *const std::os::raw::c_char) -> *mut std::os::raw::c_char {{", mangled).unwrap();
943                            emit_catch_unwind_open(&mut out);
944                            emit_null_handle_check(&mut out, "std::ptr::null_mut()");
945                            emit_registry_deref(&mut out, "std::ptr::null_mut()");
946                            writeln!(out, "        let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
947                            writeln!(out, "        let key_str = unsafe {{ std::ffi::CStr::from_ptr(key).to_string_lossy().into_owned() }};").unwrap();
948                            writeln!(out, "        match map.get(&key_str) {{").unwrap();
949                            writeln!(out, "            Some(val) => match std::ffi::CString::new(val.clone()) {{").unwrap();
950                            writeln!(out, "                Ok(cstr) => cstr.into_raw(),").unwrap();
951                            writeln!(out, "                Err(_) => {{ logos_set_last_error(\"Value contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
952                            writeln!(out, "            }},").unwrap();
953                            writeln!(out, "            None => std::ptr::null_mut(),").unwrap();
954                            writeln!(out, "        }}").unwrap();
955                            emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
956                            writeln!(out, "}}\n").unwrap();
957                        } else {
958                            writeln!(out, "#[no_mangle]").unwrap();
959                            writeln!(out, "pub extern \"C\" fn logos_{}_get(handle: LogosHandle, key: *const std::os::raw::c_char, out: *mut {}) -> LogosStatus {{", mangled, val_rust).unwrap();
960                            emit_catch_unwind_open(&mut out);
961                            emit_null_handle_check(&mut out, "LogosStatus::NullPointer");
962                            emit_null_out_check(&mut out, "LogosStatus::NullPointer");
963                            emit_registry_deref(&mut out, "LogosStatus::InvalidHandle");
964                            writeln!(out, "        let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
965                            writeln!(out, "        let key_str = unsafe {{ std::ffi::CStr::from_ptr(key).to_string_lossy().into_owned() }};").unwrap();
966                            writeln!(out, "        match map.get(&key_str) {{").unwrap();
967                            writeln!(out, "            Some(val) => {{ unsafe {{ *out = val.clone(); }} LogosStatus::Ok }}").unwrap();
968                            writeln!(out, "            None => {{ logos_set_last_error(format!(\"Key not found: {{}}\", key_str)); LogosStatus::Error }}").unwrap();
969                            writeln!(out, "        }}").unwrap();
970                            emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
971                            writeln!(out, "}}\n").unwrap();
972                        }
973                    } else {
974                        if is_val_text {
975                            writeln!(out, "#[no_mangle]").unwrap();
976                            writeln!(out, "pub extern \"C\" fn logos_{}_get(handle: LogosHandle, key: {}) -> *mut std::os::raw::c_char {{", mangled, key_rust).unwrap();
977                            emit_catch_unwind_open(&mut out);
978                            emit_null_handle_check(&mut out, "std::ptr::null_mut()");
979                            emit_registry_deref(&mut out, "std::ptr::null_mut()");
980                            writeln!(out, "        let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
981                            writeln!(out, "        match map.get(&key) {{").unwrap();
982                            writeln!(out, "            Some(val) => match std::ffi::CString::new(val.clone()) {{").unwrap();
983                            writeln!(out, "                Ok(cstr) => cstr.into_raw(),").unwrap();
984                            writeln!(out, "                Err(_) => {{ logos_set_last_error(\"Value contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
985                            writeln!(out, "            }},").unwrap();
986                            writeln!(out, "            None => std::ptr::null_mut(),").unwrap();
987                            writeln!(out, "        }}").unwrap();
988                            emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
989                            writeln!(out, "}}\n").unwrap();
990                        } else {
991                            writeln!(out, "#[no_mangle]").unwrap();
992                            writeln!(out, "pub extern \"C\" fn logos_{}_get(handle: LogosHandle, key: {}, out: *mut {}) -> LogosStatus {{", mangled, key_rust, val_rust).unwrap();
993                            emit_catch_unwind_open(&mut out);
994                            emit_null_handle_check(&mut out, "LogosStatus::NullPointer");
995                            emit_null_out_check(&mut out, "LogosStatus::NullPointer");
996                            emit_registry_deref(&mut out, "LogosStatus::InvalidHandle");
997                            writeln!(out, "        let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
998                            writeln!(out, "        match map.get(&key) {{").unwrap();
999                            writeln!(out, "            Some(val) => {{ unsafe {{ *out = val.clone(); }} LogosStatus::Ok }}").unwrap();
1000                            writeln!(out, "            None => {{ logos_set_last_error(format!(\"Key not found: {{}}\", key)); LogosStatus::Error }}").unwrap();
1001                            writeln!(out, "        }}").unwrap();
1002                            emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
1003                            writeln!(out, "}}\n").unwrap();
1004                        }
1005                    }
1006
1007                    // keys
1008                    writeln!(out, "#[no_mangle]").unwrap();
1009                    writeln!(out, "pub extern \"C\" fn logos_{}_keys(handle: LogosHandle) -> LogosHandle {{", mangled).unwrap();
1010                    emit_catch_unwind_open(&mut out);
1011                    emit_null_handle_check(&mut out, "std::ptr::null_mut() as LogosHandle");
1012                    emit_registry_deref(&mut out, "std::ptr::null_mut() as LogosHandle");
1013                    writeln!(out, "        let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
1014                    writeln!(out, "        let keys: Vec<{}> = map.keys().cloned().collect();", key_rust).unwrap();
1015                    writeln!(out, "        let __kptr = Box::into_raw(Box::new(keys)) as usize;").unwrap();
1016                    writeln!(out, "        let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
1017                    writeln!(out, "        let (__id, _) = __reg.register(__kptr);").unwrap();
1018                    writeln!(out, "        __id as LogosHandle").unwrap();
1019                    emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
1020                    writeln!(out, "}}\n").unwrap();
1021
1022                    // values
1023                    writeln!(out, "#[no_mangle]").unwrap();
1024                    writeln!(out, "pub extern \"C\" fn logos_{}_values(handle: LogosHandle) -> LogosHandle {{", mangled).unwrap();
1025                    emit_catch_unwind_open(&mut out);
1026                    emit_null_handle_check(&mut out, "std::ptr::null_mut() as LogosHandle");
1027                    emit_registry_deref(&mut out, "std::ptr::null_mut() as LogosHandle");
1028                    writeln!(out, "        let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
1029                    writeln!(out, "        let values: Vec<{}> = map.values().cloned().collect();", val_rust).unwrap();
1030                    writeln!(out, "        let __vptr = Box::into_raw(Box::new(values)) as usize;").unwrap();
1031                    writeln!(out, "        let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
1032                    writeln!(out, "        let (__id, _) = __reg.register(__vptr);").unwrap();
1033                    writeln!(out, "        __id as LogosHandle").unwrap();
1034                    emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
1035                    writeln!(out, "}}\n").unwrap();
1036
1037                    // create
1038                    writeln!(out, "#[no_mangle]").unwrap();
1039                    writeln!(out, "pub extern \"C\" fn logos_{}_create() -> LogosHandle {{", mangled).unwrap();
1040                    emit_catch_unwind_open(&mut out);
1041                    emit_registry_create(&mut out, &format!("std::collections::HashMap::<{}, {}>::new()", key_rust, val_rust), &map_type);
1042                    emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
1043                    writeln!(out, "}}\n").unwrap();
1044
1045                    // insert
1046                    if is_key_text {
1047                        let val_param = if is_val_text {
1048                            "value: *const std::os::raw::c_char".to_string()
1049                        } else {
1050                            format!("value: {}", val_rust)
1051                        };
1052                        writeln!(out, "#[no_mangle]").unwrap();
1053                        writeln!(out, "pub extern \"C\" fn logos_{}_insert(handle: LogosHandle, key: *const std::os::raw::c_char, {}) {{", mangled, val_param).unwrap();
1054                        emit_catch_unwind_open(&mut out);
1055                        emit_null_handle_check(&mut out, "()");
1056                        emit_registry_deref(&mut out, "()");
1057                        writeln!(out, "        let map = unsafe {{ &mut *(__ptr as *mut {}) }};", map_type).unwrap();
1058                        writeln!(out, "        let key_str = unsafe {{ std::ffi::CStr::from_ptr(key).to_string_lossy().into_owned() }};").unwrap();
1059                        if is_val_text {
1060                            writeln!(out, "        let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
1061                            writeln!(out, "        map.insert(key_str, val_str);").unwrap();
1062                        } else {
1063                            writeln!(out, "        map.insert(key_str, value);").unwrap();
1064                        }
1065                        emit_catch_unwind_close(&mut out, "()");
1066                        writeln!(out, "}}\n").unwrap();
1067                    } else {
1068                        let val_param = if is_val_text {
1069                            "value: *const std::os::raw::c_char".to_string()
1070                        } else {
1071                            format!("value: {}", val_rust)
1072                        };
1073                        writeln!(out, "#[no_mangle]").unwrap();
1074                        writeln!(out, "pub extern \"C\" fn logos_{}_insert(handle: LogosHandle, key: {}, {}) {{", mangled, key_rust, val_param).unwrap();
1075                        emit_catch_unwind_open(&mut out);
1076                        emit_null_handle_check(&mut out, "()");
1077                        emit_registry_deref(&mut out, "()");
1078                        writeln!(out, "        let map = unsafe {{ &mut *(__ptr as *mut {}) }};", map_type).unwrap();
1079                        if is_val_text {
1080                            writeln!(out, "        let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
1081                            writeln!(out, "        map.insert(key, val_str);").unwrap();
1082                        } else {
1083                            writeln!(out, "        map.insert(key, value);").unwrap();
1084                        }
1085                        emit_catch_unwind_close(&mut out, "()");
1086                        writeln!(out, "}}\n").unwrap();
1087                    }
1088
1089                    // remove
1090                    if is_key_text {
1091                        writeln!(out, "#[no_mangle]").unwrap();
1092                        writeln!(out, "pub extern \"C\" fn logos_{}_remove(handle: LogosHandle, key: *const std::os::raw::c_char) -> bool {{", mangled).unwrap();
1093                        emit_catch_unwind_open(&mut out);
1094                        emit_null_handle_check(&mut out, "false");
1095                        emit_registry_deref(&mut out, "false");
1096                        writeln!(out, "        let map = unsafe {{ &mut *(__ptr as *mut {}) }};", map_type).unwrap();
1097                        writeln!(out, "        let key_str = unsafe {{ std::ffi::CStr::from_ptr(key).to_string_lossy().into_owned() }};").unwrap();
1098                        writeln!(out, "        map.remove(&key_str).is_some()").unwrap();
1099                        emit_catch_unwind_close(&mut out, "false");
1100                        writeln!(out, "}}\n").unwrap();
1101                    } else {
1102                        writeln!(out, "#[no_mangle]").unwrap();
1103                        writeln!(out, "pub extern \"C\" fn logos_{}_remove(handle: LogosHandle, key: {}) -> bool {{", mangled, key_rust).unwrap();
1104                        emit_catch_unwind_open(&mut out);
1105                        emit_null_handle_check(&mut out, "false");
1106                        emit_registry_deref(&mut out, "false");
1107                        writeln!(out, "        let map = unsafe {{ &mut *(__ptr as *mut {}) }};", map_type).unwrap();
1108                        writeln!(out, "        map.remove(&key).is_some()").unwrap();
1109                        emit_catch_unwind_close(&mut out, "false");
1110                        writeln!(out, "}}\n").unwrap();
1111                    }
1112
1113                    // to_json
1114                    writeln!(out, "#[no_mangle]").unwrap();
1115                    writeln!(out, "pub extern \"C\" fn logos_{}_to_json(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled).unwrap();
1116                    emit_catch_unwind_open(&mut out);
1117                    emit_null_handle_check(&mut out, "std::ptr::null_mut()");
1118                    emit_registry_deref(&mut out, "std::ptr::null_mut()");
1119                    writeln!(out, "        let map = unsafe {{ &*(__ptr as *const {}) }};", map_type).unwrap();
1120                    writeln!(out, "        match serde_json::to_string(map) {{").unwrap();
1121                    writeln!(out, "            Ok(json) => match std::ffi::CString::new(json) {{").unwrap();
1122                    writeln!(out, "                Ok(cstr) => cstr.into_raw(),").unwrap();
1123                    writeln!(out, "                Err(_) => {{ logos_set_last_error(\"JSON contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
1124                    writeln!(out, "            }},").unwrap();
1125                    writeln!(out, "            Err(e) => {{ logos_set_last_error(e.to_string()); std::ptr::null_mut() }}").unwrap();
1126                    writeln!(out, "        }}").unwrap();
1127                    emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
1128                    writeln!(out, "}}\n").unwrap();
1129
1130                    // from_json
1131                    writeln!(out, "#[no_mangle]").unwrap();
1132                    writeln!(out, "pub extern \"C\" fn logos_{}_from_json(json: *const std::os::raw::c_char, out: *mut LogosHandle) -> LogosStatus {{", mangled).unwrap();
1133                    emit_catch_unwind_open(&mut out);
1134                    writeln!(out, "        if json.is_null() {{ logos_set_last_error(\"Null JSON pointer\".to_string()); return LogosStatus::NullPointer; }}").unwrap();
1135                    emit_null_out_check(&mut out, "LogosStatus::NullPointer");
1136                    writeln!(out, "        let json_str = unsafe {{ std::ffi::CStr::from_ptr(json).to_string_lossy() }};").unwrap();
1137                    writeln!(out, "        match serde_json::from_str::<{}>(&json_str) {{", map_type).unwrap();
1138                    writeln!(out, "            Ok(val) => {{").unwrap();
1139                    writeln!(out, "                let __ptr = Box::into_raw(Box::new(val)) as usize;").unwrap();
1140                    writeln!(out, "                let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
1141                    writeln!(out, "                let (__id, _) = __reg.register(__ptr);").unwrap();
1142                    writeln!(out, "                unsafe {{ *out = __id as LogosHandle; }}").unwrap();
1143                    writeln!(out, "                LogosStatus::Ok").unwrap();
1144                    writeln!(out, "            }}").unwrap();
1145                    writeln!(out, "            Err(e) => {{ logos_set_last_error(e.to_string()); LogosStatus::DeserializationFailed }}").unwrap();
1146                    writeln!(out, "        }}").unwrap();
1147                    emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
1148                    writeln!(out, "}}\n").unwrap();
1149
1150                    // free
1151                    writeln!(out, "#[no_mangle]").unwrap();
1152                    writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled).unwrap();
1153                    emit_catch_unwind_open(&mut out);
1154                    emit_registry_free(&mut out, &map_type);
1155                    emit_catch_unwind_close(&mut out, "()");
1156                    writeln!(out, "}}\n").unwrap();
1157                }
1158
1159                "Set" | "HashSet" if !params.is_empty() => {
1160                    let inner_rust_type = codegen_type_expr(&params[0], interner);
1161                    let is_inner_text = is_text_type(&params[0], interner);
1162                    let set_type = format!("std::collections::HashSet<{}>", inner_rust_type);
1163
1164                    // len
1165                    writeln!(out, "#[no_mangle]").unwrap();
1166                    writeln!(out, "pub extern \"C\" fn logos_{}_len(handle: LogosHandle) -> usize {{", mangled).unwrap();
1167                    emit_catch_unwind_open(&mut out);
1168                    emit_null_handle_check(&mut out, "0");
1169                    emit_registry_deref(&mut out, "0");
1170                    writeln!(out, "        let set = unsafe {{ &*(__ptr as *const {}) }};", set_type).unwrap();
1171                    writeln!(out, "        set.len()").unwrap();
1172                    emit_catch_unwind_close(&mut out, "0");
1173                    writeln!(out, "}}\n").unwrap();
1174
1175                    // contains
1176                    if is_inner_text {
1177                        writeln!(out, "#[no_mangle]").unwrap();
1178                        writeln!(out, "pub extern \"C\" fn logos_{}_contains(handle: LogosHandle, value: *const std::os::raw::c_char) -> bool {{", mangled).unwrap();
1179                        emit_catch_unwind_open(&mut out);
1180                        emit_null_handle_check(&mut out, "false");
1181                        emit_registry_deref(&mut out, "false");
1182                        writeln!(out, "        let set = unsafe {{ &*(__ptr as *const {}) }};", set_type).unwrap();
1183                        writeln!(out, "        let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
1184                        writeln!(out, "        set.contains(&val_str)").unwrap();
1185                        emit_catch_unwind_close(&mut out, "false");
1186                        writeln!(out, "}}\n").unwrap();
1187                    } else {
1188                        writeln!(out, "#[no_mangle]").unwrap();
1189                        writeln!(out, "pub extern \"C\" fn logos_{}_contains(handle: LogosHandle, value: {}) -> bool {{", mangled, inner_rust_type).unwrap();
1190                        emit_catch_unwind_open(&mut out);
1191                        emit_null_handle_check(&mut out, "false");
1192                        emit_registry_deref(&mut out, "false");
1193                        writeln!(out, "        let set = unsafe {{ &*(__ptr as *const {}) }};", set_type).unwrap();
1194                        writeln!(out, "        set.contains(&value)").unwrap();
1195                        emit_catch_unwind_close(&mut out, "false");
1196                        writeln!(out, "}}\n").unwrap();
1197                    }
1198
1199                    // create
1200                    writeln!(out, "#[no_mangle]").unwrap();
1201                    writeln!(out, "pub extern \"C\" fn logos_{}_create() -> LogosHandle {{", mangled).unwrap();
1202                    emit_catch_unwind_open(&mut out);
1203                    emit_registry_create(&mut out, &format!("std::collections::HashSet::<{}>::new()", inner_rust_type), &set_type);
1204                    emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
1205                    writeln!(out, "}}\n").unwrap();
1206
1207                    // insert
1208                    if is_inner_text {
1209                        writeln!(out, "#[no_mangle]").unwrap();
1210                        writeln!(out, "pub extern \"C\" fn logos_{}_insert(handle: LogosHandle, value: *const std::os::raw::c_char) {{", mangled).unwrap();
1211                        emit_catch_unwind_open(&mut out);
1212                        emit_null_handle_check(&mut out, "()");
1213                        emit_registry_deref(&mut out, "()");
1214                        writeln!(out, "        let set = unsafe {{ &mut *(__ptr as *mut {}) }};", set_type).unwrap();
1215                        writeln!(out, "        let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
1216                        writeln!(out, "        set.insert(val_str);").unwrap();
1217                        emit_catch_unwind_close(&mut out, "()");
1218                        writeln!(out, "}}\n").unwrap();
1219                    } else {
1220                        writeln!(out, "#[no_mangle]").unwrap();
1221                        writeln!(out, "pub extern \"C\" fn logos_{}_insert(handle: LogosHandle, value: {}) {{", mangled, inner_rust_type).unwrap();
1222                        emit_catch_unwind_open(&mut out);
1223                        emit_null_handle_check(&mut out, "()");
1224                        emit_registry_deref(&mut out, "()");
1225                        writeln!(out, "        let set = unsafe {{ &mut *(__ptr as *mut {}) }};", set_type).unwrap();
1226                        writeln!(out, "        set.insert(value);").unwrap();
1227                        emit_catch_unwind_close(&mut out, "()");
1228                        writeln!(out, "}}\n").unwrap();
1229                    }
1230
1231                    // remove
1232                    if is_inner_text {
1233                        writeln!(out, "#[no_mangle]").unwrap();
1234                        writeln!(out, "pub extern \"C\" fn logos_{}_remove(handle: LogosHandle, value: *const std::os::raw::c_char) -> bool {{", mangled).unwrap();
1235                        emit_catch_unwind_open(&mut out);
1236                        emit_null_handle_check(&mut out, "false");
1237                        emit_registry_deref(&mut out, "false");
1238                        writeln!(out, "        let set = unsafe {{ &mut *(__ptr as *mut {}) }};", set_type).unwrap();
1239                        writeln!(out, "        let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
1240                        writeln!(out, "        set.remove(&val_str)").unwrap();
1241                        emit_catch_unwind_close(&mut out, "false");
1242                        writeln!(out, "}}\n").unwrap();
1243                    } else {
1244                        writeln!(out, "#[no_mangle]").unwrap();
1245                        writeln!(out, "pub extern \"C\" fn logos_{}_remove(handle: LogosHandle, value: {}) -> bool {{", mangled, inner_rust_type).unwrap();
1246                        emit_catch_unwind_open(&mut out);
1247                        emit_null_handle_check(&mut out, "false");
1248                        emit_registry_deref(&mut out, "false");
1249                        writeln!(out, "        let set = unsafe {{ &mut *(__ptr as *mut {}) }};", set_type).unwrap();
1250                        writeln!(out, "        set.remove(&value)").unwrap();
1251                        emit_catch_unwind_close(&mut out, "false");
1252                        writeln!(out, "}}\n").unwrap();
1253                    }
1254
1255                    // to_json
1256                    writeln!(out, "#[no_mangle]").unwrap();
1257                    writeln!(out, "pub extern \"C\" fn logos_{}_to_json(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled).unwrap();
1258                    emit_catch_unwind_open(&mut out);
1259                    emit_null_handle_check(&mut out, "std::ptr::null_mut()");
1260                    emit_registry_deref(&mut out, "std::ptr::null_mut()");
1261                    writeln!(out, "        let set = unsafe {{ &*(__ptr as *const {}) }};", set_type).unwrap();
1262                    writeln!(out, "        match serde_json::to_string(set) {{").unwrap();
1263                    writeln!(out, "            Ok(json) => match std::ffi::CString::new(json) {{").unwrap();
1264                    writeln!(out, "                Ok(cstr) => cstr.into_raw(),").unwrap();
1265                    writeln!(out, "                Err(_) => {{ logos_set_last_error(\"JSON contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
1266                    writeln!(out, "            }},").unwrap();
1267                    writeln!(out, "            Err(e) => {{ logos_set_last_error(e.to_string()); std::ptr::null_mut() }}").unwrap();
1268                    writeln!(out, "        }}").unwrap();
1269                    emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
1270                    writeln!(out, "}}\n").unwrap();
1271
1272                    // free
1273                    writeln!(out, "#[no_mangle]").unwrap();
1274                    writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled).unwrap();
1275                    emit_catch_unwind_open(&mut out);
1276                    emit_registry_free(&mut out, &set_type);
1277                    emit_catch_unwind_close(&mut out, "()");
1278                    writeln!(out, "}}\n").unwrap();
1279                }
1280
1281                "Option" | "Maybe" if !params.is_empty() => {
1282                    let inner_rust_type = codegen_type_expr(&params[0], interner);
1283                    let is_inner_text = is_text_type(&params[0], interner);
1284                    let opt_type = format!("Option<{}>", inner_rust_type);
1285
1286                    // is_some
1287                    writeln!(out, "#[no_mangle]").unwrap();
1288                    writeln!(out, "pub extern \"C\" fn logos_{}_is_some(handle: LogosHandle) -> bool {{", mangled).unwrap();
1289                    emit_catch_unwind_open(&mut out);
1290                    emit_null_handle_check(&mut out, "false");
1291                    emit_registry_deref(&mut out, "false");
1292                    writeln!(out, "        let opt = unsafe {{ &*(__ptr as *const {}) }};", opt_type).unwrap();
1293                    writeln!(out, "        opt.is_some()").unwrap();
1294                    emit_catch_unwind_close(&mut out, "false");
1295                    writeln!(out, "}}\n").unwrap();
1296
1297                    // unwrap
1298                    if is_inner_text {
1299                        writeln!(out, "#[no_mangle]").unwrap();
1300                        writeln!(out, "pub extern \"C\" fn logos_{}_unwrap(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled).unwrap();
1301                        emit_catch_unwind_open(&mut out);
1302                        emit_null_handle_check(&mut out, "std::ptr::null_mut()");
1303                        emit_registry_deref(&mut out, "std::ptr::null_mut()");
1304                        writeln!(out, "        let opt = unsafe {{ &*(__ptr as *const {}) }};", opt_type).unwrap();
1305                        writeln!(out, "        match opt {{").unwrap();
1306                        writeln!(out, "            Some(val) => match std::ffi::CString::new(val.clone()) {{").unwrap();
1307                        writeln!(out, "                Ok(cstr) => cstr.into_raw(),").unwrap();
1308                        writeln!(out, "                Err(_) => {{ logos_set_last_error(\"Value contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
1309                        writeln!(out, "            }},").unwrap();
1310                        writeln!(out, "            None => {{ logos_set_last_error(\"Unwrap called on None\".to_string()); std::ptr::null_mut() }}").unwrap();
1311                        writeln!(out, "        }}").unwrap();
1312                        emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
1313                        writeln!(out, "}}\n").unwrap();
1314                    } else {
1315                        writeln!(out, "#[no_mangle]").unwrap();
1316                        writeln!(out, "pub extern \"C\" fn logos_{}_unwrap(handle: LogosHandle, out: *mut {}) -> LogosStatus {{", mangled, inner_rust_type).unwrap();
1317                        emit_catch_unwind_open(&mut out);
1318                        emit_null_handle_check(&mut out, "LogosStatus::NullPointer");
1319                        emit_null_out_check(&mut out, "LogosStatus::NullPointer");
1320                        emit_registry_deref(&mut out, "LogosStatus::InvalidHandle");
1321                        writeln!(out, "        let opt = unsafe {{ &*(__ptr as *const {}) }};", opt_type).unwrap();
1322                        writeln!(out, "        match opt {{").unwrap();
1323                        writeln!(out, "            Some(val) => {{ unsafe {{ *out = val.clone(); }} LogosStatus::Ok }}").unwrap();
1324                        writeln!(out, "            None => {{ logos_set_last_error(\"Unwrap called on None\".to_string()); LogosStatus::Error }}").unwrap();
1325                        writeln!(out, "        }}").unwrap();
1326                        emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
1327                        writeln!(out, "}}\n").unwrap();
1328                    }
1329
1330                    // create_some
1331                    if is_inner_text {
1332                        writeln!(out, "#[no_mangle]").unwrap();
1333                        writeln!(out, "pub extern \"C\" fn logos_{}_some(value: *const std::os::raw::c_char) -> LogosHandle {{", mangled).unwrap();
1334                        emit_catch_unwind_open(&mut out);
1335                        writeln!(out, "        if value.is_null() {{ logos_set_last_error(\"NullPointer: value is null\".to_string()); return std::ptr::null_mut() as LogosHandle; }}").unwrap();
1336                        writeln!(out, "        let val_str = unsafe {{ std::ffi::CStr::from_ptr(value).to_string_lossy().into_owned() }};").unwrap();
1337                        writeln!(out, "        let opt: {} = Some(val_str);", opt_type).unwrap();
1338                        writeln!(out, "        let __ptr = Box::into_raw(Box::new(opt)) as usize;").unwrap();
1339                        writeln!(out, "        let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
1340                        writeln!(out, "        let (__id, _) = __reg.register(__ptr);").unwrap();
1341                        writeln!(out, "        __id as LogosHandle").unwrap();
1342                        emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
1343                        writeln!(out, "}}\n").unwrap();
1344                    } else {
1345                        writeln!(out, "#[no_mangle]").unwrap();
1346                        writeln!(out, "pub extern \"C\" fn logos_{}_some(value: {}) -> LogosHandle {{", mangled, inner_rust_type).unwrap();
1347                        emit_catch_unwind_open(&mut out);
1348                        writeln!(out, "        let opt: {} = Some(value);", opt_type).unwrap();
1349                        writeln!(out, "        let __ptr = Box::into_raw(Box::new(opt)) as usize;").unwrap();
1350                        writeln!(out, "        let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
1351                        writeln!(out, "        let (__id, _) = __reg.register(__ptr);").unwrap();
1352                        writeln!(out, "        __id as LogosHandle").unwrap();
1353                        emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
1354                        writeln!(out, "}}\n").unwrap();
1355                    }
1356
1357                    // create_none
1358                    writeln!(out, "#[no_mangle]").unwrap();
1359                    writeln!(out, "pub extern \"C\" fn logos_{}_none() -> LogosHandle {{", mangled).unwrap();
1360                    emit_catch_unwind_open(&mut out);
1361                    writeln!(out, "        let opt: {} = None;", opt_type).unwrap();
1362                    writeln!(out, "        let __ptr = Box::into_raw(Box::new(opt)) as usize;").unwrap();
1363                    writeln!(out, "        let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
1364                    writeln!(out, "        let (__id, _) = __reg.register(__ptr);").unwrap();
1365                    writeln!(out, "        __id as LogosHandle").unwrap();
1366                    emit_catch_unwind_close(&mut out, "std::ptr::null_mut() as LogosHandle");
1367                    writeln!(out, "}}\n").unwrap();
1368
1369                    // free
1370                    writeln!(out, "#[no_mangle]").unwrap();
1371                    writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled).unwrap();
1372                    emit_catch_unwind_open(&mut out);
1373                    emit_registry_free(&mut out, &opt_type);
1374                    emit_catch_unwind_close(&mut out, "()");
1375                    writeln!(out, "}}\n").unwrap();
1376                }
1377
1378                _ => {}
1379            }
1380        }
1381        TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
1382            let type_name = interner.resolve(*sym);
1383            let type_def = registry.get(*sym);
1384
1385            match type_def {
1386                Some(TypeDef::Struct { fields, is_portable, .. }) => {
1387                    let mangled_struct = type_name.to_lowercase();
1388                    let rust_struct_name = type_name.to_string();
1389
1390                    for field in fields {
1391                        let field_name = interner.resolve(field.name);
1392                        let is_field_text = match &field.ty {
1393                            FieldType::Primitive(s) | FieldType::Named(s) => {
1394                                let n = interner.resolve(*s);
1395                                n == "Text" || n == "String"
1396                            }
1397                            _ => false,
1398                        };
1399
1400                        writeln!(out, "#[no_mangle]").unwrap();
1401                        if is_field_text {
1402                            writeln!(out, "pub extern \"C\" fn logos_{}_{field}(handle: LogosHandle) -> *mut std::os::raw::c_char {{",
1403                                mangled_struct, field = field_name).unwrap();
1404                            emit_catch_unwind_open(&mut out);
1405                            emit_null_handle_check(&mut out, "std::ptr::null_mut()");
1406                            emit_registry_deref(&mut out, "std::ptr::null_mut()");
1407                            writeln!(out, "        let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_struct_name).unwrap();
1408                            writeln!(out, "        match std::ffi::CString::new(obj.{}.clone()) {{", field_name).unwrap();
1409                            writeln!(out, "            Ok(cstr) => cstr.into_raw(),").unwrap();
1410                            writeln!(out, "            Err(_) => {{ logos_set_last_error(\"Field contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
1411                            writeln!(out, "        }}").unwrap();
1412                            emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
1413                            writeln!(out, "}}\n").unwrap();
1414                        } else {
1415                            let (field_rust_type, is_char) = match &field.ty {
1416                                FieldType::Primitive(s) | FieldType::Named(s) => {
1417                                    let n = interner.resolve(*s);
1418                                    match n {
1419                                        "Int" => ("i64", false),
1420                                        "Nat" => ("u64", false),
1421                                        "Real" | "Float" => ("f64", false),
1422                                        "Bool" | "Boolean" => ("bool", false),
1423                                        "Byte" => ("u8", false),
1424                                        "Char" => ("u32", true),
1425                                        _ => (n, false),
1426                                    }
1427                                }
1428                                _ => ("LogosHandle", false),
1429                            };
1430                            writeln!(out, "pub extern \"C\" fn logos_{}_{field}(handle: LogosHandle) -> {} {{",
1431                                mangled_struct, field_rust_type, field = field_name).unwrap();
1432                            emit_catch_unwind_open(&mut out);
1433                            emit_null_handle_check(&mut out, "Default::default()");
1434                            emit_registry_deref(&mut out, "Default::default()");
1435                            writeln!(out, "        let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_struct_name).unwrap();
1436                            if is_char {
1437                                writeln!(out, "        obj.{}.clone() as u32", field_name).unwrap();
1438                            } else {
1439                                writeln!(out, "        obj.{}.clone()", field_name).unwrap();
1440                            }
1441                            emit_catch_unwind_close(&mut out, "Default::default()");
1442                            writeln!(out, "}}\n").unwrap();
1443                        }
1444                    }
1445
1446                    {
1447                        // to_json / from_json — always generated for C export reference-type structs
1448                        writeln!(out, "#[no_mangle]").unwrap();
1449                        writeln!(out, "pub extern \"C\" fn logos_{}_to_json(handle: LogosHandle) -> *mut std::os::raw::c_char {{", mangled_struct).unwrap();
1450                        emit_catch_unwind_open(&mut out);
1451                        emit_null_handle_check(&mut out, "std::ptr::null_mut()");
1452                        emit_registry_deref(&mut out, "std::ptr::null_mut()");
1453                        writeln!(out, "        let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_struct_name).unwrap();
1454                        writeln!(out, "        match serde_json::to_string(obj) {{").unwrap();
1455                        writeln!(out, "            Ok(json) => match std::ffi::CString::new(json) {{").unwrap();
1456                        writeln!(out, "                Ok(cstr) => cstr.into_raw(),").unwrap();
1457                        writeln!(out, "                Err(_) => {{ logos_set_last_error(\"JSON contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
1458                        writeln!(out, "            }},").unwrap();
1459                        writeln!(out, "            Err(e) => {{ logos_set_last_error(e.to_string()); std::ptr::null_mut() }}").unwrap();
1460                        writeln!(out, "        }}").unwrap();
1461                        emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
1462                        writeln!(out, "}}\n").unwrap();
1463
1464                        // from_json — uses registry to register the deserialized handle
1465                        writeln!(out, "#[no_mangle]").unwrap();
1466                        writeln!(out, "pub extern \"C\" fn logos_{}_from_json(json: *const std::os::raw::c_char, out: *mut LogosHandle) -> LogosStatus {{", mangled_struct).unwrap();
1467                        emit_catch_unwind_open(&mut out);
1468                        writeln!(out, "        if json.is_null() {{ logos_set_last_error(\"Null JSON pointer\".to_string()); return LogosStatus::NullPointer; }}").unwrap();
1469                        emit_null_out_check(&mut out, "LogosStatus::NullPointer");
1470                        writeln!(out, "        let json_str = unsafe {{ std::ffi::CStr::from_ptr(json).to_string_lossy() }};").unwrap();
1471                        writeln!(out, "        match serde_json::from_str::<{}>(&json_str) {{", rust_struct_name).unwrap();
1472                        writeln!(out, "            Ok(val) => {{").unwrap();
1473                        writeln!(out, "                let __ptr = Box::into_raw(Box::new(val)) as usize;").unwrap();
1474                        writeln!(out, "                let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
1475                        writeln!(out, "                let (__id, _) = __reg.register(__ptr);").unwrap();
1476                        writeln!(out, "                unsafe {{ *out = __id as LogosHandle; }}").unwrap();
1477                        writeln!(out, "                LogosStatus::Ok").unwrap();
1478                        writeln!(out, "            }}").unwrap();
1479                        writeln!(out, "            Err(e) => {{ logos_set_last_error(e.to_string()); LogosStatus::DeserializationFailed }}").unwrap();
1480                        writeln!(out, "        }}").unwrap();
1481                        emit_catch_unwind_close(&mut out, "LogosStatus::ThreadPanic");
1482                        writeln!(out, "}}\n").unwrap();
1483                    }
1484
1485                    // free
1486                    writeln!(out, "#[no_mangle]").unwrap();
1487                    writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled_struct).unwrap();
1488                    emit_catch_unwind_open(&mut out);
1489                    emit_registry_free(&mut out, &rust_struct_name);
1490                    emit_catch_unwind_close(&mut out, "()");
1491                    writeln!(out, "}}\n").unwrap();
1492                }
1493                Some(TypeDef::Enum { variants, .. }) => {
1494                    let mangled_enum = type_name.to_lowercase();
1495                    let rust_enum_name = type_name.to_string();
1496
1497                    // tag accessor
1498                    writeln!(out, "#[no_mangle]").unwrap();
1499                    writeln!(out, "pub extern \"C\" fn logos_{}_tag(handle: LogosHandle) -> i32 {{", mangled_enum).unwrap();
1500                    emit_catch_unwind_open(&mut out);
1501                    emit_null_handle_check(&mut out, "-1");
1502                    emit_registry_deref(&mut out, "-1");
1503                    writeln!(out, "        let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_enum_name).unwrap();
1504                    writeln!(out, "        match obj {{").unwrap();
1505                    for (i, variant) in variants.iter().enumerate() {
1506                        let vname = interner.resolve(variant.name);
1507                        if variant.fields.is_empty() {
1508                            writeln!(out, "            {}::{} => {},", rust_enum_name, vname, i).unwrap();
1509                        } else {
1510                            writeln!(out, "            {}::{}{{ .. }} => {},", rust_enum_name, vname, i).unwrap();
1511                        }
1512                    }
1513                    writeln!(out, "        }}").unwrap();
1514                    emit_catch_unwind_close(&mut out, "-1");
1515                    writeln!(out, "}}\n").unwrap();
1516
1517                    for variant in variants {
1518                        let vname = interner.resolve(variant.name);
1519                        let vname_lower = vname.to_lowercase();
1520                        for field in &variant.fields {
1521                            let fname = interner.resolve(field.name);
1522                            let is_field_text = match &field.ty {
1523                                FieldType::Primitive(s) | FieldType::Named(s) => {
1524                                    let n = interner.resolve(*s);
1525                                    n == "Text" || n == "String"
1526                                }
1527                                _ => false,
1528                            };
1529
1530                            writeln!(out, "#[no_mangle]").unwrap();
1531                            if is_field_text {
1532                                writeln!(out, "pub extern \"C\" fn logos_{}_{}_{fname}(handle: LogosHandle) -> *mut std::os::raw::c_char {{",
1533                                    mangled_enum, vname_lower, fname = fname).unwrap();
1534                                emit_catch_unwind_open(&mut out);
1535                                emit_null_handle_check(&mut out, "std::ptr::null_mut()");
1536                                emit_registry_deref(&mut out, "std::ptr::null_mut()");
1537                                writeln!(out, "        let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_enum_name).unwrap();
1538                                writeln!(out, "        if let {}::{} {{ {fname}, .. }} = obj {{", rust_enum_name, vname, fname = fname).unwrap();
1539                                writeln!(out, "            match std::ffi::CString::new({fname}.clone()) {{", fname = fname).unwrap();
1540                                writeln!(out, "                Ok(cstr) => cstr.into_raw(),").unwrap();
1541                                writeln!(out, "                Err(_) => {{ logos_set_last_error(\"Field contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
1542                                writeln!(out, "            }}").unwrap();
1543                                writeln!(out, "        }} else {{ logos_set_last_error(\"Wrong variant: expected {}\".to_string()); std::ptr::null_mut() }}", vname).unwrap();
1544                                emit_catch_unwind_close(&mut out, "std::ptr::null_mut()");
1545                                writeln!(out, "}}\n").unwrap();
1546                            } else {
1547                                let field_rust_type = match &field.ty {
1548                                    FieldType::Primitive(s) | FieldType::Named(s) => {
1549                                        let n = interner.resolve(*s);
1550                                        match n {
1551                                            "Int" => "i64",
1552                                            "Nat" => "u64",
1553                                            "Real" | "Float" => "f64",
1554                                            "Bool" | "Boolean" => "bool",
1555                                            "Byte" => "u8",
1556                                            "Char" => "u32",
1557                                            _ => n,
1558                                        }
1559                                    }
1560                                    _ => "LogosHandle",
1561                                };
1562                                writeln!(out, "pub extern \"C\" fn logos_{}_{}_{fname}(handle: LogosHandle) -> {} {{",
1563                                    mangled_enum, vname_lower, field_rust_type, fname = fname).unwrap();
1564                                emit_catch_unwind_open(&mut out);
1565                                emit_null_handle_check(&mut out, "Default::default()");
1566                                emit_registry_deref(&mut out, "Default::default()");
1567                                writeln!(out, "        let obj = unsafe {{ &*(__ptr as *const {}) }};", rust_enum_name).unwrap();
1568                                writeln!(out, "        if let {}::{} {{ {fname}, .. }} = obj {{", rust_enum_name, vname, fname = fname).unwrap();
1569                                writeln!(out, "            {fname}.clone()", fname = fname).unwrap();
1570                                writeln!(out, "        }} else {{ logos_set_last_error(\"Wrong variant: expected {}\".to_string()); Default::default() }}", vname).unwrap();
1571                                emit_catch_unwind_close(&mut out, "Default::default()");
1572                                writeln!(out, "}}\n").unwrap();
1573                            }
1574                        }
1575                    }
1576
1577                    // free
1578                    writeln!(out, "#[no_mangle]").unwrap();
1579                    writeln!(out, "pub extern \"C\" fn logos_{}_free(handle: LogosHandle) {{", mangled_enum).unwrap();
1580                    emit_catch_unwind_open(&mut out);
1581                    emit_registry_free(&mut out, &rust_enum_name);
1582                    emit_catch_unwind_close(&mut out, "()");
1583                    writeln!(out, "}}\n").unwrap();
1584                }
1585                _ => {}
1586            }
1587        }
1588        _ => {}
1589    }
1590
1591    out
1592}
1593
1594/// Collect all unique reference types that appear in C-exported function signatures.
1595/// Used to emit accessor functions once per type.
1596fn collect_c_export_reference_types<'a>(
1597    stmts: &'a [Stmt<'a>],
1598    interner: &Interner,
1599    registry: &TypeRegistry,
1600) -> Vec<&'a TypeExpr<'a>> {
1601    let mut seen = HashSet::new();
1602    let mut types = Vec::new();
1603
1604    for stmt in stmts {
1605        if let Stmt::FunctionDef { is_exported: true, export_target, params, return_type, .. } = stmt {
1606            let is_c = match export_target {
1607                None => true,
1608                Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
1609            };
1610            if !is_c { continue; }
1611
1612            for (_, ty) in params.iter() {
1613                if classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType {
1614                    let mangled = mangle_type_for_c(ty, interner);
1615                    if seen.insert(mangled) {
1616                        types.push(*ty);
1617                    }
1618                }
1619            }
1620            if let Some(ty) = return_type {
1621                if classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType {
1622                    let mangled = mangle_type_for_c(ty, interner);
1623                    if seen.insert(mangled) {
1624                        types.push(*ty);
1625                    }
1626                }
1627            }
1628        }
1629    }
1630
1631    types
1632}
1633
1634/// Collect all user-defined struct Symbols that are used as C ABI value types in exports.
1635/// These structs need `#[repr(C)]` for stable field layout.
1636fn collect_c_export_value_type_structs(
1637    stmts: &[Stmt],
1638    interner: &Interner,
1639    registry: &TypeRegistry,
1640) -> HashSet<Symbol> {
1641    let mut value_structs = HashSet::new();
1642
1643    for stmt in stmts {
1644        if let Stmt::FunctionDef { is_exported: true, export_target, params, return_type, .. } = stmt {
1645            let is_c = match export_target {
1646                None => true,
1647                Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
1648            };
1649            if !is_c { continue; }
1650
1651            let all_types: Vec<&TypeExpr> = params.iter()
1652                .map(|(_, ty)| *ty)
1653                .chain(return_type.iter().copied())
1654                .collect();
1655
1656            for ty in all_types {
1657                if let TypeExpr::Primitive(sym) | TypeExpr::Named(sym) = ty {
1658                    if classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ValueType {
1659                        if registry.get(*sym).is_some() {
1660                            value_structs.insert(*sym);
1661                        }
1662                    }
1663                }
1664            }
1665        }
1666    }
1667
1668    value_structs
1669}
1670
1671/// Collect all user-defined struct Symbols that are used as C ABI reference types in exports.
1672/// These structs need serde derives for from_json/to_json support.
1673fn collect_c_export_ref_structs(
1674    stmts: &[Stmt],
1675    interner: &Interner,
1676    registry: &TypeRegistry,
1677) -> HashSet<Symbol> {
1678    let mut ref_structs = HashSet::new();
1679    let ref_types = collect_c_export_reference_types(stmts, interner, registry);
1680    for ty in ref_types {
1681        if let TypeExpr::Primitive(sym) | TypeExpr::Named(sym) = ty {
1682            if registry.get(*sym).map_or(false, |d| matches!(d, TypeDef::Struct { .. })) {
1683                ref_structs.insert(*sym);
1684            }
1685        }
1686    }
1687    ref_structs
1688}
1689
1690/// Generate the C header (.h) content for all C-exported functions.
1691///
1692/// Includes:
1693/// - Runtime types (logos_status_t, logos_handle_t)
1694/// - Runtime functions (logos_get_last_error, logos_clear_error, logos_free_string)
1695/// - Value-type struct definitions
1696/// - Exported function declarations
1697/// - Accessor function declarations for reference types
1698pub fn generate_c_header(
1699    stmts: &[Stmt],
1700    module_name: &str,
1701    interner: &Interner,
1702    registry: &TypeRegistry,
1703) -> String {
1704    let mut out = String::new();
1705    let guard = module_name.to_uppercase().replace('-', "_");
1706
1707    writeln!(out, "// Generated from {}.lg — LogicAffeine Universal ABI", module_name).unwrap();
1708    writeln!(out, "#ifndef {}_H", guard).unwrap();
1709    writeln!(out, "#define {}_H\n", guard).unwrap();
1710    writeln!(out, "#include <stdint.h>").unwrap();
1711    writeln!(out, "#include <stdbool.h>").unwrap();
1712    writeln!(out, "#include <stddef.h>\n").unwrap();
1713
1714    writeln!(out, "#ifdef __cplusplus").unwrap();
1715    writeln!(out, "extern \"C\" {{").unwrap();
1716    writeln!(out, "#endif\n").unwrap();
1717
1718    // Runtime types
1719    writeln!(out, "// ═══ Runtime ═══").unwrap();
1720    writeln!(out, "typedef enum {{").unwrap();
1721    writeln!(out, "    LOGOS_STATUS_OK = 0,").unwrap();
1722    writeln!(out, "    LOGOS_STATUS_ERROR = 1,").unwrap();
1723    writeln!(out, "    LOGOS_STATUS_REFINEMENT_VIOLATION = 2,").unwrap();
1724    writeln!(out, "    LOGOS_STATUS_NULL_POINTER = 3,").unwrap();
1725    writeln!(out, "    LOGOS_STATUS_OUT_OF_BOUNDS = 4,").unwrap();
1726    writeln!(out, "    LOGOS_STATUS_DESERIALIZATION_FAILED = 5,").unwrap();
1727    writeln!(out, "    LOGOS_STATUS_INVALID_HANDLE = 6,").unwrap();
1728    writeln!(out, "    LOGOS_STATUS_CONTAINS_NULL_BYTE = 7,").unwrap();
1729    writeln!(out, "    LOGOS_STATUS_THREAD_PANIC = 8,").unwrap();
1730    writeln!(out, "    LOGOS_STATUS_MEMORY_EXHAUSTED = 9,").unwrap();
1731    writeln!(out, "}} logos_status_t;\n").unwrap();
1732    writeln!(out, "typedef void* logos_handle_t;\n").unwrap();
1733    writeln!(out, "const char* logos_last_error(void);").unwrap();
1734    writeln!(out, "const char* logos_get_last_error(void);").unwrap();
1735    writeln!(out, "void logos_clear_error(void);").unwrap();
1736    writeln!(out, "void logos_free_string(char* str);\n").unwrap();
1737
1738    writeln!(out, "#define LOGOS_ABI_VERSION 1").unwrap();
1739    writeln!(out, "const char* logos_version(void);").unwrap();
1740    writeln!(out, "uint32_t logos_abi_version(void);\n").unwrap();
1741
1742    // Collect value-type user structs used in exports
1743    let mut emitted_structs = HashSet::new();
1744    for stmt in stmts {
1745        if let Stmt::FunctionDef { is_exported: true, export_target, params, return_type, .. } = stmt {
1746            let is_c = match export_target {
1747                None => true,
1748                Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
1749            };
1750            if !is_c { continue; }
1751
1752            // Check params and return for user struct types
1753            let all_types: Vec<&TypeExpr> = params.iter()
1754                .map(|(_, ty)| *ty)
1755                .chain(return_type.iter().copied())
1756                .collect();
1757
1758            for ty in all_types {
1759                if let TypeExpr::Primitive(sym) | TypeExpr::Named(sym) = ty {
1760                    if classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ValueType {
1761                        if let Some(TypeDef::Struct { fields, .. }) = registry.get(*sym) {
1762                            let name = interner.resolve(*sym);
1763                            if emitted_structs.insert(name.to_string()) {
1764                                writeln!(out, "// ═══ Value Types ═══").unwrap();
1765                                writeln!(out, "typedef struct {{").unwrap();
1766                                for field in fields {
1767                                    let c_type = map_field_type_to_c(&field.ty, interner);
1768                                    writeln!(out, "    {} {};", c_type, interner.resolve(field.name)).unwrap();
1769                                }
1770                                writeln!(out, "}} {};\n", name).unwrap();
1771                            }
1772                        }
1773                    }
1774                }
1775            }
1776        }
1777    }
1778
1779    // Exported function declarations
1780    writeln!(out, "// ═══ Exported Functions ═══").unwrap();
1781    for stmt in stmts {
1782        if let Stmt::FunctionDef { name, is_exported: true, export_target, params, return_type, .. } = stmt {
1783            let is_c = match export_target {
1784                None => true,
1785                Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
1786            };
1787            if !is_c { continue; }
1788
1789            let func_name = format!("logos_{}", interner.resolve(*name));
1790            let has_ref_return = return_type.map_or(false, |ty| {
1791                classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType
1792            });
1793            let has_text_return = return_type.map_or(false, |ty| is_text_type(ty, interner));
1794            let has_result_return = return_type.map_or(false, |ty| is_result_type(ty, interner));
1795            let has_refinement_param = params.iter().any(|(_, ty)| matches!(ty, TypeExpr::Refinement { .. }));
1796
1797            // Status-code pattern matches codegen: ref/text/result returns or refinement params
1798            let uses_status_code = has_ref_return || has_result_return || has_text_return || has_refinement_param;
1799
1800            // Build C parameter list (ref-type params always become logos_handle_t)
1801            let mut c_params = Vec::new();
1802            for (pname, ptype) in params.iter() {
1803                let pn = interner.resolve(*pname);
1804                if classify_type_for_c_abi(ptype, interner, registry) == CAbiClass::ReferenceType {
1805                    c_params.push(format!("logos_handle_t {}", pn));
1806                } else {
1807                    c_params.push(format!("{} {}", map_type_to_c_header(ptype, interner, false), pn));
1808                }
1809            }
1810
1811            if uses_status_code {
1812                // Out parameter for return value
1813                if let Some(ret_ty) = return_type {
1814                    if is_result_type(ret_ty, interner) {
1815                        if let TypeExpr::Generic { params: ref rparams, .. } = ret_ty {
1816                            if !rparams.is_empty() {
1817                                let ok_ty = &rparams[0];
1818                                if classify_type_for_c_abi(ok_ty, interner, registry) == CAbiClass::ReferenceType {
1819                                    c_params.push("logos_handle_t* out".to_string());
1820                                } else {
1821                                    c_params.push(format!("{}* out", map_type_to_c_header(ok_ty, interner, false)));
1822                                }
1823                            }
1824                        }
1825                    } else if classify_type_for_c_abi(ret_ty, interner, registry) == CAbiClass::ReferenceType {
1826                        c_params.push("logos_handle_t* out".to_string());
1827                    } else if has_text_return {
1828                        c_params.push("char** out".to_string());
1829                    }
1830                }
1831                writeln!(out, "logos_status_t {}({});", func_name, c_params.join(", ")).unwrap();
1832            } else {
1833                // Direct value return
1834                let ret = return_type
1835                    .map(|ty| map_type_to_c_header(ty, interner, true))
1836                    .unwrap_or_else(|| "void".to_string());
1837                writeln!(out, "{} {}({});", ret, func_name, c_params.join(", ")).unwrap();
1838            }
1839        }
1840    }
1841    writeln!(out).unwrap();
1842
1843    // Accessor function declarations for reference types
1844    let ref_types = collect_c_export_reference_types(stmts, interner, registry);
1845    if !ref_types.is_empty() {
1846        for ref_ty in &ref_types {
1847            let mangled = mangle_type_for_c(ref_ty, interner);
1848            writeln!(out, "// ═══ {} Accessors ═══", mangled).unwrap();
1849
1850            match ref_ty {
1851                TypeExpr::Generic { base, params } => {
1852                    let base_name = interner.resolve(*base);
1853                    match base_name {
1854                        "Seq" | "List" | "Vec" if !params.is_empty() => {
1855                            let is_inner_text = is_text_type(&params[0], interner);
1856                            writeln!(out, "size_t logos_{}_len(logos_handle_t handle);", mangled).unwrap();
1857                            if is_inner_text {
1858                                writeln!(out, "char* logos_{}_at(logos_handle_t handle, size_t index);", mangled).unwrap();
1859                            } else {
1860                                let inner_c = map_type_to_c_header(&params[0], interner, false);
1861                                writeln!(out, "logos_status_t logos_{}_at(logos_handle_t handle, size_t index, {}* out);", mangled, inner_c).unwrap();
1862                            }
1863                            writeln!(out, "logos_handle_t logos_{}_create(void);", mangled).unwrap();
1864                            if is_inner_text {
1865                                writeln!(out, "void logos_{}_push(logos_handle_t handle, const char* value);", mangled).unwrap();
1866                            } else {
1867                                let inner_c = map_type_to_c_header(&params[0], interner, false);
1868                                writeln!(out, "void logos_{}_push(logos_handle_t handle, {} value);", mangled, inner_c).unwrap();
1869                            }
1870                            if is_inner_text {
1871                                writeln!(out, "char* logos_{}_pop(logos_handle_t handle);", mangled).unwrap();
1872                            } else {
1873                                let inner_c = map_type_to_c_header(&params[0], interner, false);
1874                                writeln!(out, "logos_status_t logos_{}_pop(logos_handle_t handle, {}* out);", mangled, inner_c).unwrap();
1875                            }
1876                            writeln!(out, "char* logos_{}_to_json(logos_handle_t handle);", mangled).unwrap();
1877                            writeln!(out, "logos_status_t logos_{}_from_json(const char* json, logos_handle_t* out);", mangled).unwrap();
1878                            writeln!(out, "void logos_{}_free(logos_handle_t handle);", mangled).unwrap();
1879                        }
1880                        "Map" | "HashMap" if params.len() >= 2 => {
1881                            let is_key_text = is_text_type(&params[0], interner);
1882                            let is_val_text = is_text_type(&params[1], interner);
1883                            writeln!(out, "size_t logos_{}_len(logos_handle_t handle);", mangled).unwrap();
1884                            if is_key_text {
1885                                if is_val_text {
1886                                    writeln!(out, "char* logos_{}_get(logos_handle_t handle, const char* key);", mangled).unwrap();
1887                                } else {
1888                                    let val_c = map_type_to_c_header(&params[1], interner, false);
1889                                    writeln!(out, "logos_status_t logos_{}_get(logos_handle_t handle, const char* key, {}* out);", mangled, val_c).unwrap();
1890                                }
1891                            } else {
1892                                let key_c = map_type_to_c_header(&params[0], interner, false);
1893                                if is_val_text {
1894                                    writeln!(out, "char* logos_{}_get(logos_handle_t handle, {} key);", mangled, key_c).unwrap();
1895                                } else {
1896                                    let val_c = map_type_to_c_header(&params[1], interner, false);
1897                                    writeln!(out, "logos_status_t logos_{}_get(logos_handle_t handle, {} key, {}* out);", mangled, key_c, val_c).unwrap();
1898                                }
1899                            }
1900                            writeln!(out, "logos_handle_t logos_{}_keys(logos_handle_t handle);", mangled).unwrap();
1901                            writeln!(out, "logos_handle_t logos_{}_values(logos_handle_t handle);", mangled).unwrap();
1902                            writeln!(out, "logos_handle_t logos_{}_create(void);", mangled).unwrap();
1903                            if is_key_text {
1904                                let val_c = if is_val_text { "const char*".to_string() } else { map_type_to_c_header(&params[1], interner, false) };
1905                                writeln!(out, "void logos_{}_insert(logos_handle_t handle, const char* key, {} value);", mangled, val_c).unwrap();
1906                            } else {
1907                                let key_c = map_type_to_c_header(&params[0], interner, false);
1908                                let val_c = if is_val_text { "const char*".to_string() } else { map_type_to_c_header(&params[1], interner, false) };
1909                                writeln!(out, "void logos_{}_insert(logos_handle_t handle, {} key, {} value);", mangled, key_c, val_c).unwrap();
1910                            }
1911                            if is_key_text {
1912                                writeln!(out, "bool logos_{}_remove(logos_handle_t handle, const char* key);", mangled).unwrap();
1913                            } else {
1914                                let key_c = map_type_to_c_header(&params[0], interner, false);
1915                                writeln!(out, "bool logos_{}_remove(logos_handle_t handle, {} key);", mangled, key_c).unwrap();
1916                            }
1917                            writeln!(out, "char* logos_{}_to_json(logos_handle_t handle);", mangled).unwrap();
1918                            writeln!(out, "logos_status_t logos_{}_from_json(const char* json, logos_handle_t* out);", mangled).unwrap();
1919                            writeln!(out, "void logos_{}_free(logos_handle_t handle);", mangled).unwrap();
1920                        }
1921                        "Set" | "HashSet" if !params.is_empty() => {
1922                            let is_inner_text = is_text_type(&params[0], interner);
1923                            writeln!(out, "size_t logos_{}_len(logos_handle_t handle);", mangled).unwrap();
1924                            if is_inner_text {
1925                                writeln!(out, "bool logos_{}_contains(logos_handle_t handle, const char* value);", mangled).unwrap();
1926                            } else {
1927                                let inner_c = map_type_to_c_header(&params[0], interner, false);
1928                                writeln!(out, "bool logos_{}_contains(logos_handle_t handle, {} value);", mangled, inner_c).unwrap();
1929                            }
1930                            writeln!(out, "logos_handle_t logos_{}_create(void);", mangled).unwrap();
1931                            if is_inner_text {
1932                                writeln!(out, "void logos_{}_insert(logos_handle_t handle, const char* value);", mangled).unwrap();
1933                            } else {
1934                                let inner_c = map_type_to_c_header(&params[0], interner, false);
1935                                writeln!(out, "void logos_{}_insert(logos_handle_t handle, {} value);", mangled, inner_c).unwrap();
1936                            }
1937                            if is_inner_text {
1938                                writeln!(out, "bool logos_{}_remove(logos_handle_t handle, const char* value);", mangled).unwrap();
1939                            } else {
1940                                let inner_c = map_type_to_c_header(&params[0], interner, false);
1941                                writeln!(out, "bool logos_{}_remove(logos_handle_t handle, {} value);", mangled, inner_c).unwrap();
1942                            }
1943                            writeln!(out, "char* logos_{}_to_json(logos_handle_t handle);", mangled).unwrap();
1944                            writeln!(out, "void logos_{}_free(logos_handle_t handle);", mangled).unwrap();
1945                        }
1946                        "Option" | "Maybe" if !params.is_empty() => {
1947                            let is_inner_text = is_text_type(&params[0], interner);
1948                            writeln!(out, "bool logos_{}_is_some(logos_handle_t handle);", mangled).unwrap();
1949                            if is_inner_text {
1950                                writeln!(out, "char* logos_{}_unwrap(logos_handle_t handle);", mangled).unwrap();
1951                            } else {
1952                                let inner_c = map_type_to_c_header(&params[0], interner, false);
1953                                writeln!(out, "logos_status_t logos_{}_unwrap(logos_handle_t handle, {}* out);", mangled, inner_c).unwrap();
1954                            }
1955                            if is_inner_text {
1956                                writeln!(out, "logos_handle_t logos_{}_some(const char* value);", mangled).unwrap();
1957                            } else {
1958                                let inner_c = map_type_to_c_header(&params[0], interner, false);
1959                                writeln!(out, "logos_handle_t logos_{}_some({} value);", mangled, inner_c).unwrap();
1960                            }
1961                            writeln!(out, "logos_handle_t logos_{}_none(void);", mangled).unwrap();
1962                            writeln!(out, "void logos_{}_free(logos_handle_t handle);", mangled).unwrap();
1963                        }
1964                        _ => {}
1965                    }
1966                }
1967                TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
1968                    let type_name = interner.resolve(*sym);
1969                    match registry.get(*sym) {
1970                        Some(TypeDef::Struct { fields, is_portable, .. }) => {
1971                            let struct_lower = type_name.to_lowercase();
1972                            for field in fields {
1973                                let field_name = interner.resolve(field.name);
1974                                let is_field_text = match &field.ty {
1975                                    FieldType::Primitive(s) | FieldType::Named(s) => {
1976                                        let n = interner.resolve(*s);
1977                                        n == "Text" || n == "String"
1978                                    }
1979                                    _ => false,
1980                                };
1981                                if is_field_text {
1982                                    writeln!(out, "char* logos_{}_{}(logos_handle_t handle);", struct_lower, field_name).unwrap();
1983                                } else {
1984                                    let c_type = map_field_type_to_c(&field.ty, interner);
1985                                    writeln!(out, "{} logos_{}_{}(logos_handle_t handle);", c_type, struct_lower, field_name).unwrap();
1986                                }
1987                            }
1988                            // to_json/from_json always available for C export reference-type structs
1989                            writeln!(out, "char* logos_{}_to_json(logos_handle_t handle);", struct_lower).unwrap();
1990                            writeln!(out, "logos_status_t logos_{}_from_json(const char* json, logos_handle_t* out);", struct_lower).unwrap();
1991                            writeln!(out, "void logos_{}_free(logos_handle_t handle);", struct_lower).unwrap();
1992                        }
1993                        Some(TypeDef::Enum { variants, .. }) => {
1994                            let enum_lower = type_name.to_lowercase();
1995                            // Tag enum constants
1996                            writeln!(out, "typedef enum {{").unwrap();
1997                            for (i, variant) in variants.iter().enumerate() {
1998                                let vname = interner.resolve(variant.name).to_uppercase();
1999                                writeln!(out, "    LOGOS_{}_{} = {},", type_name.to_uppercase(), vname, i).unwrap();
2000                            }
2001                            writeln!(out, "}} logos_{}_tag_t;", enum_lower).unwrap();
2002                            writeln!(out, "int32_t logos_{}_tag(logos_handle_t handle);", enum_lower).unwrap();
2003                            // Per-variant field accessors
2004                            for variant in variants {
2005                                let vname = interner.resolve(variant.name);
2006                                let vname_lower = vname.to_lowercase();
2007                                for field in &variant.fields {
2008                                    let fname = interner.resolve(field.name);
2009                                    let is_field_text = match &field.ty {
2010                                        FieldType::Primitive(s) | FieldType::Named(s) => {
2011                                            let n = interner.resolve(*s);
2012                                            n == "Text" || n == "String"
2013                                        }
2014                                        _ => false,
2015                                    };
2016                                    if is_field_text {
2017                                        writeln!(out, "char* logos_{}_{}_{fname}(logos_handle_t handle);", enum_lower, vname_lower, fname = fname).unwrap();
2018                                    } else {
2019                                        let c_type = map_field_type_to_c(&field.ty, interner);
2020                                        writeln!(out, "{} logos_{}_{}_{fname}(logos_handle_t handle);", c_type, enum_lower, vname_lower, fname = fname).unwrap();
2021                                    }
2022                                }
2023                            }
2024                            writeln!(out, "void logos_{}_free(logos_handle_t handle);", enum_lower).unwrap();
2025                        }
2026                        _ => {}
2027                    }
2028                }
2029                _ => {}
2030            }
2031            writeln!(out).unwrap();
2032        }
2033    }
2034
2035    writeln!(out, "#ifdef __cplusplus").unwrap();
2036    writeln!(out, "}}").unwrap();
2037    writeln!(out, "#endif\n").unwrap();
2038    writeln!(out, "#endif // {}_H", guard).unwrap();
2039
2040    out
2041}
2042
2043/// Generate Python ctypes bindings for all C-exported functions.
2044pub fn generate_python_bindings(
2045    stmts: &[Stmt],
2046    module_name: &str,
2047    interner: &Interner,
2048    registry: &TypeRegistry,
2049) -> String {
2050    let mut out = String::new();
2051
2052    writeln!(out, "\"\"\"Auto-generated Python bindings for {}.\"\"\"", module_name).unwrap();
2053    writeln!(out, "import ctypes").unwrap();
2054    writeln!(out, "from ctypes import c_int64, c_uint64, c_double, c_bool, c_char_p, c_void_p, c_size_t, POINTER").unwrap();
2055    writeln!(out, "import os").unwrap();
2056    writeln!(out, "import sys\n").unwrap();
2057
2058    writeln!(out, "class LogosError(Exception):").unwrap();
2059    writeln!(out, "    pass\n").unwrap();
2060
2061    writeln!(out, "class LogosRefinementError(LogosError):").unwrap();
2062    writeln!(out, "    pass\n").unwrap();
2063
2064    writeln!(out, "def _lib_ext():").unwrap();
2065    writeln!(out, "    if sys.platform == \"darwin\":").unwrap();
2066    writeln!(out, "        return \".dylib\"").unwrap();
2067    writeln!(out, "    elif sys.platform == \"win32\":").unwrap();
2068    writeln!(out, "        return \".dll\"").unwrap();
2069    writeln!(out, "    else:").unwrap();
2070    writeln!(out, "        return \".so\"\n").unwrap();
2071
2072    let class_name = module_name.chars().next().unwrap_or('M').to_uppercase().to_string()
2073        + &module_name[1..];
2074
2075    writeln!(out, "class {}:", class_name).unwrap();
2076    writeln!(out, "    OK = 0").unwrap();
2077    writeln!(out, "    ERROR = 1").unwrap();
2078    writeln!(out, "    REFINEMENT_VIOLATION = 2").unwrap();
2079    writeln!(out, "    NULL_POINTER = 3").unwrap();
2080    writeln!(out, "    OUT_OF_BOUNDS = 4\n").unwrap();
2081
2082    writeln!(out, "    def __init__(self, path=None):").unwrap();
2083    writeln!(out, "        if path is None:").unwrap();
2084    writeln!(out, "            path = os.path.join(os.path.dirname(__file__), \"lib{}\" + _lib_ext())", module_name).unwrap();
2085    writeln!(out, "        self._lib = ctypes.CDLL(path)").unwrap();
2086    writeln!(out, "        self._setup()\n").unwrap();
2087
2088    writeln!(out, "    def _check(self, status):").unwrap();
2089    writeln!(out, "        if status != self.OK:").unwrap();
2090    writeln!(out, "            err = self._lib.logos_get_last_error()").unwrap();
2091    writeln!(out, "            msg = err.decode(\"utf-8\") if err else \"Unknown error\"").unwrap();
2092    writeln!(out, "            self._lib.logos_clear_error()").unwrap();
2093    writeln!(out, "            if status == self.REFINEMENT_VIOLATION:").unwrap();
2094    writeln!(out, "                raise LogosRefinementError(msg)").unwrap();
2095    writeln!(out, "            raise LogosError(msg)\n").unwrap();
2096
2097    // _setup method
2098    writeln!(out, "    def _setup(self):").unwrap();
2099    writeln!(out, "        self._lib.logos_get_last_error.restype = c_char_p").unwrap();
2100    writeln!(out, "        self._lib.logos_clear_error.restype = None").unwrap();
2101    writeln!(out, "        self._lib.logos_free_string.argtypes = [c_char_p]").unwrap();
2102    writeln!(out, "        self._lib.logos_free_string.restype = None").unwrap();
2103
2104    // Per-function setup
2105    for stmt in stmts {
2106        if let Stmt::FunctionDef { name, is_exported: true, export_target, params, return_type, .. } = stmt {
2107            let is_c = match export_target {
2108                None => true,
2109                Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
2110            };
2111            if !is_c { continue; }
2112
2113            let func_name = format!("logos_{}", interner.resolve(*name));
2114            let mut argtypes = Vec::new();
2115            for (_, ptype) in params.iter() {
2116                argtypes.push(python_ctypes_type(ptype, interner, registry));
2117            }
2118            let restype = return_type
2119                .map(|ty| python_ctypes_type(ty, interner, registry))
2120                .unwrap_or_else(|| "None".to_string());
2121
2122            writeln!(out, "        self._lib.{}.argtypes = [{}]", func_name, argtypes.join(", ")).unwrap();
2123            writeln!(out, "        self._lib.{}.restype = {}", func_name, restype).unwrap();
2124        }
2125    }
2126    writeln!(out).unwrap();
2127
2128    // Per-function wrapper methods
2129    for stmt in stmts {
2130        if let Stmt::FunctionDef { name, is_exported: true, export_target, params, return_type, .. } = stmt {
2131            let is_c = match export_target {
2132                None => true,
2133                Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
2134            };
2135            if !is_c { continue; }
2136
2137            let raw_name = interner.resolve(*name);
2138            let c_func_name = format!("logos_{}", raw_name);
2139            let param_names: Vec<String> = params.iter()
2140                .map(|(pname, _)| interner.resolve(*pname).to_string())
2141                .collect();
2142            let type_hints: Vec<String> = params.iter()
2143                .map(|(pname, ptype)| {
2144                    format!("{}: {}", interner.resolve(*pname), python_type_hint(ptype, interner))
2145                })
2146                .collect();
2147            let ret_hint = return_type
2148                .map(|ty| format!(" -> {}", python_type_hint(ty, interner)))
2149                .unwrap_or_default();
2150
2151            // Python method uses the raw name for ergonomic API; delegates to prefixed C symbol
2152            writeln!(out, "    def {}(self, {}){}:", raw_name, type_hints.join(", "), ret_hint).unwrap();
2153            writeln!(out, "        return self._lib.{}({})", c_func_name, param_names.join(", ")).unwrap();
2154            writeln!(out).unwrap();
2155        }
2156    }
2157
2158    out
2159}
2160
2161fn python_ctypes_type(ty: &TypeExpr, interner: &Interner, registry: &TypeRegistry) -> String {
2162    match classify_type_for_c_abi(ty, interner, registry) {
2163        CAbiClass::ReferenceType => "c_void_p".to_string(),
2164        CAbiClass::ValueType => {
2165            match ty {
2166                TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
2167                    let name = interner.resolve(*sym);
2168                    match name {
2169                        "Int" => "c_int64".to_string(),
2170                        "Nat" => "c_uint64".to_string(),
2171                        "Real" | "Float" => "c_double".to_string(),
2172                        "Bool" | "Boolean" => "c_bool".to_string(),
2173                        "Text" | "String" => "c_char_p".to_string(),
2174                        _ => "c_void_p".to_string(),
2175                    }
2176                }
2177                _ => "c_void_p".to_string(),
2178            }
2179        }
2180    }
2181}
2182
2183fn python_type_hint(ty: &TypeExpr, interner: &Interner) -> String {
2184    match ty {
2185        TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
2186            let name = interner.resolve(*sym);
2187            match name {
2188                "Int" | "Nat" => "int".to_string(),
2189                "Real" | "Float" => "float".to_string(),
2190                "Bool" | "Boolean" => "bool".to_string(),
2191                "Text" | "String" => "str".to_string(),
2192                other => other.to_string(),
2193            }
2194        }
2195        _ => "object".to_string(),
2196    }
2197}
2198
2199/// Generate TypeScript type declarations (.d.ts) and FFI bindings (.js).
2200pub fn generate_typescript_bindings(
2201    stmts: &[Stmt],
2202    module_name: &str,
2203    interner: &Interner,
2204    registry: &TypeRegistry,
2205) -> (String, String) {
2206    let mut dts = String::new();
2207    let mut js = String::new();
2208
2209    // .d.ts
2210    writeln!(dts, "// Auto-generated TypeScript definitions for {}", module_name).unwrap();
2211    let mut ffi_entries = Vec::new();
2212
2213    for stmt in stmts {
2214        if let Stmt::FunctionDef { name, is_exported: true, export_target, params, return_type, .. } = stmt {
2215            let is_c = match export_target {
2216                None => true,
2217                Some(t) => interner.resolve(*t).eq_ignore_ascii_case("c"),
2218            };
2219            if !is_c { continue; }
2220
2221            let raw_name = interner.resolve(*name);
2222            let c_symbol = format!("logos_{}", raw_name);
2223            let ts_params: Vec<String> = params.iter()
2224                .map(|(pname, ptype)| format!("{}: {}", interner.resolve(*pname), typescript_type(ptype, interner)))
2225                .collect();
2226            let ts_ret = return_type
2227                .map(|ty| typescript_type(ty, interner))
2228                .unwrap_or_else(|| "void".to_string());
2229            writeln!(dts, "export declare function {}({}): {};", raw_name, ts_params.join(", "), ts_ret).unwrap();
2230
2231            // Collect FFI entries for .js (raw_name for JS API, c_symbol for C FFI)
2232            let ffi_params: Vec<String> = params.iter()
2233                .map(|(_, ptype)| ffi_napi_type(ptype, interner, registry))
2234                .collect();
2235            let ffi_ret = return_type
2236                .map(|ty| ffi_napi_type(ty, interner, registry))
2237                .unwrap_or_else(|| "'void'".to_string());
2238            ffi_entries.push((raw_name.to_string(), c_symbol, ffi_ret, ffi_params));
2239        }
2240    }
2241
2242    // .js — uses koffi (pure JS, no native deps)
2243    writeln!(js, "const koffi = require('koffi');").unwrap();
2244    writeln!(js, "const path = require('path');\n").unwrap();
2245    writeln!(js, "const libPath = path.join(__dirname, 'lib{}');", module_name).unwrap();
2246    writeln!(js, "const lib = koffi.load(libPath);\n").unwrap();
2247
2248    // Declare runtime functions
2249    writeln!(js, "const logos_get_last_error = lib.func('const char* logos_get_last_error()');").unwrap();
2250    writeln!(js, "const logos_clear_error = lib.func('void logos_clear_error()');").unwrap();
2251    writeln!(js, "const logos_free_string = lib.func('void logos_free_string(void* ptr)');\n").unwrap();
2252
2253    // Declare user-exported functions (C symbols use logos_ prefix)
2254    for (raw_name, c_symbol, ffi_ret, ffi_params) in &ffi_entries {
2255        let koffi_ret = ffi_napi_to_koffi(ffi_ret);
2256        let koffi_params: Vec<String> = ffi_params.iter()
2257            .enumerate()
2258            .map(|(i, p)| format!("{} arg{}", ffi_napi_to_koffi(p), i))
2259            .collect();
2260        writeln!(js, "const _{} = lib.func('{} {}({})');\n", raw_name, koffi_ret, c_symbol, koffi_params.join(", ")).unwrap();
2261    }
2262
2263    writeln!(js, "function checkStatus(status) {{").unwrap();
2264    writeln!(js, "  if (status !== 0) {{").unwrap();
2265    writeln!(js, "    const err = logos_get_last_error();").unwrap();
2266    writeln!(js, "    logos_clear_error();").unwrap();
2267    writeln!(js, "    throw new Error(err || 'Unknown LogicAffeine error');").unwrap();
2268    writeln!(js, "  }}").unwrap();
2269    writeln!(js, "}}\n").unwrap();
2270
2271    for (raw_name, _, _, _) in &ffi_entries {
2272        let params_from_stmts = stmts.iter().find_map(|s| {
2273            if let Stmt::FunctionDef { name, is_exported: true, params, .. } = s {
2274                if interner.resolve(*name) == raw_name.as_str() {
2275                    Some(params)
2276                } else {
2277                    None
2278                }
2279            } else {
2280                None
2281            }
2282        });
2283        if let Some(params) = params_from_stmts {
2284            let param_names: Vec<String> = params.iter()
2285                .map(|(pname, _)| interner.resolve(*pname).to_string())
2286                .collect();
2287            writeln!(js, "module.exports.{} = ({}) => _{}({});", raw_name, param_names.join(", "), raw_name, param_names.join(", ")).unwrap();
2288        }
2289    }
2290
2291    (js, dts)
2292}
2293
2294fn typescript_type(ty: &TypeExpr, interner: &Interner) -> String {
2295    match ty {
2296        TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
2297            let name = interner.resolve(*sym);
2298            match name {
2299                "Int" | "Nat" | "Real" | "Float" | "Byte" => "number".to_string(),
2300                "Bool" | "Boolean" => "boolean".to_string(),
2301                "Text" | "String" | "Char" => "string".to_string(),
2302                "Unit" => "void".to_string(),
2303                other => other.to_string(),
2304            }
2305        }
2306        TypeExpr::Generic { base, params } => {
2307            let base_name = interner.resolve(*base);
2308            match base_name {
2309                "Seq" | "List" | "Vec" if !params.is_empty() => {
2310                    format!("{}[]", typescript_type(&params[0], interner))
2311                }
2312                "Option" | "Maybe" if !params.is_empty() => {
2313                    format!("{} | null", typescript_type(&params[0], interner))
2314                }
2315                _ => "any".to_string(),
2316            }
2317        }
2318        _ => "any".to_string(),
2319    }
2320}
2321
2322/// Convert ffi-napi type strings to koffi type strings for TypeScript bindings.
2323fn ffi_napi_to_koffi(ffi_type: &str) -> &str {
2324    match ffi_type {
2325        "'int64'" => "int64_t",
2326        "'uint64'" => "uint64_t",
2327        "'double'" => "double",
2328        "'bool'" => "bool",
2329        "'string'" => "const char*",
2330        "'pointer'" => "void*",
2331        "'void'" => "void",
2332        _ => "void*",
2333    }
2334}
2335
2336fn ffi_napi_type(ty: &TypeExpr, interner: &Interner, registry: &TypeRegistry) -> String {
2337    match classify_type_for_c_abi(ty, interner, registry) {
2338        CAbiClass::ReferenceType => "'pointer'".to_string(),
2339        CAbiClass::ValueType => {
2340            match ty {
2341                TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
2342                    let name = interner.resolve(*sym);
2343                    match name {
2344                        "Int" => "'int64'".to_string(),
2345                        "Nat" => "'uint64'".to_string(),
2346                        "Real" | "Float" => "'double'".to_string(),
2347                        "Bool" | "Boolean" => "'bool'".to_string(),
2348                        "Text" | "String" => "'string'".to_string(),
2349                        _ => "'pointer'".to_string(),
2350                    }
2351                }
2352                _ => "'pointer'".to_string(),
2353            }
2354        }
2355    }
2356}
2357
2358/// Map a TypeExpr to its C header type representation.
2359fn map_type_to_c_header(ty: &TypeExpr, interner: &Interner, is_return: bool) -> String {
2360    match ty {
2361        TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
2362            let name = interner.resolve(*sym);
2363            match name {
2364                "Int" => "int64_t".to_string(),
2365                "Nat" => "uint64_t".to_string(),
2366                "Real" | "Float" => "double".to_string(),
2367                "Bool" | "Boolean" => "bool".to_string(),
2368                "Byte" => "uint8_t".to_string(),
2369                "Char" => "uint32_t".to_string(), // UTF-32 char
2370                "Text" | "String" => {
2371                    if is_return { "char*".to_string() } else { "const char*".to_string() }
2372                }
2373                "Unit" => "void".to_string(),
2374                other => other.to_string(), // User struct name
2375            }
2376        }
2377        TypeExpr::Refinement { base, .. } => map_type_to_c_header(base, interner, is_return),
2378        TypeExpr::Generic { .. } => "logos_handle_t".to_string(),
2379        _ => "logos_handle_t".to_string(),
2380    }
2381}
2382
2383/// Map a FieldType (from TypeRegistry) to a C header type string.
2384fn map_field_type_to_c(ft: &FieldType, interner: &Interner) -> String {
2385    match ft {
2386        FieldType::Primitive(sym) | FieldType::Named(sym) => {
2387            let name = interner.resolve(*sym);
2388            match name {
2389                "Int" => "int64_t".to_string(),
2390                "Nat" => "uint64_t".to_string(),
2391                "Real" | "Float" => "double".to_string(),
2392                "Bool" | "Boolean" => "bool".to_string(),
2393                "Byte" => "uint8_t".to_string(),
2394                "Char" => "uint32_t".to_string(),
2395                "Text" | "String" => "const char*".to_string(),
2396                other => other.to_string(),
2397            }
2398        }
2399        FieldType::Generic { .. } => "logos_handle_t".to_string(),
2400        FieldType::TypeParam(_) => "logos_handle_t".to_string(),
2401    }
2402}
2403
2404/// Check if a TypeExpr is a Result type.
2405fn is_result_type(ty: &TypeExpr, interner: &Interner) -> bool {
2406    if let TypeExpr::Generic { base, .. } = ty {
2407        interner.resolve(*base) == "Result"
2408    } else {
2409        false
2410    }
2411}
2412
2413/// Phase 51: Detect if any statements require async execution.
2414/// Returns true if the program needs #[tokio::main] async fn main().
2415fn requires_async(stmts: &[Stmt]) -> bool {
2416    stmts.iter().any(|s| requires_async_stmt(s))
2417}
2418
2419fn requires_async_stmt(stmt: &Stmt) -> bool {
2420    match stmt {
2421        // Phase 9: Concurrent blocks use tokio::join!
2422        Stmt::Concurrent { tasks } => true,
2423        // Phase 51: Network operations and Sleep are async
2424        Stmt::Listen { .. } => true,
2425        Stmt::ConnectTo { .. } => true,
2426        Stmt::Sleep { .. } => true,
2427        // Phase 52: Sync is async (GossipSub subscription)
2428        Stmt::Sync { .. } => true,
2429        // Phase 53: Mount is async (VFS file operations)
2430        Stmt::Mount { .. } => true,
2431        // Phase 53: File I/O is async (VFS operations)
2432        Stmt::ReadFrom { source: ReadSource::File(_), .. } => true,
2433        Stmt::WriteFile { .. } => true,
2434        // Phase 54: Go-like concurrency is async
2435        Stmt::LaunchTask { .. } => true,
2436        Stmt::LaunchTaskWithHandle { .. } => true,
2437        Stmt::SendPipe { .. } => true,
2438        Stmt::ReceivePipe { .. } => true,
2439        Stmt::Select { .. } => true,
2440        // While and Repeat are now always async due to check_preemption()
2441        // (handled below in recursive check)
2442        // Recursively check nested blocks
2443        Stmt::If { then_block, else_block, .. } => {
2444            then_block.iter().any(|s| requires_async_stmt(s))
2445                || else_block.map_or(false, |b| b.iter().any(|s| requires_async_stmt(s)))
2446        }
2447        Stmt::While { body, .. } => body.iter().any(|s| requires_async_stmt(s)),
2448        Stmt::Repeat { body, .. } => body.iter().any(|s| requires_async_stmt(s)),
2449        Stmt::Zone { body, .. } => body.iter().any(|s| requires_async_stmt(s)),
2450        Stmt::Parallel { tasks } => tasks.iter().any(|s| requires_async_stmt(s)),
2451        Stmt::FunctionDef { body, .. } => body.iter().any(|s| requires_async_stmt(s)),
2452        // Check Inspect arms for async operations
2453        Stmt::Inspect { arms, .. } => {
2454            arms.iter().any(|arm| arm.body.iter().any(|s| requires_async_stmt(s)))
2455        }
2456        _ => false,
2457    }
2458}
2459
2460/// Phase 53: Detect if any statements require VFS (Virtual File System).
2461/// Returns true if the program uses file operations or persistent storage.
2462fn requires_vfs(stmts: &[Stmt]) -> bool {
2463    stmts.iter().any(|s| requires_vfs_stmt(s))
2464}
2465
2466fn requires_vfs_stmt(stmt: &Stmt) -> bool {
2467    match stmt {
2468        // Phase 53: Mount uses VFS for persistent storage
2469        Stmt::Mount { .. } => true,
2470        // Phase 53: File I/O uses VFS
2471        Stmt::ReadFrom { source: ReadSource::File(_), .. } => true,
2472        Stmt::WriteFile { .. } => true,
2473        // Recursively check nested blocks
2474        Stmt::If { then_block, else_block, .. } => {
2475            then_block.iter().any(|s| requires_vfs_stmt(s))
2476                || else_block.map_or(false, |b| b.iter().any(|s| requires_vfs_stmt(s)))
2477        }
2478        Stmt::While { body, .. } => body.iter().any(|s| requires_vfs_stmt(s)),
2479        Stmt::Repeat { body, .. } => body.iter().any(|s| requires_vfs_stmt(s)),
2480        Stmt::Zone { body, .. } => body.iter().any(|s| requires_vfs_stmt(s)),
2481        Stmt::Concurrent { tasks } => tasks.iter().any(|s| requires_vfs_stmt(s)),
2482        Stmt::Parallel { tasks } => tasks.iter().any(|s| requires_vfs_stmt(s)),
2483        Stmt::FunctionDef { body, .. } => body.iter().any(|s| requires_vfs_stmt(s)),
2484        _ => false,
2485    }
2486}
2487
2488/// Phase 49b: Extract root identifier from expression for mutability analysis.
2489/// Works with both simple identifiers and field accesses.
2490fn get_root_identifier_for_mutability(expr: &Expr) -> Option<Symbol> {
2491    match expr {
2492        Expr::Identifier(sym) => Some(*sym),
2493        Expr::FieldAccess { object, .. } => get_root_identifier_for_mutability(object),
2494        _ => None,
2495    }
2496}
2497
2498/// Grand Challenge: Collect all variables that need `let mut` in Rust.
2499/// This includes:
2500/// - Variables that are targets of `Set` statements (reassignment)
2501/// - Variables that are targets of `Push` statements (mutation via push)
2502/// - Variables that are targets of `Pop` statements (mutation via pop)
2503fn collect_mutable_vars(stmts: &[Stmt]) -> HashSet<Symbol> {
2504    let mut targets = HashSet::new();
2505    for stmt in stmts {
2506        collect_mutable_vars_stmt(stmt, &mut targets);
2507    }
2508    targets
2509}
2510
2511fn collect_mutable_vars_stmt(stmt: &Stmt, targets: &mut HashSet<Symbol>) {
2512    match stmt {
2513        Stmt::Set { target, .. } => {
2514            targets.insert(*target);
2515        }
2516        Stmt::Push { collection, .. } => {
2517            // If collection is an identifier or field access, root needs to be mutable
2518            if let Some(sym) = get_root_identifier_for_mutability(collection) {
2519                targets.insert(sym);
2520            }
2521        }
2522        Stmt::Pop { collection, .. } => {
2523            // If collection is an identifier or field access, root needs to be mutable
2524            if let Some(sym) = get_root_identifier_for_mutability(collection) {
2525                targets.insert(sym);
2526            }
2527        }
2528        Stmt::Add { collection, .. } => {
2529            // If collection is an identifier (Set) or field access, root needs to be mutable
2530            if let Some(sym) = get_root_identifier_for_mutability(collection) {
2531                targets.insert(sym);
2532            }
2533        }
2534        Stmt::Remove { collection, .. } => {
2535            // If collection is an identifier (Set) or field access, root needs to be mutable
2536            if let Some(sym) = get_root_identifier_for_mutability(collection) {
2537                targets.insert(sym);
2538            }
2539        }
2540        Stmt::SetIndex { collection, .. } => {
2541            // If collection is an identifier or field access, root needs to be mutable
2542            if let Some(sym) = get_root_identifier_for_mutability(collection) {
2543                targets.insert(sym);
2544            }
2545        }
2546        Stmt::If { then_block, else_block, .. } => {
2547            for s in *then_block {
2548                collect_mutable_vars_stmt(s, targets);
2549            }
2550            if let Some(else_stmts) = else_block {
2551                for s in *else_stmts {
2552                    collect_mutable_vars_stmt(s, targets);
2553                }
2554            }
2555        }
2556        Stmt::While { body, .. } => {
2557            for s in *body {
2558                collect_mutable_vars_stmt(s, targets);
2559            }
2560        }
2561        Stmt::Repeat { body, .. } => {
2562            for s in *body {
2563                collect_mutable_vars_stmt(s, targets);
2564            }
2565        }
2566        Stmt::Zone { body, .. } => {
2567            for s in *body {
2568                collect_mutable_vars_stmt(s, targets);
2569            }
2570        }
2571        // Inspect (pattern match) arms may contain mutations
2572        Stmt::Inspect { arms, .. } => {
2573            for arm in arms.iter() {
2574                for s in arm.body.iter() {
2575                    collect_mutable_vars_stmt(s, targets);
2576                }
2577            }
2578        }
2579        // Phase 9: Structured Concurrency blocks
2580        Stmt::Concurrent { tasks } | Stmt::Parallel { tasks } => {
2581            for s in *tasks {
2582                collect_mutable_vars_stmt(s, targets);
2583            }
2584        }
2585        // Phase 49b: CRDT operations require mutable access
2586        Stmt::IncreaseCrdt { object, .. } | Stmt::DecreaseCrdt { object, .. } => {
2587            // Extract root variable from field access (e.g., g.score -> g)
2588            if let Some(sym) = get_root_identifier_for_mutability(object) {
2589                targets.insert(sym);
2590            }
2591        }
2592        Stmt::AppendToSequence { sequence, .. } => {
2593            if let Some(sym) = get_root_identifier_for_mutability(sequence) {
2594                targets.insert(sym);
2595            }
2596        }
2597        Stmt::ResolveConflict { object, .. } => {
2598            if let Some(sym) = get_root_identifier_for_mutability(object) {
2599                targets.insert(sym);
2600            }
2601        }
2602        // Phase 49b: SetField on MVRegister/LWWRegister uses .set() which requires &mut self
2603        Stmt::SetField { object, .. } => {
2604            if let Some(sym) = get_root_identifier_for_mutability(object) {
2605                targets.insert(sym);
2606            }
2607        }
2608        _ => {}
2609    }
2610}
2611
2612// =============================================================================
2613// Phase 50: Policy Method Generation
2614// =============================================================================
2615
2616/// Generate impl blocks with predicate and capability methods for security policies.
2617fn codegen_policy_impls(policies: &PolicyRegistry, interner: &Interner) -> String {
2618    let mut output = String::new();
2619
2620    // Collect all types that have policies
2621    let mut type_predicates: HashMap<Symbol, Vec<&PredicateDef>> = HashMap::new();
2622    let mut type_capabilities: HashMap<Symbol, Vec<&CapabilityDef>> = HashMap::new();
2623
2624    for (type_sym, predicates) in policies.iter_predicates() {
2625        type_predicates.entry(*type_sym).or_insert_with(Vec::new).extend(predicates.iter());
2626    }
2627
2628    for (type_sym, capabilities) in policies.iter_capabilities() {
2629        type_capabilities.entry(*type_sym).or_insert_with(Vec::new).extend(capabilities.iter());
2630    }
2631
2632    // Get all types that have any policies
2633    let mut all_types: HashSet<Symbol> = HashSet::new();
2634    all_types.extend(type_predicates.keys().copied());
2635    all_types.extend(type_capabilities.keys().copied());
2636
2637    // Generate impl block for each type
2638    for type_sym in all_types {
2639        let type_name = interner.resolve(type_sym);
2640
2641        writeln!(output, "impl {} {{", type_name).unwrap();
2642
2643        // Generate predicate methods
2644        if let Some(predicates) = type_predicates.get(&type_sym) {
2645            for pred in predicates {
2646                let pred_name = interner.resolve(pred.predicate_name).to_lowercase();
2647                writeln!(output, "    pub fn is_{}(&self) -> bool {{", pred_name).unwrap();
2648                let condition_code = codegen_policy_condition(&pred.condition, interner);
2649                writeln!(output, "        {}", condition_code).unwrap();
2650                writeln!(output, "    }}\n").unwrap();
2651            }
2652        }
2653
2654        // Generate capability methods
2655        if let Some(capabilities) = type_capabilities.get(&type_sym) {
2656            for cap in capabilities {
2657                let action_name = interner.resolve(cap.action).to_lowercase();
2658                let object_type = interner.resolve(cap.object_type);
2659                let object_param = object_type.to_lowercase();
2660
2661                writeln!(output, "    pub fn can_{}(&self, {}: &{}) -> bool {{",
2662                         action_name, object_param, object_type).unwrap();
2663                let condition_code = codegen_policy_condition(&cap.condition, interner);
2664                writeln!(output, "        {}", condition_code).unwrap();
2665                writeln!(output, "    }}\n").unwrap();
2666            }
2667        }
2668
2669        writeln!(output, "}}\n").unwrap();
2670    }
2671
2672    output
2673}
2674
2675/// Generate Rust code for a policy condition.
2676fn codegen_policy_condition(condition: &PolicyCondition, interner: &Interner) -> String {
2677    match condition {
2678        PolicyCondition::FieldEquals { field, value, is_string_literal } => {
2679            let field_name = interner.resolve(*field);
2680            let value_str = interner.resolve(*value);
2681            if *is_string_literal {
2682                format!("self.{} == \"{}\"", field_name, value_str)
2683            } else {
2684                format!("self.{} == {}", field_name, value_str)
2685            }
2686        }
2687        PolicyCondition::FieldBool { field, value } => {
2688            let field_name = interner.resolve(*field);
2689            format!("self.{} == {}", field_name, value)
2690        }
2691        PolicyCondition::Predicate { subject: _, predicate } => {
2692            let pred_name = interner.resolve(*predicate).to_lowercase();
2693            format!("self.is_{}()", pred_name)
2694        }
2695        PolicyCondition::ObjectFieldEquals { subject: _, object, field } => {
2696            let object_name = interner.resolve(*object).to_lowercase();
2697            let field_name = interner.resolve(*field);
2698            format!("self == &{}.{}", object_name, field_name)
2699        }
2700        PolicyCondition::Or(left, right) => {
2701            let left_code = codegen_policy_condition(left, interner);
2702            let right_code = codegen_policy_condition(right, interner);
2703            format!("{} || {}", left_code, right_code)
2704        }
2705        PolicyCondition::And(left, right) => {
2706            let left_code = codegen_policy_condition(left, interner);
2707            let right_code = codegen_policy_condition(right, interner);
2708            format!("{} && {}", left_code, right_code)
2709        }
2710    }
2711}
2712
2713/// Collect CRDT register field paths for special handling in SetField codegen.
2714/// Returns two sets:
2715/// - LWW fields: (type_name, field_name) pairs where field is LastWriteWins (needs timestamp)
2716/// - MV fields: (type_name, field_name) pairs where field is Divergent/MVRegister (no timestamp)
2717fn collect_crdt_register_fields(registry: &TypeRegistry, interner: &Interner) -> (HashSet<(String, String)>, HashSet<(String, String)>) {
2718    let mut lww_fields = HashSet::new();
2719    let mut mv_fields = HashSet::new();
2720    for (type_sym, def) in registry.iter_types() {
2721        if let TypeDef::Struct { fields, .. } = def {
2722            let type_name = interner.resolve(*type_sym).to_string();
2723            for field in fields {
2724                if let FieldType::Generic { base, .. } = &field.ty {
2725                    let base_name = interner.resolve(*base);
2726                    let field_name = interner.resolve(field.name).to_string();
2727                    if base_name == "LastWriteWins" {
2728                        lww_fields.insert((type_name.clone(), field_name));
2729                    } else if base_name == "Divergent" || base_name == "MVRegister" {
2730                        mv_fields.insert((type_name.clone(), field_name));
2731                    }
2732                }
2733            }
2734        }
2735    }
2736    (lww_fields, mv_fields)
2737}
2738
2739/// Phase 102: Collect enum fields that need Box<T> for recursion.
2740/// Returns a set of (EnumName, VariantName, FieldName) tuples.
2741fn collect_boxed_fields(registry: &TypeRegistry, interner: &Interner) -> HashSet<(String, String, String)> {
2742    let mut boxed_fields = HashSet::new();
2743    for (type_sym, def) in registry.iter_types() {
2744        if let TypeDef::Enum { variants, .. } = def {
2745            let enum_name = interner.resolve(*type_sym);
2746            for variant in variants {
2747                let variant_name = interner.resolve(variant.name);
2748                for field in &variant.fields {
2749                    if is_recursive_field(&field.ty, enum_name, interner) {
2750                        let field_name = interner.resolve(field.name).to_string();
2751                        boxed_fields.insert((
2752                            enum_name.to_string(),
2753                            variant_name.to_string(),
2754                            field_name,
2755                        ));
2756                    }
2757                }
2758            }
2759        }
2760    }
2761    boxed_fields
2762}
2763
2764/// Phase 54: Collect function names that are async.
2765/// Used by LaunchTask codegen to determine if .await is needed.
2766///
2767/// Two-pass analysis:
2768/// 1. First pass: Collect directly async functions (have Sleep, LaunchTask, etc.)
2769/// 2. Second pass: Iterate until fixed point - if function calls an async function, mark it async
2770pub fn collect_async_functions(stmts: &[Stmt]) -> HashSet<Symbol> {
2771    // First, collect all function definitions
2772    let mut func_bodies: HashMap<Symbol, &[Stmt]> = HashMap::new();
2773    for stmt in stmts {
2774        if let Stmt::FunctionDef { name, body, .. } = stmt {
2775            func_bodies.insert(*name, *body);
2776        }
2777    }
2778
2779    // Pass 1: Collect directly async functions
2780    let mut async_fns = HashSet::new();
2781    for stmt in stmts {
2782        if let Stmt::FunctionDef { name, body, .. } = stmt {
2783            if body.iter().any(|s| requires_async_stmt(s)) {
2784                async_fns.insert(*name);
2785            }
2786        }
2787    }
2788
2789    // Pass 2: Propagate async-ness through call graph until fixed point
2790    loop {
2791        let mut changed = false;
2792        for (func_name, body) in &func_bodies {
2793            if async_fns.contains(func_name) {
2794                continue; // Already marked async
2795            }
2796            // Check if this function calls any async function
2797            if body.iter().any(|s| calls_async_function(s, &async_fns)) {
2798                async_fns.insert(*func_name);
2799                changed = true;
2800            }
2801        }
2802        if !changed {
2803            break;
2804        }
2805    }
2806
2807    async_fns
2808}
2809
2810/// Helper: Check if a statement calls any function in the async_fns set
2811fn calls_async_function(stmt: &Stmt, async_fns: &HashSet<Symbol>) -> bool {
2812    match stmt {
2813        Stmt::Call { function, args } => {
2814            // Check if the called function is async OR if any argument expression calls an async function
2815            async_fns.contains(function)
2816                || args.iter().any(|a| calls_async_function_in_expr(a, async_fns))
2817        }
2818        Stmt::If { cond, then_block, else_block } => {
2819            calls_async_function_in_expr(cond, async_fns)
2820                || then_block.iter().any(|s| calls_async_function(s, async_fns))
2821                || else_block.map_or(false, |b| b.iter().any(|s| calls_async_function(s, async_fns)))
2822        }
2823        Stmt::While { cond, body, .. } => {
2824            calls_async_function_in_expr(cond, async_fns)
2825                || body.iter().any(|s| calls_async_function(s, async_fns))
2826        }
2827        Stmt::Repeat { iterable, body, .. } => {
2828            calls_async_function_in_expr(iterable, async_fns)
2829                || body.iter().any(|s| calls_async_function(s, async_fns))
2830        }
2831        Stmt::Zone { body, .. } => {
2832            body.iter().any(|s| calls_async_function(s, async_fns))
2833        }
2834        Stmt::Concurrent { tasks } | Stmt::Parallel { tasks } => {
2835            tasks.iter().any(|s| calls_async_function(s, async_fns))
2836        }
2837        Stmt::FunctionDef { body, .. } => {
2838            body.iter().any(|s| calls_async_function(s, async_fns))
2839        }
2840        // Check Let statements for async function calls in the value expression
2841        Stmt::Let { value, .. } => calls_async_function_in_expr(value, async_fns),
2842        // Check Set statements for async function calls in the value expression
2843        Stmt::Set { value, .. } => calls_async_function_in_expr(value, async_fns),
2844        // Check Return statements for async function calls in the return value
2845        Stmt::Return { value } => {
2846            value.as_ref().map_or(false, |v| calls_async_function_in_expr(v, async_fns))
2847        }
2848        // Check RuntimeAssert condition for async calls
2849        Stmt::RuntimeAssert { condition } => calls_async_function_in_expr(condition, async_fns),
2850        // Check Show for async calls
2851        Stmt::Show { object, .. } => calls_async_function_in_expr(object, async_fns),
2852        // Check Push for async calls
2853        Stmt::Push { collection, value } => {
2854            calls_async_function_in_expr(collection, async_fns)
2855                || calls_async_function_in_expr(value, async_fns)
2856        }
2857        // Check SetIndex for async calls
2858        Stmt::SetIndex { collection, index, value } => {
2859            calls_async_function_in_expr(collection, async_fns)
2860                || calls_async_function_in_expr(index, async_fns)
2861                || calls_async_function_in_expr(value, async_fns)
2862        }
2863        // Check SendPipe for async calls
2864        Stmt::SendPipe { value, pipe } | Stmt::TrySendPipe { value, pipe, .. } => {
2865            calls_async_function_in_expr(value, async_fns)
2866                || calls_async_function_in_expr(pipe, async_fns)
2867        }
2868        // Check Inspect arms for async function calls
2869        Stmt::Inspect { target, arms, .. } => {
2870            calls_async_function_in_expr(target, async_fns)
2871                || arms.iter().any(|arm| arm.body.iter().any(|s| calls_async_function(s, async_fns)))
2872        }
2873        _ => false,
2874    }
2875}
2876
2877/// Helper: Check if an expression calls any function in the async_fns set
2878fn calls_async_function_in_expr(expr: &Expr, async_fns: &HashSet<Symbol>) -> bool {
2879    match expr {
2880        Expr::Call { function, args } => {
2881            async_fns.contains(function)
2882                || args.iter().any(|a| calls_async_function_in_expr(a, async_fns))
2883        }
2884        Expr::BinaryOp { left, right, .. } => {
2885            calls_async_function_in_expr(left, async_fns)
2886                || calls_async_function_in_expr(right, async_fns)
2887        }
2888        Expr::Index { collection, index } => {
2889            calls_async_function_in_expr(collection, async_fns)
2890                || calls_async_function_in_expr(index, async_fns)
2891        }
2892        Expr::FieldAccess { object, .. } => calls_async_function_in_expr(object, async_fns),
2893        Expr::List(items) | Expr::Tuple(items) => {
2894            items.iter().any(|i| calls_async_function_in_expr(i, async_fns))
2895        }
2896        Expr::Closure { body, .. } => {
2897            match body {
2898                crate::ast::stmt::ClosureBody::Expression(expr) => calls_async_function_in_expr(expr, async_fns),
2899                crate::ast::stmt::ClosureBody::Block(_) => false,
2900            }
2901        }
2902        Expr::CallExpr { callee, args } => {
2903            calls_async_function_in_expr(callee, async_fns)
2904                || args.iter().any(|a| calls_async_function_in_expr(a, async_fns))
2905        }
2906        _ => false,
2907    }
2908}
2909
2910// =============================================================================
2911// Purity Analysis
2912// =============================================================================
2913
2914fn collect_pure_functions(stmts: &[Stmt]) -> HashSet<Symbol> {
2915    let mut func_bodies: HashMap<Symbol, &[Stmt]> = HashMap::new();
2916    for stmt in stmts {
2917        if let Stmt::FunctionDef { name, body, .. } = stmt {
2918            func_bodies.insert(*name, *body);
2919        }
2920    }
2921
2922    // Pass 1: Mark functions as impure if they directly contain impure statements
2923    let mut impure_fns = HashSet::new();
2924    for (func_name, body) in &func_bodies {
2925        if body.iter().any(|s| is_directly_impure_stmt(s)) {
2926            impure_fns.insert(*func_name);
2927        }
2928    }
2929
2930    // Pass 2: Propagate impurity through call graph until fixed point
2931    loop {
2932        let mut changed = false;
2933        for (func_name, body) in &func_bodies {
2934            if impure_fns.contains(func_name) {
2935                continue;
2936            }
2937            if body.iter().any(|s| calls_impure_function(s, &impure_fns)) {
2938                impure_fns.insert(*func_name);
2939                changed = true;
2940            }
2941        }
2942        if !changed {
2943            break;
2944        }
2945    }
2946
2947    // Pure = all functions NOT in impure set
2948    let mut pure_fns = HashSet::new();
2949    for func_name in func_bodies.keys() {
2950        if !impure_fns.contains(func_name) {
2951            pure_fns.insert(*func_name);
2952        }
2953    }
2954    pure_fns
2955}
2956
2957fn is_directly_impure_stmt(stmt: &Stmt) -> bool {
2958    match stmt {
2959        Stmt::Show { .. }
2960        | Stmt::Give { .. }
2961        | Stmt::WriteFile { .. }
2962        | Stmt::ReadFrom { .. }
2963        | Stmt::Listen { .. }
2964        | Stmt::ConnectTo { .. }
2965        | Stmt::SendMessage { .. }
2966        | Stmt::AwaitMessage { .. }
2967        | Stmt::Sleep { .. }
2968        | Stmt::Sync { .. }
2969        | Stmt::Mount { .. }
2970        | Stmt::MergeCrdt { .. }
2971        | Stmt::IncreaseCrdt { .. }
2972        | Stmt::DecreaseCrdt { .. }
2973        | Stmt::AppendToSequence { .. }
2974        | Stmt::ResolveConflict { .. }
2975        | Stmt::CreatePipe { .. }
2976        | Stmt::SendPipe { .. }
2977        | Stmt::ReceivePipe { .. }
2978        | Stmt::TrySendPipe { .. }
2979        | Stmt::TryReceivePipe { .. }
2980        | Stmt::LaunchTask { .. }
2981        | Stmt::LaunchTaskWithHandle { .. }
2982        | Stmt::StopTask { .. }
2983        | Stmt::Concurrent { .. }
2984        | Stmt::Parallel { .. } => true,
2985        Stmt::If { then_block, else_block, .. } => {
2986            then_block.iter().any(|s| is_directly_impure_stmt(s))
2987                || else_block.map_or(false, |b| b.iter().any(|s| is_directly_impure_stmt(s)))
2988        }
2989        Stmt::While { body, .. } | Stmt::Repeat { body, .. } => {
2990            body.iter().any(|s| is_directly_impure_stmt(s))
2991        }
2992        Stmt::Zone { body, .. } => {
2993            body.iter().any(|s| is_directly_impure_stmt(s))
2994        }
2995        Stmt::Inspect { arms, .. } => {
2996            arms.iter().any(|arm| arm.body.iter().any(|s| is_directly_impure_stmt(s)))
2997        }
2998        _ => false,
2999    }
3000}
3001
3002fn calls_impure_function(stmt: &Stmt, impure_fns: &HashSet<Symbol>) -> bool {
3003    match stmt {
3004        Stmt::Call { function, args } => {
3005            impure_fns.contains(function)
3006                || args.iter().any(|a| expr_calls_impure(a, impure_fns))
3007        }
3008        Stmt::Let { value, .. } => expr_calls_impure(value, impure_fns),
3009        Stmt::Set { value, .. } => expr_calls_impure(value, impure_fns),
3010        Stmt::Return { value } => value.as_ref().map_or(false, |v| expr_calls_impure(v, impure_fns)),
3011        Stmt::If { cond, then_block, else_block } => {
3012            expr_calls_impure(cond, impure_fns)
3013                || then_block.iter().any(|s| calls_impure_function(s, impure_fns))
3014                || else_block.map_or(false, |b| b.iter().any(|s| calls_impure_function(s, impure_fns)))
3015        }
3016        Stmt::While { cond, body, .. } => {
3017            expr_calls_impure(cond, impure_fns)
3018                || body.iter().any(|s| calls_impure_function(s, impure_fns))
3019        }
3020        Stmt::Repeat { body, .. } => body.iter().any(|s| calls_impure_function(s, impure_fns)),
3021        Stmt::Zone { body, .. } => body.iter().any(|s| calls_impure_function(s, impure_fns)),
3022        Stmt::Inspect { arms, .. } => {
3023            arms.iter().any(|arm| arm.body.iter().any(|s| calls_impure_function(s, impure_fns)))
3024        }
3025        Stmt::Show { object, .. } => expr_calls_impure(object, impure_fns),
3026        Stmt::Push { value, collection } | Stmt::Add { value, collection } | Stmt::Remove { value, collection } => {
3027            expr_calls_impure(value, impure_fns) || expr_calls_impure(collection, impure_fns)
3028        }
3029        _ => false,
3030    }
3031}
3032
3033fn expr_calls_impure(expr: &Expr, impure_fns: &HashSet<Symbol>) -> bool {
3034    match expr {
3035        Expr::Call { function, args } => {
3036            impure_fns.contains(function)
3037                || args.iter().any(|a| expr_calls_impure(a, impure_fns))
3038        }
3039        Expr::BinaryOp { left, right, .. } => {
3040            expr_calls_impure(left, impure_fns) || expr_calls_impure(right, impure_fns)
3041        }
3042        Expr::Index { collection, index } => {
3043            expr_calls_impure(collection, impure_fns) || expr_calls_impure(index, impure_fns)
3044        }
3045        Expr::FieldAccess { object, .. } => expr_calls_impure(object, impure_fns),
3046        Expr::List(items) | Expr::Tuple(items) => items.iter().any(|i| expr_calls_impure(i, impure_fns)),
3047        Expr::CallExpr { callee, args } => {
3048            expr_calls_impure(callee, impure_fns)
3049                || args.iter().any(|a| expr_calls_impure(a, impure_fns))
3050        }
3051        _ => false,
3052    }
3053}
3054
3055// =============================================================================
3056// Memoization Detection
3057// =============================================================================
3058
3059fn count_self_calls(func_name: Symbol, body: &[Stmt]) -> usize {
3060    let mut count = 0;
3061    for stmt in body {
3062        count += count_self_calls_in_stmt(func_name, stmt);
3063    }
3064    count
3065}
3066
3067fn count_self_calls_in_stmt(func_name: Symbol, stmt: &Stmt) -> usize {
3068    match stmt {
3069        Stmt::Return { value: Some(expr) } => count_self_calls_in_expr(func_name, expr),
3070        Stmt::Let { value, .. } => count_self_calls_in_expr(func_name, value),
3071        Stmt::Set { value, .. } => count_self_calls_in_expr(func_name, value),
3072        Stmt::Call { function, args } => {
3073            let mut c = if *function == func_name { 1 } else { 0 };
3074            c += args.iter().map(|a| count_self_calls_in_expr(func_name, a)).sum::<usize>();
3075            c
3076        }
3077        Stmt::If { cond, then_block, else_block } => {
3078            let mut c = count_self_calls_in_expr(func_name, cond);
3079            c += count_self_calls(func_name, then_block);
3080            if let Some(else_stmts) = else_block {
3081                c += count_self_calls(func_name, else_stmts);
3082            }
3083            c
3084        }
3085        Stmt::While { cond, body, .. } => {
3086            count_self_calls_in_expr(func_name, cond) + count_self_calls(func_name, body)
3087        }
3088        Stmt::Repeat { body, .. } => count_self_calls(func_name, body),
3089        Stmt::Show { object, .. } => count_self_calls_in_expr(func_name, object),
3090        _ => 0,
3091    }
3092}
3093
3094fn count_self_calls_in_expr(func_name: Symbol, expr: &Expr) -> usize {
3095    match expr {
3096        Expr::Call { function, args } => {
3097            let mut c = if *function == func_name { 1 } else { 0 };
3098            c += args.iter().map(|a| count_self_calls_in_expr(func_name, a)).sum::<usize>();
3099            c
3100        }
3101        Expr::BinaryOp { left, right, .. } => {
3102            count_self_calls_in_expr(func_name, left) + count_self_calls_in_expr(func_name, right)
3103        }
3104        Expr::Index { collection, index } => {
3105            count_self_calls_in_expr(func_name, collection) + count_self_calls_in_expr(func_name, index)
3106        }
3107        Expr::FieldAccess { object, .. } => count_self_calls_in_expr(func_name, object),
3108        Expr::List(items) | Expr::Tuple(items) => {
3109            items.iter().map(|i| count_self_calls_in_expr(func_name, i)).sum()
3110        }
3111        _ => 0,
3112    }
3113}
3114
3115fn is_hashable_type(ty: &TypeExpr, interner: &Interner) -> bool {
3116    match ty {
3117        TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
3118            let name = interner.resolve(*sym);
3119            matches!(name, "Int" | "Nat" | "Bool" | "Char" | "Byte" | "Text"
3120                | "i64" | "u64" | "bool" | "char" | "u8" | "String")
3121        }
3122        TypeExpr::Refinement { base, .. } => is_hashable_type(base, interner),
3123        _ => false,
3124    }
3125}
3126
3127fn is_copy_type_expr(ty: &TypeExpr, interner: &Interner) -> bool {
3128    match ty {
3129        TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
3130            let name = interner.resolve(*sym);
3131            matches!(name, "Int" | "Nat" | "Bool" | "Char" | "Byte"
3132                | "i64" | "u64" | "bool" | "char" | "u8")
3133        }
3134        TypeExpr::Refinement { base, .. } => is_copy_type_expr(base, interner),
3135        _ => false,
3136    }
3137}
3138
3139fn should_memoize(
3140    name: Symbol,
3141    body: &[Stmt],
3142    params: &[(Symbol, &TypeExpr)],
3143    return_type: Option<&TypeExpr>,
3144    is_pure: bool,
3145    interner: &Interner,
3146) -> bool {
3147    if !is_pure {
3148        return false;
3149    }
3150    if !body_contains_self_call(name, body) {
3151        return false;
3152    }
3153    if count_self_calls(name, body) < 2 {
3154        return false;
3155    }
3156    if params.is_empty() {
3157        return false;
3158    }
3159    if !params.iter().all(|(_, ty)| is_hashable_type(ty, interner)) {
3160        return false;
3161    }
3162    if return_type.is_none() {
3163        return false;
3164    }
3165    true
3166}
3167
3168// =============================================================================
3169// Tail Call Elimination (TCE) Detection
3170// =============================================================================
3171
3172fn expr_is_self_call(func_name: Symbol, expr: &Expr) -> bool {
3173    matches!(expr, Expr::Call { function, .. } if *function == func_name)
3174}
3175
3176fn has_tail_call_in_stmt(func_name: Symbol, stmt: &Stmt) -> bool {
3177    match stmt {
3178        Stmt::Return { value: Some(expr) } => {
3179            if expr_is_self_call(func_name, expr) {
3180                return true;
3181            }
3182            // Check for nested self-call pattern: f(a, f(b, c))
3183            // The outer call is in tail position even if an arg is also a self-call
3184            if let Expr::Call { function, args } = expr {
3185                if *function == func_name {
3186                    return true;
3187                }
3188                // The outer is a self-call with a nested self-call arg — still tail position
3189                let _ = args;
3190            }
3191            false
3192        }
3193        Stmt::If { then_block, else_block, .. } => {
3194            let then_tail = then_block.last()
3195                .map_or(false, |s| has_tail_call_in_stmt(func_name, s));
3196            let else_tail = else_block
3197                .and_then(|block| block.last())
3198                .map_or(false, |s| has_tail_call_in_stmt(func_name, s));
3199            then_tail || else_tail
3200        }
3201        _ => false,
3202    }
3203}
3204
3205fn is_tail_recursive(func_name: Symbol, body: &[Stmt]) -> bool {
3206    body.iter().any(|s| has_tail_call_in_stmt(func_name, s))
3207}
3208
3209fn body_contains_self_call(func_name: Symbol, body: &[Stmt]) -> bool {
3210    body.iter().any(|s| stmt_contains_self_call(func_name, s))
3211}
3212
3213fn stmt_contains_self_call(func_name: Symbol, stmt: &Stmt) -> bool {
3214    match stmt {
3215        Stmt::Return { value: Some(expr) } => expr_contains_self_call(func_name, expr),
3216        Stmt::Return { value: None } => false,
3217        Stmt::Let { value, .. } => expr_contains_self_call(func_name, value),
3218        Stmt::Set { value, .. } => expr_contains_self_call(func_name, value),
3219        Stmt::Call { function, args } => {
3220            *function == func_name || args.iter().any(|a| expr_contains_self_call(func_name, a))
3221        }
3222        Stmt::If { cond, then_block, else_block } => {
3223            expr_contains_self_call(func_name, cond)
3224                || then_block.iter().any(|s| stmt_contains_self_call(func_name, s))
3225                || else_block.map_or(false, |b| b.iter().any(|s| stmt_contains_self_call(func_name, s)))
3226        }
3227        Stmt::While { cond, body, .. } => {
3228            expr_contains_self_call(func_name, cond)
3229                || body.iter().any(|s| stmt_contains_self_call(func_name, s))
3230        }
3231        Stmt::Repeat { body, .. } => {
3232            body.iter().any(|s| stmt_contains_self_call(func_name, s))
3233        }
3234        Stmt::Show { object, .. } => expr_contains_self_call(func_name, object),
3235        _ => false,
3236    }
3237}
3238
3239fn expr_contains_self_call(func_name: Symbol, expr: &Expr) -> bool {
3240    match expr {
3241        Expr::Call { function, args } => {
3242            *function == func_name || args.iter().any(|a| expr_contains_self_call(func_name, a))
3243        }
3244        Expr::BinaryOp { left, right, .. } => {
3245            expr_contains_self_call(func_name, left) || expr_contains_self_call(func_name, right)
3246        }
3247        Expr::Index { collection, index } => {
3248            expr_contains_self_call(func_name, collection) || expr_contains_self_call(func_name, index)
3249        }
3250        Expr::FieldAccess { object, .. } => expr_contains_self_call(func_name, object),
3251        Expr::List(items) | Expr::Tuple(items) => {
3252            items.iter().any(|i| expr_contains_self_call(func_name, i))
3253        }
3254        _ => false,
3255    }
3256}
3257
3258// =============================================================================
3259// Inline Annotation Detection
3260// =============================================================================
3261
3262fn should_inline(name: Symbol, body: &[Stmt], is_native: bool, is_exported: bool, is_async: bool) -> bool {
3263    !is_native && !is_exported && !is_async
3264        && body.len() <= 5
3265        && !body_contains_self_call(name, body)
3266}
3267
3268// =============================================================================
3269// Accumulator Introduction — Detection
3270// =============================================================================
3271
3272#[derive(Debug)]
3273enum NonRecSide { Left, Right }
3274
3275#[derive(Debug)]
3276struct AccumulatorInfo {
3277    op: BinaryOpKind,
3278    identity: &'static str,
3279    non_recursive_side: NonRecSide,
3280}
3281
3282fn detect_accumulator_pattern(func_name: Symbol, body: &[Stmt]) -> Option<AccumulatorInfo> {
3283    if has_non_return_self_calls(func_name, body) {
3284        return None;
3285    }
3286    let (base_count, recursive_count) = count_recursive_returns(func_name, body);
3287    if recursive_count != 1 {
3288        return None;
3289    }
3290    if base_count == 0 {
3291        return None;
3292    }
3293    find_accumulator_return(func_name, body)
3294}
3295
3296fn count_recursive_returns(func_name: Symbol, body: &[Stmt]) -> (usize, usize) {
3297    let mut base = 0;
3298    let mut recursive = 0;
3299    for stmt in body {
3300        match stmt {
3301            Stmt::Return { value: Some(expr) } => {
3302                if expr_contains_self_call(func_name, expr) {
3303                    recursive += 1;
3304                } else {
3305                    base += 1;
3306                }
3307            }
3308            Stmt::Return { value: None } => {
3309                base += 1;
3310            }
3311            Stmt::If { then_block, else_block, .. } => {
3312                let (tb, tr) = count_recursive_returns(func_name, then_block);
3313                base += tb;
3314                recursive += tr;
3315                if let Some(else_stmts) = else_block {
3316                    let (eb, er) = count_recursive_returns(func_name, else_stmts);
3317                    base += eb;
3318                    recursive += er;
3319                }
3320            }
3321            _ => {}
3322        }
3323    }
3324    (base, recursive)
3325}
3326
3327fn has_non_return_self_calls(func_name: Symbol, body: &[Stmt]) -> bool {
3328    for stmt in body {
3329        match stmt {
3330            Stmt::Return { .. } => {}
3331            Stmt::If { cond, then_block, else_block } => {
3332                if expr_contains_self_call(func_name, cond) {
3333                    return true;
3334                }
3335                if has_non_return_self_calls(func_name, then_block) {
3336                    return true;
3337                }
3338                if let Some(else_stmts) = else_block {
3339                    if has_non_return_self_calls(func_name, else_stmts) {
3340                        return true;
3341                    }
3342                }
3343            }
3344            Stmt::Let { value, .. } => {
3345                if expr_contains_self_call(func_name, value) {
3346                    return true;
3347                }
3348            }
3349            Stmt::Set { value, .. } => {
3350                if expr_contains_self_call(func_name, value) {
3351                    return true;
3352                }
3353            }
3354            Stmt::Show { object, .. } => {
3355                if expr_contains_self_call(func_name, object) {
3356                    return true;
3357                }
3358            }
3359            Stmt::While { cond, body, .. } => {
3360                if expr_contains_self_call(func_name, cond) {
3361                    return true;
3362                }
3363                if has_non_return_self_calls(func_name, body) {
3364                    return true;
3365                }
3366            }
3367            Stmt::Repeat { body, .. } => {
3368                if has_non_return_self_calls(func_name, body) {
3369                    return true;
3370                }
3371            }
3372            Stmt::Call { function, args } => {
3373                if *function == func_name || args.iter().any(|a| expr_contains_self_call(func_name, a)) {
3374                    return true;
3375                }
3376            }
3377            _ => {}
3378        }
3379    }
3380    false
3381}
3382
3383fn find_accumulator_return(func_name: Symbol, body: &[Stmt]) -> Option<AccumulatorInfo> {
3384    for stmt in body {
3385        match stmt {
3386            Stmt::Return { value: Some(expr) } => {
3387                if let Expr::BinaryOp { op, left, right } = expr {
3388                    match op {
3389                        BinaryOpKind::Add | BinaryOpKind::Multiply => {
3390                            let left_has_call = expr_is_self_call(func_name, left);
3391                            let right_has_call = expr_is_self_call(func_name, right);
3392                            let left_contains_call = expr_contains_self_call(func_name, left);
3393                            let right_contains_call = expr_contains_self_call(func_name, right);
3394                            let identity = match op {
3395                                BinaryOpKind::Add => "0",
3396                                BinaryOpKind::Multiply => "1",
3397                                _ => unreachable!(),
3398                            };
3399                            if left_has_call && !right_contains_call {
3400                                return Some(AccumulatorInfo {
3401                                    op: *op,
3402                                    identity,
3403                                    non_recursive_side: NonRecSide::Right,
3404                                });
3405                            }
3406                            if right_has_call && !left_contains_call {
3407                                return Some(AccumulatorInfo {
3408                                    op: *op,
3409                                    identity,
3410                                    non_recursive_side: NonRecSide::Left,
3411                                });
3412                            }
3413                        }
3414                        _ => {}
3415                    }
3416                }
3417            }
3418            Stmt::If { then_block, else_block, .. } => {
3419                if let Some(info) = find_accumulator_return(func_name, then_block) {
3420                    return Some(info);
3421                }
3422                if let Some(else_stmts) = else_block {
3423                    if let Some(info) = find_accumulator_return(func_name, else_stmts) {
3424                        return Some(info);
3425                    }
3426                }
3427            }
3428            _ => {}
3429        }
3430    }
3431    None
3432}
3433
3434// =============================================================================
3435// Accumulator Introduction — Statement Emitter
3436// =============================================================================
3437
3438fn codegen_stmt_acc<'a>(
3439    stmt: &Stmt<'a>,
3440    func_name: Symbol,
3441    param_names: &[Symbol],
3442    acc_info: &AccumulatorInfo,
3443    interner: &Interner,
3444    indent: usize,
3445    mutable_vars: &HashSet<Symbol>,
3446    ctx: &mut RefinementContext<'a>,
3447    lww_fields: &HashSet<(String, String)>,
3448    mv_fields: &HashSet<(String, String)>,
3449    synced_vars: &mut HashSet<Symbol>,
3450    var_caps: &HashMap<Symbol, VariableCapabilities>,
3451    async_functions: &HashSet<Symbol>,
3452    pipe_vars: &HashSet<Symbol>,
3453    boxed_fields: &HashSet<(String, String, String)>,
3454    registry: &TypeRegistry,
3455) -> String {
3456    let indent_str = "    ".repeat(indent);
3457    let op_str = match acc_info.op {
3458        BinaryOpKind::Add => "+",
3459        BinaryOpKind::Multiply => "*",
3460        _ => unreachable!(),
3461    };
3462
3463    match stmt {
3464        // Recursive return: BinaryOp(op, self_call, non_rec) or swapped
3465        Stmt::Return { value: Some(expr) } if expr_contains_self_call(func_name, expr) => {
3466            if let Expr::BinaryOp { left, right, .. } = expr {
3467                let (call_expr, non_rec_expr) = match acc_info.non_recursive_side {
3468                    NonRecSide::Left => (right, left),
3469                    NonRecSide::Right => (left, right),
3470                };
3471                // Extract args from the self-call
3472                if let Expr::Call { args, .. } = call_expr {
3473                    let mut output = String::new();
3474                    writeln!(output, "{}{{", indent_str).unwrap();
3475                    let non_rec_str = codegen_expr_with_async(non_rec_expr, interner, synced_vars, async_functions, ctx.get_variable_types());
3476                    writeln!(output, "{}    let __acc_expr = {};", indent_str, non_rec_str).unwrap();
3477                    writeln!(output, "{}    __acc = __acc {} __acc_expr;", indent_str, op_str).unwrap();
3478                    // Evaluate args into temporaries
3479                    for (i, arg) in args.iter().enumerate() {
3480                        let arg_str = codegen_expr_with_async(arg, interner, synced_vars, async_functions, ctx.get_variable_types());
3481                        writeln!(output, "{}    let __tce_{} = {};", indent_str, i, arg_str).unwrap();
3482                    }
3483                    // Assign temporaries to params
3484                    for (i, param_sym) in param_names.iter().enumerate() {
3485                        let param_name = interner.resolve(*param_sym);
3486                        writeln!(output, "{}    {} = __tce_{};", indent_str, param_name, i).unwrap();
3487                    }
3488                    writeln!(output, "{}    continue;", indent_str).unwrap();
3489                    writeln!(output, "{}}}", indent_str).unwrap();
3490                    return output;
3491                }
3492            }
3493            // Fallback
3494            codegen_stmt(stmt, interner, indent, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
3495        }
3496
3497        // Base return: no self-call
3498        Stmt::Return { value: Some(expr) } => {
3499            let val_str = codegen_expr_with_async(expr, interner, synced_vars, async_functions, ctx.get_variable_types());
3500            format!("{}return __acc {} {};\n", indent_str, op_str, val_str)
3501        }
3502
3503        Stmt::Return { value: None } => {
3504            format!("{}return __acc;\n", indent_str)
3505        }
3506
3507        // If: recurse into branches
3508        Stmt::If { cond, then_block, else_block } => {
3509            let cond_str = codegen_expr_with_async(cond, interner, synced_vars, async_functions, ctx.get_variable_types());
3510            let mut output = String::new();
3511            writeln!(output, "{}if {} {{", indent_str, cond_str).unwrap();
3512            ctx.push_scope();
3513            for s in *then_block {
3514                output.push_str(&codegen_stmt_acc(s, func_name, param_names, acc_info, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
3515            }
3516            ctx.pop_scope();
3517            if let Some(else_stmts) = else_block {
3518                writeln!(output, "{}}} else {{", indent_str).unwrap();
3519                ctx.push_scope();
3520                for s in *else_stmts {
3521                    output.push_str(&codegen_stmt_acc(s, func_name, param_names, acc_info, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
3522                }
3523                ctx.pop_scope();
3524            }
3525            writeln!(output, "{}}}", indent_str).unwrap();
3526            output
3527        }
3528
3529        // Everything else: delegate
3530        _ => codegen_stmt(stmt, interner, indent, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry),
3531    }
3532}
3533
3534// =============================================================================
3535// Mutual Tail Call Optimization — Detection
3536// =============================================================================
3537
3538fn find_tail_call_targets(func_name: Symbol, body: &[Stmt]) -> HashSet<Symbol> {
3539    let mut targets = HashSet::new();
3540    for stmt in body {
3541        collect_tail_targets(func_name, stmt, &mut targets);
3542    }
3543    targets
3544}
3545
3546fn collect_tail_targets(func_name: Symbol, stmt: &Stmt, targets: &mut HashSet<Symbol>) {
3547    match stmt {
3548        Stmt::Return { value: Some(Expr::Call { function, .. }) } => {
3549            if *function != func_name {
3550                targets.insert(*function);
3551            }
3552        }
3553        Stmt::If { then_block, else_block, .. } => {
3554            if let Some(last) = then_block.last() {
3555                collect_tail_targets(func_name, last, targets);
3556            }
3557            if let Some(else_stmts) = else_block {
3558                if let Some(last) = else_stmts.last() {
3559                    collect_tail_targets(func_name, last, targets);
3560                }
3561            }
3562        }
3563        _ => {}
3564    }
3565}
3566
3567fn detect_mutual_tce_pairs<'a>(stmts: &'a [Stmt<'a>], interner: &Interner) -> Vec<(Symbol, Symbol)> {
3568    // Collect function definitions
3569    let mut func_defs: HashMap<Symbol, (&[(Symbol, &TypeExpr)], &[Stmt], Option<&TypeExpr>, bool, bool, bool)> = HashMap::new();
3570    for stmt in stmts {
3571        if let Stmt::FunctionDef { name, params, body, return_type, is_native, is_exported, .. } = stmt {
3572            let is_async_fn = false; // Will be checked properly later
3573            func_defs.insert(*name, (params, body, return_type.as_ref().copied(), *is_native, *is_exported, is_async_fn));
3574        }
3575    }
3576
3577    // Build tail-call graph
3578    let mut tail_targets: HashMap<Symbol, HashSet<Symbol>> = HashMap::new();
3579    for (name, (_, body, _, _, _, _)) in &func_defs {
3580        tail_targets.insert(*name, find_tail_call_targets(*name, body));
3581    }
3582
3583    // Find mutually tail-calling pairs
3584    let mut pairs = Vec::new();
3585    let mut used = HashSet::new();
3586    let names: Vec<Symbol> = func_defs.keys().copied().collect();
3587
3588    for i in 0..names.len() {
3589        for j in (i + 1)..names.len() {
3590            let a = names[i];
3591            let b = names[j];
3592            if used.contains(&a) || used.contains(&b) {
3593                continue;
3594            }
3595
3596            let a_targets = tail_targets.get(&a).cloned().unwrap_or_default();
3597            let b_targets = tail_targets.get(&b).cloned().unwrap_or_default();
3598
3599            // Both must tail-call each other
3600            if !a_targets.contains(&b) || !b_targets.contains(&a) {
3601                continue;
3602            }
3603
3604            let (a_params, _, a_ret, a_native, a_exported, _) = func_defs[&a];
3605            let (b_params, _, b_ret, b_native, b_exported, _) = func_defs[&b];
3606
3607            // Neither can be native or exported
3608            if a_native || b_native || a_exported || b_exported {
3609                continue;
3610            }
3611
3612            // Same number of params
3613            if a_params.len() != b_params.len() {
3614                continue;
3615            }
3616
3617            // Same param types
3618            let same_params = a_params.iter().zip(b_params.iter()).all(|((_, t1), (_, t2))| {
3619                codegen_type_expr(t1, interner) == codegen_type_expr(t2, interner)
3620            });
3621            if !same_params {
3622                continue;
3623            }
3624
3625            // Same return type
3626            let a_ret_str = a_ret.map(|t| codegen_type_expr(t, interner));
3627            let b_ret_str = b_ret.map(|t| codegen_type_expr(t, interner));
3628            if a_ret_str != b_ret_str {
3629                continue;
3630            }
3631
3632            // Verify that the mutual calls are actually in tail position
3633            // (the targets above only collect Return { Call } patterns, so they are)
3634            pairs.push((a, b));
3635            used.insert(a);
3636            used.insert(b);
3637        }
3638    }
3639
3640    pairs
3641}
3642
3643// =============================================================================
3644// Mutual Tail Call Optimization — Code Generation
3645// =============================================================================
3646
3647fn codegen_mutual_tce_pair<'a>(
3648    func_a: Symbol,
3649    func_b: Symbol,
3650    stmts: &'a [Stmt<'a>],
3651    interner: &Interner,
3652    lww_fields: &HashSet<(String, String)>,
3653    mv_fields: &HashSet<(String, String)>,
3654    async_functions: &HashSet<Symbol>,
3655    boxed_fields: &HashSet<(String, String, String)>,
3656    registry: &TypeRegistry,
3657) -> String {
3658    // Extract function defs
3659    let mut a_def = None;
3660    let mut b_def = None;
3661    for stmt in stmts {
3662        if let Stmt::FunctionDef { name, params, body, return_type, .. } = stmt {
3663            if *name == func_a {
3664                a_def = Some((params.as_slice(), *body, return_type.as_ref().copied()));
3665            } else if *name == func_b {
3666                b_def = Some((params.as_slice(), *body, return_type.as_ref().copied()));
3667            }
3668        }
3669    }
3670    let (a_params, a_body, a_ret) = a_def.expect("mutual TCE: func_a not found");
3671    let (b_params, b_body, _b_ret) = b_def.expect("mutual TCE: func_b not found");
3672
3673    let a_name = escape_rust_ident(interner.resolve(func_a));
3674    let b_name = escape_rust_ident(interner.resolve(func_b));
3675    let merged_name = format!("__mutual_{}_{}", a_name, b_name);
3676
3677    // Build param list (using func_a's param names, since types match)
3678    let params_str: Vec<String> = a_params.iter()
3679        .map(|(p, t)| format!("mut {}: {}", interner.resolve(*p), codegen_type_expr(t, interner)))
3680        .collect();
3681
3682    let ret_str = a_ret.map(|t| codegen_type_expr(t, interner));
3683
3684    let mut output = String::new();
3685
3686    // Merged function
3687    let sig = if let Some(ref r) = ret_str {
3688        if r != "()" {
3689            format!("fn {}(mut __tag: u8, {}) -> {}", merged_name, params_str.join(", "), r)
3690        } else {
3691            format!("fn {}(mut __tag: u8, {})", merged_name, params_str.join(", "))
3692        }
3693    } else {
3694        format!("fn {}(mut __tag: u8, {})", merged_name, params_str.join(", "))
3695    };
3696
3697    writeln!(output, "{} {{", sig).unwrap();
3698    writeln!(output, "    loop {{").unwrap();
3699    writeln!(output, "        match __tag {{").unwrap();
3700
3701    // Tag 0: func_a body
3702    writeln!(output, "            0 => {{").unwrap();
3703    let a_mutable = collect_mutable_vars(a_body);
3704    let mut a_ctx = RefinementContext::new();
3705    let mut a_synced = HashSet::new();
3706    let a_caps = HashMap::new();
3707    let a_pipes = HashSet::new();
3708    let a_param_syms: Vec<Symbol> = a_params.iter().map(|(s, _)| *s).collect();
3709    for s in a_body {
3710        output.push_str(&codegen_stmt_mutual_tce(s, func_a, func_b, &a_param_syms, 0, 1, interner, 4, &a_mutable, &mut a_ctx, lww_fields, mv_fields, &mut a_synced, &a_caps, async_functions, &a_pipes, boxed_fields, registry));
3711    }
3712    writeln!(output, "            }}").unwrap();
3713
3714    // Tag 1: func_b body
3715    writeln!(output, "            1 => {{").unwrap();
3716    let b_mutable = collect_mutable_vars(b_body);
3717    let mut b_ctx = RefinementContext::new();
3718    let mut b_synced = HashSet::new();
3719    let b_caps = HashMap::new();
3720    let b_pipes = HashSet::new();
3721    let b_param_syms: Vec<Symbol> = b_params.iter().map(|(s, _)| *s).collect();
3722    // Map b's param names to a's param names for assignment
3723    for s in b_body {
3724        output.push_str(&codegen_stmt_mutual_tce(s, func_b, func_a, &b_param_syms, 1, 0, interner, 4, &b_mutable, &mut b_ctx, lww_fields, mv_fields, &mut b_synced, &b_caps, async_functions, &b_pipes, boxed_fields, registry));
3725    }
3726    writeln!(output, "            }}").unwrap();
3727
3728    writeln!(output, "            _ => unreachable!()").unwrap();
3729    writeln!(output, "        }}").unwrap();
3730    writeln!(output, "    }}").unwrap();
3731    writeln!(output, "}}\n").unwrap();
3732
3733    // Wrapper for func_a
3734    let wrapper_params_a: Vec<String> = a_params.iter()
3735        .map(|(p, t)| format!("{}: {}", interner.resolve(*p), codegen_type_expr(t, interner)))
3736        .collect();
3737    let wrapper_args_a: Vec<String> = a_params.iter()
3738        .map(|(p, _)| interner.resolve(*p).to_string())
3739        .collect();
3740    writeln!(output, "#[inline]").unwrap();
3741    if let Some(ref r) = ret_str {
3742        if r != "()" {
3743            writeln!(output, "fn {}({}) -> {} {{ {}(0, {}) }}\n", a_name, wrapper_params_a.join(", "), r, merged_name, wrapper_args_a.join(", ")).unwrap();
3744        } else {
3745            writeln!(output, "fn {}({}) {{ {}(0, {}) }}\n", a_name, wrapper_params_a.join(", "), merged_name, wrapper_args_a.join(", ")).unwrap();
3746        }
3747    } else {
3748        writeln!(output, "fn {}({}) {{ {}(0, {}) }}\n", a_name, wrapper_params_a.join(", "), merged_name, wrapper_args_a.join(", ")).unwrap();
3749    }
3750
3751    // Wrapper for func_b
3752    let wrapper_params_b: Vec<String> = b_params.iter()
3753        .map(|(p, t)| format!("{}: {}", interner.resolve(*p), codegen_type_expr(t, interner)))
3754        .collect();
3755    let wrapper_args_b: Vec<String> = b_params.iter()
3756        .map(|(p, _)| interner.resolve(*p).to_string())
3757        .collect();
3758    writeln!(output, "#[inline]").unwrap();
3759    if let Some(ref r) = ret_str {
3760        if r != "()" {
3761            writeln!(output, "fn {}({}) -> {} {{ {}(1, {}) }}\n", b_name, wrapper_params_b.join(", "), r, merged_name, wrapper_args_b.join(", ")).unwrap();
3762        } else {
3763            writeln!(output, "fn {}({}) {{ {}(1, {}) }}\n", b_name, wrapper_params_b.join(", "), merged_name, wrapper_args_b.join(", ")).unwrap();
3764        }
3765    } else {
3766        writeln!(output, "fn {}({}) {{ {}(1, {}) }}\n", b_name, wrapper_params_b.join(", "), merged_name, wrapper_args_b.join(", ")).unwrap();
3767    }
3768
3769    output
3770}
3771
3772fn codegen_stmt_mutual_tce<'a>(
3773    stmt: &Stmt<'a>,
3774    self_name: Symbol,
3775    partner_name: Symbol,
3776    param_names: &[Symbol],
3777    self_tag: u8,
3778    partner_tag: u8,
3779    interner: &Interner,
3780    indent: usize,
3781    mutable_vars: &HashSet<Symbol>,
3782    ctx: &mut RefinementContext<'a>,
3783    lww_fields: &HashSet<(String, String)>,
3784    mv_fields: &HashSet<(String, String)>,
3785    synced_vars: &mut HashSet<Symbol>,
3786    var_caps: &HashMap<Symbol, VariableCapabilities>,
3787    async_functions: &HashSet<Symbol>,
3788    pipe_vars: &HashSet<Symbol>,
3789    boxed_fields: &HashSet<(String, String, String)>,
3790    registry: &TypeRegistry,
3791) -> String {
3792    let indent_str = "    ".repeat(indent);
3793
3794    match stmt {
3795        // Return with a call to partner → switch tag + continue
3796        Stmt::Return { value: Some(expr) } if expr_is_call_to(partner_name, expr) => {
3797            if let Expr::Call { args, .. } = expr {
3798                let mut output = String::new();
3799                writeln!(output, "{}{{", indent_str).unwrap();
3800                for (i, arg) in args.iter().enumerate() {
3801                    let arg_str = codegen_expr_with_async(arg, interner, synced_vars, async_functions, ctx.get_variable_types());
3802                    writeln!(output, "{}    let __tce_{} = {};", indent_str, i, arg_str).unwrap();
3803                }
3804                for (i, param_sym) in param_names.iter().enumerate() {
3805                    let param_name = interner.resolve(*param_sym);
3806                    writeln!(output, "{}    {} = __tce_{};", indent_str, param_name, i).unwrap();
3807                }
3808                writeln!(output, "{}    __tag = {};", indent_str, partner_tag).unwrap();
3809                writeln!(output, "{}    continue;", indent_str).unwrap();
3810                writeln!(output, "{}}}", indent_str).unwrap();
3811                return output;
3812            }
3813            codegen_stmt(stmt, interner, indent, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
3814        }
3815
3816        // Return with a call to self → standard self-TCE
3817        Stmt::Return { value: Some(expr) } if expr_is_call_to(self_name, expr) => {
3818            if let Expr::Call { args, .. } = expr {
3819                let mut output = String::new();
3820                writeln!(output, "{}{{", indent_str).unwrap();
3821                for (i, arg) in args.iter().enumerate() {
3822                    let arg_str = codegen_expr_with_async(arg, interner, synced_vars, async_functions, ctx.get_variable_types());
3823                    writeln!(output, "{}    let __tce_{} = {};", indent_str, i, arg_str).unwrap();
3824                }
3825                for (i, param_sym) in param_names.iter().enumerate() {
3826                    let param_name = interner.resolve(*param_sym);
3827                    writeln!(output, "{}    {} = __tce_{};", indent_str, param_name, i).unwrap();
3828                }
3829                writeln!(output, "{}    continue;", indent_str).unwrap();
3830                writeln!(output, "{}}}", indent_str).unwrap();
3831                return output;
3832            }
3833            codegen_stmt(stmt, interner, indent, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
3834        }
3835
3836        // If: recurse into branches
3837        Stmt::If { cond, then_block, else_block } => {
3838            let cond_str = codegen_expr_with_async(cond, interner, synced_vars, async_functions, ctx.get_variable_types());
3839            let mut output = String::new();
3840            writeln!(output, "{}if {} {{", indent_str, cond_str).unwrap();
3841            ctx.push_scope();
3842            for s in *then_block {
3843                output.push_str(&codegen_stmt_mutual_tce(s, self_name, partner_name, param_names, self_tag, partner_tag, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
3844            }
3845            ctx.pop_scope();
3846            if let Some(else_stmts) = else_block {
3847                writeln!(output, "{}}} else {{", indent_str).unwrap();
3848                ctx.push_scope();
3849                for s in *else_stmts {
3850                    output.push_str(&codegen_stmt_mutual_tce(s, self_name, partner_name, param_names, self_tag, partner_tag, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
3851                }
3852                ctx.pop_scope();
3853            }
3854            writeln!(output, "{}}}", indent_str).unwrap();
3855            output
3856        }
3857
3858        // Everything else: delegate
3859        _ => codegen_stmt(stmt, interner, indent, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry),
3860    }
3861}
3862
3863fn expr_is_call_to(target: Symbol, expr: &Expr) -> bool {
3864    matches!(expr, Expr::Call { function, .. } if *function == target)
3865}
3866
3867// =============================================================================
3868// Tail Call Elimination (TCE) Statement Emitter
3869// =============================================================================
3870
3871fn codegen_stmt_tce<'a>(
3872    stmt: &Stmt<'a>,
3873    func_name: Symbol,
3874    param_names: &[Symbol],
3875    interner: &Interner,
3876    indent: usize,
3877    mutable_vars: &HashSet<Symbol>,
3878    ctx: &mut RefinementContext<'a>,
3879    lww_fields: &HashSet<(String, String)>,
3880    mv_fields: &HashSet<(String, String)>,
3881    synced_vars: &mut HashSet<Symbol>,
3882    var_caps: &HashMap<Symbol, VariableCapabilities>,
3883    async_functions: &HashSet<Symbol>,
3884    pipe_vars: &HashSet<Symbol>,
3885    boxed_fields: &HashSet<(String, String, String)>,
3886    registry: &TypeRegistry,
3887) -> String {
3888    let indent_str = "    ".repeat(indent);
3889
3890    match stmt {
3891        // Case 1 & 2: Return with a self-call in tail position
3892        Stmt::Return { value: Some(expr) } if expr_is_self_call(func_name, expr) => {
3893            if let Expr::Call { args, .. } = expr {
3894                let mut output = String::new();
3895                writeln!(output, "{}{{", indent_str).unwrap();
3896                // Evaluate all args into temporaries first (prevents ordering bugs)
3897                for (i, arg) in args.iter().enumerate() {
3898                    let arg_str = codegen_expr_with_async(arg, interner, synced_vars, async_functions, ctx.get_variable_types());
3899                    writeln!(output, "{}    let __tce_{} = {};", indent_str, i, arg_str).unwrap();
3900                }
3901                // Assign temporaries to params
3902                for (i, param_sym) in param_names.iter().enumerate() {
3903                    let param_name = interner.resolve(*param_sym);
3904                    writeln!(output, "{}    {} = __tce_{};", indent_str, param_name, i).unwrap();
3905                }
3906                writeln!(output, "{}    continue;", indent_str).unwrap();
3907                writeln!(output, "{}}}", indent_str).unwrap();
3908                return output;
3909            }
3910            // Shouldn't reach here, but fall through to default
3911            codegen_stmt(stmt, interner, indent, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
3912        }
3913
3914        // Case 2: Return with outer self-call that has a nested self-call arg (Ackermann pattern)
3915        Stmt::Return { value: Some(expr) } => {
3916            if let Expr::Call { function, args } = expr {
3917                if *function == func_name {
3918                    let mut output = String::new();
3919                    writeln!(output, "{}{{", indent_str).unwrap();
3920                    // Evaluate args — nested self-calls remain as normal recursion,
3921                    // but the outer call becomes a loop iteration
3922                    for (i, arg) in args.iter().enumerate() {
3923                        if expr_is_self_call(func_name, arg) {
3924                            // Inner self-call: evaluate as normal recursive call
3925                            let arg_str = codegen_expr_with_async(arg, interner, synced_vars, async_functions, ctx.get_variable_types());
3926                            writeln!(output, "{}    let __tce_{} = {};", indent_str, i, arg_str).unwrap();
3927                        } else {
3928                            let arg_str = codegen_expr_with_async(arg, interner, synced_vars, async_functions, ctx.get_variable_types());
3929                            writeln!(output, "{}    let __tce_{} = {};", indent_str, i, arg_str).unwrap();
3930                        }
3931                    }
3932                    // Assign temporaries to params
3933                    for (i, param_sym) in param_names.iter().enumerate() {
3934                        let param_name = interner.resolve(*param_sym);
3935                        writeln!(output, "{}    {} = __tce_{};", indent_str, param_name, i).unwrap();
3936                    }
3937                    writeln!(output, "{}    continue;", indent_str).unwrap();
3938                    writeln!(output, "{}}}", indent_str).unwrap();
3939                    return output;
3940                }
3941            }
3942            // Not a self-call — delegate to normal codegen
3943            codegen_stmt(stmt, interner, indent, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
3944        }
3945
3946        // Case 3: If statement — recurse into branches
3947        Stmt::If { cond, then_block, else_block } => {
3948            let cond_str = codegen_expr_with_async(cond, interner, synced_vars, async_functions, ctx.get_variable_types());
3949            let mut output = String::new();
3950            writeln!(output, "{}if {} {{", indent_str, cond_str).unwrap();
3951            ctx.push_scope();
3952            for s in *then_block {
3953                output.push_str(&codegen_stmt_tce(s, func_name, param_names, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
3954            }
3955            ctx.pop_scope();
3956            if let Some(else_stmts) = else_block {
3957                writeln!(output, "{}}} else {{", indent_str).unwrap();
3958                ctx.push_scope();
3959                for s in *else_stmts {
3960                    output.push_str(&codegen_stmt_tce(s, func_name, param_names, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
3961                }
3962                ctx.pop_scope();
3963            }
3964            writeln!(output, "{}}}", indent_str).unwrap();
3965            output
3966        }
3967
3968        // Case 4: Everything else — delegate
3969        _ => codegen_stmt(stmt, interner, indent, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry),
3970    }
3971}
3972
3973/// Phase 54: Collect parameters that are used as pipe senders in function body.
3974/// If a param appears in `SendPipe { pipe: Expr::Identifier(param) }`, it's a sender.
3975pub fn collect_pipe_sender_params(body: &[Stmt]) -> HashSet<Symbol> {
3976    let mut senders = HashSet::new();
3977    for stmt in body {
3978        collect_pipe_sender_params_stmt(stmt, &mut senders);
3979    }
3980    senders
3981}
3982
3983fn collect_pipe_sender_params_stmt(stmt: &Stmt, senders: &mut HashSet<Symbol>) {
3984    match stmt {
3985        Stmt::SendPipe { pipe, .. } | Stmt::TrySendPipe { pipe, .. } => {
3986            if let Expr::Identifier(sym) = pipe {
3987                senders.insert(*sym);
3988            }
3989        }
3990        Stmt::If { then_block, else_block, .. } => {
3991            for s in *then_block {
3992                collect_pipe_sender_params_stmt(s, senders);
3993            }
3994            if let Some(else_stmts) = else_block {
3995                for s in *else_stmts {
3996                    collect_pipe_sender_params_stmt(s, senders);
3997                }
3998            }
3999        }
4000        Stmt::While { body, .. } | Stmt::Repeat { body, .. } | Stmt::Zone { body, .. } => {
4001            for s in *body {
4002                collect_pipe_sender_params_stmt(s, senders);
4003            }
4004        }
4005        Stmt::Concurrent { tasks } | Stmt::Parallel { tasks } => {
4006            for s in *tasks {
4007                collect_pipe_sender_params_stmt(s, senders);
4008            }
4009        }
4010        _ => {}
4011    }
4012}
4013
4014/// Phase 54: Collect variables that are pipe declarations (created with CreatePipe).
4015/// These have _tx/_rx suffixes, while pipe parameters don't.
4016pub fn collect_pipe_vars(stmts: &[Stmt]) -> HashSet<Symbol> {
4017    let mut pipe_vars = HashSet::new();
4018    for stmt in stmts {
4019        collect_pipe_vars_stmt(stmt, &mut pipe_vars);
4020    }
4021    pipe_vars
4022}
4023
4024fn collect_pipe_vars_stmt(stmt: &Stmt, pipe_vars: &mut HashSet<Symbol>) {
4025    match stmt {
4026        Stmt::CreatePipe { var, .. } => {
4027            pipe_vars.insert(*var);
4028        }
4029        Stmt::If { then_block, else_block, .. } => {
4030            for s in *then_block {
4031                collect_pipe_vars_stmt(s, pipe_vars);
4032            }
4033            if let Some(else_stmts) = else_block {
4034                for s in *else_stmts {
4035                    collect_pipe_vars_stmt(s, pipe_vars);
4036                }
4037            }
4038        }
4039        Stmt::While { body, .. } | Stmt::Repeat { body, .. } | Stmt::Zone { body, .. } => {
4040            for s in *body {
4041                collect_pipe_vars_stmt(s, pipe_vars);
4042            }
4043        }
4044        Stmt::Concurrent { tasks } | Stmt::Parallel { tasks } => {
4045            for s in *tasks {
4046                collect_pipe_vars_stmt(s, pipe_vars);
4047            }
4048        }
4049        _ => {}
4050    }
4051}
4052
4053/// Collect all identifier symbols from an expression recursively.
4054/// Used by Concurrent/Parallel codegen to find variables that need cloning.
4055fn collect_expr_identifiers(expr: &Expr, identifiers: &mut HashSet<Symbol>) {
4056    match expr {
4057        Expr::Identifier(sym) => {
4058            identifiers.insert(*sym);
4059        }
4060        Expr::BinaryOp { left, right, .. } => {
4061            collect_expr_identifiers(left, identifiers);
4062            collect_expr_identifiers(right, identifiers);
4063        }
4064        Expr::Call { args, .. } => {
4065            for arg in args {
4066                collect_expr_identifiers(arg, identifiers);
4067            }
4068        }
4069        Expr::Index { collection, index } => {
4070            collect_expr_identifiers(collection, identifiers);
4071            collect_expr_identifiers(index, identifiers);
4072        }
4073        Expr::Slice { collection, start, end } => {
4074            collect_expr_identifiers(collection, identifiers);
4075            collect_expr_identifiers(start, identifiers);
4076            collect_expr_identifiers(end, identifiers);
4077        }
4078        Expr::Copy { expr: inner } | Expr::Give { value: inner } | Expr::Length { collection: inner } => {
4079            collect_expr_identifiers(inner, identifiers);
4080        }
4081        Expr::Contains { collection, value } | Expr::Union { left: collection, right: value } | Expr::Intersection { left: collection, right: value } => {
4082            collect_expr_identifiers(collection, identifiers);
4083            collect_expr_identifiers(value, identifiers);
4084        }
4085        Expr::ManifestOf { zone } | Expr::ChunkAt { zone, .. } => {
4086            collect_expr_identifiers(zone, identifiers);
4087        }
4088        Expr::List(items) | Expr::Tuple(items) => {
4089            for item in items {
4090                collect_expr_identifiers(item, identifiers);
4091            }
4092        }
4093        Expr::Range { start, end } => {
4094            collect_expr_identifiers(start, identifiers);
4095            collect_expr_identifiers(end, identifiers);
4096        }
4097        Expr::FieldAccess { object, .. } => {
4098            collect_expr_identifiers(object, identifiers);
4099        }
4100        Expr::New { init_fields, .. } => {
4101            for (_, value) in init_fields {
4102                collect_expr_identifiers(value, identifiers);
4103            }
4104        }
4105        Expr::NewVariant { fields, .. } => {
4106            for (_, value) in fields {
4107                collect_expr_identifiers(value, identifiers);
4108            }
4109        }
4110        Expr::OptionSome { value } => {
4111            collect_expr_identifiers(value, identifiers);
4112        }
4113        Expr::WithCapacity { value, capacity } => {
4114            collect_expr_identifiers(value, identifiers);
4115            collect_expr_identifiers(capacity, identifiers);
4116        }
4117        Expr::Closure { body, .. } => {
4118            match body {
4119                crate::ast::stmt::ClosureBody::Expression(expr) => collect_expr_identifiers(expr, identifiers),
4120                crate::ast::stmt::ClosureBody::Block(_) => {}
4121            }
4122        }
4123        Expr::CallExpr { callee, args } => {
4124            collect_expr_identifiers(callee, identifiers);
4125            for arg in args {
4126                collect_expr_identifiers(arg, identifiers);
4127            }
4128        }
4129        Expr::OptionNone => {}
4130        Expr::Escape { .. } => {}
4131        Expr::Literal(_) => {}
4132    }
4133}
4134
4135/// Collect identifiers from a statement's expressions (for Concurrent/Parallel variable capture).
4136fn collect_stmt_identifiers(stmt: &Stmt, identifiers: &mut HashSet<Symbol>) {
4137    match stmt {
4138        Stmt::Let { value, .. } => {
4139            collect_expr_identifiers(value, identifiers);
4140        }
4141        Stmt::Call { args, .. } => {
4142            for arg in args {
4143                collect_expr_identifiers(arg, identifiers);
4144            }
4145        }
4146        _ => {}
4147    }
4148}
4149
4150/// Generate a complete Rust program from LOGOS statements.
4151///
4152/// This is the main entry point for code generation. It produces a complete,
4153/// compilable Rust program including:
4154///
4155/// 1. Prelude imports (`logicaffeine_data`, `logicaffeine_system`)
4156/// 2. User-defined types in `mod user_types`
4157/// 3. Policy impl blocks with predicate/capability methods
4158/// 4. Function definitions
4159/// 5. `fn main()` with the program logic
4160///
4161/// # Arguments
4162///
4163/// * `stmts` - The parsed LOGOS statements
4164/// * `registry` - Type registry containing struct/enum definitions
4165/// * `policies` - Policy registry containing security predicates and capabilities
4166/// * `interner` - Symbol interner for name resolution
4167///
4168/// # Returns
4169///
4170/// A complete Rust source code string ready to compile.
4171///
4172/// # Generated Features
4173///
4174/// - Wraps user types in `mod user_types` for visibility control
4175/// - Emits function definitions before main
4176/// - Handles CRDT field mutations with proper `.set()` calls
4177/// - Generates policy predicate and capability methods
4178/// - Adds `#[tokio::main]` async when needed
4179/// - Injects VFS when file operations detected
4180/// - Uses `Distributed<T>` when both Mount and Sync detected
4181/// - Boxes recursive enum fields
4182/// Generates a complete Rust program from LOGOS statements.
4183///
4184/// This is the main entry point for code generation. It produces a full Rust
4185/// program including:
4186/// - Prelude imports (`use logicaffeine_data::*;`)
4187/// - Type definitions (structs, enums, inductive types)
4188/// - Policy structs with capability methods
4189/// - Main function with async runtime if needed
4190/// - VFS initialization for file operations
4191///
4192/// # Arguments
4193///
4194/// * `stmts` - The parsed LOGOS statements to compile
4195/// * `registry` - Type definitions discovered during parsing
4196/// * `policies` - Policy definitions for access control
4197/// * `interner` - Symbol interner for resolving names
4198///
4199/// # Returns
4200///
4201/// A complete Rust source code string ready for compilation.
4202pub fn codegen_program(stmts: &[Stmt], registry: &TypeRegistry, policies: &PolicyRegistry, interner: &Interner) -> String {
4203    let mut output = String::new();
4204
4205    // Prelude
4206    // Use extracted crates instead of logos_core
4207    writeln!(output, "#[allow(unused_imports)]").unwrap();
4208    writeln!(output, "use std::fmt::Write as _;").unwrap();
4209    writeln!(output, "use logicaffeine_data::*;").unwrap();
4210    writeln!(output, "use logicaffeine_system::*;\n").unwrap();
4211
4212    // FFI: Emit wasm_bindgen preamble if any function is exported for WASM
4213    if has_wasm_exports(stmts, interner) {
4214        writeln!(output, "use wasm_bindgen::prelude::*;\n").unwrap();
4215    }
4216
4217    // FFI: Emit CStr/CString imports if any C export uses Text types
4218    if has_c_exports_with_text(stmts, interner) {
4219        writeln!(output, "use std::ffi::{{CStr, CString}};\n").unwrap();
4220    }
4221
4222    // Universal ABI: Emit LogosStatus runtime preamble if any C exports exist
4223    let c_exports_exist = has_c_exports(stmts, interner);
4224    if c_exports_exist {
4225        output.push_str(&codegen_logos_runtime_preamble());
4226    }
4227
4228    // Phase 49: Collect CRDT register fields for special SetField handling
4229    // LWW fields need timestamp, MV fields don't
4230    let (lww_fields, mv_fields) = collect_crdt_register_fields(registry, interner);
4231
4232    // Phase 54: Collect async functions for Launch codegen
4233    let async_functions = collect_async_functions(stmts);
4234
4235    // Purity analysis for memoization
4236    let pure_functions = collect_pure_functions(stmts);
4237
4238    // Phase 54: Collect pipe declarations (variables with _tx/_rx suffixes)
4239    let main_pipe_vars = collect_pipe_vars(stmts);
4240
4241    // Phase 102: Collect boxed fields for recursive enum handling
4242    let boxed_fields = collect_boxed_fields(registry, interner);
4243
4244    // Collect value-type struct names used in C exports (need #[repr(C)])
4245    let c_abi_value_structs: HashSet<Symbol> = if c_exports_exist {
4246        collect_c_export_value_type_structs(stmts, interner, registry)
4247    } else {
4248        HashSet::new()
4249    };
4250
4251    // Collect reference-type struct names used in C exports (need serde derives for from_json/to_json)
4252    let c_abi_ref_structs: HashSet<Symbol> = if c_exports_exist {
4253        collect_c_export_ref_structs(stmts, interner, registry)
4254    } else {
4255        HashSet::new()
4256    };
4257
4258    // Collect user-defined structs from registry (Phase 34: generics, Phase 47: is_portable, Phase 49: is_shared)
4259    let structs: Vec<_> = registry.iter_types()
4260        .filter_map(|(name, def)| {
4261            if let TypeDef::Struct { fields, generics, is_portable, is_shared } = def {
4262                if !fields.is_empty() || !generics.is_empty() {
4263                    Some((*name, fields.clone(), generics.clone(), *is_portable, *is_shared))
4264                } else {
4265                    None
4266                }
4267            } else {
4268                None
4269            }
4270        })
4271        .collect();
4272
4273    // Phase 33/34: Collect user-defined enums from registry (generics, Phase 47: is_portable, Phase 49: is_shared)
4274    let enums: Vec<_> = registry.iter_types()
4275        .filter_map(|(name, def)| {
4276            if let TypeDef::Enum { variants, generics, is_portable, is_shared } = def {
4277                if !variants.is_empty() || !generics.is_empty() {
4278                    Some((*name, variants.clone(), generics.clone(), *is_portable, *is_shared))
4279                } else {
4280                    None
4281                }
4282            } else {
4283                None
4284            }
4285        })
4286        .collect();
4287
4288    // Emit struct and enum definitions in user_types module if any exist
4289    if !structs.is_empty() || !enums.is_empty() {
4290        writeln!(output, "pub mod user_types {{").unwrap();
4291        writeln!(output, "    use super::*;\n").unwrap();
4292
4293        for (name, fields, generics, is_portable, is_shared) in &structs {
4294            output.push_str(&codegen_struct_def(*name, fields, generics, *is_portable, *is_shared, interner, 4, &c_abi_value_structs, &c_abi_ref_structs));
4295        }
4296
4297        for (name, variants, generics, is_portable, is_shared) in &enums {
4298            output.push_str(&codegen_enum_def(*name, variants, generics, *is_portable, *is_shared, interner, 4));
4299        }
4300
4301        writeln!(output, "}}\n").unwrap();
4302        writeln!(output, "use user_types::*;\n").unwrap();
4303    }
4304
4305    // Phase 50: Generate policy impl blocks with predicate and capability methods
4306    output.push_str(&codegen_policy_impls(policies, interner));
4307
4308    // Mutual TCO: Detect pairs of mutually tail-calling functions
4309    let mutual_tce_pairs = detect_mutual_tce_pairs(stmts, interner);
4310    let mut mutual_tce_members: HashSet<Symbol> = HashSet::new();
4311    for (a, b) in &mutual_tce_pairs {
4312        mutual_tce_members.insert(*a);
4313        mutual_tce_members.insert(*b);
4314    }
4315    let mut mutual_tce_emitted: HashSet<Symbol> = HashSet::new();
4316
4317    // Phase 32/38: Emit function definitions before main
4318    for stmt in stmts {
4319        if let Stmt::FunctionDef { name, params, body, return_type, is_native, native_path, is_exported, export_target } = stmt {
4320            if mutual_tce_members.contains(name) {
4321                // Part of a mutual pair — emit merged function when we see the first member
4322                if !mutual_tce_emitted.contains(name) {
4323                    // Find the pair this function belongs to
4324                    if let Some((a, b)) = mutual_tce_pairs.iter().find(|(a, b)| *a == *name || *b == *name) {
4325                        output.push_str(&codegen_mutual_tce_pair(*a, *b, stmts, interner, &lww_fields, &mv_fields, &async_functions, &boxed_fields, registry));
4326                        mutual_tce_emitted.insert(*a);
4327                        mutual_tce_emitted.insert(*b);
4328                    }
4329                }
4330                // Skip individual emission — already emitted as part of merged pair
4331            } else {
4332                output.push_str(&codegen_function_def(*name, params, body, return_type.as_ref().copied(), *is_native, *native_path, *is_exported, *export_target, interner, &lww_fields, &mv_fields, &async_functions, &boxed_fields, registry, &pure_functions));
4333            }
4334        }
4335    }
4336
4337    // Universal ABI: Emit accessor/free functions for reference types in C exports
4338    if c_exports_exist {
4339        let ref_types = collect_c_export_reference_types(stmts, interner, registry);
4340        for ref_ty in &ref_types {
4341            output.push_str(&codegen_c_accessors(ref_ty, interner, registry));
4342        }
4343    }
4344
4345    // Grand Challenge: Collect variables that need to be mutable
4346    let main_stmts: Vec<&Stmt> = stmts.iter()
4347        .filter(|s| !matches!(s, Stmt::FunctionDef { .. }))
4348        .collect();
4349    let mut main_mutable_vars = HashSet::new();
4350    for stmt in &main_stmts {
4351        collect_mutable_vars_stmt(stmt, &mut main_mutable_vars);
4352    }
4353
4354    // Main function
4355    // Phase 51: Use async main when async operations are present
4356    if requires_async(stmts) {
4357        writeln!(output, "#[tokio::main]").unwrap();
4358        writeln!(output, "async fn main() {{").unwrap();
4359    } else {
4360        writeln!(output, "fn main() {{").unwrap();
4361    }
4362    // Phase 53: Inject VFS when file operations or persistence is used
4363    if requires_vfs(stmts) {
4364        writeln!(output, "    let vfs = logicaffeine_system::fs::NativeVfs::new(\".\");").unwrap();
4365    }
4366    let mut main_ctx = RefinementContext::new();
4367    let mut main_synced_vars = HashSet::new();  // Phase 52: Track synced variables in main
4368    // Phase 56: Pre-scan for Mount+Sync combinations
4369    let main_var_caps = analyze_variable_capabilities(stmts, interner);
4370    {
4371        let stmt_refs: Vec<&Stmt> = stmts.iter().collect();
4372        let mut i = 0;
4373        while i < stmt_refs.len() {
4374            // Skip function definitions - they're already emitted above
4375            if matches!(stmt_refs[i], Stmt::FunctionDef { .. }) {
4376                i += 1;
4377                continue;
4378            }
4379            // Peephole: Vec fill pattern optimization (most specific — check first)
4380            if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, i, interner, 1) {
4381                output.push_str(&code);
4382                i += 1 + skip;
4383                continue;
4384            }
4385            // Peephole: For-range loop optimization
4386            if let Some((code, skip)) = try_emit_for_range_pattern(&stmt_refs, i, interner, 1, &main_mutable_vars, &mut main_ctx, &lww_fields, &mv_fields, &mut main_synced_vars, &main_var_caps, &async_functions, &main_pipe_vars, &boxed_fields, registry) {
4387                output.push_str(&code);
4388                i += 1 + skip;
4389                continue;
4390            }
4391            // Peephole: swap pattern optimization
4392            if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, i, interner, 1, main_ctx.get_variable_types()) {
4393                output.push_str(&code);
4394                i += 1 + skip;
4395                continue;
4396            }
4397            output.push_str(&codegen_stmt(stmt_refs[i], interner, 1, &main_mutable_vars, &mut main_ctx, &lww_fields, &mv_fields, &mut main_synced_vars, &main_var_caps, &async_functions, &main_pipe_vars, &boxed_fields, registry));
4398            i += 1;
4399        }
4400    }
4401    writeln!(output, "}}").unwrap();
4402    output
4403}
4404
4405/// Phase 32/38: Generate a function definition.
4406/// Phase 38: Updated for native functions and TypeExpr types.
4407/// Phase 49: Accepts lww_fields for LWWRegister SetField handling.
4408/// Phase 103: Accepts registry for polymorphic enum type inference.
4409fn codegen_function_def(
4410    name: Symbol,
4411    params: &[(Symbol, &TypeExpr)],
4412    body: &[Stmt],
4413    return_type: Option<&TypeExpr>,
4414    is_native: bool,
4415    native_path: Option<Symbol>,
4416    is_exported: bool,
4417    export_target: Option<Symbol>,
4418    interner: &Interner,
4419    lww_fields: &HashSet<(String, String)>,
4420    mv_fields: &HashSet<(String, String)>,  // Phase 49b: MVRegister fields
4421    async_functions: &HashSet<Symbol>,  // Phase 54
4422    boxed_fields: &HashSet<(String, String, String)>,  // Phase 102
4423    registry: &TypeRegistry,  // Phase 103
4424    pure_functions: &HashSet<Symbol>,
4425) -> String {
4426    let mut output = String::new();
4427    let raw_name = interner.resolve(name);
4428    let func_name = escape_rust_ident(raw_name);
4429    let export_target_lower = export_target.map(|s| interner.resolve(s).to_lowercase());
4430
4431    // Phase 54: Detect which parameters are used as pipe senders
4432    let pipe_sender_params = collect_pipe_sender_params(body);
4433
4434    // FFI: Exported functions need special signatures
4435    let is_c_export_early = is_exported && matches!(export_target_lower.as_deref(), None | Some("c"));
4436
4437    // TCE: Detect tail recursion eligibility
4438    let is_tce = !is_native && !is_c_export_early && is_tail_recursive(name, body);
4439    let param_syms: Vec<Symbol> = params.iter().map(|(s, _)| *s).collect();
4440
4441    // Accumulator Introduction: Detect non-tail single-call + / * patterns
4442    let acc_info = if !is_tce && !is_native && !is_c_export_early {
4443        detect_accumulator_pattern(name, body)
4444    } else {
4445        None
4446    };
4447    let is_acc = acc_info.is_some();
4448
4449    // Memoization: Detect pure multi-call recursive functions with hashable params
4450    let is_memo = !is_tce && !is_acc && !is_native && !is_c_export_early
4451        && should_memoize(name, body, params, return_type, pure_functions.contains(&name), interner);
4452
4453    let needs_mut_params = is_tce || is_acc;
4454
4455    // Build parameter list using TypeExpr
4456    let params_str: Vec<String> = params.iter()
4457        .map(|(param_name, param_type)| {
4458            let pname = interner.resolve(*param_name);
4459            let ty = codegen_type_expr(param_type, interner);
4460            // Phase 54: If param is used as a pipe sender, wrap type in Sender<T>
4461            if pipe_sender_params.contains(param_name) {
4462                format!("{}: tokio::sync::mpsc::Sender<{}>", pname, ty)
4463            } else if needs_mut_params {
4464                format!("mut {}: {}", pname, ty)
4465            } else {
4466                format!("{}: {}", pname, ty)
4467            }
4468        })
4469        .collect();
4470
4471    // Get return type string from TypeExpr or infer from body
4472    let return_type_str = return_type
4473        .map(|t| codegen_type_expr(t, interner))
4474        .or_else(|| infer_return_type_from_body(body, interner));
4475
4476    // Phase 51/54: Check if function is async (includes transitive async detection)
4477    let is_async = async_functions.contains(&name);
4478    let fn_keyword = if is_async { "async fn" } else { "fn" };
4479
4480    // FFI: Exported functions need special signatures
4481    let is_c_export = is_c_export_early;
4482
4483    // FFI: Check if C export needs type marshaling
4484    // Triggers for: Text params/return, reference types, Result return, refinement params
4485    let needs_c_marshaling = is_c_export && {
4486        let has_text_param = params.iter().any(|(_, ty)| is_text_type(ty, interner));
4487        let has_text_return = return_type.map_or(false, |ty| is_text_type(ty, interner));
4488        let has_ref_param = params.iter().any(|(_, ty)| {
4489            classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType
4490        });
4491        let has_ref_return = return_type.map_or(false, |ty| {
4492            classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType
4493        });
4494        let has_result_return = return_type.map_or(false, |ty| is_result_type(ty, interner));
4495        let has_refinement_param = params.iter().any(|(_, ty)| {
4496            matches!(ty, TypeExpr::Refinement { .. })
4497        });
4498        has_text_param || has_text_return || has_ref_param || has_ref_return
4499            || has_result_return || has_refinement_param
4500    };
4501
4502    if needs_c_marshaling {
4503        // Generate two-function pattern: inner function + C ABI wrapper
4504        return codegen_c_export_with_marshaling(
4505            name, params, body, return_type, interner,
4506            lww_fields, mv_fields, async_functions, boxed_fields, registry,
4507        );
4508    }
4509
4510    // Build function signature
4511    let (vis_prefix, abi_prefix) = if is_exported {
4512        match export_target_lower.as_deref() {
4513            None | Some("c") => ("pub ", "extern \"C\" "),
4514            Some("wasm") => ("pub ", ""),
4515            _ => ("pub ", ""),
4516        }
4517    } else {
4518        ("", "")
4519    };
4520
4521    let signature = if let Some(ref ret_ty) = return_type_str {
4522        if ret_ty != "()" {
4523            format!("{}{}{} {}({}) -> {}", vis_prefix, abi_prefix, fn_keyword, func_name, params_str.join(", "), ret_ty)
4524        } else {
4525            format!("{}{}{} {}({})", vis_prefix, abi_prefix, fn_keyword, func_name, params_str.join(", "))
4526        }
4527    } else {
4528        format!("{}{}{} {}({})", vis_prefix, abi_prefix, fn_keyword, func_name, params_str.join(", "))
4529    };
4530
4531    // Emit #[inline] for small non-recursive, non-exported functions
4532    if !is_tce && !is_acc && should_inline(name, body, is_native, is_exported, is_async) {
4533        writeln!(output, "#[inline]").unwrap();
4534    }
4535
4536    // FFI: Emit export attributes before the function
4537    if is_exported {
4538        match export_target_lower.as_deref() {
4539            None | Some("c") => {
4540                writeln!(output, "#[export_name = \"logos_{}\"]", raw_name).unwrap();
4541            }
4542            Some("wasm") => {
4543                writeln!(output, "#[wasm_bindgen]").unwrap();
4544            }
4545            _ => {}
4546        }
4547    }
4548
4549    // Phase 38: Handle native functions
4550    if is_native {
4551        let arg_names: Vec<&str> = params.iter()
4552            .map(|(n, _)| interner.resolve(*n))
4553            .collect();
4554
4555        if let Some(path_sym) = native_path {
4556            // User-defined native path: call the Rust path directly
4557            let path = interner.resolve(path_sym);
4558            // Validate path looks like a valid Rust path (identifiers separated by ::)
4559            let is_valid_path = !path.is_empty() && path.split("::").all(|seg| {
4560                !seg.is_empty() && seg.chars().all(|c| c.is_alphanumeric() || c == '_')
4561            });
4562            if is_valid_path {
4563                writeln!(output, "{} {{", signature).unwrap();
4564                writeln!(output, "    {}({})", path, arg_names.join(", ")).unwrap();
4565                writeln!(output, "}}\n").unwrap();
4566            } else {
4567                writeln!(output, "{} {{", signature).unwrap();
4568                writeln!(output, "    compile_error!(\"Invalid native function path: '{}'. Path must be a valid Rust path like \\\"crate::module::function\\\".\")", path).unwrap();
4569                writeln!(output, "}}\n").unwrap();
4570            }
4571        } else {
4572            // Legacy system functions: use map_native_function()
4573            if let Some((module, core_fn)) = map_native_function(raw_name) {
4574                writeln!(output, "{} {{", signature).unwrap();
4575                writeln!(output, "    logicaffeine_system::{}::{}({})", module, core_fn, arg_names.join(", ")).unwrap();
4576                writeln!(output, "}}\n").unwrap();
4577            } else {
4578                writeln!(output, "{} {{", signature).unwrap();
4579                writeln!(output, "    compile_error!(\"Unknown system native function: '{}'. Use `is native \\\"crate::path\\\"` syntax for user-defined native functions.\")", raw_name).unwrap();
4580                writeln!(output, "}}\n").unwrap();
4581            }
4582        }
4583    } else {
4584        // Non-native: emit body (also used for exported functions which have bodies)
4585        // Grand Challenge: Collect mutable vars for this function
4586        let func_mutable_vars = collect_mutable_vars(body);
4587        writeln!(output, "{} {{", signature).unwrap();
4588
4589        // Wrap exported C functions in catch_unwind for panic safety
4590        let wrap_catch_unwind = is_c_export;
4591        if wrap_catch_unwind {
4592            writeln!(output, "    match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {{").unwrap();
4593        }
4594
4595        let mut func_ctx = RefinementContext::new();
4596        let mut func_synced_vars = HashSet::new();  // Phase 52: Track synced variables in function
4597        // Phase 56: Pre-scan for Mount+Sync combinations in function body
4598        let func_var_caps = analyze_variable_capabilities(body, interner);
4599
4600        // Phase 50: Register parameter types for capability Check resolution
4601        for (param_name, param_type) in params {
4602            let type_name = codegen_type_expr(param_type, interner);
4603            func_ctx.register_variable_type(*param_name, type_name);
4604        }
4605
4606        // Phase 54: Functions receive pipe senders as parameters, no local pipe declarations
4607        let func_pipe_vars = HashSet::new();
4608
4609        if is_tce {
4610            // TCE: Wrap body in loop, use TCE-aware statement emitter
4611            writeln!(output, "    loop {{").unwrap();
4612            let stmt_refs: Vec<&Stmt> = body.iter().collect();
4613            let mut si = 0;
4614            while si < stmt_refs.len() {
4615                if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 2) {
4616                    output.push_str(&code);
4617                    si += 1 + skip;
4618                    continue;
4619                }
4620                if let Some((code, skip)) = try_emit_for_range_pattern(&stmt_refs, si, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry) {
4621                    output.push_str(&code);
4622                    si += 1 + skip;
4623                    continue;
4624                }
4625                if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 2, func_ctx.get_variable_types()) {
4626                    output.push_str(&code);
4627                    si += 1 + skip;
4628                    continue;
4629                }
4630                output.push_str(&codegen_stmt_tce(stmt_refs[si], name, &param_syms, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry));
4631                si += 1;
4632            }
4633            writeln!(output, "    }}").unwrap();
4634        } else if let Some(ref acc) = acc_info {
4635            // Accumulator Introduction: Wrap body in loop with accumulator variable
4636            writeln!(output, "    let mut __acc: i64 = {};", acc.identity).unwrap();
4637            writeln!(output, "    loop {{").unwrap();
4638            let stmt_refs: Vec<&Stmt> = body.iter().collect();
4639            let mut si = 0;
4640            while si < stmt_refs.len() {
4641                if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 2) {
4642                    output.push_str(&code);
4643                    si += 1 + skip;
4644                    continue;
4645                }
4646                if let Some((code, skip)) = try_emit_for_range_pattern(&stmt_refs, si, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry) {
4647                    output.push_str(&code);
4648                    si += 1 + skip;
4649                    continue;
4650                }
4651                if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 2, func_ctx.get_variable_types()) {
4652                    output.push_str(&code);
4653                    si += 1 + skip;
4654                    continue;
4655                }
4656                output.push_str(&codegen_stmt_acc(stmt_refs[si], name, &param_syms, acc, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry));
4657                si += 1;
4658            }
4659            writeln!(output, "    }}").unwrap();
4660        } else if is_memo {
4661            // Memoization: Wrap body in closure with thread-local cache
4662            let ret_ty = return_type_str.as_deref().unwrap_or("i64");
4663            let memo_name = format!("__MEMO_{}", func_name.to_uppercase());
4664
4665            // Build key type and key expression
4666            let (key_type, key_expr, copy_method) = if params.len() == 1 {
4667                let ty = codegen_type_expr(params[0].1, interner);
4668                let pname = interner.resolve(params[0].0).to_string();
4669                let copy = if is_copy_type_expr(params[0].1, interner) { "copied" } else { "cloned" };
4670                (ty, pname, copy)
4671            } else {
4672                let types: Vec<String> = params.iter().map(|(_, t)| codegen_type_expr(t, interner)).collect();
4673                let names: Vec<String> = params.iter().map(|(n, _)| interner.resolve(*n).to_string()).collect();
4674                let copy = if params.iter().all(|(_, t)| is_copy_type_expr(t, interner)) { "copied" } else { "cloned" };
4675                (format!("({})", types.join(", ")), format!("({})", names.join(", ")), copy)
4676            };
4677
4678            writeln!(output, "    use std::cell::RefCell;").unwrap();
4679            writeln!(output, "    use std::collections::HashMap;").unwrap();
4680            writeln!(output, "    thread_local! {{").unwrap();
4681            writeln!(output, "        static {}: RefCell<HashMap<{}, {}>> = RefCell::new(HashMap::new());", memo_name, key_type, ret_ty).unwrap();
4682            writeln!(output, "    }}").unwrap();
4683            writeln!(output, "    if let Some(__v) = {}.with(|c| c.borrow().get(&{}).{}()) {{", memo_name, key_expr, copy_method).unwrap();
4684            writeln!(output, "        return __v;").unwrap();
4685            writeln!(output, "    }}").unwrap();
4686            writeln!(output, "    let __memo_result = (|| -> {} {{", ret_ty).unwrap();
4687            let stmt_refs: Vec<&Stmt> = body.iter().collect();
4688            let mut si = 0;
4689            while si < stmt_refs.len() {
4690                if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 2) {
4691                    output.push_str(&code);
4692                    si += 1 + skip;
4693                    continue;
4694                }
4695                if let Some((code, skip)) = try_emit_for_range_pattern(&stmt_refs, si, interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry) {
4696                    output.push_str(&code);
4697                    si += 1 + skip;
4698                    continue;
4699                }
4700                if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 2, func_ctx.get_variable_types()) {
4701                    output.push_str(&code);
4702                    si += 1 + skip;
4703                    continue;
4704                }
4705                output.push_str(&codegen_stmt(stmt_refs[si], interner, 2, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry));
4706                si += 1;
4707            }
4708            writeln!(output, "    }})();").unwrap();
4709            writeln!(output, "    {}.with(|c| c.borrow_mut().insert({}, __memo_result));", memo_name, key_expr).unwrap();
4710            writeln!(output, "    __memo_result").unwrap();
4711        } else {
4712            let stmt_refs: Vec<&Stmt> = body.iter().collect();
4713            let mut si = 0;
4714            while si < stmt_refs.len() {
4715                if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 1) {
4716                    output.push_str(&code);
4717                    si += 1 + skip;
4718                    continue;
4719                }
4720                if let Some((code, skip)) = try_emit_for_range_pattern(&stmt_refs, si, interner, 1, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry) {
4721                    output.push_str(&code);
4722                    si += 1 + skip;
4723                    continue;
4724                }
4725                if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 1, func_ctx.get_variable_types()) {
4726                    output.push_str(&code);
4727                    si += 1 + skip;
4728                    continue;
4729                }
4730                output.push_str(&codegen_stmt(stmt_refs[si], interner, 1, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry));
4731                si += 1;
4732            }
4733        }
4734
4735        if wrap_catch_unwind {
4736            writeln!(output, "    }})) {{").unwrap();
4737            writeln!(output, "        Ok(__v) => __v,").unwrap();
4738            writeln!(output, "        Err(__panic) => {{").unwrap();
4739            writeln!(output, "            let __msg = if let Some(s) = __panic.downcast_ref::<String>() {{ s.clone() }} else if let Some(s) = __panic.downcast_ref::<&str>() {{ s.to_string() }} else {{ \"Unknown panic\".to_string() }};").unwrap();
4740            writeln!(output, "            logos_set_last_error(__msg);").unwrap();
4741            // Determine default for panic case based on return type
4742            if let Some(ref ret_str) = return_type_str {
4743                if ret_str != "()" {
4744                    writeln!(output, "            Default::default()").unwrap();
4745                }
4746            }
4747            writeln!(output, "        }}").unwrap();
4748            writeln!(output, "    }}").unwrap();
4749        }
4750
4751        writeln!(output, "}}\n").unwrap();
4752    }
4753
4754    output
4755}
4756
4757/// Phase 38: Map native function names to logicaffeine_system module paths.
4758/// For system functions only — user-defined native paths bypass this entirely.
4759/// Returns None for unknown functions (caller emits compile_error!).
4760fn map_native_function(name: &str) -> Option<(&'static str, &'static str)> {
4761    match name {
4762        "read" => Some(("file", "read")),
4763        "write" => Some(("file", "write")),
4764        "now" => Some(("time", "now")),
4765        "sleep" => Some(("time", "sleep")),
4766        "randomInt" => Some(("random", "randomInt")),
4767        "randomFloat" => Some(("random", "randomFloat")),
4768        "get" => Some(("env", "get")),
4769        "args" => Some(("env", "args")),
4770        "parseInt" => Some(("text", "parseInt")),
4771        "parseFloat" => Some(("text", "parseFloat")),
4772        "format" => Some(("fmt", "format")),
4773        _ => None,
4774    }
4775}
4776
4777/// FFI: Check if a TypeExpr resolves to Text/String.
4778fn is_text_type(ty: &TypeExpr, interner: &Interner) -> bool {
4779    match ty {
4780        TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
4781            matches!(interner.resolve(*sym), "Text" | "String")
4782        }
4783        TypeExpr::Refinement { base, .. } => is_text_type(base, interner),
4784        _ => false,
4785    }
4786}
4787
4788/// FFI: Map a TypeExpr to its C ABI representation.
4789/// Primitives pass through; Text becomes raw pointer.
4790fn map_type_to_c_abi(ty: &TypeExpr, interner: &Interner, is_return: bool) -> String {
4791    if is_text_type(ty, interner) {
4792        if is_return {
4793            "*mut std::os::raw::c_char".to_string()
4794        } else {
4795            "*const std::os::raw::c_char".to_string()
4796        }
4797    } else {
4798        codegen_type_expr(ty, interner)
4799    }
4800}
4801
4802/// FFI: Generate a C-exported function with Universal ABI marshaling.
4803///
4804/// Produces: 1) an inner function with normal Rust types, 2) a #[no_mangle] extern "C" wrapper.
4805///
4806/// The wrapper handles:
4807/// - Text param/return marshaling (*const c_char ↔ String)
4808/// - Reference type params/returns via opaque LogosHandle
4809/// - Result<T, E> returns via status code + out-parameter
4810/// - Refinement type boundary guards
4811fn codegen_c_export_with_marshaling(
4812    name: Symbol,
4813    params: &[(Symbol, &TypeExpr)],
4814    body: &[Stmt],
4815    return_type: Option<&TypeExpr>,
4816    interner: &Interner,
4817    lww_fields: &HashSet<(String, String)>,
4818    mv_fields: &HashSet<(String, String)>,
4819    async_functions: &HashSet<Symbol>,
4820    boxed_fields: &HashSet<(String, String, String)>,
4821    registry: &crate::analysis::registry::TypeRegistry,
4822) -> String {
4823    let mut output = String::new();
4824    let raw_name = interner.resolve(name);
4825    // All exported C ABI symbols use the `logos_` prefix to avoid keyword
4826    // collisions in target languages (C, Python, JS, etc.) and to provide
4827    // a consistent namespace for the generated library.
4828    let func_name = format!("logos_{}", raw_name);
4829    let inner_name = escape_rust_ident(raw_name);
4830
4831    // Classify return type
4832    let has_ref_return = return_type.map_or(false, |ty| {
4833        classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType
4834    });
4835    let has_result_return = return_type.map_or(false, |ty| is_result_type(ty, interner));
4836    let has_text_return = return_type.map_or(false, |t| is_text_type(t, interner));
4837
4838    // Determine if we need status-code return pattern
4839    // Status code is needed when the return value requires an out-parameter (ref/text/result)
4840    // or when refinement parameters need validation error paths.
4841    // Ref-type parameters do NOT force status code — catch_unwind handles invalid handle panics.
4842    let uses_status_code = has_ref_return || has_result_return || has_text_return
4843        || params.iter().any(|(_, ty)| matches!(ty, TypeExpr::Refinement { .. }));
4844
4845    // 1) Emit the inner function with normal Rust types
4846    let inner_params: Vec<String> = params.iter()
4847        .map(|(pname, ptype)| {
4848            format!("{}: {}", interner.resolve(*pname), codegen_type_expr(ptype, interner))
4849        })
4850        .collect();
4851    let inner_ret = return_type.map(|t| codegen_type_expr(t, interner));
4852
4853    let inner_sig = if let Some(ref ret) = inner_ret {
4854        if ret != "()" {
4855            format!("fn {}({}) -> {}", inner_name, inner_params.join(", "), ret)
4856        } else {
4857            format!("fn {}({})", inner_name, inner_params.join(", "))
4858        }
4859    } else {
4860        format!("fn {}({})", inner_name, inner_params.join(", "))
4861    };
4862
4863    writeln!(output, "{} {{", inner_sig).unwrap();
4864    let func_mutable_vars = collect_mutable_vars(body);
4865    let mut func_ctx = RefinementContext::new();
4866    let mut func_synced_vars = HashSet::new();
4867    let func_var_caps = analyze_variable_capabilities(body, interner);
4868    for (param_name, param_type) in params {
4869        let type_name = codegen_type_expr(param_type, interner);
4870        func_ctx.register_variable_type(*param_name, type_name);
4871    }
4872    let func_pipe_vars = HashSet::new();
4873    {
4874        let stmt_refs: Vec<&Stmt> = body.iter().collect();
4875        let mut si = 0;
4876        while si < stmt_refs.len() {
4877            if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 1) {
4878                output.push_str(&code);
4879                si += 1 + skip;
4880                continue;
4881            }
4882            if let Some((code, skip)) = try_emit_for_range_pattern(&stmt_refs, si, interner, 1, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry) {
4883                output.push_str(&code);
4884                si += 1 + skip;
4885                continue;
4886            }
4887            if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 1, func_ctx.get_variable_types()) {
4888                output.push_str(&code);
4889                si += 1 + skip;
4890                continue;
4891            }
4892            output.push_str(&codegen_stmt(stmt_refs[si], interner, 1, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry));
4893            si += 1;
4894        }
4895    }
4896    writeln!(output, "}}\n").unwrap();
4897
4898    // 2) Build the C ABI wrapper parameters
4899    let mut c_params: Vec<String> = Vec::new();
4900
4901    for (pname, ptype) in params.iter() {
4902        let pn = interner.resolve(*pname);
4903        if classify_type_for_c_abi(ptype, interner, registry) == CAbiClass::ReferenceType {
4904            c_params.push(format!("{}: LogosHandle", pn));
4905        } else if is_text_type(ptype, interner) {
4906            c_params.push(format!("{}: *const std::os::raw::c_char", pn));
4907        } else {
4908            c_params.push(format!("{}: {}", pn, codegen_type_expr(ptype, interner)));
4909        }
4910    }
4911
4912    // Add out-parameter if using status-code pattern with return value
4913    if uses_status_code {
4914        if let Some(ret_ty) = return_type {
4915            if has_result_return {
4916                // Result<T, E>: out param for the Ok(T) type
4917                if let TypeExpr::Generic { params: ref rparams, .. } = ret_ty {
4918                    if !rparams.is_empty() {
4919                        let ok_ty = &rparams[0];
4920                        if classify_type_for_c_abi(ok_ty, interner, registry) == CAbiClass::ReferenceType {
4921                            c_params.push("out: *mut LogosHandle".to_string());
4922                        } else if is_text_type(ok_ty, interner) {
4923                            c_params.push("out: *mut *mut std::os::raw::c_char".to_string());
4924                        } else {
4925                            let ty_str = codegen_type_expr(ok_ty, interner);
4926                            c_params.push(format!("out: *mut {}", ty_str));
4927                        }
4928                    }
4929                }
4930            } else if has_ref_return {
4931                c_params.push("out: *mut LogosHandle".to_string());
4932            } else if has_text_return {
4933                c_params.push("out: *mut *mut std::os::raw::c_char".to_string());
4934            }
4935        }
4936    }
4937
4938    // Build the wrapper signature
4939    let c_sig = if uses_status_code {
4940        format!("pub extern \"C\" fn {}({}) -> LogosStatus", func_name, c_params.join(", "))
4941    } else if has_text_return {
4942        format!("pub extern \"C\" fn {}({}) -> *mut std::os::raw::c_char", func_name, c_params.join(", "))
4943    } else if let Some(ret_ty) = return_type {
4944        let ret_str = codegen_type_expr(ret_ty, interner);
4945        if ret_str != "()" {
4946            format!("pub extern \"C\" fn {}({}) -> {}", func_name, c_params.join(", "), ret_str)
4947        } else {
4948            format!("pub extern \"C\" fn {}({})", func_name, c_params.join(", "))
4949        }
4950    } else {
4951        format!("pub extern \"C\" fn {}({})", func_name, c_params.join(", "))
4952    };
4953
4954    writeln!(output, "#[no_mangle]").unwrap();
4955    writeln!(output, "{} {{", c_sig).unwrap();
4956
4957    // 3) Marshal parameters
4958    let call_args: Vec<String> = params.iter()
4959        .map(|(pname, ptype)| {
4960            let pname_str = interner.resolve(*pname);
4961            if classify_type_for_c_abi(ptype, interner, registry) == CAbiClass::ReferenceType {
4962                // Look up handle in registry, dereference, and clone for inner
4963                let rust_ty = codegen_type_expr(ptype, interner);
4964                writeln!(output, "    let {pn} = {{", pn = pname_str).unwrap();
4965                writeln!(output, "        let __id = {pn} as u64;", pn = pname_str).unwrap();
4966                writeln!(output, "        let __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
4967                writeln!(output, "        let __ptr = __reg.deref(__id).expect(\"InvalidHandle: handle not found in registry\");").unwrap();
4968                writeln!(output, "        drop(__reg);").unwrap();
4969                writeln!(output, "        unsafe {{ &*(__ptr as *const {ty}) }}.clone()", ty = rust_ty).unwrap();
4970                writeln!(output, "    }};").unwrap();
4971            } else if is_text_type(ptype, interner) {
4972                // Null-safety: check for NULL *const c_char before CStr::from_ptr
4973                if uses_status_code {
4974                    writeln!(output, "    if {pn}.is_null() {{ logos_set_last_error(\"NullPointer: text parameter '{pn}' is null\".to_string()); return LogosStatus::NullPointer; }}",
4975                        pn = pname_str).unwrap();
4976                    writeln!(output, "    let {pn} = unsafe {{ std::ffi::CStr::from_ptr({pn}).to_string_lossy().into_owned() }};",
4977                        pn = pname_str).unwrap();
4978                } else {
4979                    // Non-status-code function: substitute empty string for NULL
4980                    writeln!(output, "    let {pn} = if {pn}.is_null() {{ String::new() }} else {{ unsafe {{ std::ffi::CStr::from_ptr({pn}).to_string_lossy().into_owned() }} }};",
4981                        pn = pname_str).unwrap();
4982                }
4983            }
4984            pname_str.to_string()
4985        })
4986        .collect();
4987
4988    // 4) Emit refinement guards for parameters
4989    for (pname, ptype) in params.iter() {
4990        if let TypeExpr::Refinement { base: _, var, predicate } = ptype {
4991            let pname_str = interner.resolve(*pname);
4992            let bound = interner.resolve(*var);
4993            let assertion = codegen_assertion(predicate, interner);
4994            let check = if bound == pname_str {
4995                assertion
4996            } else {
4997                replace_word(&assertion, bound, pname_str)
4998            };
4999            writeln!(output, "    if !({}) {{", check).unwrap();
5000            writeln!(output, "        logos_set_last_error(format!(\"Refinement violation: expected {check}, got {pn} = {{}}\", {pn}));",
5001                check = check, pn = pname_str).unwrap();
5002            writeln!(output, "        return LogosStatus::RefinementViolation;").unwrap();
5003            writeln!(output, "    }}").unwrap();
5004        }
5005    }
5006
5007    // 4b) Null out-parameter check (before catch_unwind to avoid calling inner fn)
5008    if uses_status_code && (has_ref_return || has_text_return || has_result_return) {
5009        writeln!(output, "    if out.is_null() {{ logos_set_last_error(\"NullPointer: output parameter is null\".to_string()); return LogosStatus::NullPointer; }}").unwrap();
5010    }
5011
5012    // 5) Determine panic default for catch_unwind error arm
5013    let panic_default = if uses_status_code {
5014        "LogosStatus::ThreadPanic"
5015    } else if has_text_return {
5016        "std::ptr::null_mut()"
5017    } else if return_type.map_or(false, |t| codegen_type_expr(t, interner) != "()") {
5018        "Default::default()"
5019    } else {
5020        "" // void function
5021    };
5022
5023    // 6) Open catch_unwind panic boundary
5024    writeln!(output, "    match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {{").unwrap();
5025
5026    // 7) Call inner and marshal return (inside catch_unwind closure)
5027    if uses_status_code {
5028        if has_result_return {
5029            // Result<T, E>: match on Ok/Err
5030            writeln!(output, "    match {}({}) {{", inner_name, call_args.join(", ")).unwrap();
5031            writeln!(output, "        Ok(val) => {{").unwrap();
5032
5033            if let Some(TypeExpr::Generic { params: ref rparams, .. }) = return_type {
5034                if !rparams.is_empty() {
5035                    let ok_ty = &rparams[0];
5036                    if classify_type_for_c_abi(ok_ty, interner, registry) == CAbiClass::ReferenceType {
5037                        writeln!(output, "            let __ptr = Box::into_raw(Box::new(val)) as usize;").unwrap();
5038                        writeln!(output, "            let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
5039                        writeln!(output, "            let (__id, _) = __reg.register(__ptr);").unwrap();
5040                        writeln!(output, "            unsafe {{ *out = __id as LogosHandle; }}").unwrap();
5041                    } else if is_text_type(ok_ty, interner) {
5042                        writeln!(output, "            match std::ffi::CString::new(val) {{").unwrap();
5043                        writeln!(output, "                Ok(cstr) => unsafe {{ *out = cstr.into_raw(); }},").unwrap();
5044                        writeln!(output, "                Err(_) => {{").unwrap();
5045                        writeln!(output, "                    logos_set_last_error(\"Return value contains null byte\".to_string());").unwrap();
5046                        writeln!(output, "                    return LogosStatus::ContainsNullByte;").unwrap();
5047                        writeln!(output, "                }}").unwrap();
5048                        writeln!(output, "            }}").unwrap();
5049                    } else {
5050                        writeln!(output, "            unsafe {{ *out = val; }}").unwrap();
5051                    }
5052                }
5053            }
5054
5055            writeln!(output, "            LogosStatus::Ok").unwrap();
5056            writeln!(output, "        }}").unwrap();
5057            writeln!(output, "        Err(e) => {{").unwrap();
5058            writeln!(output, "            logos_set_last_error(format!(\"{{}}\", e));").unwrap();
5059            writeln!(output, "            LogosStatus::Error").unwrap();
5060            writeln!(output, "        }}").unwrap();
5061            writeln!(output, "    }}").unwrap();
5062        } else if has_ref_return {
5063            // Reference type return → box, register in handle registry, and write to out-parameter
5064            writeln!(output, "    let result = {}({});", inner_name, call_args.join(", ")).unwrap();
5065            writeln!(output, "    let __ptr = Box::into_raw(Box::new(result)) as usize;").unwrap();
5066            writeln!(output, "    let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
5067            writeln!(output, "    let (__id, _) = __reg.register(__ptr);").unwrap();
5068            writeln!(output, "    unsafe {{ *out = __id as LogosHandle; }}").unwrap();
5069            writeln!(output, "    LogosStatus::Ok").unwrap();
5070        } else if has_text_return {
5071            // Text return with status code → write to out-parameter
5072            writeln!(output, "    let result = {}({});", inner_name, call_args.join(", ")).unwrap();
5073            writeln!(output, "    match std::ffi::CString::new(result) {{").unwrap();
5074            writeln!(output, "        Ok(cstr) => {{").unwrap();
5075            writeln!(output, "            unsafe {{ *out = cstr.into_raw(); }}").unwrap();
5076            writeln!(output, "            LogosStatus::Ok").unwrap();
5077            writeln!(output, "        }}").unwrap();
5078            writeln!(output, "        Err(_) => {{").unwrap();
5079            writeln!(output, "            logos_set_last_error(\"Return value contains null byte\".to_string());").unwrap();
5080            writeln!(output, "            LogosStatus::ContainsNullByte").unwrap();
5081            writeln!(output, "        }}").unwrap();
5082            writeln!(output, "    }}").unwrap();
5083        } else {
5084            // No return value but status code (e.g., refinement-only)
5085            writeln!(output, "    {}({});", inner_name, call_args.join(", ")).unwrap();
5086            writeln!(output, "    LogosStatus::Ok").unwrap();
5087        }
5088    } else if has_text_return {
5089        // Text-only marshaling (legacy path, no status code)
5090        writeln!(output, "    let result = {}({});", inner_name, call_args.join(", ")).unwrap();
5091        writeln!(output, "    match std::ffi::CString::new(result) {{").unwrap();
5092        writeln!(output, "        Ok(cstr) => cstr.into_raw(),").unwrap();
5093        writeln!(output, "        Err(_) => {{ logos_set_last_error(\"Return value contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
5094        writeln!(output, "    }}").unwrap();
5095    } else if return_type.is_some() {
5096        writeln!(output, "    {}({})", inner_name, call_args.join(", ")).unwrap();
5097    } else {
5098        writeln!(output, "    {}({})", inner_name, call_args.join(", ")).unwrap();
5099    }
5100
5101    // 8) Close catch_unwind with panic handler
5102    writeln!(output, "    }})) {{").unwrap();
5103    writeln!(output, "        Ok(__v) => __v,").unwrap();
5104    writeln!(output, "        Err(__panic) => {{").unwrap();
5105    writeln!(output, "            let __msg = if let Some(s) = __panic.downcast_ref::<String>() {{ s.clone() }} else if let Some(s) = __panic.downcast_ref::<&str>() {{ s.to_string() }} else {{ \"Unknown panic\".to_string() }};").unwrap();
5106    writeln!(output, "            logos_set_last_error(__msg);").unwrap();
5107    if !panic_default.is_empty() {
5108        writeln!(output, "            {}", panic_default).unwrap();
5109    }
5110    writeln!(output, "        }}").unwrap();
5111    writeln!(output, "    }}").unwrap();
5112
5113    writeln!(output, "}}\n").unwrap();
5114
5115    output
5116}
5117
5118/// Phase 38: Convert TypeExpr to Rust type string.
5119fn codegen_type_expr(ty: &TypeExpr, interner: &Interner) -> String {
5120    match ty {
5121        TypeExpr::Primitive(sym) => {
5122            map_type_to_rust(interner.resolve(*sym))
5123        }
5124        TypeExpr::Named(sym) => {
5125            let name = interner.resolve(*sym);
5126            // Check for common mappings
5127            map_type_to_rust(name)
5128        }
5129        TypeExpr::Generic { base, params } => {
5130            let base_name = interner.resolve(*base);
5131            let params_str: Vec<String> = params.iter()
5132                .map(|p| codegen_type_expr(p, interner))
5133                .collect();
5134
5135            match base_name {
5136                "Result" => {
5137                    if params_str.len() == 2 {
5138                        format!("Result<{}, {}>", params_str[0], params_str[1])
5139                    } else if params_str.len() == 1 {
5140                        format!("Result<{}, String>", params_str[0])
5141                    } else {
5142                        "Result<(), String>".to_string()
5143                    }
5144                }
5145                "Option" | "Maybe" => {
5146                    if !params_str.is_empty() {
5147                        format!("Option<{}>", params_str[0])
5148                    } else {
5149                        "Option<()>".to_string()
5150                    }
5151                }
5152                "Seq" | "List" | "Vec" => {
5153                    if !params_str.is_empty() {
5154                        format!("Vec<{}>", params_str[0])
5155                    } else {
5156                        "Vec<()>".to_string()
5157                    }
5158                }
5159                "Map" | "HashMap" => {
5160                    if params_str.len() >= 2 {
5161                        format!("std::collections::HashMap<{}, {}>", params_str[0], params_str[1])
5162                    } else {
5163                        "std::collections::HashMap<String, String>".to_string()
5164                    }
5165                }
5166                "Set" | "HashSet" => {
5167                    if !params_str.is_empty() {
5168                        format!("std::collections::HashSet<{}>", params_str[0])
5169                    } else {
5170                        "std::collections::HashSet<()>".to_string()
5171                    }
5172                }
5173                other => {
5174                    if params_str.is_empty() {
5175                        other.to_string()
5176                    } else {
5177                        format!("{}<{}>", other, params_str.join(", "))
5178                    }
5179                }
5180            }
5181        }
5182        TypeExpr::Function { inputs, output } => {
5183            let inputs_str: Vec<String> = inputs.iter()
5184                .map(|i| codegen_type_expr(i, interner))
5185                .collect();
5186            let output_str = codegen_type_expr(output, interner);
5187            format!("impl Fn({}) -> {}", inputs_str.join(", "), output_str)
5188        }
5189        // Phase 43C: Refinement types use the base type for Rust type annotation
5190        // The constraint predicate is handled separately via debug_assert!
5191        TypeExpr::Refinement { base, .. } => {
5192            codegen_type_expr(base, interner)
5193        }
5194        // Phase 53: Persistent storage wrapper
5195        TypeExpr::Persistent { inner } => {
5196            let inner_type = codegen_type_expr(inner, interner);
5197            format!("logicaffeine_system::storage::Persistent<{}>", inner_type)
5198        }
5199    }
5200}
5201
5202/// Infer return type from function body by looking at Return statements.
5203fn infer_return_type_from_body(body: &[Stmt], _interner: &Interner) -> Option<String> {
5204    for stmt in body {
5205        if let Stmt::Return { value: Some(_) } = stmt {
5206            // For now, assume i64 for any expression return
5207            // TODO: Implement proper type inference
5208            return Some("i64".to_string());
5209        }
5210    }
5211    None
5212}
5213
5214/// Map LOGOS type names to Rust types.
5215fn map_type_to_rust(ty: &str) -> String {
5216    match ty {
5217        "Int" => "i64".to_string(),
5218        "Nat" => "u64".to_string(),
5219        "Text" => "String".to_string(),
5220        "Bool" | "Boolean" => "bool".to_string(),
5221        "Real" | "Float" => "f64".to_string(),
5222        "Char" => "char".to_string(),
5223        "Byte" => "u8".to_string(),
5224        "Unit" | "()" => "()".to_string(),
5225        "Duration" => "std::time::Duration".to_string(),
5226        other => other.to_string(),
5227    }
5228}
5229
5230/// Generate a single struct definition with derives and visibility.
5231/// Phase 34: Now supports generic type parameters.
5232/// Phase 47: Now supports is_portable for Serialize/Deserialize derives.
5233/// Phase 49: Now supports is_shared for CRDT Merge impl.
5234fn codegen_struct_def(name: Symbol, fields: &[FieldDef], generics: &[Symbol], is_portable: bool, is_shared: bool, interner: &Interner, indent: usize, c_abi_value_structs: &HashSet<Symbol>, c_abi_ref_structs: &HashSet<Symbol>) -> String {
5235    let ind = " ".repeat(indent);
5236    let mut output = String::new();
5237
5238    // Build generic parameter string: <T, U> or empty
5239    let generic_str = if generics.is_empty() {
5240        String::new()
5241    } else {
5242        let params: Vec<&str> = generics.iter()
5243            .map(|g| interner.resolve(*g))
5244            .collect();
5245        format!("<{}>", params.join(", "))
5246    };
5247
5248    // Value-type structs used in C ABI exports need #[repr(C)] for stable field layout
5249    if c_abi_value_structs.contains(&name) {
5250        writeln!(output, "{}#[repr(C)]", ind).unwrap();
5251    }
5252
5253    // Phase 47: Add Serialize, Deserialize derives if portable
5254    // Phase 50: Add PartialEq for policy equality comparisons
5255    // Phase 52: Shared types also need Serialize/Deserialize for Synced<T>
5256    // C ABI reference-type structs also need serde for from_json/to_json support
5257    if is_portable || is_shared || c_abi_ref_structs.contains(&name) {
5258        writeln!(output, "{}#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]", ind).unwrap();
5259    } else {
5260        writeln!(output, "{}#[derive(Default, Debug, Clone, PartialEq)]", ind).unwrap();
5261    }
5262    writeln!(output, "{}pub struct {}{} {{", ind, interner.resolve(name), generic_str).unwrap();
5263
5264    for field in fields {
5265        let vis = if field.is_public { "pub " } else { "" };
5266        let rust_type = codegen_field_type(&field.ty, interner);
5267        writeln!(output, "{}    {}{}: {},", ind, vis, interner.resolve(field.name), rust_type).unwrap();
5268    }
5269
5270    writeln!(output, "{}}}\n", ind).unwrap();
5271
5272    // Phase 49: Generate Merge impl for Shared structs
5273    if is_shared {
5274        output.push_str(&codegen_merge_impl(name, fields, generics, interner, indent));
5275    }
5276
5277    output
5278}
5279
5280/// Phase 49: Generate impl Merge for a Shared struct.
5281fn codegen_merge_impl(name: Symbol, fields: &[FieldDef], generics: &[Symbol], interner: &Interner, indent: usize) -> String {
5282    let ind = " ".repeat(indent);
5283    let name_str = interner.resolve(name);
5284    let mut output = String::new();
5285
5286    // Build generic parameter string: <T, U> or empty
5287    let generic_str = if generics.is_empty() {
5288        String::new()
5289    } else {
5290        let params: Vec<&str> = generics.iter()
5291            .map(|g| interner.resolve(*g))
5292            .collect();
5293        format!("<{}>", params.join(", "))
5294    };
5295
5296    writeln!(output, "{}impl{} logicaffeine_data::crdt::Merge for {}{} {{", ind, generic_str, name_str, generic_str).unwrap();
5297    writeln!(output, "{}    fn merge(&mut self, other: &Self) {{", ind).unwrap();
5298
5299    for field in fields {
5300        let field_name = interner.resolve(field.name);
5301        // Only merge fields that implement Merge (CRDT types)
5302        if is_crdt_field_type(&field.ty, interner) {
5303            writeln!(output, "{}        self.{}.merge(&other.{});", ind, field_name, field_name).unwrap();
5304        }
5305    }
5306
5307    writeln!(output, "{}    }}", ind).unwrap();
5308    writeln!(output, "{}}}\n", ind).unwrap();
5309
5310    output
5311}
5312
5313/// Phase 49: Check if a field type is a CRDT type that implements Merge.
5314fn is_crdt_field_type(ty: &FieldType, interner: &Interner) -> bool {
5315    match ty {
5316        FieldType::Named(sym) => {
5317            let name = interner.resolve(*sym);
5318            matches!(name,
5319                "ConvergentCount" | "GCounter" |
5320                "Tally" | "PNCounter"
5321            )
5322        }
5323        FieldType::Generic { base, .. } => {
5324            let name = interner.resolve(*base);
5325            matches!(name,
5326                "LastWriteWins" | "LWWRegister" |
5327                "SharedSet" | "ORSet" | "SharedSet_AddWins" | "SharedSet_RemoveWins" |
5328                "SharedSequence" | "RGA" | "SharedSequence_YATA" | "CollaborativeSequence" |
5329                "SharedMap" | "ORMap" |
5330                "Divergent" | "MVRegister"
5331            )
5332        }
5333        _ => false,
5334    }
5335}
5336
5337/// Phase 33/34: Generate enum definition with optional generic parameters.
5338/// Phase 47: Now supports is_portable for Serialize/Deserialize derives.
5339/// Phase 49: Now accepts is_shared parameter (enums don't generate Merge impl yet).
5340fn codegen_enum_def(name: Symbol, variants: &[VariantDef], generics: &[Symbol], is_portable: bool, _is_shared: bool, interner: &Interner, indent: usize) -> String {
5341    let ind = " ".repeat(indent);
5342    let mut output = String::new();
5343
5344    // Build generic parameter string: <T, U> or empty
5345    let generic_str = if generics.is_empty() {
5346        String::new()
5347    } else {
5348        let params: Vec<&str> = generics.iter()
5349            .map(|g| interner.resolve(*g))
5350            .collect();
5351        format!("<{}>", params.join(", "))
5352    };
5353
5354    // Phase 47: Add Serialize, Deserialize derives if portable
5355    if is_portable {
5356        writeln!(output, "{}#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]", ind).unwrap();
5357    } else {
5358        writeln!(output, "{}#[derive(Debug, Clone, PartialEq)]", ind).unwrap();
5359    }
5360    writeln!(output, "{}pub enum {}{} {{", ind, interner.resolve(name), generic_str).unwrap();
5361
5362    for variant in variants {
5363        let variant_name = interner.resolve(variant.name);
5364        if variant.fields.is_empty() {
5365            // Unit variant
5366            writeln!(output, "{}    {},", ind, variant_name).unwrap();
5367        } else {
5368            // Struct variant with named fields
5369            // Phase 102: Detect and box recursive fields
5370            let enum_name_str = interner.resolve(name);
5371            let fields_str: Vec<String> = variant.fields.iter()
5372                .map(|f| {
5373                    let rust_type = codegen_field_type(&f.ty, interner);
5374                    let field_name = interner.resolve(f.name);
5375                    // Check if this field references the enum itself (recursive type)
5376                    if is_recursive_field(&f.ty, enum_name_str, interner) {
5377                        format!("{}: Box<{}>", field_name, rust_type)
5378                    } else {
5379                        format!("{}: {}", field_name, rust_type)
5380                    }
5381                })
5382                .collect();
5383            writeln!(output, "{}    {} {{ {} }},", ind, variant_name, fields_str.join(", ")).unwrap();
5384        }
5385    }
5386
5387    writeln!(output, "{}}}\n", ind).unwrap();
5388
5389    // Generate Default impl for enum (defaults to first variant)
5390    // This is needed when the enum is used as a struct field and the struct derives Default
5391    // Only for non-generic enums — generic enums can't assume their type params implement Default
5392    if generics.is_empty() {
5393    if let Some(first_variant) = variants.first() {
5394        let enum_name_str = interner.resolve(name);
5395        let first_variant_name = interner.resolve(first_variant.name);
5396        writeln!(output, "{}impl{} Default for {}{} {{", ind, generic_str, enum_name_str, generic_str).unwrap();
5397        writeln!(output, "{}    fn default() -> Self {{", ind).unwrap();
5398        if first_variant.fields.is_empty() {
5399            writeln!(output, "{}        {}::{}", ind, enum_name_str, first_variant_name).unwrap();
5400        } else {
5401            // Default with default field values
5402            let default_fields: Vec<String> = first_variant.fields.iter()
5403                .map(|f| {
5404                    let field_name = interner.resolve(f.name);
5405                    let enum_name_check = interner.resolve(name);
5406                    if is_recursive_field(&f.ty, enum_name_check, interner) {
5407                        format!("{}: Box::new(Default::default())", field_name)
5408                    } else {
5409                        format!("{}: Default::default()", field_name)
5410                    }
5411                })
5412                .collect();
5413            writeln!(output, "{}        {}::{} {{ {} }}", ind, enum_name_str, first_variant_name, default_fields.join(", ")).unwrap();
5414        }
5415        writeln!(output, "{}    }}", ind).unwrap();
5416        writeln!(output, "{}}}\n", ind).unwrap();
5417    }
5418    }
5419
5420    output
5421}
5422
5423/// Convert FieldType to Rust type string.
5424fn codegen_field_type(ty: &FieldType, interner: &Interner) -> String {
5425    match ty {
5426        FieldType::Primitive(sym) => {
5427            match interner.resolve(*sym) {
5428                "Int" => "i64".to_string(),
5429                "Nat" => "u64".to_string(),
5430                "Text" => "String".to_string(),
5431                "Bool" | "Boolean" => "bool".to_string(),
5432                "Real" | "Float" => "f64".to_string(),
5433                "Char" => "char".to_string(),
5434                "Byte" => "u8".to_string(),
5435                "Unit" => "()".to_string(),
5436                "Duration" => "std::time::Duration".to_string(),
5437                other => other.to_string(),
5438            }
5439        }
5440        FieldType::Named(sym) => {
5441            let name = interner.resolve(*sym);
5442            match name {
5443                // Phase 49: CRDT type mapping
5444                "ConvergentCount" => "logicaffeine_data::crdt::GCounter".to_string(),
5445                // Phase 49b: New CRDT types (Wave 5)
5446                "Tally" => "logicaffeine_data::crdt::PNCounter".to_string(),
5447                _ => name.to_string(),
5448            }
5449        }
5450        FieldType::Generic { base, params } => {
5451            let base_name = interner.resolve(*base);
5452            let param_strs: Vec<String> = params.iter()
5453                .map(|p| codegen_field_type(p, interner))
5454                .collect();
5455
5456            // Phase 49c: Handle CRDT types with bias/algorithm modifiers
5457            match base_name {
5458                // SharedSet with explicit bias
5459                "SharedSet_RemoveWins" => {
5460                    return format!("logicaffeine_data::crdt::ORSet<{}, logicaffeine_data::crdt::RemoveWins>", param_strs.join(", "));
5461                }
5462                "SharedSet_AddWins" => {
5463                    return format!("logicaffeine_data::crdt::ORSet<{}, logicaffeine_data::crdt::AddWins>", param_strs.join(", "));
5464                }
5465                // SharedSequence with YATA algorithm
5466                "SharedSequence_YATA" | "CollaborativeSequence" => {
5467                    return format!("logicaffeine_data::crdt::YATA<{}>", param_strs.join(", "));
5468                }
5469                _ => {}
5470            }
5471
5472            let base_str = match base_name {
5473                "List" | "Seq" => "Vec",
5474                "Set" => "std::collections::HashSet",
5475                "Map" => "std::collections::HashMap",
5476                "Option" | "Maybe" => "Option",
5477                "Result" => "Result",
5478                // Phase 49: CRDT generic type
5479                "LastWriteWins" => "logicaffeine_data::crdt::LWWRegister",
5480                // Phase 49b: New CRDT generic types (Wave 5) - default to AddWins for ORSet
5481                "SharedSet" | "ORSet" => "logicaffeine_data::crdt::ORSet",
5482                "SharedSequence" | "RGA" => "logicaffeine_data::crdt::RGA",
5483                "SharedMap" | "ORMap" => "logicaffeine_data::crdt::ORMap",
5484                "Divergent" | "MVRegister" => "logicaffeine_data::crdt::MVRegister",
5485                other => other,
5486            };
5487            format!("{}<{}>", base_str, param_strs.join(", "))
5488        }
5489        // Phase 34: Type parameter reference (T, U, etc.)
5490        FieldType::TypeParam(sym) => interner.resolve(*sym).to_string(),
5491    }
5492}
5493
5494/// Phase 102: Check if a field type references the containing enum (recursive type).
5495/// Recursive types need to be wrapped in Box<T> for Rust to know the size.
5496fn is_recursive_field(ty: &FieldType, enum_name: &str, interner: &Interner) -> bool {
5497    match ty {
5498        FieldType::Primitive(sym) => interner.resolve(*sym) == enum_name,
5499        FieldType::Named(sym) => interner.resolve(*sym) == enum_name,
5500        FieldType::TypeParam(_) => false,
5501        FieldType::Generic { base, params } => {
5502            // Check if base matches or any type parameter contains the enum
5503            interner.resolve(*base) == enum_name ||
5504            params.iter().any(|p| is_recursive_field(p, enum_name, interner))
5505        }
5506    }
5507}
5508
5509/// Phase 103: Infer type annotation for multi-param generic enum variants.
5510/// Returns Some(type_annotation) if the enum has multiple type params, None otherwise.
5511fn infer_variant_type_annotation(
5512    expr: &Expr,
5513    registry: &TypeRegistry,
5514    interner: &Interner,
5515) -> Option<String> {
5516    // Only handle NewVariant expressions
5517    let (enum_name, variant_name, field_values) = match expr {
5518        Expr::NewVariant { enum_name, variant, fields } => (*enum_name, *variant, fields),
5519        _ => return None,
5520    };
5521
5522    // Look up the enum in the registry
5523    let enum_def = registry.get(enum_name)?;
5524    let (generics, variants) = match enum_def {
5525        TypeDef::Enum { generics, variants, .. } => (generics, variants),
5526        _ => return None,
5527    };
5528
5529    // Only generate type annotations for multi-param generics
5530    if generics.len() < 2 {
5531        return None;
5532    }
5533
5534    // Find the variant definition
5535    let variant_def = variants.iter().find(|v| v.name == variant_name)?;
5536
5537    // Collect which type params are bound by which field types
5538    let mut type_param_types: HashMap<Symbol, String> = HashMap::new();
5539    for (field_name, field_value) in field_values {
5540        // Find the field in the variant definition
5541        if let Some(field_def) = variant_def.fields.iter().find(|f| f.name == *field_name) {
5542            // If the field type is a type parameter, infer its type from the value
5543            if let FieldType::TypeParam(type_param) = &field_def.ty {
5544                let inferred = infer_rust_type_from_expr(field_value, interner);
5545                type_param_types.insert(*type_param, inferred);
5546            }
5547        }
5548    }
5549
5550    // Build the type annotation: EnumName<T1, T2, ...>
5551    // For bound params, use the inferred type; for unbound, use ()
5552    let enum_str = interner.resolve(enum_name);
5553    let param_strs: Vec<String> = generics.iter()
5554        .map(|g| {
5555            type_param_types.get(g)
5556                .cloned()
5557                .unwrap_or_else(|| "()".to_string())
5558        })
5559        .collect();
5560
5561    Some(format!("{}<{}>", enum_str, param_strs.join(", ")))
5562}
5563
5564/// Phase 103: Infer Rust type from a LOGOS expression.
5565fn infer_rust_type_from_expr(expr: &Expr, interner: &Interner) -> String {
5566    match expr {
5567        Expr::Literal(lit) => match lit {
5568            Literal::Number(_) => "i64".to_string(),
5569            Literal::Float(_) => "f64".to_string(),
5570            Literal::Text(_) => "String".to_string(),
5571            Literal::Boolean(_) => "bool".to_string(),
5572            Literal::Char(_) => "char".to_string(),
5573            Literal::Nothing => "()".to_string(),
5574            Literal::Duration(_) => "std::time::Duration".to_string(),
5575            Literal::Date(_) => "LogosDate".to_string(),
5576            Literal::Moment(_) => "LogosMoment".to_string(),
5577            Literal::Span { .. } => "LogosSpan".to_string(),
5578            Literal::Time(_) => "LogosTime".to_string(),
5579        },
5580        // For identifiers and complex expressions, let Rust infer
5581        _ => "_".to_string(),
5582    }
5583}
5584
5585/// Peephole optimization: detect `Let counter = start. While counter <= limit: body; Set counter to counter + 1`
5586/// and emit `for counter in start..=limit { body } let mut counter = limit + 1;` instead.
5587/// The for-range form enables LLVM trip count analysis, unrolling, and vectorization.
5588/// Returns (generated_code, number_of_extra_statements_consumed) or None if pattern doesn't match.
5589fn try_emit_for_range_pattern<'a>(
5590    stmts: &[&Stmt<'a>],
5591    idx: usize,
5592    interner: &Interner,
5593    indent: usize,
5594    mutable_vars: &HashSet<Symbol>,
5595    ctx: &mut RefinementContext<'a>,
5596    lww_fields: &HashSet<(String, String)>,
5597    mv_fields: &HashSet<(String, String)>,
5598    synced_vars: &mut HashSet<Symbol>,
5599    var_caps: &HashMap<Symbol, VariableCapabilities>,
5600    async_functions: &HashSet<Symbol>,
5601    pipe_vars: &HashSet<Symbol>,
5602    boxed_fields: &HashSet<(String, String, String)>,
5603    registry: &TypeRegistry,
5604) -> Option<(String, usize)> {
5605    if idx + 1 >= stmts.len() {
5606        return None;
5607    }
5608
5609    // Statement 1: Let counter = start_literal (integer)
5610    // Note: mutable flag may be false in AST even when counter is mutated via Set.
5611    // The counter's mutability is proven by the while body's increment statement.
5612    let (counter_sym, counter_start) = match stmts[idx] {
5613        Stmt::Let { var, value: Expr::Literal(Literal::Number(n)), .. } => {
5614            (*var, *n)
5615        }
5616        _ => return None,
5617    };
5618
5619    // Statement 2: While (counter <= limit) or (counter < limit)
5620    let (body, limit_expr, is_exclusive) = match stmts[idx + 1] {
5621        Stmt::While { cond, body, .. } => {
5622            match cond {
5623                Expr::BinaryOp { op: BinaryOpKind::LtEq, left, right } => {
5624                    if let Expr::Identifier(sym) = left {
5625                        if *sym == counter_sym {
5626                            (body, *right, false)
5627                        } else {
5628                            return None;
5629                        }
5630                    } else {
5631                        return None;
5632                    }
5633                }
5634                Expr::BinaryOp { op: BinaryOpKind::Lt, left, right } => {
5635                    if let Expr::Identifier(sym) = left {
5636                        if *sym == counter_sym {
5637                            (body, *right, true)
5638                        } else {
5639                            return None;
5640                        }
5641                    } else {
5642                        return None;
5643                    }
5644                }
5645                _ => return None,
5646            }
5647        }
5648        _ => return None,
5649    };
5650
5651    // Body must have at least 1 statement (the counter increment)
5652    if body.is_empty() {
5653        return None;
5654    }
5655
5656    // Last body statement must be: Set counter to counter + 1
5657    let last = &body[body.len() - 1];
5658    match last {
5659        Stmt::Set { target, value, .. } => {
5660            if *target != counter_sym {
5661                return None;
5662            }
5663            match value {
5664                Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
5665                    let is_counter_plus_1 = match (left, right) {
5666                        (Expr::Identifier(s), Expr::Literal(Literal::Number(1))) if *s == counter_sym => true,
5667                        (Expr::Literal(Literal::Number(1)), Expr::Identifier(s)) if *s == counter_sym => true,
5668                        _ => false,
5669                    };
5670                    if !is_counter_plus_1 {
5671                        return None;
5672                    }
5673                }
5674                _ => return None,
5675            }
5676        }
5677        _ => return None,
5678    }
5679
5680    // Validity: counter must NOT be modified anywhere in the body EXCEPT the last statement.
5681    // Walk all body statements (excluding the last) and check for Set { target: counter_sym }.
5682    let body_without_increment = &body[..body.len() - 1];
5683    if body_modifies_var(body_without_increment, counter_sym) {
5684        return None;
5685    }
5686
5687    // Pattern matched! Emit for-range loop.
5688    let indent_str = "    ".repeat(indent);
5689    let counter_name = interner.resolve(counter_sym);
5690    let limit_str = codegen_expr_simple(limit_expr, interner);
5691
5692    // Always use exclusive ranges (Range) instead of inclusive (RangeInclusive).
5693    // RangeInclusive has a known performance overhead in Rust due to internal
5694    // bookkeeping for edge cases, which compounds in hot inner loops.
5695    // Convert `i <= limit` to `i < (limit + 1)`.
5696    let range_str = if is_exclusive {
5697        format!("{}..{}", counter_start, limit_str)
5698    } else {
5699        // For literal limits, compute limit+1 at compile time
5700        if let Expr::Literal(Literal::Number(n)) = limit_expr {
5701            format!("{}..{}", counter_start, n + 1)
5702        } else {
5703            format!("{}..({} + 1)", counter_start, limit_str)
5704        }
5705    };
5706
5707    let mut output = String::new();
5708    writeln!(output, "{}for {} in {} {{", indent_str, counter_name, range_str).unwrap();
5709
5710    // Emit body statements (excluding the final counter increment)
5711    ctx.push_scope();
5712    let body_refs: Vec<&Stmt> = body_without_increment.iter().collect();
5713    let mut bi = 0;
5714    while bi < body_refs.len() {
5715        if let Some((code, skip)) = try_emit_swap_pattern(&body_refs, bi, interner, indent + 1, ctx.get_variable_types()) {
5716            output.push_str(&code);
5717            bi += 1 + skip;
5718            continue;
5719        }
5720        output.push_str(&codegen_stmt(body_refs[bi], interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
5721        bi += 1;
5722    }
5723    ctx.pop_scope();
5724    writeln!(output, "{}}}", indent_str).unwrap();
5725
5726    // Emit post-loop counter value so subsequent code sees the correct value.
5727    // After `while (i <= limit) { ...; i++ }`, i == limit + 1.
5728    // After `while (i < limit) { ...; i++ }`, i == limit.
5729    let post_value = if is_exclusive {
5730        limit_str
5731    } else {
5732        format!("({} + 1)", limit_str)
5733    };
5734    writeln!(output, "{}let mut {} = {};", indent_str, counter_name, post_value).unwrap();
5735
5736    Some((output, 1)) // consumed 1 extra statement (the While)
5737}
5738
5739/// Check if a slice of statements modifies a specific variable (used for for-range validity).
5740/// Recursively walks into nested If/While/Repeat blocks.
5741fn body_modifies_var(stmts: &[Stmt], sym: Symbol) -> bool {
5742    for stmt in stmts {
5743        match stmt {
5744            Stmt::Set { target, .. } if *target == sym => return true,
5745            Stmt::If { then_block, else_block, .. } => {
5746                if body_modifies_var(then_block, sym) {
5747                    return true;
5748                }
5749                if let Some(else_stmts) = else_block {
5750                    if body_modifies_var(else_stmts, sym) {
5751                        return true;
5752                    }
5753                }
5754            }
5755            Stmt::While { body, .. } => {
5756                if body_modifies_var(body, sym) {
5757                    return true;
5758                }
5759            }
5760            Stmt::Repeat { body, .. } => {
5761                if body_modifies_var(body, sym) {
5762                    return true;
5763                }
5764            }
5765            Stmt::Zone { body, .. } => {
5766                if body_modifies_var(body, sym) {
5767                    return true;
5768                }
5769            }
5770            _ => {}
5771        }
5772    }
5773    false
5774}
5775
5776/// Check if a loop body mutates a specific collection (used for iterator optimization).
5777/// Scans for Push, Pop, SetIndex, Remove, Set, and Add targeting the collection.
5778/// Recursively walks into nested If/While/Repeat/Zone blocks.
5779fn body_mutates_collection(stmts: &[Stmt], coll_sym: Symbol) -> bool {
5780    for stmt in stmts {
5781        match stmt {
5782            Stmt::Push { collection, .. } | Stmt::Pop { collection, .. }
5783            | Stmt::Add { collection, .. } | Stmt::Remove { collection, .. } => {
5784                if let Expr::Identifier(sym) = collection {
5785                    if *sym == coll_sym {
5786                        return true;
5787                    }
5788                }
5789            }
5790            Stmt::SetIndex { collection, .. } => {
5791                if let Expr::Identifier(sym) = collection {
5792                    if *sym == coll_sym {
5793                        return true;
5794                    }
5795                }
5796            }
5797            Stmt::Set { target, .. } if *target == coll_sym => return true,
5798            Stmt::If { then_block, else_block, .. } => {
5799                if body_mutates_collection(then_block, coll_sym) {
5800                    return true;
5801                }
5802                if let Some(else_stmts) = else_block {
5803                    if body_mutates_collection(else_stmts, coll_sym) {
5804                        return true;
5805                    }
5806                }
5807            }
5808            Stmt::While { body, .. } | Stmt::Repeat { body, .. } => {
5809                if body_mutates_collection(body, coll_sym) {
5810                    return true;
5811                }
5812            }
5813            Stmt::Zone { body, .. } => {
5814                if body_mutates_collection(body, coll_sym) {
5815                    return true;
5816                }
5817            }
5818            _ => {}
5819        }
5820    }
5821    false
5822}
5823
5824/// Peephole optimization: detect `Let vec = new Seq. Let i = 0. While i <= limit: push const to vec, i = i+1`
5825/// and emit `let mut vec: Vec<T> = vec![const; (limit + 1) as usize]` instead.
5826/// Returns (generated_code, number_of_extra_statements_consumed) or None if pattern doesn't match.
5827fn try_emit_vec_fill_pattern<'a>(
5828    stmts: &[&Stmt<'a>],
5829    idx: usize,
5830    interner: &Interner,
5831    indent: usize,
5832) -> Option<(String, usize)> {
5833    if idx + 2 >= stmts.len() {
5834        return None;
5835    }
5836
5837    // Statement 1: Let mutable vec_var be a new Seq of T.
5838    let (vec_sym, elem_type) = match stmts[idx] {
5839        Stmt::Let { var, value, mutable: true, ty, .. } => {
5840            // Check for explicit type annotation like `: Seq of Bool`
5841            let type_from_annotation = if let Some(TypeExpr::Generic { base, params }) = ty {
5842                let base_name = interner.resolve(*base);
5843                if matches!(base_name, "Seq" | "List" | "Vec") && !params.is_empty() {
5844                    Some(codegen_type_expr(&params[0], interner))
5845                } else {
5846                    None
5847                }
5848            } else {
5849                None
5850            };
5851
5852            // Check for `a new Seq of T`
5853            let type_from_new = if let Expr::New { type_name, type_args, init_fields } = value {
5854                let tn = interner.resolve(*type_name);
5855                if matches!(tn, "Seq" | "List" | "Vec") && init_fields.is_empty() {
5856                    if !type_args.is_empty() {
5857                        Some(codegen_type_expr(&type_args[0], interner))
5858                    } else {
5859                        None
5860                    }
5861                } else {
5862                    None
5863                }
5864            } else {
5865                None
5866            };
5867
5868            match type_from_annotation.or(type_from_new) {
5869                Some(t) => (*var, t),
5870                None => return None,
5871            }
5872        }
5873        _ => return None,
5874    };
5875
5876    // Statement 2: Let mutable counter = 0 (or 1).
5877    let (counter_sym, counter_start) = match stmts[idx + 1] {
5878        Stmt::Let { var, value: Expr::Literal(Literal::Number(n)), mutable: true, .. } => {
5879            (*var, *n)
5880        }
5881        _ => return None,
5882    };
5883
5884    // Statement 3: While counter <= limit (or counter < limit): Push const_val to vec_var. Set counter to counter + 1.
5885    match stmts[idx + 2] {
5886        Stmt::While { cond, body, .. } => {
5887            // Check condition: counter <= limit OR counter < limit
5888            let (limit_expr, is_exclusive) = match cond {
5889                Expr::BinaryOp { op: BinaryOpKind::LtEq, left, right } => {
5890                    if let Expr::Identifier(sym) = left {
5891                        if *sym == counter_sym {
5892                            (Some(*right), false)
5893                        } else {
5894                            (None, false)
5895                        }
5896                    } else {
5897                        (None, false)
5898                    }
5899                }
5900                Expr::BinaryOp { op: BinaryOpKind::Lt, left, right } => {
5901                    if let Expr::Identifier(sym) = left {
5902                        if *sym == counter_sym {
5903                            (Some(*right), true)
5904                        } else {
5905                            (None, false)
5906                        }
5907                    } else {
5908                        (None, false)
5909                    }
5910                }
5911                _ => (None, false),
5912            };
5913            let limit_expr = limit_expr?;
5914
5915            // Body must have exactly 2 statements: Push and Set
5916            if body.len() != 2 {
5917                return None;
5918            }
5919
5920            // First body stmt: Push const_val to vec_var
5921            let push_val = match &body[0] {
5922                Stmt::Push { value, collection } => {
5923                    if let Expr::Identifier(sym) = collection {
5924                        if *sym == vec_sym {
5925                            Some(*value)
5926                        } else {
5927                            None
5928                        }
5929                    } else {
5930                        None
5931                    }
5932                }
5933                _ => None,
5934            }?;
5935
5936            // Push value must be a constant literal
5937            let val_str = match push_val {
5938                Expr::Literal(Literal::Number(n)) => format!("{}", n),
5939                Expr::Literal(Literal::Float(f)) => format!("{:.1}", f),
5940                Expr::Literal(Literal::Boolean(b)) => format!("{}", b),
5941                Expr::Literal(Literal::Char(c)) => format!("'{}'", c),
5942                Expr::Literal(Literal::Text(s)) => format!("{}.to_string()", interner.resolve(*s)),
5943                _ => return None,
5944            };
5945
5946            // Second body stmt: Set counter to counter + 1
5947            match &body[1] {
5948                Stmt::Set { target, value, .. } => {
5949                    if *target != counter_sym {
5950                        return None;
5951                    }
5952                    // Value must be counter + 1
5953                    match value {
5954                        Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
5955                            let is_counter_plus_1 = match (left, right) {
5956                                (Expr::Identifier(s), Expr::Literal(Literal::Number(1))) if *s == counter_sym => true,
5957                                (Expr::Literal(Literal::Number(1)), Expr::Identifier(s)) if *s == counter_sym => true,
5958                                _ => false,
5959                            };
5960                            if !is_counter_plus_1 {
5961                                return None;
5962                            }
5963                        }
5964                        _ => return None,
5965                    }
5966                }
5967                _ => return None,
5968            }
5969
5970            // Pattern matched! Emit optimized code.
5971            let indent_str = "    ".repeat(indent);
5972            let vec_name = interner.resolve(vec_sym);
5973            let limit_str = codegen_expr_simple(limit_expr, interner);
5974
5975            // Calculate count based on bound type (exclusive vs inclusive) and start value
5976            // Inclusive (<=): count = limit - start + 1
5977            // Exclusive (<): count = limit - start
5978            let count_expr = if is_exclusive {
5979                // Exclusive bound: counter < limit
5980                if counter_start == 0 {
5981                    format!("{} as usize", limit_str)
5982                } else {
5983                    format!("({} - {}) as usize", limit_str, counter_start)
5984                }
5985            } else {
5986                // Inclusive bound: counter <= limit
5987                if counter_start == 0 {
5988                    format!("({} + 1) as usize", limit_str)
5989                } else if counter_start == 1 {
5990                    format!("{} as usize", limit_str)
5991                } else {
5992                    format!("({} - {} + 1) as usize", limit_str, counter_start)
5993                }
5994            };
5995
5996            let mut output = String::new();
5997            writeln!(output, "{}let mut {}: Vec<{}> = vec![{}; {}];",
5998                indent_str, vec_name, elem_type, val_str, count_expr).unwrap();
5999            // Re-emit counter variable declaration (it may be reused after the fill loop)
6000            let counter_name = interner.resolve(counter_sym);
6001            writeln!(output, "{}let mut {} = {};",
6002                indent_str, counter_name, counter_start).unwrap();
6003
6004            Some((output, 2)) // consumed 2 extra statements (counter init + while loop)
6005        }
6006        _ => None,
6007    }
6008}
6009
6010/// Simple expression codegen for peephole patterns (no async/context needed).
6011fn codegen_expr_simple(expr: &Expr, interner: &Interner) -> String {
6012    match expr {
6013        Expr::Literal(Literal::Number(n)) => format!("{}", n),
6014        Expr::Literal(Literal::Float(f)) => format!("{:.1}", f),
6015        Expr::Literal(Literal::Boolean(b)) => format!("{}", b),
6016        Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
6017        Expr::BinaryOp { op, left, right } => {
6018            let l = codegen_expr_simple(left, interner);
6019            let r = codegen_expr_simple(right, interner);
6020            let op_str = match op {
6021                BinaryOpKind::Add => "+",
6022                BinaryOpKind::Subtract => "-",
6023                BinaryOpKind::Multiply => "*",
6024                BinaryOpKind::Divide => "/",
6025                BinaryOpKind::Modulo => "%",
6026                _ => return format!("({})", l),
6027            };
6028            format!("({} {} {})", l, op_str, r)
6029        }
6030        _ => "_".to_string(),
6031    }
6032}
6033
6034/// Check if two expressions are structurally equal (for swap pattern detection).
6035fn exprs_equal(a: &Expr, b: &Expr) -> bool {
6036    match (a, b) {
6037        (Expr::Identifier(s1), Expr::Identifier(s2)) => s1 == s2,
6038        (Expr::Literal(Literal::Number(n1)), Expr::Literal(Literal::Number(n2))) => n1 == n2,
6039        (Expr::BinaryOp { op: op1, left: l1, right: r1 }, Expr::BinaryOp { op: op2, left: l2, right: r2 }) => {
6040            op1 == op2 && exprs_equal(l1, l2) && exprs_equal(r1, r2)
6041        }
6042        _ => false,
6043    }
6044}
6045
6046/// Peephole optimization: detect swap pattern:
6047///   Let a be item j of arr. Let b be item (j+1) of arr.
6048///   If a > b then: Set item j of arr to b. Set item (j+1) of arr to a.
6049/// and emit `arr.swap((j-1) as usize, ((j+1)-1) as usize)` instead.
6050/// Returns (generated_code, number_of_extra_statements_consumed) or None.
6051fn try_emit_swap_pattern<'a>(
6052    stmts: &[&Stmt<'a>],
6053    idx: usize,
6054    interner: &Interner,
6055    indent: usize,
6056    variable_types: &HashMap<Symbol, String>,
6057) -> Option<(String, usize)> {
6058    if idx + 2 >= stmts.len() {
6059        return None;
6060    }
6061
6062    // Statement 1: Let a be item j of arr (index expression)
6063    let (a_sym, arr_sym_1, idx_expr_1) = match stmts[idx] {
6064        Stmt::Let { var, value: Expr::Index { collection, index }, mutable: false, .. } => {
6065            if let Expr::Identifier(coll_sym) = collection {
6066                (*var, *coll_sym, *index)
6067            } else {
6068                return None;
6069            }
6070        }
6071        _ => return None,
6072    };
6073
6074    // Only optimize for known Vec types (direct indexing)
6075    if let Some(t) = variable_types.get(&arr_sym_1) {
6076        if !t.starts_with("Vec") {
6077            return None;
6078        }
6079    } else {
6080        return None;
6081    }
6082
6083    // Statement 2: Let b be item (j+1) of arr (adjacent index)
6084    let (b_sym, arr_sym_2, idx_expr_2) = match stmts[idx + 1] {
6085        Stmt::Let { var, value: Expr::Index { collection, index }, mutable: false, .. } => {
6086            if let Expr::Identifier(coll_sym) = collection {
6087                (*var, *coll_sym, *index)
6088            } else {
6089                return None;
6090            }
6091        }
6092        _ => return None,
6093    };
6094
6095    // Must be the same array
6096    if arr_sym_1 != arr_sym_2 {
6097        return None;
6098    }
6099
6100    // idx_expr_2 must be idx_expr_1 + 1
6101    let is_adjacent = match idx_expr_2 {
6102        Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
6103            (exprs_equal(left, idx_expr_1) && matches!(right, Expr::Literal(Literal::Number(1))))
6104            || (matches!(left, Expr::Literal(Literal::Number(1))) && exprs_equal(right, idx_expr_1))
6105        }
6106        _ => false,
6107    };
6108    if !is_adjacent {
6109        return None;
6110    }
6111
6112    // Statement 3: If a > b (or a < b, etc.) then: SetIndex arr j b, SetIndex arr j+1 a
6113    match stmts[idx + 2] {
6114        Stmt::If { cond, then_block, else_block } => {
6115            // Condition must compare a and b
6116            let compares_a_b = match cond {
6117                Expr::BinaryOp { op, left, right } => {
6118                    matches!(op, BinaryOpKind::Gt | BinaryOpKind::Lt | BinaryOpKind::GtEq | BinaryOpKind::LtEq | BinaryOpKind::Eq | BinaryOpKind::NotEq) &&
6119                    ((matches!(left, Expr::Identifier(s) if *s == a_sym) && matches!(right, Expr::Identifier(s) if *s == b_sym)) ||
6120                     (matches!(left, Expr::Identifier(s) if *s == b_sym) && matches!(right, Expr::Identifier(s) if *s == a_sym)))
6121                }
6122                _ => false,
6123            };
6124            if !compares_a_b {
6125                return None;
6126            }
6127
6128            // Must have no else block
6129            if else_block.is_some() {
6130                return None;
6131            }
6132
6133            // Then block must have exactly 2 SetIndex statements forming a cross-swap
6134            if then_block.len() != 2 {
6135                return None;
6136            }
6137
6138            // Check: SetIndex arr idx1 b, SetIndex arr idx2 a (cross pattern)
6139            let swap_ok = match (&then_block[0], &then_block[1]) {
6140                (
6141                    Stmt::SetIndex { collection: c1, index: i1, value: v1 },
6142                    Stmt::SetIndex { collection: c2, index: i2, value: v2 },
6143                ) => {
6144                    // c1 and c2 must be the same array
6145                    let same_arr = matches!((c1, c2), (Expr::Identifier(s1), Expr::Identifier(s2)) if *s1 == arr_sym_1 && *s2 == arr_sym_1);
6146                    // Cross pattern: set idx1 to b, set idx2 to a
6147                    let cross = exprs_equal(i1, idx_expr_1) && exprs_equal(i2, idx_expr_2) &&
6148                        matches!(v1, Expr::Identifier(s) if *s == b_sym) &&
6149                        matches!(v2, Expr::Identifier(s) if *s == a_sym);
6150                    // Also check reverse: set idx1 to b via idx2/a pattern
6151                    let cross_rev = exprs_equal(i1, idx_expr_2) && exprs_equal(i2, idx_expr_1) &&
6152                        matches!(v1, Expr::Identifier(s) if *s == a_sym) &&
6153                        matches!(v2, Expr::Identifier(s) if *s == b_sym);
6154                    same_arr && (cross || cross_rev)
6155                }
6156                _ => false,
6157            };
6158
6159            if !swap_ok {
6160                return None;
6161            }
6162
6163            // Pattern matched! Emit optimized swap
6164            let indent_str = "    ".repeat(indent);
6165            let arr_name = interner.resolve(arr_sym_1);
6166            let idx1_str = codegen_expr_simple(idx_expr_1, interner);
6167            let idx2_str = codegen_expr_simple(idx_expr_2, interner);
6168
6169            let op_str = match cond {
6170                Expr::BinaryOp { op, .. } => match op {
6171                    BinaryOpKind::Gt => ">", BinaryOpKind::Lt => "<",
6172                    BinaryOpKind::GtEq => ">=", BinaryOpKind::LtEq => "<=",
6173                    BinaryOpKind::Eq => "==", BinaryOpKind::NotEq => "!=",
6174                    _ => unreachable!(),
6175                },
6176                _ => unreachable!(),
6177            };
6178
6179            let mut output = String::new();
6180            writeln!(output, "{}if {}[({} - 1) as usize] {} {}[({} - 1) as usize] {{",
6181                indent_str, arr_name, idx1_str, op_str, arr_name, idx2_str,
6182            ).unwrap();
6183            writeln!(output, "{}    {}.swap(({} - 1) as usize, ({} - 1) as usize);",
6184                indent_str, arr_name, idx1_str, idx2_str).unwrap();
6185            writeln!(output, "{}}}", indent_str).unwrap();
6186
6187            Some((output, 2)) // consumed 2 extra statements
6188        }
6189        _ => None,
6190    }
6191}
6192
6193pub fn codegen_stmt<'a>(
6194    stmt: &Stmt<'a>,
6195    interner: &Interner,
6196    indent: usize,
6197    mutable_vars: &HashSet<Symbol>,
6198    ctx: &mut RefinementContext<'a>,
6199    lww_fields: &HashSet<(String, String)>,
6200    mv_fields: &HashSet<(String, String)>,  // Phase 49b: MVRegister fields (no timestamp)
6201    synced_vars: &mut HashSet<Symbol>,  // Phase 52: Track synced variables
6202    var_caps: &HashMap<Symbol, VariableCapabilities>,  // Phase 56: Mount+Sync detection
6203    async_functions: &HashSet<Symbol>,  // Phase 54: Functions that are async
6204    pipe_vars: &HashSet<Symbol>,  // Phase 54: Pipe declarations (have _tx/_rx suffixes)
6205    boxed_fields: &HashSet<(String, String, String)>,  // Phase 102: Recursive enum fields
6206    registry: &TypeRegistry,  // Phase 103: For type annotations on polymorphic enums
6207) -> String {
6208    let indent_str = "    ".repeat(indent);
6209    let mut output = String::new();
6210
6211    match stmt {
6212        Stmt::Let { var, ty, value, mutable } => {
6213            let var_name = interner.resolve(*var);
6214
6215            // Register collection type for direct indexing optimization.
6216            // Check explicit type annotation first, then infer from Expr::New.
6217            if let Some(TypeExpr::Generic { base, params }) = ty {
6218                let base_name = interner.resolve(*base);
6219                match base_name {
6220                    "Seq" | "List" | "Vec" => {
6221                        let rust_type = if !params.is_empty() {
6222                            format!("Vec<{}>", codegen_type_expr(&params[0], interner))
6223                        } else {
6224                            "Vec<()>".to_string()
6225                        };
6226                        ctx.register_variable_type(*var, rust_type);
6227                    }
6228                    "Map" | "HashMap" => {
6229                        let rust_type = if params.len() >= 2 {
6230                            format!("std::collections::HashMap<{}, {}>", codegen_type_expr(&params[0], interner), codegen_type_expr(&params[1], interner))
6231                        } else {
6232                            "std::collections::HashMap<String, String>".to_string()
6233                        };
6234                        ctx.register_variable_type(*var, rust_type);
6235                    }
6236                    _ => {}
6237                }
6238            } else if let Expr::New { type_name, type_args, .. } = value {
6239                let type_str = interner.resolve(*type_name);
6240                match type_str {
6241                    "Seq" | "List" | "Vec" => {
6242                        let rust_type = if !type_args.is_empty() {
6243                            format!("Vec<{}>", codegen_type_expr(&type_args[0], interner))
6244                        } else {
6245                            "Vec<()>".to_string()
6246                        };
6247                        ctx.register_variable_type(*var, rust_type);
6248                    }
6249                    "Map" | "HashMap" => {
6250                        let rust_type = if type_args.len() >= 2 {
6251                            format!("std::collections::HashMap<{}, {}>", codegen_type_expr(&type_args[0], interner), codegen_type_expr(&type_args[1], interner))
6252                        } else {
6253                            "std::collections::HashMap<String, String>".to_string()
6254                        };
6255                        ctx.register_variable_type(*var, rust_type);
6256                    }
6257                    _ => {}
6258                }
6259            } else if let Expr::List(items) = value {
6260                // Infer element type from first literal in the list for Copy elimination
6261                let elem_type = items.first()
6262                    .map(|e| infer_rust_type_from_expr(e, interner))
6263                    .unwrap_or_else(|| "_".to_string());
6264                ctx.register_variable_type(*var, format!("Vec<{}>", elem_type));
6265            }
6266
6267            // Phase 54+: Use codegen_expr_boxed with string+type tracking for proper codegen
6268            let value_str = codegen_expr_boxed_with_types(
6269                value, interner, synced_vars, boxed_fields, registry, async_functions,
6270                ctx.get_string_vars(), ctx.get_variable_types()
6271            );
6272
6273            // Phase 103: Get explicit type annotation or infer for multi-param generic enums
6274            let type_annotation = ty.map(|t| codegen_type_expr(t, interner))
6275                .or_else(|| infer_variant_type_annotation(value, registry, interner));
6276
6277            // Grand Challenge: Variable is mutable if explicitly marked OR if it's a Set target
6278            let is_mutable = *mutable || mutable_vars.contains(var);
6279
6280            match (is_mutable, type_annotation) {
6281                (true, Some(t)) => writeln!(output, "{}let mut {}: {} = {};", indent_str, var_name, t, value_str).unwrap(),
6282                (true, None) => writeln!(output, "{}let mut {} = {};", indent_str, var_name, value_str).unwrap(),
6283                (false, Some(t)) => writeln!(output, "{}let {}: {} = {};", indent_str, var_name, t, value_str).unwrap(),
6284                (false, None) => writeln!(output, "{}let {} = {};", indent_str, var_name, value_str).unwrap(),
6285            }
6286
6287            // Track string variables for proper concatenation in subsequent expressions
6288            if is_definitely_string_expr_with_vars(value, ctx.get_string_vars()) {
6289                ctx.register_string_var(*var);
6290            }
6291
6292            // Phase 43C: Handle refinement type
6293            if let Some(TypeExpr::Refinement { base: _, var: bound_var, predicate }) = ty {
6294                emit_refinement_check(var_name, *bound_var, predicate, interner, &indent_str, &mut output);
6295                ctx.register(*var, *bound_var, predicate);
6296            }
6297        }
6298
6299        Stmt::Set { target, value } => {
6300            let target_name = interner.resolve(*target);
6301            let string_vars = ctx.get_string_vars();
6302            let var_types = ctx.get_variable_types();
6303
6304            // Optimization: detect self-append pattern (result = result + x + y)
6305            // and emit write!(result, "{}{}", x, y) instead of result = format!(...).
6306            // This is O(n) amortized (in-place append) vs O(n²) (full copy each iteration).
6307            let used_write = if ctx.is_string_var(*target)
6308                && is_definitely_string_expr_with_vars(value, string_vars)
6309            {
6310                let mut operands = Vec::new();
6311                collect_string_concat_operands(value, string_vars, &mut operands);
6312
6313                // Need at least 2 operands, leftmost must be the target variable
6314                if operands.len() >= 2 && matches!(operands[0], Expr::Identifier(sym) if *sym == *target) {
6315                    // Check no other operand references target (would cause borrow conflict)
6316                    let tail = &operands[1..];
6317                    let mut tail_ids = HashSet::new();
6318                    for op in tail {
6319                        collect_expr_identifiers(op, &mut tail_ids);
6320                    }
6321
6322                    if !tail_ids.contains(target) {
6323                        // Safe to emit write!() — target not referenced in tail operands
6324                        let placeholders: String = tail.iter().map(|_| "{}").collect::<Vec<_>>().join("");
6325                        let values: Vec<String> = tail.iter().map(|e| {
6326                            // String literals can be &str inside write!() — no heap allocation needed
6327                            if let Expr::Literal(Literal::Text(sym)) = e {
6328                                format!("\"{}\"", interner.resolve(*sym))
6329                            } else {
6330                                codegen_expr_boxed_with_types(
6331                                    e, interner, synced_vars, boxed_fields, registry, async_functions,
6332                                    string_vars, var_types
6333                                )
6334                            }
6335                        }).collect();
6336                        writeln!(output, "{}write!({}, \"{}\", {}).unwrap();",
6337                            indent_str, target_name, placeholders, values.join(", ")).unwrap();
6338                        true
6339                    } else {
6340                        false
6341                    }
6342                } else {
6343                    false
6344                }
6345            } else {
6346                false
6347            };
6348
6349            if !used_write {
6350                // Fallback: standard assignment with format!
6351                let value_str = codegen_expr_boxed_with_types(
6352                    value, interner, synced_vars, boxed_fields, registry, async_functions,
6353                    string_vars, var_types
6354                );
6355                writeln!(output, "{}{} = {};", indent_str, target_name, value_str).unwrap();
6356            }
6357
6358            // Phase 43C: Check if this variable has a refinement constraint
6359            if let Some((bound_var, predicate)) = ctx.get_constraint(*target) {
6360                emit_refinement_check(target_name, bound_var, predicate, interner, &indent_str, &mut output);
6361            }
6362        }
6363
6364        Stmt::Call { function, args } => {
6365            let func_name = escape_rust_ident(interner.resolve(*function));
6366            let args_str: Vec<String> = args.iter().map(|a| codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types())).collect();
6367            // Add .await if calling an async function
6368            let await_suffix = if async_functions.contains(function) { ".await" } else { "" };
6369            writeln!(output, "{}{}({}){};", indent_str, func_name, args_str.join(", "), await_suffix).unwrap();
6370        }
6371
6372        Stmt::If { cond, then_block, else_block } => {
6373            let cond_str = codegen_expr_with_async(cond, interner, synced_vars, async_functions, ctx.get_variable_types());
6374            writeln!(output, "{}if {} {{", indent_str, cond_str).unwrap();
6375            ctx.push_scope();
6376            for stmt in *then_block {
6377                output.push_str(&codegen_stmt(stmt, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
6378            }
6379            ctx.pop_scope();
6380            if let Some(else_stmts) = else_block {
6381                writeln!(output, "{}}} else {{", indent_str).unwrap();
6382                ctx.push_scope();
6383                for stmt in *else_stmts {
6384                    output.push_str(&codegen_stmt(stmt, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
6385                }
6386                ctx.pop_scope();
6387            }
6388            writeln!(output, "{}}}", indent_str).unwrap();
6389        }
6390
6391        Stmt::While { cond, body, decreasing: _ } => {
6392            // decreasing is compile-time only, ignored at runtime
6393            let cond_str = codegen_expr_with_async(cond, interner, synced_vars, async_functions, ctx.get_variable_types());
6394            writeln!(output, "{}while {} {{", indent_str, cond_str).unwrap();
6395            ctx.push_scope();
6396            // Peephole: process body statements with peephole optimizations
6397            let body_refs: Vec<&Stmt> = body.iter().collect();
6398            let mut bi = 0;
6399            while bi < body_refs.len() {
6400                if let Some((code, skip)) = try_emit_vec_fill_pattern(&body_refs, bi, interner, indent + 1) {
6401                    output.push_str(&code);
6402                    bi += 1 + skip;
6403                    continue;
6404                }
6405                if let Some((code, skip)) = try_emit_for_range_pattern(&body_refs, bi, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry) {
6406                    output.push_str(&code);
6407                    bi += 1 + skip;
6408                    continue;
6409                }
6410                if let Some((code, skip)) = try_emit_swap_pattern(&body_refs, bi, interner, indent + 1, ctx.get_variable_types()) {
6411                    output.push_str(&code);
6412                    bi += 1 + skip;
6413                    continue;
6414                }
6415                output.push_str(&codegen_stmt(body_refs[bi], interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
6416                bi += 1;
6417            }
6418            ctx.pop_scope();
6419            writeln!(output, "{}}}", indent_str).unwrap();
6420        }
6421
6422        Stmt::Repeat { pattern, iterable, body } => {
6423            use crate::ast::stmt::Pattern;
6424
6425            // Generate pattern string for Rust code
6426            let pattern_str = match pattern {
6427                Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
6428                Pattern::Tuple(syms) => {
6429                    let names = syms.iter()
6430                        .map(|s| interner.resolve(*s))
6431                        .collect::<Vec<_>>()
6432                        .join(", ");
6433                    format!("({})", names)
6434                }
6435            };
6436
6437            let iter_str = codegen_expr_with_async(iterable, interner, synced_vars, async_functions, ctx.get_variable_types());
6438
6439            // Check if body contains async operations - if so, use while-let pattern
6440            // because standard for loops cannot contain .await
6441            let body_has_async = body.iter().any(|s| {
6442                requires_async_stmt(s) || calls_async_function(s, async_functions)
6443            });
6444
6445            if body_has_async {
6446                // Use while-let with explicit iterator for async compatibility
6447                writeln!(output, "{}let mut __iter = ({}).into_iter();", indent_str, iter_str).unwrap();
6448                writeln!(output, "{}while let Some({}) = __iter.next() {{", indent_str, pattern_str).unwrap();
6449            } else {
6450                // Optimization: for known Vec<T> with Copy element type and non-mutating body,
6451                // use .iter().copied() instead of .clone() to avoid copying the entire collection.
6452                let use_iter_copied = if let Expr::Identifier(coll_sym) = iterable {
6453                    if let Some(coll_type) = ctx.get_variable_types().get(coll_sym) {
6454                        coll_type.starts_with("Vec") && has_copy_element_type(coll_type)
6455                            && !body_mutates_collection(body, *coll_sym)
6456                    } else {
6457                        false
6458                    }
6459                } else {
6460                    false
6461                };
6462
6463                if use_iter_copied {
6464                    writeln!(output, "{}for {} in {}.iter().copied() {{", indent_str, pattern_str, iter_str).unwrap();
6465                } else {
6466                    // Clone the collection before iterating to avoid moving it.
6467                    // This allows the collection to be reused after the loop.
6468                    writeln!(output, "{}for {} in {}.clone() {{", indent_str, pattern_str, iter_str).unwrap();
6469                }
6470            }
6471            ctx.push_scope();
6472            // Peephole: process body statements with swap pattern detection
6473            {
6474                let body_refs: Vec<&Stmt> = body.iter().collect();
6475                let mut bi = 0;
6476                while bi < body_refs.len() {
6477                    if let Some((code, skip)) = try_emit_swap_pattern(&body_refs, bi, interner, indent + 1, ctx.get_variable_types()) {
6478                        output.push_str(&code);
6479                        bi += 1 + skip;
6480                        continue;
6481                    }
6482                    output.push_str(&codegen_stmt(body_refs[bi], interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
6483                    bi += 1;
6484                }
6485            }
6486            ctx.pop_scope();
6487            writeln!(output, "{}}}", indent_str).unwrap();
6488        }
6489
6490        Stmt::Return { value } => {
6491            if let Some(v) = value {
6492                let value_str = codegen_expr_with_async(v, interner, synced_vars, async_functions, ctx.get_variable_types());
6493                writeln!(output, "{}return {};", indent_str, value_str).unwrap();
6494            } else {
6495                writeln!(output, "{}return;", indent_str).unwrap();
6496            }
6497        }
6498
6499        Stmt::Assert { proposition } => {
6500            let condition = codegen_assertion(proposition, interner);
6501            writeln!(output, "{}debug_assert!({});", indent_str, condition).unwrap();
6502        }
6503
6504        // Phase 35: Trust with documented justification
6505        Stmt::Trust { proposition, justification } => {
6506            let reason = interner.resolve(*justification);
6507            // Strip quotes if present (string literals include their quotes)
6508            let reason_clean = reason.trim_matches('"');
6509            writeln!(output, "{}// TRUST: {}", indent_str, reason_clean).unwrap();
6510            let condition = codegen_assertion(proposition, interner);
6511            writeln!(output, "{}debug_assert!({});", indent_str, condition).unwrap();
6512        }
6513
6514        Stmt::RuntimeAssert { condition } => {
6515            let cond_str = codegen_expr_with_async(condition, interner, synced_vars, async_functions, ctx.get_variable_types());
6516            writeln!(output, "{}debug_assert!({});", indent_str, cond_str).unwrap();
6517        }
6518
6519        // Phase 50: Security Check - mandatory runtime guard (NEVER optimized out)
6520        Stmt::Check { subject, predicate, is_capability, object, source_text, span } => {
6521            let subj_name = interner.resolve(*subject);
6522            let pred_name = interner.resolve(*predicate).to_lowercase();
6523
6524            let call = if *is_capability {
6525                let obj_sym = object.expect("capability must have object");
6526                let obj_word = interner.resolve(obj_sym);
6527
6528                // Phase 50: Type-based resolution
6529                // "Check that user can publish the document" -> find variable of type Document
6530                // First try to find a variable whose type matches the object word
6531                let obj_name = ctx.find_variable_by_type(obj_word, interner)
6532                    .unwrap_or_else(|| obj_word.to_string());
6533
6534                format!("{}.can_{}(&{})", subj_name, pred_name, obj_name)
6535            } else {
6536                format!("{}.is_{}()", subj_name, pred_name)
6537            };
6538
6539            writeln!(output, "{}if !({}) {{", indent_str, call).unwrap();
6540            writeln!(output, "{}    logicaffeine_system::panic_with(\"Security Check Failed at line {}: {}\");",
6541                     indent_str, span.start, source_text).unwrap();
6542            writeln!(output, "{}}}", indent_str).unwrap();
6543        }
6544
6545        // Phase 51: P2P Networking - Listen on network address
6546        Stmt::Listen { address } => {
6547            let addr_str = codegen_expr_with_async(address, interner, synced_vars, async_functions, ctx.get_variable_types());
6548            // Pass &str instead of String
6549            writeln!(output, "{}logicaffeine_system::network::listen(&{}).await.expect(\"Failed to listen\");",
6550                     indent_str, addr_str).unwrap();
6551        }
6552
6553        // Phase 51: P2P Networking - Connect to remote peer
6554        Stmt::ConnectTo { address } => {
6555            let addr_str = codegen_expr_with_async(address, interner, synced_vars, async_functions, ctx.get_variable_types());
6556            // Pass &str instead of String
6557            writeln!(output, "{}logicaffeine_system::network::connect(&{}).await.expect(\"Failed to connect\");",
6558                     indent_str, addr_str).unwrap();
6559        }
6560
6561        // Phase 51: P2P Networking - Create PeerAgent remote handle
6562        Stmt::LetPeerAgent { var, address } => {
6563            let var_name = interner.resolve(*var);
6564            let addr_str = codegen_expr_with_async(address, interner, synced_vars, async_functions, ctx.get_variable_types());
6565            // Pass &str instead of String
6566            writeln!(output, "{}let {} = logicaffeine_system::network::PeerAgent::new(&{}).expect(\"Invalid address\");",
6567                     indent_str, var_name, addr_str).unwrap();
6568        }
6569
6570        // Phase 51: Sleep - supports Duration literals or milliseconds
6571        Stmt::Sleep { milliseconds } => {
6572            let expr_str = codegen_expr_with_async(milliseconds, interner, synced_vars, async_functions, ctx.get_variable_types());
6573            let inferred_type = infer_rust_type_from_expr(milliseconds, interner);
6574
6575            if inferred_type == "std::time::Duration" {
6576                // Duration type: use directly (already a std::time::Duration)
6577                writeln!(output, "{}tokio::time::sleep({}).await;",
6578                         indent_str, expr_str).unwrap();
6579            } else {
6580                // Assume milliseconds (integer) - legacy behavior
6581                writeln!(output, "{}tokio::time::sleep(std::time::Duration::from_millis({} as u64)).await;",
6582                         indent_str, expr_str).unwrap();
6583            }
6584        }
6585
6586        // Phase 52/56: Sync CRDT variable on topic
6587        Stmt::Sync { var, topic } => {
6588            let var_name = interner.resolve(*var);
6589            let topic_str = codegen_expr_with_async(topic, interner, synced_vars, async_functions, ctx.get_variable_types());
6590
6591            // Phase 56: Check if this variable is also mounted
6592            if let Some(caps) = var_caps.get(var) {
6593                if caps.mounted {
6594                    // Both Mount and Sync: use Distributed<T>
6595                    // Mount statement will handle the Distributed::mount call
6596                    // Here we just track it as synced
6597                    synced_vars.insert(*var);
6598                    return output;  // Skip - Mount will emit Distributed<T>
6599                }
6600            }
6601
6602            // Sync-only: use Synced<T>
6603            writeln!(
6604                output,
6605                "{}let {} = logicaffeine_system::crdt::Synced::new({}, &{}).await;",
6606                indent_str, var_name, var_name, topic_str
6607            ).unwrap();
6608            synced_vars.insert(*var);
6609        }
6610
6611        // Phase 53/56: Mount persistent CRDT from journal
6612        Stmt::Mount { var, path } => {
6613            let var_name = interner.resolve(*var);
6614            let path_str = codegen_expr_with_async(path, interner, synced_vars, async_functions, ctx.get_variable_types());
6615
6616            // Phase 56: Check if this variable is also synced
6617            if let Some(caps) = var_caps.get(var) {
6618                if caps.synced {
6619                    // Both Mount and Sync: use Distributed<T>
6620                    let topic_str = caps.sync_topic.as_ref()
6621                        .map(|s| s.as_str())
6622                        .unwrap_or("\"default\"");
6623                    writeln!(
6624                        output,
6625                        "{}let {} = logicaffeine_system::distributed::Distributed::mount(std::sync::Arc::new(vfs.clone()), &{}, Some({}.to_string())).await.expect(\"Failed to mount\");",
6626                        indent_str, var_name, path_str, topic_str
6627                    ).unwrap();
6628                    synced_vars.insert(*var);
6629                    return output;
6630                }
6631            }
6632
6633            // Mount-only: use Persistent<T>
6634            writeln!(
6635                output,
6636                "{}let {} = logicaffeine_system::storage::Persistent::mount(&vfs, &{}).await.expect(\"Failed to mount\");",
6637                indent_str, var_name, path_str
6638            ).unwrap();
6639            synced_vars.insert(*var);
6640        }
6641
6642        // =====================================================================
6643        // Phase 54: Go-like Concurrency Codegen
6644        // =====================================================================
6645
6646        Stmt::LaunchTask { function, args } => {
6647            let fn_name = escape_rust_ident(interner.resolve(*function));
6648            // Phase 54: When passing a pipe variable, pass the sender (_tx)
6649            let args_str: Vec<String> = args.iter()
6650                .map(|a| {
6651                    if let Expr::Identifier(sym) = a {
6652                        if pipe_vars.contains(sym) {
6653                            return format!("{}_tx.clone()", interner.resolve(*sym));
6654                        }
6655                    }
6656                    codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types())
6657                })
6658                .collect();
6659            // Phase 54: Add .await only if the function is async
6660            let await_suffix = if async_functions.contains(function) { ".await" } else { "" };
6661            writeln!(
6662                output,
6663                "{}tokio::spawn(async move {{ {}({}){await_suffix}; }});",
6664                indent_str, fn_name, args_str.join(", ")
6665            ).unwrap();
6666        }
6667
6668        Stmt::LaunchTaskWithHandle { handle, function, args } => {
6669            let handle_name = interner.resolve(*handle);
6670            let fn_name = escape_rust_ident(interner.resolve(*function));
6671            // Phase 54: When passing a pipe variable, pass the sender (_tx)
6672            let args_str: Vec<String> = args.iter()
6673                .map(|a| {
6674                    if let Expr::Identifier(sym) = a {
6675                        if pipe_vars.contains(sym) {
6676                            return format!("{}_tx.clone()", interner.resolve(*sym));
6677                        }
6678                    }
6679                    codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types())
6680                })
6681                .collect();
6682            // Phase 54: Add .await only if the function is async
6683            let await_suffix = if async_functions.contains(function) { ".await" } else { "" };
6684            writeln!(
6685                output,
6686                "{}let {} = tokio::spawn(async move {{ {}({}){await_suffix} }});",
6687                indent_str, handle_name, fn_name, args_str.join(", ")
6688            ).unwrap();
6689        }
6690
6691        Stmt::CreatePipe { var, element_type, capacity } => {
6692            let var_name = interner.resolve(*var);
6693            let type_name = interner.resolve(*element_type);
6694            let cap = capacity.unwrap_or(32);
6695            // Map LOGOS types to Rust types
6696            let rust_type = match type_name {
6697                "Int" => "i64",
6698                "Nat" => "u64",
6699                "Text" => "String",
6700                "Bool" => "bool",
6701                _ => type_name,
6702            };
6703            writeln!(
6704                output,
6705                "{}let ({}_tx, mut {}_rx) = tokio::sync::mpsc::channel::<{}>({});",
6706                indent_str, var_name, var_name, rust_type, cap
6707            ).unwrap();
6708        }
6709
6710        Stmt::SendPipe { value, pipe } => {
6711            let val_str = codegen_expr_boxed_with_types(value, interner, synced_vars, boxed_fields, registry, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
6712            let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6713            // Phase 54: Check if pipe is a local declaration (has _tx suffix) or parameter (no suffix)
6714            let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6715                pipe_vars.contains(sym)
6716            } else {
6717                false
6718            };
6719            if is_local_pipe {
6720                writeln!(
6721                    output,
6722                    "{}{}_tx.send({}).await.expect(\"pipe send failed\");",
6723                    indent_str, pipe_str, val_str
6724                ).unwrap();
6725            } else {
6726                writeln!(
6727                    output,
6728                    "{}{}.send({}).await.expect(\"pipe send failed\");",
6729                    indent_str, pipe_str, val_str
6730                ).unwrap();
6731            }
6732        }
6733
6734        Stmt::ReceivePipe { var, pipe } => {
6735            let var_name = interner.resolve(*var);
6736            let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6737            // Phase 54: Check if pipe is a local declaration (has _rx suffix) or parameter (no suffix)
6738            let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6739                pipe_vars.contains(sym)
6740            } else {
6741                false
6742            };
6743            if is_local_pipe {
6744                writeln!(
6745                    output,
6746                    "{}let {} = {}_rx.recv().await.expect(\"pipe closed\");",
6747                    indent_str, var_name, pipe_str
6748                ).unwrap();
6749            } else {
6750                writeln!(
6751                    output,
6752                    "{}let {} = {}.recv().await.expect(\"pipe closed\");",
6753                    indent_str, var_name, pipe_str
6754                ).unwrap();
6755            }
6756        }
6757
6758        Stmt::TrySendPipe { value, pipe, result } => {
6759            let val_str = codegen_expr_boxed_with_types(value, interner, synced_vars, boxed_fields, registry, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
6760            let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6761            // Phase 54: Check if pipe is a local declaration
6762            let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6763                pipe_vars.contains(sym)
6764            } else {
6765                false
6766            };
6767            let suffix = if is_local_pipe { "_tx" } else { "" };
6768            if let Some(res) = result {
6769                let res_name = interner.resolve(*res);
6770                writeln!(
6771                    output,
6772                    "{}let {} = {}{}.try_send({}).is_ok();",
6773                    indent_str, res_name, pipe_str, suffix, val_str
6774                ).unwrap();
6775            } else {
6776                writeln!(
6777                    output,
6778                    "{}let _ = {}{}.try_send({});",
6779                    indent_str, pipe_str, suffix, val_str
6780                ).unwrap();
6781            }
6782        }
6783
6784        Stmt::TryReceivePipe { var, pipe } => {
6785            let var_name = interner.resolve(*var);
6786            let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6787            // Phase 54: Check if pipe is a local declaration
6788            let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6789                pipe_vars.contains(sym)
6790            } else {
6791                false
6792            };
6793            let suffix = if is_local_pipe { "_rx" } else { "" };
6794            writeln!(
6795                output,
6796                "{}let {} = {}{}.try_recv().ok();",
6797                indent_str, var_name, pipe_str, suffix
6798            ).unwrap();
6799        }
6800
6801        Stmt::StopTask { handle } => {
6802            let handle_str = codegen_expr_with_async(handle, interner, synced_vars, async_functions, ctx.get_variable_types());
6803            writeln!(output, "{}{}.abort();", indent_str, handle_str).unwrap();
6804        }
6805
6806        Stmt::Select { branches } => {
6807            use crate::ast::stmt::SelectBranch;
6808
6809            writeln!(output, "{}tokio::select! {{", indent_str).unwrap();
6810            for branch in branches {
6811                match branch {
6812                    SelectBranch::Receive { var, pipe, body } => {
6813                        let var_name = interner.resolve(*var);
6814                        let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6815                        // Check if pipe is a local declaration (has _rx suffix) or a parameter (no suffix)
6816                        let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6817                            pipe_vars.contains(sym)
6818                        } else {
6819                            false
6820                        };
6821                        let suffix = if is_local_pipe { "_rx" } else { "" };
6822                        writeln!(
6823                            output,
6824                            "{}    {} = {}{}.recv() => {{",
6825                            indent_str, var_name, pipe_str, suffix
6826                        ).unwrap();
6827                        writeln!(
6828                            output,
6829                            "{}        if let Some({}) = {} {{",
6830                            indent_str, var_name, var_name
6831                        ).unwrap();
6832                        for stmt in *body {
6833                            let stmt_code = codegen_stmt(stmt, interner, indent + 3, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry);
6834                            write!(output, "{}", stmt_code).unwrap();
6835                        }
6836                        writeln!(output, "{}        }}", indent_str).unwrap();
6837                        writeln!(output, "{}    }}", indent_str).unwrap();
6838                    }
6839                    SelectBranch::Timeout { milliseconds, body } => {
6840                        let ms_str = codegen_expr_with_async(milliseconds, interner, synced_vars, async_functions, ctx.get_variable_types());
6841                        // Convert seconds to milliseconds if the value looks like seconds
6842                        writeln!(
6843                            output,
6844                            "{}    _ = tokio::time::sleep(std::time::Duration::from_secs({} as u64)) => {{",
6845                            indent_str, ms_str
6846                        ).unwrap();
6847                        for stmt in *body {
6848                            let stmt_code = codegen_stmt(stmt, interner, indent + 2, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry);
6849                            write!(output, "{}", stmt_code).unwrap();
6850                        }
6851                        writeln!(output, "{}    }}", indent_str).unwrap();
6852                    }
6853                }
6854            }
6855            writeln!(output, "{}}}", indent_str).unwrap();
6856        }
6857
6858        Stmt::Give { object, recipient } => {
6859            // Move semantics: pass ownership without borrowing
6860            let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
6861            let recv_str = codegen_expr_with_async(recipient, interner, synced_vars, async_functions, ctx.get_variable_types());
6862            writeln!(output, "{}{}({});", indent_str, recv_str, obj_str).unwrap();
6863        }
6864
6865        Stmt::Show { object, recipient } => {
6866            // Borrow semantics: pass immutable reference
6867            // Use string_vars for proper concatenation of string variables
6868            let obj_str = codegen_expr_with_async_and_strings(object, interner, synced_vars, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
6869            let recv_str = codegen_expr_with_async(recipient, interner, synced_vars, async_functions, ctx.get_variable_types());
6870            writeln!(output, "{}{}(&{});", indent_str, recv_str, obj_str).unwrap();
6871        }
6872
6873        Stmt::SetField { object, field, value } => {
6874            let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
6875            let field_name = interner.resolve(*field);
6876            let value_str = codegen_expr_boxed_with_types(value, interner, synced_vars, boxed_fields, registry, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
6877
6878            // Phase 49: Check if this field is an LWWRegister or MVRegister
6879            // LWW needs .set(value, timestamp), MV needs .set(value)
6880            let is_lww = lww_fields.iter().any(|(_, f)| f == field_name);
6881            let is_mv = mv_fields.iter().any(|(_, f)| f == field_name);
6882            if is_lww {
6883                // LWWRegister needs a timestamp - use current system time in microseconds
6884                writeln!(output, "{}{}.{}.set({}, std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_micros() as u64);", indent_str, obj_str, field_name, value_str).unwrap();
6885            } else if is_mv {
6886                // MVRegister just needs the value
6887                writeln!(output, "{}{}.{}.set({});", indent_str, obj_str, field_name, value_str).unwrap();
6888            } else {
6889                writeln!(output, "{}{}.{} = {};", indent_str, obj_str, field_name, value_str).unwrap();
6890            }
6891        }
6892
6893        Stmt::StructDef { .. } => {
6894            // Struct definitions are handled in codegen_program, not here
6895        }
6896
6897        Stmt::FunctionDef { .. } => {
6898            // Function definitions are handled in codegen_program, not here
6899        }
6900
6901        Stmt::Inspect { target, arms, .. } => {
6902            let target_str = codegen_expr_with_async(target, interner, synced_vars, async_functions, ctx.get_variable_types());
6903
6904            // Phase 102: Track which bindings come from boxed fields for inner Inspects
6905            // Use NAMES (strings) not symbols, because parser may create different symbols
6906            // for the same identifier in different syntactic positions.
6907            let mut inner_boxed_binding_names: HashSet<String> = HashSet::new();
6908
6909            writeln!(output, "{}match {} {{", indent_str, target_str).unwrap();
6910
6911            for arm in arms {
6912                if let Some(variant) = arm.variant {
6913                    let variant_name = interner.resolve(variant);
6914                    // Get the enum name from the arm, or fallback to just variant name
6915                    let enum_name_str = arm.enum_name.map(|e| interner.resolve(e));
6916                    let enum_prefix = enum_name_str
6917                        .map(|e| format!("{}::", e))
6918                        .unwrap_or_default();
6919
6920                    if arm.bindings.is_empty() {
6921                        // Unit variant pattern
6922                        writeln!(output, "{}    {}{} => {{", indent_str, enum_prefix, variant_name).unwrap();
6923                    } else {
6924                        // Pattern with bindings
6925                        // Phase 102: Check which bindings are from boxed fields
6926                        let bindings_str: Vec<String> = arm.bindings.iter()
6927                            .map(|(field, binding)| {
6928                                let field_name = interner.resolve(*field);
6929                                let binding_name = interner.resolve(*binding);
6930
6931                                // Check if this field is boxed
6932                                if let Some(enum_name) = enum_name_str {
6933                                    let key = (enum_name.to_string(), variant_name.to_string(), field_name.to_string());
6934                                    if boxed_fields.contains(&key) {
6935                                        inner_boxed_binding_names.insert(binding_name.to_string());
6936                                    }
6937                                }
6938
6939                                if field_name == binding_name {
6940                                    field_name.to_string()
6941                                } else {
6942                                    format!("{}: {}", field_name, binding_name)
6943                                }
6944                            })
6945                            .collect();
6946                        writeln!(output, "{}    {}{} {{ {} }} => {{", indent_str, enum_prefix, variant_name, bindings_str.join(", ")).unwrap();
6947                    }
6948                } else {
6949                    // Otherwise (wildcard) pattern
6950                    writeln!(output, "{}    _ => {{", indent_str).unwrap();
6951                }
6952
6953                ctx.push_scope();
6954
6955                // Generate explicit dereferences for boxed bindings at the start of the arm
6956                // This makes them usable as regular values in the rest of the body
6957                for binding_name in &inner_boxed_binding_names {
6958                    writeln!(output, "{}        let {} = (*{}).clone();", indent_str, binding_name, binding_name).unwrap();
6959                }
6960
6961                for stmt in arm.body {
6962                    // Phase 102: Handle inner Inspect statements with boxed bindings
6963                    // Note: Since we now dereference boxed bindings at the start of the arm,
6964                    // inner matches don't need the `*` dereference operator.
6965                    let inner_stmt_code = if let Stmt::Inspect { target: inner_target, .. } = stmt {
6966                        // Check if the inner target is a boxed binding (already dereferenced above)
6967                        // Use name comparison since symbols may differ between binding and reference
6968                        if let Expr::Identifier(sym) = inner_target {
6969                            let target_name = interner.resolve(*sym);
6970                            if inner_boxed_binding_names.contains(target_name) {
6971                                // Generate match (binding was already dereferenced at arm start)
6972                                let mut inner_output = String::new();
6973                                writeln!(inner_output, "{}match {} {{", "    ".repeat(indent + 2), target_name).unwrap();
6974
6975                                if let Stmt::Inspect { arms: inner_arms, .. } = stmt {
6976                                    for inner_arm in inner_arms.iter() {
6977                                        if let Some(v) = inner_arm.variant {
6978                                            let v_name = interner.resolve(v);
6979                                            let inner_enum_prefix = inner_arm.enum_name
6980                                                .map(|e| format!("{}::", interner.resolve(e)))
6981                                                .unwrap_or_default();
6982
6983                                            if inner_arm.bindings.is_empty() {
6984                                                writeln!(inner_output, "{}    {}{} => {{", "    ".repeat(indent + 2), inner_enum_prefix, v_name).unwrap();
6985                                            } else {
6986                                                let bindings: Vec<String> = inner_arm.bindings.iter()
6987                                                    .map(|(f, b)| {
6988                                                        let fn_name = interner.resolve(*f);
6989                                                        let bn_name = interner.resolve(*b);
6990                                                        if fn_name == bn_name { fn_name.to_string() }
6991                                                        else { format!("{}: {}", fn_name, bn_name) }
6992                                                    })
6993                                                    .collect();
6994                                                writeln!(inner_output, "{}    {}{} {{ {} }} => {{", "    ".repeat(indent + 2), inner_enum_prefix, v_name, bindings.join(", ")).unwrap();
6995                                            }
6996                                        } else {
6997                                            writeln!(inner_output, "{}    _ => {{", "    ".repeat(indent + 2)).unwrap();
6998                                        }
6999
7000                                        ctx.push_scope();
7001                                        for inner_stmt in inner_arm.body {
7002                                            inner_output.push_str(&codegen_stmt(inner_stmt, interner, indent + 4, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
7003                                        }
7004                                        ctx.pop_scope();
7005                                        writeln!(inner_output, "{}    }}", "    ".repeat(indent + 2)).unwrap();
7006                                    }
7007                                }
7008                                writeln!(inner_output, "{}}}", "    ".repeat(indent + 2)).unwrap();
7009                                inner_output
7010                            } else {
7011                                codegen_stmt(stmt, interner, indent + 2, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
7012                            }
7013                        } else {
7014                            codegen_stmt(stmt, interner, indent + 2, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
7015                        }
7016                    } else {
7017                        codegen_stmt(stmt, interner, indent + 2, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
7018                    };
7019                    output.push_str(&inner_stmt_code);
7020                }
7021                ctx.pop_scope();
7022                writeln!(output, "{}    }}", indent_str).unwrap();
7023            }
7024
7025            writeln!(output, "{}}}", indent_str).unwrap();
7026        }
7027
7028        Stmt::Push { value, collection } => {
7029            let val_str = codegen_expr_boxed_with_types(value, interner, synced_vars, boxed_fields, registry, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
7030            let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
7031            writeln!(output, "{}{}.push({});", indent_str, coll_str, val_str).unwrap();
7032        }
7033
7034        Stmt::Pop { collection, into } => {
7035            let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
7036            match into {
7037                Some(var) => {
7038                    let var_name = interner.resolve(*var);
7039                    // Unwrap the Option returned by pop() - panics if empty
7040                    writeln!(output, "{}let {} = {}.pop().expect(\"Pop from empty collection\");", indent_str, var_name, coll_str).unwrap();
7041                }
7042                None => {
7043                    writeln!(output, "{}{}.pop();", indent_str, coll_str).unwrap();
7044                }
7045            }
7046        }
7047
7048        Stmt::Add { value, collection } => {
7049            let val_str = codegen_expr_boxed_with_types(value, interner, synced_vars, boxed_fields, registry, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
7050            let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
7051            writeln!(output, "{}{}.insert({});", indent_str, coll_str, val_str).unwrap();
7052        }
7053
7054        Stmt::Remove { value, collection } => {
7055            let val_str = codegen_expr_boxed_with_types(value, interner, synced_vars, boxed_fields, registry, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
7056            let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
7057            writeln!(output, "{}{}.remove(&{});", indent_str, coll_str, val_str).unwrap();
7058        }
7059
7060        Stmt::SetIndex { collection, index, value } => {
7061            let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
7062            let index_str = codegen_expr_with_async(index, interner, synced_vars, async_functions, ctx.get_variable_types());
7063            let value_str = codegen_expr_boxed_with_types(value, interner, synced_vars, boxed_fields, registry, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
7064
7065            // Direct indexing for known collection types (avoids trait dispatch)
7066            let known_type = if let Expr::Identifier(sym) = collection {
7067                ctx.get_variable_types().get(sym).map(|s| s.as_str())
7068            } else {
7069                None
7070            };
7071
7072            match known_type {
7073                Some(t) if t.starts_with("Vec") => {
7074                    // Evaluate value first if it references the same collection (borrow safety)
7075                    if value_str.contains(&coll_str) {
7076                        writeln!(output, "{}let __set_tmp = {};", indent_str, value_str).unwrap();
7077                        writeln!(output, "{}{}[({} - 1) as usize] = __set_tmp;", indent_str, coll_str, index_str).unwrap();
7078                    } else {
7079                        writeln!(output, "{}{}[({} - 1) as usize] = {};", indent_str, coll_str, index_str, value_str).unwrap();
7080                    }
7081                }
7082                Some(t) if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") => {
7083                    writeln!(output, "{}{}.insert({}, {});", indent_str, coll_str, index_str, value_str).unwrap();
7084                }
7085                _ => {
7086                    // Fallback: polymorphic indexing via trait
7087                    if value_str.contains("logos_get") && value_str.contains(&coll_str) {
7088                        writeln!(output, "{}let __set_tmp = {};", indent_str, value_str).unwrap();
7089                        writeln!(output, "{}LogosIndexMut::logos_set(&mut {}, {}, __set_tmp);", indent_str, coll_str, index_str).unwrap();
7090                    } else {
7091                        writeln!(output, "{}LogosIndexMut::logos_set(&mut {}, {}, {});", indent_str, coll_str, index_str, value_str).unwrap();
7092                    }
7093                }
7094            }
7095        }
7096
7097        // Phase 8.5: Zone (memory arena) block
7098        Stmt::Zone { name, capacity, source_file, body } => {
7099            let zone_name = interner.resolve(*name);
7100
7101            // Generate zone creation based on type
7102            if let Some(path_sym) = source_file {
7103                // Memory-mapped file zone
7104                let path = interner.resolve(*path_sym);
7105                writeln!(
7106                    output,
7107                    "{}let {} = logicaffeine_system::memory::Zone::new_mapped(\"{}\").expect(\"Failed to map file\");",
7108                    indent_str, zone_name, path
7109                ).unwrap();
7110            } else {
7111                // Heap arena zone
7112                let cap = capacity.unwrap_or(4096); // Default 4KB
7113                writeln!(
7114                    output,
7115                    "{}let {} = logicaffeine_system::memory::Zone::new_heap({});",
7116                    indent_str, zone_name, cap
7117                ).unwrap();
7118            }
7119
7120            // Open block scope
7121            writeln!(output, "{}{{", indent_str).unwrap();
7122            ctx.push_scope();
7123
7124            // Generate body statements
7125            for stmt in *body {
7126                output.push_str(&codegen_stmt(stmt, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
7127            }
7128
7129            ctx.pop_scope();
7130            writeln!(output, "{}}}", indent_str).unwrap();
7131        }
7132
7133        // Phase 9: Concurrent execution block (async, I/O-bound)
7134        // Generates tokio::join! for concurrent task execution
7135        // Phase 51: Variables used across multiple tasks are cloned to avoid move issues
7136        Stmt::Concurrent { tasks } => {
7137            // Collect Let statements to generate tuple destructuring
7138            let let_bindings: Vec<_> = tasks.iter().filter_map(|s| {
7139                if let Stmt::Let { var, .. } = s {
7140                    Some(interner.resolve(*var).to_string())
7141                } else {
7142                    None
7143                }
7144            }).collect();
7145
7146            // Collect variables DEFINED in this block (to exclude from cloning)
7147            let defined_vars: HashSet<Symbol> = tasks.iter().filter_map(|s| {
7148                if let Stmt::Let { var, .. } = s {
7149                    Some(*var)
7150                } else {
7151                    None
7152                }
7153            }).collect();
7154
7155            // Check if there are intra-block dependencies (a later task uses a var from earlier task)
7156            // If so, fall back to sequential execution
7157            let mut has_intra_dependency = false;
7158            let mut seen_defs: HashSet<Symbol> = HashSet::new();
7159            for s in *tasks {
7160                // Check if this task uses any variable defined by previous tasks in this block
7161                let mut used_in_task: HashSet<Symbol> = HashSet::new();
7162                collect_stmt_identifiers(s, &mut used_in_task);
7163                for used_var in &used_in_task {
7164                    if seen_defs.contains(used_var) {
7165                        has_intra_dependency = true;
7166                        break;
7167                    }
7168                }
7169                // Track variables defined by this task
7170                if let Stmt::Let { var, .. } = s {
7171                    seen_defs.insert(*var);
7172                }
7173                if has_intra_dependency {
7174                    break;
7175                }
7176            }
7177
7178            // Collect ALL variables used in task expressions (not just Call args)
7179            // Exclude variables defined within this block
7180            let mut used_syms: HashSet<Symbol> = HashSet::new();
7181            for s in *tasks {
7182                collect_stmt_identifiers(s, &mut used_syms);
7183            }
7184            // Remove variables that are defined in this block
7185            for def_var in &defined_vars {
7186                used_syms.remove(def_var);
7187            }
7188            let used_vars: HashSet<String> = used_syms.iter()
7189                .map(|sym| interner.resolve(*sym).to_string())
7190                .collect();
7191
7192            // If there are intra-block dependencies, execute sequentially
7193            if has_intra_dependency {
7194                // Generate sequential Let bindings
7195                for stmt in *tasks {
7196                    output.push_str(&codegen_stmt(stmt, interner, indent, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry));
7197                }
7198            } else {
7199                // Generate concurrent execution with tokio::join!
7200                if !let_bindings.is_empty() {
7201                    // Generate tuple destructuring for concurrent Let bindings
7202                    writeln!(output, "{}let ({}) = tokio::join!(", indent_str, let_bindings.join(", ")).unwrap();
7203                } else {
7204                    writeln!(output, "{}tokio::join!(", indent_str).unwrap();
7205                }
7206
7207                for (i, stmt) in tasks.iter().enumerate() {
7208                    // For Let statements, generate only the VALUE so the async block returns it
7209                    // For Call statements, generate the call with .await
7210                    let inner_code = match stmt {
7211                        Stmt::Let { value, .. } => {
7212                            // Return the value expression directly (not "let x = value;")
7213                            // Phase 54+: Use codegen_expr_with_async to handle all nested async calls
7214                            codegen_expr_with_async(value, interner, synced_vars, async_functions, ctx.get_variable_types())
7215                        }
7216                        Stmt::Call { function, args } => {
7217                            let func_name = interner.resolve(*function);
7218                            let args_str: Vec<String> = args.iter()
7219                                .map(|a| codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types()))
7220                                .collect();
7221                            // Only add .await for async functions
7222                            let await_suffix = if async_functions.contains(function) { ".await" } else { "" };
7223                            format!("{}({}){}", func_name, args_str.join(", "), await_suffix)
7224                        }
7225                        _ => {
7226                            // Fallback for other statement types
7227                            let inner = codegen_stmt(stmt, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry);
7228                            inner.trim().to_string()
7229                        }
7230                    };
7231
7232                    // For tasks that use shared variables, wrap in a block that clones them
7233                    if !used_vars.is_empty() && i < tasks.len() - 1 {
7234                        // Clone variables for all tasks except the last one
7235                        let clones: Vec<String> = used_vars.iter()
7236                            .map(|v| format!("let {} = {}.clone();", v, v))
7237                            .collect();
7238                        write!(output, "{}    {{ {} async move {{ {} }} }}",
7239                               indent_str, clones.join(" "), inner_code).unwrap();
7240                    } else {
7241                        // Last task can use original variables
7242                        write!(output, "{}    async {{ {} }}", indent_str, inner_code).unwrap();
7243                    }
7244
7245                    if i < tasks.len() - 1 {
7246                        writeln!(output, ",").unwrap();
7247                    } else {
7248                        writeln!(output).unwrap();
7249                    }
7250                }
7251
7252                writeln!(output, "{});", indent_str).unwrap();
7253            }
7254        }
7255
7256        // Phase 9: Parallel execution block (CPU-bound)
7257        // Generates rayon::join for two tasks, or thread::spawn for 3+ tasks
7258        Stmt::Parallel { tasks } => {
7259            // Collect Let statements to generate tuple destructuring
7260            let let_bindings: Vec<_> = tasks.iter().filter_map(|s| {
7261                if let Stmt::Let { var, .. } = s {
7262                    Some(interner.resolve(*var).to_string())
7263                } else {
7264                    None
7265                }
7266            }).collect();
7267
7268            if tasks.len() == 2 {
7269                // Use rayon::join for exactly 2 tasks
7270                if !let_bindings.is_empty() {
7271                    writeln!(output, "{}let ({}) = rayon::join(", indent_str, let_bindings.join(", ")).unwrap();
7272                } else {
7273                    writeln!(output, "{}rayon::join(", indent_str).unwrap();
7274                }
7275
7276                for (i, stmt) in tasks.iter().enumerate() {
7277                    // For Let statements, generate only the VALUE so the closure returns it
7278                    let inner_code = match stmt {
7279                        Stmt::Let { value, .. } => {
7280                            // Return the value expression directly (not "let x = value;")
7281                            codegen_expr(value, interner, synced_vars)
7282                        }
7283                        Stmt::Call { function, args } => {
7284                            let func_name = interner.resolve(*function);
7285                            let args_str: Vec<String> = args.iter()
7286                                .map(|a| codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types()))
7287                                .collect();
7288                            format!("{}({})", func_name, args_str.join(", "))
7289                        }
7290                        _ => {
7291                            // Fallback for other statement types
7292                            let inner = codegen_stmt(stmt, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry);
7293                            inner.trim().to_string()
7294                        }
7295                    };
7296                    write!(output, "{}    || {{ {} }}", indent_str, inner_code).unwrap();
7297                    if i == 0 {
7298                        writeln!(output, ",").unwrap();
7299                    } else {
7300                        writeln!(output).unwrap();
7301                    }
7302                }
7303                writeln!(output, "{});", indent_str).unwrap();
7304            } else {
7305                // For 3+ tasks, use thread::spawn pattern
7306                writeln!(output, "{}{{", indent_str).unwrap();
7307                writeln!(output, "{}    let handles: Vec<_> = vec![", indent_str).unwrap();
7308                for stmt in *tasks {
7309                    // For Let statements, generate only the VALUE so the closure returns it
7310                    let inner_code = match stmt {
7311                        Stmt::Let { value, .. } => {
7312                            codegen_expr(value, interner, synced_vars)
7313                        }
7314                        Stmt::Call { function, args } => {
7315                            let func_name = interner.resolve(*function);
7316                            let args_str: Vec<String> = args.iter()
7317                                .map(|a| codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types()))
7318                                .collect();
7319                            format!("{}({})", func_name, args_str.join(", "))
7320                        }
7321                        _ => {
7322                            let inner = codegen_stmt(stmt, interner, indent + 2, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry);
7323                            inner.trim().to_string()
7324                        }
7325                    };
7326                    writeln!(output, "{}        std::thread::spawn(move || {{ {} }}),",
7327                             indent_str, inner_code).unwrap();
7328                }
7329                writeln!(output, "{}    ];", indent_str).unwrap();
7330                writeln!(output, "{}    for h in handles {{ h.join().unwrap(); }}", indent_str).unwrap();
7331                writeln!(output, "{}}}", indent_str).unwrap();
7332            }
7333        }
7334
7335        // Phase 10: Read from console or file
7336        // Phase 53: File reads now use async VFS
7337        Stmt::ReadFrom { var, source } => {
7338            let var_name = interner.resolve(*var);
7339            match source {
7340                ReadSource::Console => {
7341                    writeln!(output, "{}let {} = logicaffeine_system::io::read_line();", indent_str, var_name).unwrap();
7342                }
7343                ReadSource::File(path_expr) => {
7344                    let path_str = codegen_expr_with_async(path_expr, interner, synced_vars, async_functions, ctx.get_variable_types());
7345                    // Phase 53: Use VFS with async
7346                    writeln!(
7347                        output,
7348                        "{}let {} = vfs.read_to_string(&{}).await.expect(\"Failed to read file\");",
7349                        indent_str, var_name, path_str
7350                    ).unwrap();
7351                }
7352            }
7353        }
7354
7355        // Phase 10: Write to file
7356        // Phase 53: File writes now use async VFS
7357        Stmt::WriteFile { content, path } => {
7358            let content_str = codegen_expr_with_async(content, interner, synced_vars, async_functions, ctx.get_variable_types());
7359            let path_str = codegen_expr_with_async(path, interner, synced_vars, async_functions, ctx.get_variable_types());
7360            // Phase 53: Use VFS with async
7361            writeln!(
7362                output,
7363                "{}vfs.write(&{}, {}.as_bytes()).await.expect(\"Failed to write file\");",
7364                indent_str, path_str, content_str
7365            ).unwrap();
7366        }
7367
7368        // Phase 46: Spawn an agent
7369        Stmt::Spawn { agent_type, name } => {
7370            let type_name = interner.resolve(*agent_type);
7371            let agent_name = interner.resolve(*name);
7372            // Generate agent spawn with tokio channel
7373            writeln!(
7374                output,
7375                "{}let {} = tokio::spawn(async move {{ /* {} agent loop */ }});",
7376                indent_str, agent_name, type_name
7377            ).unwrap();
7378        }
7379
7380        // Phase 46: Send message to agent
7381        Stmt::SendMessage { message, destination } => {
7382            let msg_str = codegen_expr_with_async(message, interner, synced_vars, async_functions, ctx.get_variable_types());
7383            let dest_str = codegen_expr_with_async(destination, interner, synced_vars, async_functions, ctx.get_variable_types());
7384            writeln!(
7385                output,
7386                "{}{}.send({}).await.expect(\"Failed to send message\");",
7387                indent_str, dest_str, msg_str
7388            ).unwrap();
7389        }
7390
7391        // Phase 46: Await response from agent
7392        Stmt::AwaitMessage { source, into } => {
7393            let src_str = codegen_expr_with_async(source, interner, synced_vars, async_functions, ctx.get_variable_types());
7394            let var_name = interner.resolve(*into);
7395            writeln!(
7396                output,
7397                "{}let {} = {}.recv().await.expect(\"Failed to receive message\");",
7398                indent_str, var_name, src_str
7399            ).unwrap();
7400        }
7401
7402        // Phase 49: Merge CRDT state
7403        Stmt::MergeCrdt { source, target } => {
7404            let src_str = codegen_expr_with_async(source, interner, synced_vars, async_functions, ctx.get_variable_types());
7405            let tgt_str = codegen_expr_with_async(target, interner, synced_vars, async_functions, ctx.get_variable_types());
7406            writeln!(
7407                output,
7408                "{}{}.merge(&{});",
7409                indent_str, tgt_str, src_str
7410            ).unwrap();
7411        }
7412
7413        // Phase 49: Increment GCounter
7414        // Phase 52: If object is synced, wrap in .mutate() for auto-publish
7415        Stmt::IncreaseCrdt { object, field, amount } => {
7416            let field_name = interner.resolve(*field);
7417            let amount_str = codegen_expr_with_async(amount, interner, synced_vars, async_functions, ctx.get_variable_types());
7418
7419            // Check if the root object is synced
7420            let root_sym = get_root_identifier(object);
7421            if let Some(sym) = root_sym {
7422                if synced_vars.contains(&sym) {
7423                    // Synced: use .mutate() for auto-publish
7424                    let obj_name = interner.resolve(sym);
7425                    writeln!(
7426                        output,
7427                        "{}{}.mutate(|inner| inner.{}.increment({} as u64)).await;",
7428                        indent_str, obj_name, field_name, amount_str
7429                    ).unwrap();
7430                    return output;
7431                }
7432            }
7433
7434            // Not synced: direct access
7435            let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
7436            writeln!(
7437                output,
7438                "{}{}.{}.increment({} as u64);",
7439                indent_str, obj_str, field_name, amount_str
7440            ).unwrap();
7441        }
7442
7443        // Phase 49b: Decrement PNCounter
7444        Stmt::DecreaseCrdt { object, field, amount } => {
7445            let field_name = interner.resolve(*field);
7446            let amount_str = codegen_expr_with_async(amount, interner, synced_vars, async_functions, ctx.get_variable_types());
7447
7448            // Check if the root object is synced
7449            let root_sym = get_root_identifier(object);
7450            if let Some(sym) = root_sym {
7451                if synced_vars.contains(&sym) {
7452                    // Synced: use .mutate() for auto-publish
7453                    let obj_name = interner.resolve(sym);
7454                    writeln!(
7455                        output,
7456                        "{}{}.mutate(|inner| inner.{}.decrement({} as u64)).await;",
7457                        indent_str, obj_name, field_name, amount_str
7458                    ).unwrap();
7459                    return output;
7460                }
7461            }
7462
7463            // Not synced: direct access
7464            let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
7465            writeln!(
7466                output,
7467                "{}{}.{}.decrement({} as u64);",
7468                indent_str, obj_str, field_name, amount_str
7469            ).unwrap();
7470        }
7471
7472        // Phase 49b: Append to SharedSequence (RGA)
7473        Stmt::AppendToSequence { sequence, value } => {
7474            let seq_str = codegen_expr_with_async(sequence, interner, synced_vars, async_functions, ctx.get_variable_types());
7475            let val_str = codegen_expr_boxed_with_types(value, interner, synced_vars, boxed_fields, registry, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
7476            writeln!(
7477                output,
7478                "{}{}.append({});",
7479                indent_str, seq_str, val_str
7480            ).unwrap();
7481        }
7482
7483        // Phase 49b: Resolve MVRegister conflicts
7484        Stmt::ResolveConflict { object, field, value } => {
7485            let field_name = interner.resolve(*field);
7486            let val_str = codegen_expr_boxed_with_types(value, interner, synced_vars, boxed_fields, registry, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
7487            let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
7488            writeln!(
7489                output,
7490                "{}{}.{}.resolve({});",
7491                indent_str, obj_str, field_name, val_str
7492            ).unwrap();
7493        }
7494
7495        // Escape hatch: emit raw foreign code wrapped in braces for scope isolation
7496        Stmt::Escape { code, .. } => {
7497            let raw_code = interner.resolve(*code);
7498            write!(output, "{}{{\n", indent_str).unwrap();
7499            for line in raw_code.lines() {
7500                write!(output, "{}    {}\n", indent_str, line).unwrap();
7501            }
7502            write!(output, "{}}}\n", indent_str).unwrap();
7503        }
7504
7505        // Dependencies are metadata; no Rust code emitted.
7506        Stmt::Require { .. } => {}
7507
7508        // Phase 63: Theorems are verified at compile-time, no runtime code generated
7509        Stmt::Theorem(_) => {
7510            // Theorems don't generate runtime code - they're processed separately
7511            // by compile_theorem() at the meta-level
7512        }
7513    }
7514
7515    output
7516}
7517
7518/// Phase 52: Extract the root identifier from an expression.
7519/// For `x.field.subfield`, returns `x`.
7520fn get_root_identifier(expr: &Expr) -> Option<Symbol> {
7521    match expr {
7522        Expr::Identifier(sym) => Some(*sym),
7523        Expr::FieldAccess { object, .. } => get_root_identifier(object),
7524        _ => None,
7525    }
7526}
7527
7528/// Check if a type string represents a Copy type (no .clone() needed on indexing).
7529fn is_copy_type(ty: &str) -> bool {
7530    matches!(ty, "i64" | "u64" | "f64" | "i32" | "u32" | "f32" | "bool" | "char" | "u8" | "i8" | "()")
7531}
7532
7533/// Check if a Vec<T> type has a Copy element type.
7534fn has_copy_element_type(vec_type: &str) -> bool {
7535    if let Some(inner) = vec_type.strip_prefix("Vec<").and_then(|s| s.strip_suffix('>')) {
7536        is_copy_type(inner)
7537    } else {
7538        false
7539    }
7540}
7541
7542/// Check if a HashMap<K, V> type has a Copy value type.
7543fn has_copy_value_type(map_type: &str) -> bool {
7544    let inner = map_type.strip_prefix("std::collections::HashMap<")
7545        .or_else(|| map_type.strip_prefix("HashMap<"));
7546    if let Some(inner) = inner.and_then(|s| s.strip_suffix('>')) {
7547        // Split on ", " to get key and value types
7548        if let Some((_key, value)) = inner.split_once(", ") {
7549            return is_copy_type(value);
7550        }
7551    }
7552    false
7553}
7554
7555pub fn codegen_expr(expr: &Expr, interner: &Interner, synced_vars: &HashSet<Symbol>) -> String {
7556    // Use empty registry, boxed_fields, and async_functions for simple expression codegen
7557    let empty_registry = TypeRegistry::new();
7558    let empty_async = HashSet::new();
7559    codegen_expr_boxed(expr, interner, synced_vars, &HashSet::new(), &empty_registry, &empty_async)
7560}
7561
7562/// Phase 54+: Codegen expression with async function tracking.
7563/// Adds .await to async function calls at the expression level, handling nested calls.
7564pub fn codegen_expr_with_async(
7565    expr: &Expr,
7566    interner: &Interner,
7567    synced_vars: &HashSet<Symbol>,
7568    async_functions: &HashSet<Symbol>,
7569    variable_types: &HashMap<Symbol, String>,
7570) -> String {
7571    let empty_registry = TypeRegistry::new();
7572    let empty_strings = HashSet::new();
7573    codegen_expr_boxed_internal(expr, interner, synced_vars, &HashSet::new(), &empty_registry, async_functions, &HashSet::new(), &empty_strings, variable_types)
7574}
7575
7576/// Codegen expression with async support and string variable tracking.
7577fn codegen_expr_with_async_and_strings(
7578    expr: &Expr,
7579    interner: &Interner,
7580    synced_vars: &HashSet<Symbol>,
7581    async_functions: &HashSet<Symbol>,
7582    string_vars: &HashSet<Symbol>,
7583    variable_types: &HashMap<Symbol, String>,
7584) -> String {
7585    let empty_registry = TypeRegistry::new();
7586    codegen_expr_boxed_internal(expr, interner, synced_vars, &HashSet::new(), &empty_registry, async_functions, &HashSet::new(), string_vars, variable_types)
7587}
7588
7589/// Check if an expression is definitely numeric (safe to use + operator).
7590/// This is conservative for Add operations - treats it as string concat only
7591/// when clearly dealing with strings (string literals).
7592fn is_definitely_numeric_expr(expr: &Expr) -> bool {
7593    match expr {
7594        Expr::Literal(Literal::Number(_)) => true,
7595        Expr::Literal(Literal::Float(_)) => true,
7596        Expr::Literal(Literal::Duration(_)) => true,
7597        // Identifiers might be strings, but without a string literal nearby,
7598        // assume numeric (Rust will catch type errors)
7599        Expr::Identifier(_) => true,
7600        // Arithmetic operations are numeric
7601        Expr::BinaryOp { op: BinaryOpKind::Subtract, .. } => true,
7602        Expr::BinaryOp { op: BinaryOpKind::Multiply, .. } => true,
7603        Expr::BinaryOp { op: BinaryOpKind::Divide, .. } => true,
7604        Expr::BinaryOp { op: BinaryOpKind::Modulo, .. } => true,
7605        // Length always returns a number
7606        Expr::Length { .. } => true,
7607        // Add is numeric if both operands seem numeric
7608        Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
7609            is_definitely_numeric_expr(left) && is_definitely_numeric_expr(right)
7610        }
7611        // Function calls - assume numeric (Rust type checker will validate)
7612        Expr::Call { .. } => true,
7613        // Index expressions - assume numeric
7614        Expr::Index { .. } => true,
7615        _ => true,
7616    }
7617}
7618
7619/// Check if an expression is definitely a string (needs format! for concatenation).
7620/// Takes a set of known string variable symbols for identifier lookup.
7621fn is_definitely_string_expr_with_vars(expr: &Expr, string_vars: &HashSet<Symbol>) -> bool {
7622    match expr {
7623        // String literals are definitely strings
7624        Expr::Literal(Literal::Text(_)) => true,
7625        // Variables known to be strings
7626        Expr::Identifier(sym) => string_vars.contains(sym),
7627        // Concat always produces strings
7628        Expr::BinaryOp { op: BinaryOpKind::Concat, .. } => true,
7629        // Add with a string operand produces a string
7630        Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
7631            is_definitely_string_expr_with_vars(left, string_vars)
7632                || is_definitely_string_expr_with_vars(right, string_vars)
7633        }
7634        // WithCapacity wrapping a string value is a string
7635        Expr::WithCapacity { value, .. } => is_definitely_string_expr_with_vars(value, string_vars),
7636        _ => false,
7637    }
7638}
7639
7640/// Check if an expression is definitely a string (without variable tracking).
7641/// This is a fallback for contexts where string_vars isn't available.
7642fn is_definitely_string_expr(expr: &Expr) -> bool {
7643    let empty = HashSet::new();
7644    is_definitely_string_expr_with_vars(expr, &empty)
7645}
7646
7647/// Collect leaf operands from a chain of string Add/Concat operations.
7648///
7649/// Walks left-leaning trees of `+` (on strings) and `Concat` operations,
7650/// collecting all leaf expressions into a flat Vec. This enables emitting
7651/// a single `format!("{}{}{}", a, b, c)` instead of nested
7652/// `format!("{}{}", format!("{}{}", a, b), c)`, avoiding O(n^2) allocation.
7653fn collect_string_concat_operands<'a, 'b>(
7654    expr: &'b Expr<'a>,
7655    string_vars: &HashSet<Symbol>,
7656    operands: &mut Vec<&'b Expr<'a>>,
7657) {
7658    match expr {
7659        Expr::BinaryOp { op: BinaryOpKind::Concat, left, right } => {
7660            collect_string_concat_operands(left, string_vars, operands);
7661            collect_string_concat_operands(right, string_vars, operands);
7662        }
7663        Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
7664            let has_string = is_definitely_string_expr_with_vars(left, string_vars)
7665                || is_definitely_string_expr_with_vars(right, string_vars);
7666            if has_string {
7667                collect_string_concat_operands(left, string_vars, operands);
7668                collect_string_concat_operands(right, string_vars, operands);
7669            } else {
7670                operands.push(expr);
7671            }
7672        }
7673        _ => {
7674            operands.push(expr);
7675        }
7676    }
7677}
7678
7679/// Phase 102: Codegen with boxed field support for recursive enums.
7680/// Phase 103: Added registry for polymorphic enum type inference.
7681/// Phase 54+: Added async_functions for proper .await on nested async calls.
7682fn codegen_expr_boxed(
7683    expr: &Expr,
7684    interner: &Interner,
7685    synced_vars: &HashSet<Symbol>,
7686    boxed_fields: &HashSet<(String, String, String)>,  // (EnumName, VariantName, FieldName)
7687    registry: &TypeRegistry,  // Phase 103: For type annotations on polymorphic enums
7688    async_functions: &HashSet<Symbol>,  // Phase 54+: Functions that are async
7689) -> String {
7690    // Delegate to codegen_expr_full with empty context for boxed bindings and string vars
7691    let empty_boxed = HashSet::new();
7692    let empty_strings = HashSet::new();
7693    let empty_types = HashMap::new();
7694    codegen_expr_boxed_internal(expr, interner, synced_vars, boxed_fields, registry, async_functions, &empty_boxed, &empty_strings, &empty_types)
7695}
7696
7697/// Codegen with string variable tracking for proper string concatenation.
7698fn codegen_expr_boxed_with_strings(
7699    expr: &Expr,
7700    interner: &Interner,
7701    synced_vars: &HashSet<Symbol>,
7702    boxed_fields: &HashSet<(String, String, String)>,
7703    registry: &TypeRegistry,
7704    async_functions: &HashSet<Symbol>,
7705    string_vars: &HashSet<Symbol>,
7706) -> String {
7707    let empty_boxed = HashSet::new();
7708    let empty_types = HashMap::new();
7709    codegen_expr_boxed_internal(expr, interner, synced_vars, boxed_fields, registry, async_functions, &empty_boxed, string_vars, &empty_types)
7710}
7711
7712/// Codegen with variable type tracking for direct collection indexing optimization.
7713fn codegen_expr_boxed_with_types(
7714    expr: &Expr,
7715    interner: &Interner,
7716    synced_vars: &HashSet<Symbol>,
7717    boxed_fields: &HashSet<(String, String, String)>,
7718    registry: &TypeRegistry,
7719    async_functions: &HashSet<Symbol>,
7720    string_vars: &HashSet<Symbol>,
7721    variable_types: &HashMap<Symbol, String>,
7722) -> String {
7723    let empty_boxed = HashSet::new();
7724    codegen_expr_boxed_internal(expr, interner, synced_vars, boxed_fields, registry, async_functions, &empty_boxed, string_vars, variable_types)
7725}
7726
7727/// Internal implementation of codegen_expr_boxed that can handle extra context.
7728fn codegen_expr_boxed_internal(
7729    expr: &Expr,
7730    interner: &Interner,
7731    synced_vars: &HashSet<Symbol>,
7732    boxed_fields: &HashSet<(String, String, String)>,
7733    registry: &TypeRegistry,
7734    async_functions: &HashSet<Symbol>,
7735    boxed_bindings: &HashSet<Symbol>,
7736    string_vars: &HashSet<Symbol>,
7737    variable_types: &HashMap<Symbol, String>,
7738) -> String {
7739    // Helper macro for recursive calls with all context
7740    macro_rules! recurse {
7741        ($e:expr) => {
7742            codegen_expr_boxed_internal($e, interner, synced_vars, boxed_fields, registry, async_functions, boxed_bindings, string_vars, variable_types)
7743        };
7744    }
7745
7746    match expr {
7747        Expr::Literal(lit) => codegen_literal(lit, interner),
7748
7749        Expr::Identifier(sym) => {
7750            let name = interner.resolve(*sym).to_string();
7751            // Dereference boxed bindings from enum destructuring
7752            if boxed_bindings.contains(sym) {
7753                format!("(*{})", name)
7754            } else {
7755                name
7756            }
7757        }
7758
7759        Expr::BinaryOp { op, left, right } => {
7760            // Flatten chained string concat/add into a single format! call.
7761            // Turns O(n^2) nested format! into O(n) single-allocation.
7762            let is_string_concat = matches!(op, BinaryOpKind::Concat)
7763                || (matches!(op, BinaryOpKind::Add)
7764                    && (is_definitely_string_expr_with_vars(left, string_vars)
7765                        || is_definitely_string_expr_with_vars(right, string_vars)));
7766
7767            if is_string_concat {
7768                let mut operands = Vec::new();
7769                collect_string_concat_operands(expr, string_vars, &mut operands);
7770                let placeholders: String = operands.iter().map(|_| "{}").collect::<Vec<_>>().join("");
7771                let values: Vec<String> = operands.iter().map(|e| {
7772                    // String literals can be &str inside format!() — no heap allocation needed
7773                    if let Expr::Literal(Literal::Text(sym)) = e {
7774                        format!("\"{}\"", interner.resolve(*sym))
7775                    } else {
7776                        recurse!(e)
7777                    }
7778                }).collect();
7779                return format!("format!(\"{}\", {})", placeholders, values.join(", "));
7780            }
7781
7782            // Optimize HashMap .get() for equality comparisons to avoid cloning
7783            if matches!(op, BinaryOpKind::Eq | BinaryOpKind::NotEq) {
7784                let neg = matches!(op, BinaryOpKind::NotEq);
7785                // Check if left side is a HashMap index
7786                if let Expr::Index { collection, index } = left {
7787                    if let Expr::Identifier(sym) = collection {
7788                        if let Some(t) = variable_types.get(sym) {
7789                            if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") {
7790                                let coll_str = recurse!(collection);
7791                                let key_str = recurse!(index);
7792                                let val_str = recurse!(right);
7793                                let cmp = if neg { "!=" } else { "==" };
7794                                if has_copy_value_type(t) {
7795                                    return format!("({}.get(&({})).copied() {} Some({}))", coll_str, key_str, cmp, val_str);
7796                                } else {
7797                                    return format!("({}.get(&({})) {} Some(&({})))", coll_str, key_str, cmp, val_str);
7798                                }
7799                            }
7800                        }
7801                    }
7802                }
7803                // Check if right side is a HashMap index
7804                if let Expr::Index { collection, index } = right {
7805                    if let Expr::Identifier(sym) = collection {
7806                        if let Some(t) = variable_types.get(sym) {
7807                            if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") {
7808                                let coll_str = recurse!(collection);
7809                                let key_str = recurse!(index);
7810                                let val_str = recurse!(left);
7811                                let cmp = if neg { "!=" } else { "==" };
7812                                if has_copy_value_type(t) {
7813                                    return format!("(Some({}) {} {}.get(&({})).copied())", val_str, cmp, coll_str, key_str);
7814                                } else {
7815                                    return format!("(Some(&({})) {} {}.get(&({})))", val_str, cmp, coll_str, key_str);
7816                                }
7817                            }
7818                        }
7819                    }
7820                }
7821            }
7822
7823            let left_str = recurse!(left);
7824            let right_str = recurse!(right);
7825            let op_str = match op {
7826                BinaryOpKind::Add => "+",
7827                BinaryOpKind::Subtract => "-",
7828                BinaryOpKind::Multiply => "*",
7829                BinaryOpKind::Divide => "/",
7830                BinaryOpKind::Modulo => "%",
7831                BinaryOpKind::Eq => "==",
7832                BinaryOpKind::NotEq => "!=",
7833                BinaryOpKind::Lt => "<",
7834                BinaryOpKind::Gt => ">",
7835                BinaryOpKind::LtEq => "<=",
7836                BinaryOpKind::GtEq => ">=",
7837                BinaryOpKind::And => "&&",
7838                BinaryOpKind::Or => "||",
7839                BinaryOpKind::Concat => unreachable!(), // Handled above
7840            };
7841            format!("({} {} {})", left_str, op_str, right_str)
7842        }
7843
7844        Expr::Call { function, args } => {
7845            let func_name = escape_rust_ident(interner.resolve(*function));
7846            // Recursively codegen args with full context
7847            let args_str: Vec<String> = args.iter()
7848                .map(|a| recurse!(a))
7849                .collect();
7850            // Add .await if this function is async
7851            if async_functions.contains(function) {
7852                format!("{}({}).await", func_name, args_str.join(", "))
7853            } else {
7854                format!("{}({})", func_name, args_str.join(", "))
7855            }
7856        }
7857
7858        Expr::Index { collection, index } => {
7859            let coll_str = recurse!(collection);
7860            let index_str = recurse!(index);
7861            // Direct indexing for known collection types (avoids trait dispatch)
7862            let known_type = if let Expr::Identifier(sym) = collection {
7863                variable_types.get(sym).map(|s| s.as_str())
7864            } else {
7865                None
7866            };
7867            match known_type {
7868                Some(t) if t.starts_with("Vec") => {
7869                    let suffix = if has_copy_element_type(t) { "" } else { ".clone()" };
7870                    format!("{}[({} - 1) as usize]{}", coll_str, index_str, suffix)
7871                }
7872                Some(t) if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") => {
7873                    let suffix = if has_copy_value_type(t) { "" } else { ".clone()" };
7874                    format!("{}[&({})]{}", coll_str, index_str, suffix)
7875                }
7876                _ => {
7877                    // Fallback: polymorphic indexing via trait
7878                    format!("LogosIndex::logos_get(&{}, {})", coll_str, index_str)
7879                }
7880            }
7881        }
7882
7883        Expr::Slice { collection, start, end } => {
7884            let coll_str = recurse!(collection);
7885            let start_str = recurse!(start);
7886            let end_str = recurse!(end);
7887            // Phase 43D: 1-indexed inclusive to 0-indexed exclusive
7888            // "items 1 through 3" → &items[0..3] (elements at indices 0, 1, 2)
7889            format!("&{}[({} - 1) as usize..{} as usize]", coll_str, start_str, end_str)
7890        }
7891
7892        Expr::Copy { expr: inner } => {
7893            let expr_str = recurse!(inner);
7894            // Phase 43D: Explicit owned copy — .to_owned() is universal:
7895            // - &[T] (slices) → Vec<T> via [T]: ToOwned<Owned=Vec<T>>
7896            // - Vec<T>, HashMap<K,V>, HashSet<T> → Self via Clone blanket impl
7897            format!("{}.to_owned()", expr_str)
7898        }
7899
7900        Expr::Give { value } => {
7901            // Ownership transfer: emit value without .clone()
7902            // The move semantics are implicit in Rust - no special syntax needed
7903            recurse!(value)
7904        }
7905
7906        Expr::Length { collection } => {
7907            let coll_str = recurse!(collection);
7908            // Phase 43D: Collection length - cast to i64 for LOGOS integer semantics
7909            format!("({}.len() as i64)", coll_str)
7910        }
7911
7912        Expr::Contains { collection, value } => {
7913            let coll_str = recurse!(collection);
7914            let val_str = recurse!(value);
7915            // Use LogosContains trait for unified contains across List, Set, Map, Text
7916            format!("{}.logos_contains(&{})", coll_str, val_str)
7917        }
7918
7919        Expr::Union { left, right } => {
7920            let left_str = recurse!(left);
7921            let right_str = recurse!(right);
7922            format!("{}.union(&{}).cloned().collect::<std::collections::HashSet<_>>()", left_str, right_str)
7923        }
7924
7925        Expr::Intersection { left, right } => {
7926            let left_str = recurse!(left);
7927            let right_str = recurse!(right);
7928            format!("{}.intersection(&{}).cloned().collect::<std::collections::HashSet<_>>()", left_str, right_str)
7929        }
7930
7931        // Phase 48: Sipping Protocol expressions
7932        Expr::ManifestOf { zone } => {
7933            let zone_str = recurse!(zone);
7934            format!("logicaffeine_system::network::FileSipper::from_zone(&{}).manifest()", zone_str)
7935        }
7936
7937        Expr::ChunkAt { index, zone } => {
7938            let zone_str = recurse!(zone);
7939            let index_str = recurse!(index);
7940            // LOGOS uses 1-indexed, Rust uses 0-indexed
7941            format!("logicaffeine_system::network::FileSipper::from_zone(&{}).get_chunk(({} - 1) as usize)", zone_str, index_str)
7942        }
7943
7944        Expr::List(ref items) => {
7945            let item_strs: Vec<String> = items.iter()
7946                .map(|i| recurse!(i))
7947                .collect();
7948            format!("vec![{}]", item_strs.join(", "))
7949        }
7950
7951        Expr::Tuple(ref items) => {
7952            let item_strs: Vec<String> = items.iter()
7953                .map(|i| format!("Value::from({})", recurse!(i)))
7954                .collect();
7955            // Tuples as Vec<Value> for heterogeneous support
7956            format!("vec![{}]", item_strs.join(", "))
7957        }
7958
7959        Expr::Range { start, end } => {
7960            let start_str = recurse!(start);
7961            let end_str = recurse!(end);
7962            format!("({}..={})", start_str, end_str)
7963        }
7964
7965        Expr::FieldAccess { object, field } => {
7966            let field_name = interner.resolve(*field);
7967
7968            // Phase 52: Check if root object is synced - use .get().await
7969            let root_sym = get_root_identifier(object);
7970            if let Some(sym) = root_sym {
7971                if synced_vars.contains(&sym) {
7972                    let obj_name = interner.resolve(sym);
7973                    return format!("{}.get().await.{}", obj_name, field_name);
7974                }
7975            }
7976
7977            let obj_str = recurse!(object);
7978            format!("{}.{}", obj_str, field_name)
7979        }
7980
7981        Expr::New { type_name, type_args, init_fields } => {
7982            let type_str = interner.resolve(*type_name);
7983            if !init_fields.is_empty() {
7984                // Struct initialization with fields: Point { x: 10, y: 20, ..Default::default() }
7985                // Always add ..Default::default() to handle partial initialization (e.g., CRDT fields)
7986                let fields_str = init_fields.iter()
7987                    .map(|(name, value)| {
7988                        let field_name = interner.resolve(*name);
7989                        let value_str = recurse!(value);
7990                        format!("{}: {}", field_name, value_str)
7991                    })
7992                    .collect::<Vec<_>>()
7993                    .join(", ");
7994                format!("{} {{ {}, ..Default::default() }}", type_str, fields_str)
7995            } else if type_args.is_empty() {
7996                format!("{}::default()", type_str)
7997            } else {
7998                // Phase 34: Turbofish syntax for generic instantiation
7999                // Bug fix: Use codegen_type_expr to support nested types like Seq of (Seq of Int)
8000                let args_str = type_args.iter()
8001                    .map(|t| codegen_type_expr(t, interner))
8002                    .collect::<Vec<_>>()
8003                    .join(", ");
8004                format!("{}::<{}>::default()", type_str, args_str)
8005            }
8006        }
8007
8008        Expr::NewVariant { enum_name, variant, fields } => {
8009            let enum_str = interner.resolve(*enum_name);
8010            let variant_str = interner.resolve(*variant);
8011            if fields.is_empty() {
8012                // Unit variant: Shape::Point
8013                format!("{}::{}", enum_str, variant_str)
8014            } else {
8015                // Phase 103: Count identifier usage to handle cloning for reused values
8016                // We need to clone on all uses except the last one
8017                let mut identifier_counts: HashMap<Symbol, usize> = HashMap::new();
8018                for (_, value) in fields.iter() {
8019                    if let Expr::Identifier(sym) = value {
8020                        *identifier_counts.entry(*sym).or_insert(0) += 1;
8021                    }
8022                }
8023
8024                // Track remaining uses for each identifier
8025                let mut remaining_uses: HashMap<Symbol, usize> = identifier_counts.clone();
8026
8027                // Struct variant: Shape::Circle { radius: 10 }
8028                // Phase 102: Check if any field is recursive and needs Box::new()
8029                let fields_str: Vec<String> = fields.iter()
8030                    .map(|(field_name, value)| {
8031                        let name = interner.resolve(*field_name);
8032
8033                        // Phase 103: Clone identifiers that are used multiple times
8034                        // Clone on all uses except the last one (to allow move on final use)
8035                        let val = if let Expr::Identifier(sym) = value {
8036                            let total = identifier_counts.get(sym).copied().unwrap_or(0);
8037                            let remaining = remaining_uses.get_mut(sym);
8038                            let base_name = if boxed_bindings.contains(sym) {
8039                                format!("(*{})", interner.resolve(*sym))
8040                            } else {
8041                                interner.resolve(*sym).to_string()
8042                            };
8043                            if total > 1 {
8044                                if let Some(r) = remaining {
8045                                    *r -= 1;
8046                                    if *r > 0 {
8047                                        // Not the last use, need to clone
8048                                        format!("{}.clone()", base_name)
8049                                    } else {
8050                                        // Last use, can move
8051                                        base_name
8052                                    }
8053                                } else {
8054                                    base_name
8055                                }
8056                            } else {
8057                                base_name
8058                            }
8059                        } else {
8060                            recurse!(value)
8061                        };
8062
8063                        // Check if this field needs to be boxed (recursive type)
8064                        let key = (enum_str.to_string(), variant_str.to_string(), name.to_string());
8065                        if boxed_fields.contains(&key) {
8066                            format!("{}: Box::new({})", name, val)
8067                        } else {
8068                            format!("{}: {}", name, val)
8069                        }
8070                    })
8071                    .collect();
8072                format!("{}::{} {{ {} }}", enum_str, variant_str, fields_str.join(", "))
8073            }
8074        }
8075
8076        Expr::OptionSome { value } => {
8077            format!("Some({})", recurse!(value))
8078        }
8079
8080        Expr::OptionNone => {
8081            "None".to_string()
8082        }
8083
8084        Expr::Escape { code, .. } => {
8085            let raw_code = interner.resolve(*code);
8086            let mut block = String::from("{\n");
8087            for line in raw_code.lines() {
8088                block.push_str("    ");
8089                block.push_str(line);
8090                block.push('\n');
8091            }
8092            block.push('}');
8093            block
8094        }
8095
8096        Expr::WithCapacity { value, capacity } => {
8097            let cap_str = recurse!(capacity);
8098            match value {
8099                // Empty string → String::with_capacity(cap)
8100                Expr::Literal(Literal::Text(sym)) if interner.resolve(*sym).is_empty() => {
8101                    format!("String::with_capacity(({}) as usize)", cap_str)
8102                }
8103                // Non-empty string → { let mut __s = String::with_capacity(cap); __s.push_str("..."); __s }
8104                Expr::Literal(Literal::Text(sym)) => {
8105                    let text = interner.resolve(*sym);
8106                    format!("{{ let mut __s = String::with_capacity(({}) as usize); __s.push_str(\"{}\"); __s }}", cap_str, text)
8107                }
8108                // Collection Expr::New → Type::with_capacity(cap)
8109                Expr::New { type_name, type_args, .. } => {
8110                    let type_str = interner.resolve(*type_name);
8111                    match type_str {
8112                        "Seq" | "List" | "Vec" => {
8113                            let elem = if !type_args.is_empty() {
8114                                codegen_type_expr(&type_args[0], interner)
8115                            } else { "()".to_string() };
8116                            format!("{{ let __v: Vec<{}> = Vec::with_capacity(({}) as usize); __v }}", elem, cap_str)
8117                        }
8118                        "Map" | "HashMap" => {
8119                            let (k, v) = if type_args.len() >= 2 {
8120                                (codegen_type_expr(&type_args[0], interner),
8121                                 codegen_type_expr(&type_args[1], interner))
8122                            } else { ("String".to_string(), "String".to_string()) };
8123                            format!("{{ let __m: std::collections::HashMap<{}, {}> = std::collections::HashMap::with_capacity(({}) as usize); __m }}", k, v, cap_str)
8124                        }
8125                        "Set" | "HashSet" => {
8126                            let elem = if !type_args.is_empty() {
8127                                codegen_type_expr(&type_args[0], interner)
8128                            } else { "()".to_string() };
8129                            format!("{{ let __s: std::collections::HashSet<{}> = std::collections::HashSet::with_capacity(({}) as usize); __s }}", elem, cap_str)
8130                        }
8131                        _ => recurse!(value) // Unknown type — ignore capacity
8132                    }
8133                }
8134                // Other expressions — ignore capacity hint
8135                _ => recurse!(value)
8136            }
8137        }
8138
8139        Expr::Closure { params, body, .. } => {
8140            use crate::ast::stmt::ClosureBody;
8141            let params_str: Vec<String> = params.iter()
8142                .map(|(name, ty)| {
8143                    let param_name = escape_rust_ident(interner.resolve(*name));
8144                    let param_type = codegen_type_expr(ty, interner);
8145                    format!("{}: {}", param_name, param_type)
8146                })
8147                .collect();
8148
8149            match body {
8150                ClosureBody::Expression(expr) => {
8151                    let body_str = recurse!(expr);
8152                    format!("move |{}| {{ {} }}", params_str.join(", "), body_str)
8153                }
8154                ClosureBody::Block(stmts) => {
8155                    let mut body_str = String::new();
8156                    let mut ctx = RefinementContext::new();
8157                    let empty_mutable = collect_mutable_vars(stmts);
8158                    let empty_lww = HashSet::new();
8159                    let empty_mv = HashSet::new();
8160                    let mut empty_synced = HashSet::new();
8161                    let empty_caps = HashMap::new();
8162                    let empty_pipes = HashSet::new();
8163                    let empty_boxed = HashSet::new();
8164                    let empty_registry = TypeRegistry::new();
8165                    for stmt in stmts.iter() {
8166                        body_str.push_str(&codegen_stmt(
8167                            stmt, interner, 2, &empty_mutable, &mut ctx,
8168                            &empty_lww, &empty_mv, &mut empty_synced, &empty_caps,
8169                            async_functions, &empty_pipes, &empty_boxed, &empty_registry,
8170                        ));
8171                    }
8172                    format!("move |{}| {{\n{}{}}}", params_str.join(", "), body_str, "    ")
8173                }
8174            }
8175        }
8176
8177        Expr::CallExpr { callee, args } => {
8178            let callee_str = recurse!(callee);
8179            let args_str: Vec<String> = args.iter().map(|a| recurse!(a)).collect();
8180            format!("({})({})", callee_str, args_str.join(", "))
8181        }
8182    }
8183}
8184
8185fn codegen_literal(lit: &Literal, interner: &Interner) -> String {
8186    match lit {
8187        Literal::Number(n) => n.to_string(),
8188        Literal::Float(f) => format!("{}f64", f),
8189        // String literals are converted to String for consistent Text type handling
8190        Literal::Text(sym) => format!("String::from(\"{}\")", interner.resolve(*sym)),
8191        Literal::Boolean(b) => b.to_string(),
8192        Literal::Nothing => "()".to_string(),
8193        // Character literals
8194        Literal::Char(c) => {
8195            // Handle escape sequences for special characters
8196            match c {
8197                '\n' => "'\\n'".to_string(),
8198                '\t' => "'\\t'".to_string(),
8199                '\r' => "'\\r'".to_string(),
8200                '\\' => "'\\\\'".to_string(),
8201                '\'' => "'\\''".to_string(),
8202                '\0' => "'\\0'".to_string(),
8203                c => format!("'{}'", c),
8204            }
8205        }
8206        // Temporal literals: Duration stored as nanoseconds (i64)
8207        Literal::Duration(nanos) => format!("std::time::Duration::from_nanos({}u64)", nanos),
8208        // Date stored as days since Unix epoch (i32)
8209        Literal::Date(days) => format!("LogosDate({})", days),
8210        // Moment stored as nanoseconds since Unix epoch (i64)
8211        Literal::Moment(nanos) => format!("LogosMoment({})", nanos),
8212        // Span stored as (months, days) - separate because they're incommensurable
8213        Literal::Span { months, days } => format!("LogosSpan::new({}, {})", months, days),
8214        // Time-of-day stored as nanoseconds from midnight
8215        Literal::Time(nanos) => format!("LogosTime({})", nanos),
8216    }
8217}
8218
8219/// Converts a LogicExpr to a Rust boolean expression for debug_assert!().
8220/// Uses RustFormatter to unify all logic-to-Rust translation.
8221pub fn codegen_assertion(expr: &LogicExpr, interner: &Interner) -> String {
8222    let mut registry = SymbolRegistry::new();
8223    let formatter = RustFormatter;
8224    let mut buf = String::new();
8225
8226    match expr.write_logic(&mut buf, &mut registry, interner, &formatter) {
8227        Ok(_) => buf,
8228        Err(_) => "/* error generating assertion */ false".to_string(),
8229    }
8230}
8231
8232pub fn codegen_term(term: &Term, interner: &Interner) -> String {
8233    match term {
8234        Term::Constant(sym) => interner.resolve(*sym).to_string(),
8235        Term::Variable(sym) => interner.resolve(*sym).to_string(),
8236        Term::Value { kind, .. } => match kind {
8237            NumberKind::Integer(n) => n.to_string(),
8238            NumberKind::Real(f) => f.to_string(),
8239            NumberKind::Symbolic(sym) => interner.resolve(*sym).to_string(),
8240        },
8241        Term::Function(name, args) => {
8242            let args_str: Vec<String> = args.iter()
8243                .map(|a| codegen_term(a, interner))
8244                .collect();
8245            format!("{}({})", interner.resolve(*name), args_str.join(", "))
8246        }
8247        Term::Possessed { possessor, possessed } => {
8248            let poss_str = codegen_term(possessor, interner);
8249            format!("{}.{}", poss_str, interner.resolve(*possessed))
8250        }
8251        Term::Group(members) => {
8252            let members_str: Vec<String> = members.iter()
8253                .map(|m| codegen_term(m, interner))
8254                .collect();
8255            format!("({})", members_str.join(", "))
8256        }
8257        _ => "/* unsupported Term */".to_string(),
8258    }
8259}
8260
8261#[cfg(test)]
8262mod tests {
8263    use super::*;
8264
8265    #[test]
8266    fn test_literal_number() {
8267        let interner = Interner::new();
8268        let synced_vars = HashSet::new();
8269        let expr = Expr::Literal(Literal::Number(42));
8270        assert_eq!(codegen_expr(&expr, &interner, &synced_vars), "42");
8271    }
8272
8273    #[test]
8274    fn test_literal_boolean() {
8275        let interner = Interner::new();
8276        let synced_vars = HashSet::new();
8277        assert_eq!(codegen_expr(&Expr::Literal(Literal::Boolean(true)), &interner, &synced_vars), "true");
8278        assert_eq!(codegen_expr(&Expr::Literal(Literal::Boolean(false)), &interner, &synced_vars), "false");
8279    }
8280
8281    #[test]
8282    fn test_literal_nothing() {
8283        let interner = Interner::new();
8284        let synced_vars = HashSet::new();
8285        assert_eq!(codegen_expr(&Expr::Literal(Literal::Nothing), &interner, &synced_vars), "()");
8286    }
8287}