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    // Bail out if the limit expression is too complex for codegen_expr_simple
5688    // (it returns "_" for unhandled expressions like `length of`).
5689    if !is_simple_expr(limit_expr) {
5690        return None;
5691    }
5692
5693    // Pattern matched! Emit for-range loop.
5694    let indent_str = "    ".repeat(indent);
5695    let counter_name = interner.resolve(counter_sym);
5696    let limit_str = codegen_expr_simple(limit_expr, interner);
5697
5698    // Always use exclusive ranges (Range) instead of inclusive (RangeInclusive).
5699    // RangeInclusive has a known performance overhead in Rust due to internal
5700    // bookkeeping for edge cases, which compounds in hot inner loops.
5701    // Convert `i <= limit` to `i < (limit + 1)`.
5702    let range_str = if is_exclusive {
5703        format!("{}..{}", counter_start, limit_str)
5704    } else {
5705        // For literal limits, compute limit+1 at compile time
5706        if let Expr::Literal(Literal::Number(n)) = limit_expr {
5707            format!("{}..{}", counter_start, n + 1)
5708        } else {
5709            format!("{}..({} + 1)", counter_start, limit_str)
5710        }
5711    };
5712
5713    let mut output = String::new();
5714    writeln!(output, "{}for {} in {} {{", indent_str, counter_name, range_str).unwrap();
5715
5716    // Emit body statements (excluding the final counter increment)
5717    ctx.push_scope();
5718    let body_refs: Vec<&Stmt> = body_without_increment.iter().collect();
5719    let mut bi = 0;
5720    while bi < body_refs.len() {
5721        if let Some((code, skip)) = try_emit_swap_pattern(&body_refs, bi, interner, indent + 1, ctx.get_variable_types()) {
5722            output.push_str(&code);
5723            bi += 1 + skip;
5724            continue;
5725        }
5726        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));
5727        bi += 1;
5728    }
5729    ctx.pop_scope();
5730    writeln!(output, "{}}}", indent_str).unwrap();
5731
5732    // Emit post-loop counter value so subsequent code sees the correct value.
5733    // After `while (i <= limit) { ...; i++ }`, i == limit + 1.
5734    // After `while (i < limit) { ...; i++ }`, i == limit.
5735    // If the loop never executes (start >= limit), the counter must stay at start.
5736    // Use max(start, limit) to handle both cases correctly.
5737    let post_value = if is_exclusive {
5738        if let Expr::Literal(Literal::Number(n)) = limit_expr {
5739            // Both start and limit known at compile time
5740            format!("{}", std::cmp::max(counter_start, *n))
5741        } else {
5742            format!("({}_i64).max({})", counter_start, limit_str)
5743        }
5744    } else {
5745        if let Expr::Literal(Literal::Number(n)) = limit_expr {
5746            format!("{}", std::cmp::max(counter_start, n + 1))
5747        } else {
5748            format!("({}_i64).max({} + 1)", counter_start, limit_str)
5749        }
5750    };
5751    writeln!(output, "{}let mut {} = {};", indent_str, counter_name, post_value).unwrap();
5752
5753    Some((output, 1)) // consumed 1 extra statement (the While)
5754}
5755
5756/// Check if a slice of statements modifies a specific variable (used for for-range validity).
5757/// Recursively walks into nested If/While/Repeat blocks.
5758fn body_modifies_var(stmts: &[Stmt], sym: Symbol) -> bool {
5759    for stmt in stmts {
5760        match stmt {
5761            Stmt::Set { target, .. } if *target == sym => return true,
5762            Stmt::If { then_block, else_block, .. } => {
5763                if body_modifies_var(then_block, sym) {
5764                    return true;
5765                }
5766                if let Some(else_stmts) = else_block {
5767                    if body_modifies_var(else_stmts, sym) {
5768                        return true;
5769                    }
5770                }
5771            }
5772            Stmt::While { body, .. } => {
5773                if body_modifies_var(body, sym) {
5774                    return true;
5775                }
5776            }
5777            Stmt::Repeat { body, .. } => {
5778                if body_modifies_var(body, sym) {
5779                    return true;
5780                }
5781            }
5782            Stmt::Zone { body, .. } => {
5783                if body_modifies_var(body, sym) {
5784                    return true;
5785                }
5786            }
5787            _ => {}
5788        }
5789    }
5790    false
5791}
5792
5793/// Check if a loop body mutates a specific collection (used for iterator optimization).
5794/// Scans for Push, Pop, SetIndex, Remove, Set, and Add targeting the collection.
5795/// Recursively walks into nested If/While/Repeat/Zone blocks.
5796fn body_mutates_collection(stmts: &[Stmt], coll_sym: Symbol) -> bool {
5797    for stmt in stmts {
5798        match stmt {
5799            Stmt::Push { collection, .. } | Stmt::Pop { collection, .. }
5800            | Stmt::Add { collection, .. } | Stmt::Remove { collection, .. } => {
5801                if let Expr::Identifier(sym) = collection {
5802                    if *sym == coll_sym {
5803                        return true;
5804                    }
5805                }
5806            }
5807            Stmt::SetIndex { collection, .. } => {
5808                if let Expr::Identifier(sym) = collection {
5809                    if *sym == coll_sym {
5810                        return true;
5811                    }
5812                }
5813            }
5814            Stmt::Set { target, .. } if *target == coll_sym => return true,
5815            Stmt::If { then_block, else_block, .. } => {
5816                if body_mutates_collection(then_block, coll_sym) {
5817                    return true;
5818                }
5819                if let Some(else_stmts) = else_block {
5820                    if body_mutates_collection(else_stmts, coll_sym) {
5821                        return true;
5822                    }
5823                }
5824            }
5825            Stmt::While { body, .. } | Stmt::Repeat { body, .. } => {
5826                if body_mutates_collection(body, coll_sym) {
5827                    return true;
5828                }
5829            }
5830            Stmt::Zone { body, .. } => {
5831                if body_mutates_collection(body, coll_sym) {
5832                    return true;
5833                }
5834            }
5835            _ => {}
5836        }
5837    }
5838    false
5839}
5840
5841/// Peephole optimization: detect `Let vec = new Seq. Let i = 0. While i <= limit: push const to vec, i = i+1`
5842/// and emit `let mut vec: Vec<T> = vec![const; (limit + 1) as usize]` instead.
5843/// Returns (generated_code, number_of_extra_statements_consumed) or None if pattern doesn't match.
5844fn try_emit_vec_fill_pattern<'a>(
5845    stmts: &[&Stmt<'a>],
5846    idx: usize,
5847    interner: &Interner,
5848    indent: usize,
5849) -> Option<(String, usize)> {
5850    if idx + 2 >= stmts.len() {
5851        return None;
5852    }
5853
5854    // Statement 1: Let [mutable] vec_var be a new Seq of T.
5855    // Note: mutable keyword is optional — mutability is inferred from Push in the loop body.
5856    let (vec_sym, elem_type) = match stmts[idx] {
5857        Stmt::Let { var, value, ty, .. } => {
5858            // Check for explicit type annotation like `: Seq of Bool`
5859            let type_from_annotation = if let Some(TypeExpr::Generic { base, params }) = ty {
5860                let base_name = interner.resolve(*base);
5861                if matches!(base_name, "Seq" | "List" | "Vec") && !params.is_empty() {
5862                    Some(codegen_type_expr(&params[0], interner))
5863                } else {
5864                    None
5865                }
5866            } else {
5867                None
5868            };
5869
5870            // Check for `a new Seq of T`
5871            let type_from_new = if let Expr::New { type_name, type_args, init_fields } = value {
5872                let tn = interner.resolve(*type_name);
5873                if matches!(tn, "Seq" | "List" | "Vec") && init_fields.is_empty() {
5874                    if !type_args.is_empty() {
5875                        Some(codegen_type_expr(&type_args[0], interner))
5876                    } else {
5877                        None
5878                    }
5879                } else {
5880                    None
5881                }
5882            } else {
5883                None
5884            };
5885
5886            match type_from_annotation.or(type_from_new) {
5887                Some(t) => (*var, t),
5888                None => return None,
5889            }
5890        }
5891        _ => return None,
5892    };
5893
5894    // Statement 2: Let [mutable] counter = 0 (or 1).
5895    // Note: mutable keyword is optional — mutability is inferred from Set in the loop body.
5896    let (counter_sym, counter_start) = match stmts[idx + 1] {
5897        Stmt::Let { var, value: Expr::Literal(Literal::Number(n)), .. } => {
5898            (*var, *n)
5899        }
5900        _ => return None,
5901    };
5902
5903    // Statement 3: While counter <= limit (or counter < limit): Push const_val to vec_var. Set counter to counter + 1.
5904    match stmts[idx + 2] {
5905        Stmt::While { cond, body, .. } => {
5906            // Check condition: counter <= limit OR counter < limit
5907            let (limit_expr, is_exclusive) = match cond {
5908                Expr::BinaryOp { op: BinaryOpKind::LtEq, left, right } => {
5909                    if let Expr::Identifier(sym) = left {
5910                        if *sym == counter_sym {
5911                            (Some(*right), false)
5912                        } else {
5913                            (None, false)
5914                        }
5915                    } else {
5916                        (None, false)
5917                    }
5918                }
5919                Expr::BinaryOp { op: BinaryOpKind::Lt, left, right } => {
5920                    if let Expr::Identifier(sym) = left {
5921                        if *sym == counter_sym {
5922                            (Some(*right), true)
5923                        } else {
5924                            (None, false)
5925                        }
5926                    } else {
5927                        (None, false)
5928                    }
5929                }
5930                _ => (None, false),
5931            };
5932            let limit_expr = limit_expr?;
5933
5934            // Body must have exactly 2 statements: Push and Set
5935            if body.len() != 2 {
5936                return None;
5937            }
5938
5939            // First body stmt: Push const_val to vec_var
5940            let push_val = match &body[0] {
5941                Stmt::Push { value, collection } => {
5942                    if let Expr::Identifier(sym) = collection {
5943                        if *sym == vec_sym {
5944                            Some(*value)
5945                        } else {
5946                            None
5947                        }
5948                    } else {
5949                        None
5950                    }
5951                }
5952                _ => None,
5953            }?;
5954
5955            // Push value must be a constant literal
5956            let val_str = match push_val {
5957                Expr::Literal(Literal::Number(n)) => format!("{}", n),
5958                Expr::Literal(Literal::Float(f)) => format!("{:.1}", f),
5959                Expr::Literal(Literal::Boolean(b)) => format!("{}", b),
5960                Expr::Literal(Literal::Char(c)) => format!("'{}'", c),
5961                Expr::Literal(Literal::Text(s)) => format!("{}.to_string()", interner.resolve(*s)),
5962                _ => return None,
5963            };
5964
5965            // Second body stmt: Set counter to counter + 1
5966            match &body[1] {
5967                Stmt::Set { target, value, .. } => {
5968                    if *target != counter_sym {
5969                        return None;
5970                    }
5971                    // Value must be counter + 1
5972                    match value {
5973                        Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
5974                            let is_counter_plus_1 = match (left, right) {
5975                                (Expr::Identifier(s), Expr::Literal(Literal::Number(1))) if *s == counter_sym => true,
5976                                (Expr::Literal(Literal::Number(1)), Expr::Identifier(s)) if *s == counter_sym => true,
5977                                _ => false,
5978                            };
5979                            if !is_counter_plus_1 {
5980                                return None;
5981                            }
5982                        }
5983                        _ => return None,
5984                    }
5985                }
5986                _ => return None,
5987            }
5988
5989            // Pattern matched! Emit optimized code.
5990            let indent_str = "    ".repeat(indent);
5991            let vec_name = interner.resolve(vec_sym);
5992            let limit_str = codegen_expr_simple(limit_expr, interner);
5993
5994            // Calculate count based on bound type (exclusive vs inclusive) and start value
5995            // Inclusive (<=): count = limit - start + 1
5996            // Exclusive (<): count = limit - start
5997            let count_expr = if is_exclusive {
5998                // Exclusive bound: counter < limit
5999                if counter_start == 0 {
6000                    format!("{} as usize", limit_str)
6001                } else {
6002                    format!("({} - {}) as usize", limit_str, counter_start)
6003                }
6004            } else {
6005                // Inclusive bound: counter <= limit
6006                if counter_start == 0 {
6007                    format!("({} + 1) as usize", limit_str)
6008                } else if counter_start == 1 {
6009                    format!("{} as usize", limit_str)
6010                } else {
6011                    format!("({} - {} + 1) as usize", limit_str, counter_start)
6012                }
6013            };
6014
6015            let mut output = String::new();
6016            writeln!(output, "{}let mut {}: Vec<{}> = vec![{}; {}];",
6017                indent_str, vec_name, elem_type, val_str, count_expr).unwrap();
6018            // Re-emit counter variable declaration (it may be reused after the fill loop)
6019            let counter_name = interner.resolve(counter_sym);
6020            writeln!(output, "{}let mut {} = {};",
6021                indent_str, counter_name, counter_start).unwrap();
6022
6023            Some((output, 2)) // consumed 2 extra statements (counter init + while loop)
6024        }
6025        _ => None,
6026    }
6027}
6028
6029/// Check if an expression can be handled by codegen_expr_simple without fallback.
6030fn is_simple_expr(expr: &Expr) -> bool {
6031    match expr {
6032        Expr::Literal(Literal::Number(_))
6033        | Expr::Literal(Literal::Float(_))
6034        | Expr::Literal(Literal::Boolean(_))
6035        | Expr::Identifier(_) => true,
6036        Expr::BinaryOp { op, left, right } => {
6037            matches!(op,
6038                BinaryOpKind::Add | BinaryOpKind::Subtract |
6039                BinaryOpKind::Multiply | BinaryOpKind::Divide | BinaryOpKind::Modulo
6040            ) && is_simple_expr(left) && is_simple_expr(right)
6041        }
6042        _ => false,
6043    }
6044}
6045
6046/// Simple expression codegen for peephole patterns (no async/context needed).
6047fn codegen_expr_simple(expr: &Expr, interner: &Interner) -> String {
6048    match expr {
6049        Expr::Literal(Literal::Number(n)) => format!("{}", n),
6050        Expr::Literal(Literal::Float(f)) => format!("{:.1}", f),
6051        Expr::Literal(Literal::Boolean(b)) => format!("{}", b),
6052        Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
6053        Expr::BinaryOp { op, left, right } => {
6054            let l = codegen_expr_simple(left, interner);
6055            let r = codegen_expr_simple(right, interner);
6056            let op_str = match op {
6057                BinaryOpKind::Add => "+",
6058                BinaryOpKind::Subtract => "-",
6059                BinaryOpKind::Multiply => "*",
6060                BinaryOpKind::Divide => "/",
6061                BinaryOpKind::Modulo => "%",
6062                _ => return format!("({})", l),
6063            };
6064            format!("({} {} {})", l, op_str, r)
6065        }
6066        _ => "_".to_string(),
6067    }
6068}
6069
6070/// Check if two expressions are structurally equal (for swap pattern detection).
6071fn exprs_equal(a: &Expr, b: &Expr) -> bool {
6072    match (a, b) {
6073        (Expr::Identifier(s1), Expr::Identifier(s2)) => s1 == s2,
6074        (Expr::Literal(Literal::Number(n1)), Expr::Literal(Literal::Number(n2))) => n1 == n2,
6075        (Expr::BinaryOp { op: op1, left: l1, right: r1 }, Expr::BinaryOp { op: op2, left: l2, right: r2 }) => {
6076            op1 == op2 && exprs_equal(l1, l2) && exprs_equal(r1, r2)
6077        }
6078        _ => false,
6079    }
6080}
6081
6082/// Peephole optimization: detect swap pattern:
6083///   Let a be item j of arr. Let b be item (j+1) of arr.
6084///   If a > b then: Set item j of arr to b. Set item (j+1) of arr to a.
6085/// and emit `arr.swap((j-1) as usize, ((j+1)-1) as usize)` instead.
6086/// Returns (generated_code, number_of_extra_statements_consumed) or None.
6087fn try_emit_swap_pattern<'a>(
6088    stmts: &[&Stmt<'a>],
6089    idx: usize,
6090    interner: &Interner,
6091    indent: usize,
6092    variable_types: &HashMap<Symbol, String>,
6093) -> Option<(String, usize)> {
6094    if idx + 2 >= stmts.len() {
6095        return None;
6096    }
6097
6098    // Statement 1: Let a be item j of arr (index expression)
6099    let (a_sym, arr_sym_1, idx_expr_1) = match stmts[idx] {
6100        Stmt::Let { var, value: Expr::Index { collection, index }, mutable: false, .. } => {
6101            if let Expr::Identifier(coll_sym) = collection {
6102                (*var, *coll_sym, *index)
6103            } else {
6104                return None;
6105            }
6106        }
6107        _ => return None,
6108    };
6109
6110    // Only optimize for known Vec types (direct indexing)
6111    if let Some(t) = variable_types.get(&arr_sym_1) {
6112        if !t.starts_with("Vec") {
6113            return None;
6114        }
6115    } else {
6116        return None;
6117    }
6118
6119    // Statement 2: Let b be item (j+1) of arr (adjacent index)
6120    let (b_sym, arr_sym_2, idx_expr_2) = match stmts[idx + 1] {
6121        Stmt::Let { var, value: Expr::Index { collection, index }, mutable: false, .. } => {
6122            if let Expr::Identifier(coll_sym) = collection {
6123                (*var, *coll_sym, *index)
6124            } else {
6125                return None;
6126            }
6127        }
6128        _ => return None,
6129    };
6130
6131    // Must be the same array
6132    if arr_sym_1 != arr_sym_2 {
6133        return None;
6134    }
6135
6136    // idx_expr_2 must be idx_expr_1 + 1
6137    let is_adjacent = match idx_expr_2 {
6138        Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
6139            (exprs_equal(left, idx_expr_1) && matches!(right, Expr::Literal(Literal::Number(1))))
6140            || (matches!(left, Expr::Literal(Literal::Number(1))) && exprs_equal(right, idx_expr_1))
6141        }
6142        _ => false,
6143    };
6144    if !is_adjacent {
6145        return None;
6146    }
6147
6148    // Statement 3: If a > b (or a < b, etc.) then: SetIndex arr j b, SetIndex arr j+1 a
6149    match stmts[idx + 2] {
6150        Stmt::If { cond, then_block, else_block } => {
6151            // Condition must compare a and b
6152            let compares_a_b = match cond {
6153                Expr::BinaryOp { op, left, right } => {
6154                    matches!(op, BinaryOpKind::Gt | BinaryOpKind::Lt | BinaryOpKind::GtEq | BinaryOpKind::LtEq | BinaryOpKind::Eq | BinaryOpKind::NotEq) &&
6155                    ((matches!(left, Expr::Identifier(s) if *s == a_sym) && matches!(right, Expr::Identifier(s) if *s == b_sym)) ||
6156                     (matches!(left, Expr::Identifier(s) if *s == b_sym) && matches!(right, Expr::Identifier(s) if *s == a_sym)))
6157                }
6158                _ => false,
6159            };
6160            if !compares_a_b {
6161                return None;
6162            }
6163
6164            // Must have no else block
6165            if else_block.is_some() {
6166                return None;
6167            }
6168
6169            // Then block must have exactly 2 SetIndex statements forming a cross-swap
6170            if then_block.len() != 2 {
6171                return None;
6172            }
6173
6174            // Check: SetIndex arr idx1 b, SetIndex arr idx2 a (cross pattern)
6175            let swap_ok = match (&then_block[0], &then_block[1]) {
6176                (
6177                    Stmt::SetIndex { collection: c1, index: i1, value: v1 },
6178                    Stmt::SetIndex { collection: c2, index: i2, value: v2 },
6179                ) => {
6180                    // c1 and c2 must be the same array
6181                    let same_arr = matches!((c1, c2), (Expr::Identifier(s1), Expr::Identifier(s2)) if *s1 == arr_sym_1 && *s2 == arr_sym_1);
6182                    // Cross pattern: set idx1 to b, set idx2 to a
6183                    let cross = exprs_equal(i1, idx_expr_1) && exprs_equal(i2, idx_expr_2) &&
6184                        matches!(v1, Expr::Identifier(s) if *s == b_sym) &&
6185                        matches!(v2, Expr::Identifier(s) if *s == a_sym);
6186                    // Also check reverse: set idx1 to b via idx2/a pattern
6187                    let cross_rev = exprs_equal(i1, idx_expr_2) && exprs_equal(i2, idx_expr_1) &&
6188                        matches!(v1, Expr::Identifier(s) if *s == a_sym) &&
6189                        matches!(v2, Expr::Identifier(s) if *s == b_sym);
6190                    same_arr && (cross || cross_rev)
6191                }
6192                _ => false,
6193            };
6194
6195            if !swap_ok {
6196                return None;
6197            }
6198
6199            // Pattern matched! Emit optimized swap
6200            let indent_str = "    ".repeat(indent);
6201            let arr_name = interner.resolve(arr_sym_1);
6202            let idx1_str = codegen_expr_simple(idx_expr_1, interner);
6203            let idx2_str = codegen_expr_simple(idx_expr_2, interner);
6204
6205            let op_str = match cond {
6206                Expr::BinaryOp { op, .. } => match op {
6207                    BinaryOpKind::Gt => ">", BinaryOpKind::Lt => "<",
6208                    BinaryOpKind::GtEq => ">=", BinaryOpKind::LtEq => "<=",
6209                    BinaryOpKind::Eq => "==", BinaryOpKind::NotEq => "!=",
6210                    _ => unreachable!(),
6211                },
6212                _ => unreachable!(),
6213            };
6214
6215            let mut output = String::new();
6216            writeln!(output, "{}if {}[({} - 1) as usize] {} {}[({} - 1) as usize] {{",
6217                indent_str, arr_name, idx1_str, op_str, arr_name, idx2_str,
6218            ).unwrap();
6219            writeln!(output, "{}    {}.swap(({} - 1) as usize, ({} - 1) as usize);",
6220                indent_str, arr_name, idx1_str, idx2_str).unwrap();
6221            writeln!(output, "{}}}", indent_str).unwrap();
6222
6223            Some((output, 2)) // consumed 2 extra statements
6224        }
6225        _ => None,
6226    }
6227}
6228
6229pub fn codegen_stmt<'a>(
6230    stmt: &Stmt<'a>,
6231    interner: &Interner,
6232    indent: usize,
6233    mutable_vars: &HashSet<Symbol>,
6234    ctx: &mut RefinementContext<'a>,
6235    lww_fields: &HashSet<(String, String)>,
6236    mv_fields: &HashSet<(String, String)>,  // Phase 49b: MVRegister fields (no timestamp)
6237    synced_vars: &mut HashSet<Symbol>,  // Phase 52: Track synced variables
6238    var_caps: &HashMap<Symbol, VariableCapabilities>,  // Phase 56: Mount+Sync detection
6239    async_functions: &HashSet<Symbol>,  // Phase 54: Functions that are async
6240    pipe_vars: &HashSet<Symbol>,  // Phase 54: Pipe declarations (have _tx/_rx suffixes)
6241    boxed_fields: &HashSet<(String, String, String)>,  // Phase 102: Recursive enum fields
6242    registry: &TypeRegistry,  // Phase 103: For type annotations on polymorphic enums
6243) -> String {
6244    let indent_str = "    ".repeat(indent);
6245    let mut output = String::new();
6246
6247    match stmt {
6248        Stmt::Let { var, ty, value, mutable } => {
6249            let var_name = interner.resolve(*var);
6250
6251            // Register collection type for direct indexing optimization.
6252            // Check explicit type annotation first, then infer from Expr::New.
6253            if let Some(TypeExpr::Generic { base, params }) = ty {
6254                let base_name = interner.resolve(*base);
6255                match base_name {
6256                    "Seq" | "List" | "Vec" => {
6257                        let rust_type = if !params.is_empty() {
6258                            format!("Vec<{}>", codegen_type_expr(&params[0], interner))
6259                        } else {
6260                            "Vec<()>".to_string()
6261                        };
6262                        ctx.register_variable_type(*var, rust_type);
6263                    }
6264                    "Map" | "HashMap" => {
6265                        let rust_type = if params.len() >= 2 {
6266                            format!("std::collections::HashMap<{}, {}>", codegen_type_expr(&params[0], interner), codegen_type_expr(&params[1], interner))
6267                        } else {
6268                            "std::collections::HashMap<String, String>".to_string()
6269                        };
6270                        ctx.register_variable_type(*var, rust_type);
6271                    }
6272                    _ => {}
6273                }
6274            } else if let Expr::New { type_name, type_args, .. } = value {
6275                let type_str = interner.resolve(*type_name);
6276                match type_str {
6277                    "Seq" | "List" | "Vec" => {
6278                        let rust_type = if !type_args.is_empty() {
6279                            format!("Vec<{}>", codegen_type_expr(&type_args[0], interner))
6280                        } else {
6281                            "Vec<()>".to_string()
6282                        };
6283                        ctx.register_variable_type(*var, rust_type);
6284                    }
6285                    "Map" | "HashMap" => {
6286                        let rust_type = if type_args.len() >= 2 {
6287                            format!("std::collections::HashMap<{}, {}>", codegen_type_expr(&type_args[0], interner), codegen_type_expr(&type_args[1], interner))
6288                        } else {
6289                            "std::collections::HashMap<String, String>".to_string()
6290                        };
6291                        ctx.register_variable_type(*var, rust_type);
6292                    }
6293                    _ => {}
6294                }
6295            } else if let Expr::List(items) = value {
6296                // Infer element type from first literal in the list for Copy elimination
6297                let elem_type = items.first()
6298                    .map(|e| infer_rust_type_from_expr(e, interner))
6299                    .unwrap_or_else(|| "_".to_string());
6300                ctx.register_variable_type(*var, format!("Vec<{}>", elem_type));
6301            }
6302
6303            // Phase 54+: Use codegen_expr_boxed with string+type tracking for proper codegen
6304            let value_str = codegen_expr_boxed_with_types(
6305                value, interner, synced_vars, boxed_fields, registry, async_functions,
6306                ctx.get_string_vars(), ctx.get_variable_types()
6307            );
6308
6309            // Phase 103: Get explicit type annotation or infer for multi-param generic enums
6310            let type_annotation = ty.map(|t| codegen_type_expr(t, interner))
6311                .or_else(|| infer_variant_type_annotation(value, registry, interner));
6312
6313            // Grand Challenge: Variable is mutable if explicitly marked OR if it's a Set target
6314            let is_mutable = *mutable || mutable_vars.contains(var);
6315
6316            match (is_mutable, type_annotation) {
6317                (true, Some(t)) => writeln!(output, "{}let mut {}: {} = {};", indent_str, var_name, t, value_str).unwrap(),
6318                (true, None) => writeln!(output, "{}let mut {} = {};", indent_str, var_name, value_str).unwrap(),
6319                (false, Some(t)) => writeln!(output, "{}let {}: {} = {};", indent_str, var_name, t, value_str).unwrap(),
6320                (false, None) => writeln!(output, "{}let {} = {};", indent_str, var_name, value_str).unwrap(),
6321            }
6322
6323            // Track string variables for proper concatenation in subsequent expressions
6324            if is_definitely_string_expr_with_vars(value, ctx.get_string_vars()) {
6325                ctx.register_string_var(*var);
6326            }
6327
6328            // Phase 43C: Handle refinement type
6329            if let Some(TypeExpr::Refinement { base: _, var: bound_var, predicate }) = ty {
6330                emit_refinement_check(var_name, *bound_var, predicate, interner, &indent_str, &mut output);
6331                ctx.register(*var, *bound_var, predicate);
6332            }
6333        }
6334
6335        Stmt::Set { target, value } => {
6336            let target_name = interner.resolve(*target);
6337            let string_vars = ctx.get_string_vars();
6338            let var_types = ctx.get_variable_types();
6339
6340            // Optimization: detect self-append pattern (result = result + x + y)
6341            // and emit write!(result, "{}{}", x, y) instead of result = format!(...).
6342            // This is O(n) amortized (in-place append) vs O(n²) (full copy each iteration).
6343            let used_write = if ctx.is_string_var(*target)
6344                && is_definitely_string_expr_with_vars(value, string_vars)
6345            {
6346                let mut operands = Vec::new();
6347                collect_string_concat_operands(value, string_vars, &mut operands);
6348
6349                // Need at least 2 operands, leftmost must be the target variable
6350                if operands.len() >= 2 && matches!(operands[0], Expr::Identifier(sym) if *sym == *target) {
6351                    // Check no other operand references target (would cause borrow conflict)
6352                    let tail = &operands[1..];
6353                    let mut tail_ids = HashSet::new();
6354                    for op in tail {
6355                        collect_expr_identifiers(op, &mut tail_ids);
6356                    }
6357
6358                    if !tail_ids.contains(target) {
6359                        // Safe to emit write!() — target not referenced in tail operands
6360                        let placeholders: String = tail.iter().map(|_| "{}").collect::<Vec<_>>().join("");
6361                        let values: Vec<String> = tail.iter().map(|e| {
6362                            // String literals can be &str inside write!() — no heap allocation needed
6363                            if let Expr::Literal(Literal::Text(sym)) = e {
6364                                format!("\"{}\"", interner.resolve(*sym))
6365                            } else {
6366                                codegen_expr_boxed_with_types(
6367                                    e, interner, synced_vars, boxed_fields, registry, async_functions,
6368                                    string_vars, var_types
6369                                )
6370                            }
6371                        }).collect();
6372                        writeln!(output, "{}write!({}, \"{}\", {}).unwrap();",
6373                            indent_str, target_name, placeholders, values.join(", ")).unwrap();
6374                        true
6375                    } else {
6376                        false
6377                    }
6378                } else {
6379                    false
6380                }
6381            } else {
6382                false
6383            };
6384
6385            if !used_write {
6386                // Fallback: standard assignment with format!
6387                let value_str = codegen_expr_boxed_with_types(
6388                    value, interner, synced_vars, boxed_fields, registry, async_functions,
6389                    string_vars, var_types
6390                );
6391                writeln!(output, "{}{} = {};", indent_str, target_name, value_str).unwrap();
6392            }
6393
6394            // Phase 43C: Check if this variable has a refinement constraint
6395            if let Some((bound_var, predicate)) = ctx.get_constraint(*target) {
6396                emit_refinement_check(target_name, bound_var, predicate, interner, &indent_str, &mut output);
6397            }
6398        }
6399
6400        Stmt::Call { function, args } => {
6401            let func_name = escape_rust_ident(interner.resolve(*function));
6402            let args_str: Vec<String> = args.iter().map(|a| codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types())).collect();
6403            // Add .await if calling an async function
6404            let await_suffix = if async_functions.contains(function) { ".await" } else { "" };
6405            writeln!(output, "{}{}({}){};", indent_str, func_name, args_str.join(", "), await_suffix).unwrap();
6406        }
6407
6408        Stmt::If { cond, then_block, else_block } => {
6409            let cond_str = codegen_expr_with_async(cond, interner, synced_vars, async_functions, ctx.get_variable_types());
6410            writeln!(output, "{}if {} {{", indent_str, cond_str).unwrap();
6411            ctx.push_scope();
6412            for stmt in *then_block {
6413                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));
6414            }
6415            ctx.pop_scope();
6416            if let Some(else_stmts) = else_block {
6417                writeln!(output, "{}}} else {{", indent_str).unwrap();
6418                ctx.push_scope();
6419                for stmt in *else_stmts {
6420                    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));
6421                }
6422                ctx.pop_scope();
6423            }
6424            writeln!(output, "{}}}", indent_str).unwrap();
6425        }
6426
6427        Stmt::While { cond, body, decreasing: _ } => {
6428            // decreasing is compile-time only, ignored at runtime
6429            let cond_str = codegen_expr_with_async(cond, interner, synced_vars, async_functions, ctx.get_variable_types());
6430            writeln!(output, "{}while {} {{", indent_str, cond_str).unwrap();
6431            ctx.push_scope();
6432            // Peephole: process body statements with peephole optimizations
6433            let body_refs: Vec<&Stmt> = body.iter().collect();
6434            let mut bi = 0;
6435            while bi < body_refs.len() {
6436                if let Some((code, skip)) = try_emit_vec_fill_pattern(&body_refs, bi, interner, indent + 1) {
6437                    output.push_str(&code);
6438                    bi += 1 + skip;
6439                    continue;
6440                }
6441                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) {
6442                    output.push_str(&code);
6443                    bi += 1 + skip;
6444                    continue;
6445                }
6446                if let Some((code, skip)) = try_emit_swap_pattern(&body_refs, bi, interner, indent + 1, ctx.get_variable_types()) {
6447                    output.push_str(&code);
6448                    bi += 1 + skip;
6449                    continue;
6450                }
6451                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));
6452                bi += 1;
6453            }
6454            ctx.pop_scope();
6455            writeln!(output, "{}}}", indent_str).unwrap();
6456        }
6457
6458        Stmt::Repeat { pattern, iterable, body } => {
6459            use crate::ast::stmt::Pattern;
6460
6461            // Generate pattern string for Rust code
6462            let pattern_str = match pattern {
6463                Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
6464                Pattern::Tuple(syms) => {
6465                    let names = syms.iter()
6466                        .map(|s| interner.resolve(*s))
6467                        .collect::<Vec<_>>()
6468                        .join(", ");
6469                    format!("({})", names)
6470                }
6471            };
6472
6473            let iter_str = codegen_expr_with_async(iterable, interner, synced_vars, async_functions, ctx.get_variable_types());
6474
6475            // Check if body contains async operations - if so, use while-let pattern
6476            // because standard for loops cannot contain .await
6477            let body_has_async = body.iter().any(|s| {
6478                requires_async_stmt(s) || calls_async_function(s, async_functions)
6479            });
6480
6481            if body_has_async {
6482                // Use while-let with explicit iterator for async compatibility
6483                writeln!(output, "{}let mut __iter = ({}).into_iter();", indent_str, iter_str).unwrap();
6484                writeln!(output, "{}while let Some({}) = __iter.next() {{", indent_str, pattern_str).unwrap();
6485            } else {
6486                // Optimization: for known Vec<T> with Copy element type and non-mutating body,
6487                // use .iter().copied() instead of .clone() to avoid copying the entire collection.
6488                let use_iter_copied = if let Expr::Identifier(coll_sym) = iterable {
6489                    if let Some(coll_type) = ctx.get_variable_types().get(coll_sym) {
6490                        coll_type.starts_with("Vec") && has_copy_element_type(coll_type)
6491                            && !body_mutates_collection(body, *coll_sym)
6492                    } else {
6493                        false
6494                    }
6495                } else {
6496                    false
6497                };
6498
6499                if use_iter_copied {
6500                    writeln!(output, "{}for {} in {}.iter().copied() {{", indent_str, pattern_str, iter_str).unwrap();
6501                } else {
6502                    // Clone the collection before iterating to avoid moving it.
6503                    // This allows the collection to be reused after the loop.
6504                    writeln!(output, "{}for {} in {}.clone() {{", indent_str, pattern_str, iter_str).unwrap();
6505                }
6506            }
6507            ctx.push_scope();
6508            // Peephole: process body statements with swap pattern detection
6509            {
6510                let body_refs: Vec<&Stmt> = body.iter().collect();
6511                let mut bi = 0;
6512                while bi < body_refs.len() {
6513                    if let Some((code, skip)) = try_emit_swap_pattern(&body_refs, bi, interner, indent + 1, ctx.get_variable_types()) {
6514                        output.push_str(&code);
6515                        bi += 1 + skip;
6516                        continue;
6517                    }
6518                    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));
6519                    bi += 1;
6520                }
6521            }
6522            ctx.pop_scope();
6523            writeln!(output, "{}}}", indent_str).unwrap();
6524        }
6525
6526        Stmt::Return { value } => {
6527            if let Some(v) = value {
6528                let value_str = codegen_expr_with_async(v, interner, synced_vars, async_functions, ctx.get_variable_types());
6529                writeln!(output, "{}return {};", indent_str, value_str).unwrap();
6530            } else {
6531                writeln!(output, "{}return;", indent_str).unwrap();
6532            }
6533        }
6534
6535        Stmt::Assert { proposition } => {
6536            let condition = codegen_assertion(proposition, interner);
6537            writeln!(output, "{}debug_assert!({});", indent_str, condition).unwrap();
6538        }
6539
6540        // Phase 35: Trust with documented justification
6541        Stmt::Trust { proposition, justification } => {
6542            let reason = interner.resolve(*justification);
6543            // Strip quotes if present (string literals include their quotes)
6544            let reason_clean = reason.trim_matches('"');
6545            writeln!(output, "{}// TRUST: {}", indent_str, reason_clean).unwrap();
6546            let condition = codegen_assertion(proposition, interner);
6547            writeln!(output, "{}debug_assert!({});", indent_str, condition).unwrap();
6548        }
6549
6550        Stmt::RuntimeAssert { condition } => {
6551            let cond_str = codegen_expr_with_async(condition, interner, synced_vars, async_functions, ctx.get_variable_types());
6552            writeln!(output, "{}debug_assert!({});", indent_str, cond_str).unwrap();
6553        }
6554
6555        // Phase 50: Security Check - mandatory runtime guard (NEVER optimized out)
6556        Stmt::Check { subject, predicate, is_capability, object, source_text, span } => {
6557            let subj_name = interner.resolve(*subject);
6558            let pred_name = interner.resolve(*predicate).to_lowercase();
6559
6560            let call = if *is_capability {
6561                let obj_sym = object.expect("capability must have object");
6562                let obj_word = interner.resolve(obj_sym);
6563
6564                // Phase 50: Type-based resolution
6565                // "Check that user can publish the document" -> find variable of type Document
6566                // First try to find a variable whose type matches the object word
6567                let obj_name = ctx.find_variable_by_type(obj_word, interner)
6568                    .unwrap_or_else(|| obj_word.to_string());
6569
6570                format!("{}.can_{}(&{})", subj_name, pred_name, obj_name)
6571            } else {
6572                format!("{}.is_{}()", subj_name, pred_name)
6573            };
6574
6575            writeln!(output, "{}if !({}) {{", indent_str, call).unwrap();
6576            writeln!(output, "{}    logicaffeine_system::panic_with(\"Security Check Failed at line {}: {}\");",
6577                     indent_str, span.start, source_text).unwrap();
6578            writeln!(output, "{}}}", indent_str).unwrap();
6579        }
6580
6581        // Phase 51: P2P Networking - Listen on network address
6582        Stmt::Listen { address } => {
6583            let addr_str = codegen_expr_with_async(address, interner, synced_vars, async_functions, ctx.get_variable_types());
6584            // Pass &str instead of String
6585            writeln!(output, "{}logicaffeine_system::network::listen(&{}).await.expect(\"Failed to listen\");",
6586                     indent_str, addr_str).unwrap();
6587        }
6588
6589        // Phase 51: P2P Networking - Connect to remote peer
6590        Stmt::ConnectTo { address } => {
6591            let addr_str = codegen_expr_with_async(address, interner, synced_vars, async_functions, ctx.get_variable_types());
6592            // Pass &str instead of String
6593            writeln!(output, "{}logicaffeine_system::network::connect(&{}).await.expect(\"Failed to connect\");",
6594                     indent_str, addr_str).unwrap();
6595        }
6596
6597        // Phase 51: P2P Networking - Create PeerAgent remote handle
6598        Stmt::LetPeerAgent { var, address } => {
6599            let var_name = interner.resolve(*var);
6600            let addr_str = codegen_expr_with_async(address, interner, synced_vars, async_functions, ctx.get_variable_types());
6601            // Pass &str instead of String
6602            writeln!(output, "{}let {} = logicaffeine_system::network::PeerAgent::new(&{}).expect(\"Invalid address\");",
6603                     indent_str, var_name, addr_str).unwrap();
6604        }
6605
6606        // Phase 51: Sleep - supports Duration literals or milliseconds
6607        Stmt::Sleep { milliseconds } => {
6608            let expr_str = codegen_expr_with_async(milliseconds, interner, synced_vars, async_functions, ctx.get_variable_types());
6609            let inferred_type = infer_rust_type_from_expr(milliseconds, interner);
6610
6611            if inferred_type == "std::time::Duration" {
6612                // Duration type: use directly (already a std::time::Duration)
6613                writeln!(output, "{}tokio::time::sleep({}).await;",
6614                         indent_str, expr_str).unwrap();
6615            } else {
6616                // Assume milliseconds (integer) - legacy behavior
6617                writeln!(output, "{}tokio::time::sleep(std::time::Duration::from_millis({} as u64)).await;",
6618                         indent_str, expr_str).unwrap();
6619            }
6620        }
6621
6622        // Phase 52/56: Sync CRDT variable on topic
6623        Stmt::Sync { var, topic } => {
6624            let var_name = interner.resolve(*var);
6625            let topic_str = codegen_expr_with_async(topic, interner, synced_vars, async_functions, ctx.get_variable_types());
6626
6627            // Phase 56: Check if this variable is also mounted
6628            if let Some(caps) = var_caps.get(var) {
6629                if caps.mounted {
6630                    // Both Mount and Sync: use Distributed<T>
6631                    // Mount statement will handle the Distributed::mount call
6632                    // Here we just track it as synced
6633                    synced_vars.insert(*var);
6634                    return output;  // Skip - Mount will emit Distributed<T>
6635                }
6636            }
6637
6638            // Sync-only: use Synced<T>
6639            writeln!(
6640                output,
6641                "{}let {} = logicaffeine_system::crdt::Synced::new({}, &{}).await;",
6642                indent_str, var_name, var_name, topic_str
6643            ).unwrap();
6644            synced_vars.insert(*var);
6645        }
6646
6647        // Phase 53/56: Mount persistent CRDT from journal
6648        Stmt::Mount { var, path } => {
6649            let var_name = interner.resolve(*var);
6650            let path_str = codegen_expr_with_async(path, interner, synced_vars, async_functions, ctx.get_variable_types());
6651
6652            // Phase 56: Check if this variable is also synced
6653            if let Some(caps) = var_caps.get(var) {
6654                if caps.synced {
6655                    // Both Mount and Sync: use Distributed<T>
6656                    let topic_str = caps.sync_topic.as_ref()
6657                        .map(|s| s.as_str())
6658                        .unwrap_or("\"default\"");
6659                    writeln!(
6660                        output,
6661                        "{}let {} = logicaffeine_system::distributed::Distributed::mount(std::sync::Arc::new(vfs.clone()), &{}, Some({}.to_string())).await.expect(\"Failed to mount\");",
6662                        indent_str, var_name, path_str, topic_str
6663                    ).unwrap();
6664                    synced_vars.insert(*var);
6665                    return output;
6666                }
6667            }
6668
6669            // Mount-only: use Persistent<T>
6670            writeln!(
6671                output,
6672                "{}let {} = logicaffeine_system::storage::Persistent::mount(&vfs, &{}).await.expect(\"Failed to mount\");",
6673                indent_str, var_name, path_str
6674            ).unwrap();
6675            synced_vars.insert(*var);
6676        }
6677
6678        // =====================================================================
6679        // Phase 54: Go-like Concurrency Codegen
6680        // =====================================================================
6681
6682        Stmt::LaunchTask { function, args } => {
6683            let fn_name = escape_rust_ident(interner.resolve(*function));
6684            // Phase 54: When passing a pipe variable, pass the sender (_tx)
6685            let args_str: Vec<String> = args.iter()
6686                .map(|a| {
6687                    if let Expr::Identifier(sym) = a {
6688                        if pipe_vars.contains(sym) {
6689                            return format!("{}_tx.clone()", interner.resolve(*sym));
6690                        }
6691                    }
6692                    codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types())
6693                })
6694                .collect();
6695            // Phase 54: Add .await only if the function is async
6696            let await_suffix = if async_functions.contains(function) { ".await" } else { "" };
6697            writeln!(
6698                output,
6699                "{}tokio::spawn(async move {{ {}({}){await_suffix}; }});",
6700                indent_str, fn_name, args_str.join(", ")
6701            ).unwrap();
6702        }
6703
6704        Stmt::LaunchTaskWithHandle { handle, function, args } => {
6705            let handle_name = interner.resolve(*handle);
6706            let fn_name = escape_rust_ident(interner.resolve(*function));
6707            // Phase 54: When passing a pipe variable, pass the sender (_tx)
6708            let args_str: Vec<String> = args.iter()
6709                .map(|a| {
6710                    if let Expr::Identifier(sym) = a {
6711                        if pipe_vars.contains(sym) {
6712                            return format!("{}_tx.clone()", interner.resolve(*sym));
6713                        }
6714                    }
6715                    codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types())
6716                })
6717                .collect();
6718            // Phase 54: Add .await only if the function is async
6719            let await_suffix = if async_functions.contains(function) { ".await" } else { "" };
6720            writeln!(
6721                output,
6722                "{}let {} = tokio::spawn(async move {{ {}({}){await_suffix} }});",
6723                indent_str, handle_name, fn_name, args_str.join(", ")
6724            ).unwrap();
6725        }
6726
6727        Stmt::CreatePipe { var, element_type, capacity } => {
6728            let var_name = interner.resolve(*var);
6729            let type_name = interner.resolve(*element_type);
6730            let cap = capacity.unwrap_or(32);
6731            // Map LOGOS types to Rust types
6732            let rust_type = match type_name {
6733                "Int" => "i64",
6734                "Nat" => "u64",
6735                "Text" => "String",
6736                "Bool" => "bool",
6737                _ => type_name,
6738            };
6739            writeln!(
6740                output,
6741                "{}let ({}_tx, mut {}_rx) = tokio::sync::mpsc::channel::<{}>({});",
6742                indent_str, var_name, var_name, rust_type, cap
6743            ).unwrap();
6744        }
6745
6746        Stmt::SendPipe { value, pipe } => {
6747            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());
6748            let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6749            // Phase 54: Check if pipe is a local declaration (has _tx suffix) or parameter (no suffix)
6750            let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6751                pipe_vars.contains(sym)
6752            } else {
6753                false
6754            };
6755            if is_local_pipe {
6756                writeln!(
6757                    output,
6758                    "{}{}_tx.send({}).await.expect(\"pipe send failed\");",
6759                    indent_str, pipe_str, val_str
6760                ).unwrap();
6761            } else {
6762                writeln!(
6763                    output,
6764                    "{}{}.send({}).await.expect(\"pipe send failed\");",
6765                    indent_str, pipe_str, val_str
6766                ).unwrap();
6767            }
6768        }
6769
6770        Stmt::ReceivePipe { var, pipe } => {
6771            let var_name = interner.resolve(*var);
6772            let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6773            // Phase 54: Check if pipe is a local declaration (has _rx suffix) or parameter (no suffix)
6774            let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6775                pipe_vars.contains(sym)
6776            } else {
6777                false
6778            };
6779            if is_local_pipe {
6780                writeln!(
6781                    output,
6782                    "{}let {} = {}_rx.recv().await.expect(\"pipe closed\");",
6783                    indent_str, var_name, pipe_str
6784                ).unwrap();
6785            } else {
6786                writeln!(
6787                    output,
6788                    "{}let {} = {}.recv().await.expect(\"pipe closed\");",
6789                    indent_str, var_name, pipe_str
6790                ).unwrap();
6791            }
6792        }
6793
6794        Stmt::TrySendPipe { value, pipe, result } => {
6795            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());
6796            let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6797            // Phase 54: Check if pipe is a local declaration
6798            let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6799                pipe_vars.contains(sym)
6800            } else {
6801                false
6802            };
6803            let suffix = if is_local_pipe { "_tx" } else { "" };
6804            if let Some(res) = result {
6805                let res_name = interner.resolve(*res);
6806                writeln!(
6807                    output,
6808                    "{}let {} = {}{}.try_send({}).is_ok();",
6809                    indent_str, res_name, pipe_str, suffix, val_str
6810                ).unwrap();
6811            } else {
6812                writeln!(
6813                    output,
6814                    "{}let _ = {}{}.try_send({});",
6815                    indent_str, pipe_str, suffix, val_str
6816                ).unwrap();
6817            }
6818        }
6819
6820        Stmt::TryReceivePipe { var, pipe } => {
6821            let var_name = interner.resolve(*var);
6822            let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6823            // Phase 54: Check if pipe is a local declaration
6824            let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6825                pipe_vars.contains(sym)
6826            } else {
6827                false
6828            };
6829            let suffix = if is_local_pipe { "_rx" } else { "" };
6830            writeln!(
6831                output,
6832                "{}let {} = {}{}.try_recv().ok();",
6833                indent_str, var_name, pipe_str, suffix
6834            ).unwrap();
6835        }
6836
6837        Stmt::StopTask { handle } => {
6838            let handle_str = codegen_expr_with_async(handle, interner, synced_vars, async_functions, ctx.get_variable_types());
6839            writeln!(output, "{}{}.abort();", indent_str, handle_str).unwrap();
6840        }
6841
6842        Stmt::Select { branches } => {
6843            use crate::ast::stmt::SelectBranch;
6844
6845            writeln!(output, "{}tokio::select! {{", indent_str).unwrap();
6846            for branch in branches {
6847                match branch {
6848                    SelectBranch::Receive { var, pipe, body } => {
6849                        let var_name = interner.resolve(*var);
6850                        let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6851                        // Check if pipe is a local declaration (has _rx suffix) or a parameter (no suffix)
6852                        let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6853                            pipe_vars.contains(sym)
6854                        } else {
6855                            false
6856                        };
6857                        let suffix = if is_local_pipe { "_rx" } else { "" };
6858                        writeln!(
6859                            output,
6860                            "{}    {} = {}{}.recv() => {{",
6861                            indent_str, var_name, pipe_str, suffix
6862                        ).unwrap();
6863                        writeln!(
6864                            output,
6865                            "{}        if let Some({}) = {} {{",
6866                            indent_str, var_name, var_name
6867                        ).unwrap();
6868                        for stmt in *body {
6869                            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);
6870                            write!(output, "{}", stmt_code).unwrap();
6871                        }
6872                        writeln!(output, "{}        }}", indent_str).unwrap();
6873                        writeln!(output, "{}    }}", indent_str).unwrap();
6874                    }
6875                    SelectBranch::Timeout { milliseconds, body } => {
6876                        let ms_str = codegen_expr_with_async(milliseconds, interner, synced_vars, async_functions, ctx.get_variable_types());
6877                        // Convert seconds to milliseconds if the value looks like seconds
6878                        writeln!(
6879                            output,
6880                            "{}    _ = tokio::time::sleep(std::time::Duration::from_secs({} as u64)) => {{",
6881                            indent_str, ms_str
6882                        ).unwrap();
6883                        for stmt in *body {
6884                            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);
6885                            write!(output, "{}", stmt_code).unwrap();
6886                        }
6887                        writeln!(output, "{}    }}", indent_str).unwrap();
6888                    }
6889                }
6890            }
6891            writeln!(output, "{}}}", indent_str).unwrap();
6892        }
6893
6894        Stmt::Give { object, recipient } => {
6895            // Move semantics: pass ownership without borrowing
6896            let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
6897            let recv_str = codegen_expr_with_async(recipient, interner, synced_vars, async_functions, ctx.get_variable_types());
6898            writeln!(output, "{}{}({});", indent_str, recv_str, obj_str).unwrap();
6899        }
6900
6901        Stmt::Show { object, recipient } => {
6902            // Borrow semantics: pass immutable reference
6903            // Use string_vars for proper concatenation of string variables
6904            let obj_str = codegen_expr_with_async_and_strings(object, interner, synced_vars, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
6905            let recv_str = codegen_expr_with_async(recipient, interner, synced_vars, async_functions, ctx.get_variable_types());
6906            writeln!(output, "{}{}(&{});", indent_str, recv_str, obj_str).unwrap();
6907        }
6908
6909        Stmt::SetField { object, field, value } => {
6910            let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
6911            let field_name = interner.resolve(*field);
6912            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());
6913
6914            // Phase 49: Check if this field is an LWWRegister or MVRegister
6915            // LWW needs .set(value, timestamp), MV needs .set(value)
6916            let is_lww = lww_fields.iter().any(|(_, f)| f == field_name);
6917            let is_mv = mv_fields.iter().any(|(_, f)| f == field_name);
6918            if is_lww {
6919                // LWWRegister needs a timestamp - use current system time in microseconds
6920                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();
6921            } else if is_mv {
6922                // MVRegister just needs the value
6923                writeln!(output, "{}{}.{}.set({});", indent_str, obj_str, field_name, value_str).unwrap();
6924            } else {
6925                writeln!(output, "{}{}.{} = {};", indent_str, obj_str, field_name, value_str).unwrap();
6926            }
6927        }
6928
6929        Stmt::StructDef { .. } => {
6930            // Struct definitions are handled in codegen_program, not here
6931        }
6932
6933        Stmt::FunctionDef { .. } => {
6934            // Function definitions are handled in codegen_program, not here
6935        }
6936
6937        Stmt::Inspect { target, arms, .. } => {
6938            let target_str = codegen_expr_with_async(target, interner, synced_vars, async_functions, ctx.get_variable_types());
6939
6940            // Phase 102: Track which bindings come from boxed fields for inner Inspects
6941            // Use NAMES (strings) not symbols, because parser may create different symbols
6942            // for the same identifier in different syntactic positions.
6943            let mut inner_boxed_binding_names: HashSet<String> = HashSet::new();
6944
6945            writeln!(output, "{}match {} {{", indent_str, target_str).unwrap();
6946
6947            for arm in arms {
6948                if let Some(variant) = arm.variant {
6949                    let variant_name = interner.resolve(variant);
6950                    // Get the enum name from the arm, or fallback to just variant name
6951                    let enum_name_str = arm.enum_name.map(|e| interner.resolve(e));
6952                    let enum_prefix = enum_name_str
6953                        .map(|e| format!("{}::", e))
6954                        .unwrap_or_default();
6955
6956                    if arm.bindings.is_empty() {
6957                        // Unit variant pattern
6958                        writeln!(output, "{}    {}{} => {{", indent_str, enum_prefix, variant_name).unwrap();
6959                    } else {
6960                        // Pattern with bindings
6961                        // Phase 102: Check which bindings are from boxed fields
6962                        let bindings_str: Vec<String> = arm.bindings.iter()
6963                            .map(|(field, binding)| {
6964                                let field_name = interner.resolve(*field);
6965                                let binding_name = interner.resolve(*binding);
6966
6967                                // Check if this field is boxed
6968                                if let Some(enum_name) = enum_name_str {
6969                                    let key = (enum_name.to_string(), variant_name.to_string(), field_name.to_string());
6970                                    if boxed_fields.contains(&key) {
6971                                        inner_boxed_binding_names.insert(binding_name.to_string());
6972                                    }
6973                                }
6974
6975                                if field_name == binding_name {
6976                                    field_name.to_string()
6977                                } else {
6978                                    format!("{}: {}", field_name, binding_name)
6979                                }
6980                            })
6981                            .collect();
6982                        writeln!(output, "{}    {}{} {{ {} }} => {{", indent_str, enum_prefix, variant_name, bindings_str.join(", ")).unwrap();
6983                    }
6984                } else {
6985                    // Otherwise (wildcard) pattern
6986                    writeln!(output, "{}    _ => {{", indent_str).unwrap();
6987                }
6988
6989                ctx.push_scope();
6990
6991                // Generate explicit dereferences for boxed bindings at the start of the arm
6992                // This makes them usable as regular values in the rest of the body
6993                for binding_name in &inner_boxed_binding_names {
6994                    writeln!(output, "{}        let {} = (*{}).clone();", indent_str, binding_name, binding_name).unwrap();
6995                }
6996
6997                for stmt in arm.body {
6998                    // Phase 102: Handle inner Inspect statements with boxed bindings
6999                    // Note: Since we now dereference boxed bindings at the start of the arm,
7000                    // inner matches don't need the `*` dereference operator.
7001                    let inner_stmt_code = if let Stmt::Inspect { target: inner_target, .. } = stmt {
7002                        // Check if the inner target is a boxed binding (already dereferenced above)
7003                        // Use name comparison since symbols may differ between binding and reference
7004                        if let Expr::Identifier(sym) = inner_target {
7005                            let target_name = interner.resolve(*sym);
7006                            if inner_boxed_binding_names.contains(target_name) {
7007                                // Generate match (binding was already dereferenced at arm start)
7008                                let mut inner_output = String::new();
7009                                writeln!(inner_output, "{}match {} {{", "    ".repeat(indent + 2), target_name).unwrap();
7010
7011                                if let Stmt::Inspect { arms: inner_arms, .. } = stmt {
7012                                    for inner_arm in inner_arms.iter() {
7013                                        if let Some(v) = inner_arm.variant {
7014                                            let v_name = interner.resolve(v);
7015                                            let inner_enum_prefix = inner_arm.enum_name
7016                                                .map(|e| format!("{}::", interner.resolve(e)))
7017                                                .unwrap_or_default();
7018
7019                                            if inner_arm.bindings.is_empty() {
7020                                                writeln!(inner_output, "{}    {}{} => {{", "    ".repeat(indent + 2), inner_enum_prefix, v_name).unwrap();
7021                                            } else {
7022                                                let bindings: Vec<String> = inner_arm.bindings.iter()
7023                                                    .map(|(f, b)| {
7024                                                        let fn_name = interner.resolve(*f);
7025                                                        let bn_name = interner.resolve(*b);
7026                                                        if fn_name == bn_name { fn_name.to_string() }
7027                                                        else { format!("{}: {}", fn_name, bn_name) }
7028                                                    })
7029                                                    .collect();
7030                                                writeln!(inner_output, "{}    {}{} {{ {} }} => {{", "    ".repeat(indent + 2), inner_enum_prefix, v_name, bindings.join(", ")).unwrap();
7031                                            }
7032                                        } else {
7033                                            writeln!(inner_output, "{}    _ => {{", "    ".repeat(indent + 2)).unwrap();
7034                                        }
7035
7036                                        ctx.push_scope();
7037                                        for inner_stmt in inner_arm.body {
7038                                            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));
7039                                        }
7040                                        ctx.pop_scope();
7041                                        writeln!(inner_output, "{}    }}", "    ".repeat(indent + 2)).unwrap();
7042                                    }
7043                                }
7044                                writeln!(inner_output, "{}}}", "    ".repeat(indent + 2)).unwrap();
7045                                inner_output
7046                            } else {
7047                                codegen_stmt(stmt, interner, indent + 2, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
7048                            }
7049                        } else {
7050                            codegen_stmt(stmt, interner, indent + 2, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
7051                        }
7052                    } else {
7053                        codegen_stmt(stmt, interner, indent + 2, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
7054                    };
7055                    output.push_str(&inner_stmt_code);
7056                }
7057                ctx.pop_scope();
7058                writeln!(output, "{}    }}", indent_str).unwrap();
7059            }
7060
7061            writeln!(output, "{}}}", indent_str).unwrap();
7062        }
7063
7064        Stmt::Push { value, collection } => {
7065            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());
7066            let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
7067            writeln!(output, "{}{}.push({});", indent_str, coll_str, val_str).unwrap();
7068        }
7069
7070        Stmt::Pop { collection, into } => {
7071            let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
7072            match into {
7073                Some(var) => {
7074                    let var_name = interner.resolve(*var);
7075                    // Unwrap the Option returned by pop() - panics if empty
7076                    writeln!(output, "{}let {} = {}.pop().expect(\"Pop from empty collection\");", indent_str, var_name, coll_str).unwrap();
7077                }
7078                None => {
7079                    writeln!(output, "{}{}.pop();", indent_str, coll_str).unwrap();
7080                }
7081            }
7082        }
7083
7084        Stmt::Add { value, collection } => {
7085            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());
7086            let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
7087            writeln!(output, "{}{}.insert({});", indent_str, coll_str, val_str).unwrap();
7088        }
7089
7090        Stmt::Remove { value, collection } => {
7091            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());
7092            let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
7093            writeln!(output, "{}{}.remove(&{});", indent_str, coll_str, val_str).unwrap();
7094        }
7095
7096        Stmt::SetIndex { collection, index, value } => {
7097            let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
7098            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());
7099
7100            // Direct indexing for known collection types (avoids trait dispatch)
7101            let known_type = if let Expr::Identifier(sym) = collection {
7102                ctx.get_variable_types().get(sym).map(|s| s.as_str())
7103            } else {
7104                None
7105            };
7106
7107            match known_type {
7108                Some(t) if t.starts_with("Vec") => {
7109                    // Peephole: simplify (x + 1) - 1 → x for 1-based indexing
7110                    let index_part = if let Expr::BinaryOp { op: BinaryOpKind::Add, left, right } = index {
7111                        if matches!(right, Expr::Literal(Literal::Number(1))) {
7112                            let inner = codegen_expr_with_async(left, interner, synced_vars, async_functions, ctx.get_variable_types());
7113                            format!("({}) as usize", inner)
7114                        } else if matches!(left, Expr::Literal(Literal::Number(1))) {
7115                            let inner = codegen_expr_with_async(right, interner, synced_vars, async_functions, ctx.get_variable_types());
7116                            format!("({}) as usize", inner)
7117                        } else {
7118                            let index_str = codegen_expr_with_async(index, interner, synced_vars, async_functions, ctx.get_variable_types());
7119                            format!("({} - 1) as usize", index_str)
7120                        }
7121                    } else {
7122                        let index_str = codegen_expr_with_async(index, interner, synced_vars, async_functions, ctx.get_variable_types());
7123                        format!("({} - 1) as usize", index_str)
7124                    };
7125                    // Evaluate value first if it references the same collection (borrow safety)
7126                    if value_str.contains(&coll_str) {
7127                        writeln!(output, "{}let __set_tmp = {};", indent_str, value_str).unwrap();
7128                        writeln!(output, "{}{}[{}] = __set_tmp;", indent_str, coll_str, index_part).unwrap();
7129                    } else {
7130                        writeln!(output, "{}{}[{}] = {};", indent_str, coll_str, index_part, value_str).unwrap();
7131                    }
7132                }
7133                Some(t) if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") => {
7134                    let index_str = codegen_expr_with_async(index, interner, synced_vars, async_functions, ctx.get_variable_types());
7135                    writeln!(output, "{}{}.insert({}, {});", indent_str, coll_str, index_str, value_str).unwrap();
7136                }
7137                _ => {
7138                    let index_str = codegen_expr_with_async(index, interner, synced_vars, async_functions, ctx.get_variable_types());
7139                    // Fallback: polymorphic indexing via trait
7140                    if value_str.contains("logos_get") && value_str.contains(&coll_str) {
7141                        writeln!(output, "{}let __set_tmp = {};", indent_str, value_str).unwrap();
7142                        writeln!(output, "{}LogosIndexMut::logos_set(&mut {}, {}, __set_tmp);", indent_str, coll_str, index_str).unwrap();
7143                    } else {
7144                        writeln!(output, "{}LogosIndexMut::logos_set(&mut {}, {}, {});", indent_str, coll_str, index_str, value_str).unwrap();
7145                    }
7146                }
7147            }
7148        }
7149
7150        // Phase 8.5: Zone (memory arena) block
7151        Stmt::Zone { name, capacity, source_file, body } => {
7152            let zone_name = interner.resolve(*name);
7153
7154            // Generate zone creation based on type
7155            if let Some(path_sym) = source_file {
7156                // Memory-mapped file zone
7157                let path = interner.resolve(*path_sym);
7158                writeln!(
7159                    output,
7160                    "{}let {} = logicaffeine_system::memory::Zone::new_mapped(\"{}\").expect(\"Failed to map file\");",
7161                    indent_str, zone_name, path
7162                ).unwrap();
7163            } else {
7164                // Heap arena zone
7165                let cap = capacity.unwrap_or(4096); // Default 4KB
7166                writeln!(
7167                    output,
7168                    "{}let {} = logicaffeine_system::memory::Zone::new_heap({});",
7169                    indent_str, zone_name, cap
7170                ).unwrap();
7171            }
7172
7173            // Open block scope
7174            writeln!(output, "{}{{", indent_str).unwrap();
7175            ctx.push_scope();
7176
7177            // Generate body statements
7178            for stmt in *body {
7179                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));
7180            }
7181
7182            ctx.pop_scope();
7183            writeln!(output, "{}}}", indent_str).unwrap();
7184        }
7185
7186        // Phase 9: Concurrent execution block (async, I/O-bound)
7187        // Generates tokio::join! for concurrent task execution
7188        // Phase 51: Variables used across multiple tasks are cloned to avoid move issues
7189        Stmt::Concurrent { tasks } => {
7190            // Collect Let statements to generate tuple destructuring
7191            let let_bindings: Vec<_> = tasks.iter().filter_map(|s| {
7192                if let Stmt::Let { var, .. } = s {
7193                    Some(interner.resolve(*var).to_string())
7194                } else {
7195                    None
7196                }
7197            }).collect();
7198
7199            // Collect variables DEFINED in this block (to exclude from cloning)
7200            let defined_vars: HashSet<Symbol> = tasks.iter().filter_map(|s| {
7201                if let Stmt::Let { var, .. } = s {
7202                    Some(*var)
7203                } else {
7204                    None
7205                }
7206            }).collect();
7207
7208            // Check if there are intra-block dependencies (a later task uses a var from earlier task)
7209            // If so, fall back to sequential execution
7210            let mut has_intra_dependency = false;
7211            let mut seen_defs: HashSet<Symbol> = HashSet::new();
7212            for s in *tasks {
7213                // Check if this task uses any variable defined by previous tasks in this block
7214                let mut used_in_task: HashSet<Symbol> = HashSet::new();
7215                collect_stmt_identifiers(s, &mut used_in_task);
7216                for used_var in &used_in_task {
7217                    if seen_defs.contains(used_var) {
7218                        has_intra_dependency = true;
7219                        break;
7220                    }
7221                }
7222                // Track variables defined by this task
7223                if let Stmt::Let { var, .. } = s {
7224                    seen_defs.insert(*var);
7225                }
7226                if has_intra_dependency {
7227                    break;
7228                }
7229            }
7230
7231            // Collect ALL variables used in task expressions (not just Call args)
7232            // Exclude variables defined within this block
7233            let mut used_syms: HashSet<Symbol> = HashSet::new();
7234            for s in *tasks {
7235                collect_stmt_identifiers(s, &mut used_syms);
7236            }
7237            // Remove variables that are defined in this block
7238            for def_var in &defined_vars {
7239                used_syms.remove(def_var);
7240            }
7241            let used_vars: HashSet<String> = used_syms.iter()
7242                .map(|sym| interner.resolve(*sym).to_string())
7243                .collect();
7244
7245            // If there are intra-block dependencies, execute sequentially
7246            if has_intra_dependency {
7247                // Generate sequential Let bindings
7248                for stmt in *tasks {
7249                    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));
7250                }
7251            } else {
7252                // Generate concurrent execution with tokio::join!
7253                if !let_bindings.is_empty() {
7254                    // Generate tuple destructuring for concurrent Let bindings
7255                    writeln!(output, "{}let ({}) = tokio::join!(", indent_str, let_bindings.join(", ")).unwrap();
7256                } else {
7257                    writeln!(output, "{}tokio::join!(", indent_str).unwrap();
7258                }
7259
7260                for (i, stmt) in tasks.iter().enumerate() {
7261                    // For Let statements, generate only the VALUE so the async block returns it
7262                    // For Call statements, generate the call with .await
7263                    let inner_code = match stmt {
7264                        Stmt::Let { value, .. } => {
7265                            // Return the value expression directly (not "let x = value;")
7266                            // Phase 54+: Use codegen_expr_with_async to handle all nested async calls
7267                            codegen_expr_with_async(value, interner, synced_vars, async_functions, ctx.get_variable_types())
7268                        }
7269                        Stmt::Call { function, args } => {
7270                            let func_name = interner.resolve(*function);
7271                            let args_str: Vec<String> = args.iter()
7272                                .map(|a| codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types()))
7273                                .collect();
7274                            // Only add .await for async functions
7275                            let await_suffix = if async_functions.contains(function) { ".await" } else { "" };
7276                            format!("{}({}){}", func_name, args_str.join(", "), await_suffix)
7277                        }
7278                        _ => {
7279                            // Fallback for other statement types
7280                            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);
7281                            inner.trim().to_string()
7282                        }
7283                    };
7284
7285                    // For tasks that use shared variables, wrap in a block that clones them
7286                    if !used_vars.is_empty() && i < tasks.len() - 1 {
7287                        // Clone variables for all tasks except the last one
7288                        let clones: Vec<String> = used_vars.iter()
7289                            .map(|v| format!("let {} = {}.clone();", v, v))
7290                            .collect();
7291                        write!(output, "{}    {{ {} async move {{ {} }} }}",
7292                               indent_str, clones.join(" "), inner_code).unwrap();
7293                    } else {
7294                        // Last task can use original variables
7295                        write!(output, "{}    async {{ {} }}", indent_str, inner_code).unwrap();
7296                    }
7297
7298                    if i < tasks.len() - 1 {
7299                        writeln!(output, ",").unwrap();
7300                    } else {
7301                        writeln!(output).unwrap();
7302                    }
7303                }
7304
7305                writeln!(output, "{});", indent_str).unwrap();
7306            }
7307        }
7308
7309        // Phase 9: Parallel execution block (CPU-bound)
7310        // Generates rayon::join for two tasks, or thread::spawn for 3+ tasks
7311        Stmt::Parallel { tasks } => {
7312            // Collect Let statements to generate tuple destructuring
7313            let let_bindings: Vec<_> = tasks.iter().filter_map(|s| {
7314                if let Stmt::Let { var, .. } = s {
7315                    Some(interner.resolve(*var).to_string())
7316                } else {
7317                    None
7318                }
7319            }).collect();
7320
7321            if tasks.len() == 2 {
7322                // Use rayon::join for exactly 2 tasks
7323                if !let_bindings.is_empty() {
7324                    writeln!(output, "{}let ({}) = rayon::join(", indent_str, let_bindings.join(", ")).unwrap();
7325                } else {
7326                    writeln!(output, "{}rayon::join(", indent_str).unwrap();
7327                }
7328
7329                for (i, stmt) in tasks.iter().enumerate() {
7330                    // For Let statements, generate only the VALUE so the closure returns it
7331                    let inner_code = match stmt {
7332                        Stmt::Let { value, .. } => {
7333                            // Return the value expression directly (not "let x = value;")
7334                            codegen_expr(value, interner, synced_vars)
7335                        }
7336                        Stmt::Call { function, args } => {
7337                            let func_name = interner.resolve(*function);
7338                            let args_str: Vec<String> = args.iter()
7339                                .map(|a| codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types()))
7340                                .collect();
7341                            format!("{}({})", func_name, args_str.join(", "))
7342                        }
7343                        _ => {
7344                            // Fallback for other statement types
7345                            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);
7346                            inner.trim().to_string()
7347                        }
7348                    };
7349                    write!(output, "{}    || {{ {} }}", indent_str, inner_code).unwrap();
7350                    if i == 0 {
7351                        writeln!(output, ",").unwrap();
7352                    } else {
7353                        writeln!(output).unwrap();
7354                    }
7355                }
7356                writeln!(output, "{});", indent_str).unwrap();
7357            } else {
7358                // For 3+ tasks, use thread::spawn pattern
7359                writeln!(output, "{}{{", indent_str).unwrap();
7360                writeln!(output, "{}    let handles: Vec<_> = vec![", indent_str).unwrap();
7361                for stmt in *tasks {
7362                    // For Let statements, generate only the VALUE so the closure returns it
7363                    let inner_code = match stmt {
7364                        Stmt::Let { value, .. } => {
7365                            codegen_expr(value, interner, synced_vars)
7366                        }
7367                        Stmt::Call { function, args } => {
7368                            let func_name = interner.resolve(*function);
7369                            let args_str: Vec<String> = args.iter()
7370                                .map(|a| codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types()))
7371                                .collect();
7372                            format!("{}({})", func_name, args_str.join(", "))
7373                        }
7374                        _ => {
7375                            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);
7376                            inner.trim().to_string()
7377                        }
7378                    };
7379                    writeln!(output, "{}        std::thread::spawn(move || {{ {} }}),",
7380                             indent_str, inner_code).unwrap();
7381                }
7382                writeln!(output, "{}    ];", indent_str).unwrap();
7383                writeln!(output, "{}    for h in handles {{ h.join().unwrap(); }}", indent_str).unwrap();
7384                writeln!(output, "{}}}", indent_str).unwrap();
7385            }
7386        }
7387
7388        // Phase 10: Read from console or file
7389        // Phase 53: File reads now use async VFS
7390        Stmt::ReadFrom { var, source } => {
7391            let var_name = interner.resolve(*var);
7392            match source {
7393                ReadSource::Console => {
7394                    writeln!(output, "{}let {} = logicaffeine_system::io::read_line();", indent_str, var_name).unwrap();
7395                }
7396                ReadSource::File(path_expr) => {
7397                    let path_str = codegen_expr_with_async(path_expr, interner, synced_vars, async_functions, ctx.get_variable_types());
7398                    // Phase 53: Use VFS with async
7399                    writeln!(
7400                        output,
7401                        "{}let {} = vfs.read_to_string(&{}).await.expect(\"Failed to read file\");",
7402                        indent_str, var_name, path_str
7403                    ).unwrap();
7404                }
7405            }
7406        }
7407
7408        // Phase 10: Write to file
7409        // Phase 53: File writes now use async VFS
7410        Stmt::WriteFile { content, path } => {
7411            let content_str = codegen_expr_with_async(content, interner, synced_vars, async_functions, ctx.get_variable_types());
7412            let path_str = codegen_expr_with_async(path, interner, synced_vars, async_functions, ctx.get_variable_types());
7413            // Phase 53: Use VFS with async
7414            writeln!(
7415                output,
7416                "{}vfs.write(&{}, {}.as_bytes()).await.expect(\"Failed to write file\");",
7417                indent_str, path_str, content_str
7418            ).unwrap();
7419        }
7420
7421        // Phase 46: Spawn an agent
7422        Stmt::Spawn { agent_type, name } => {
7423            let type_name = interner.resolve(*agent_type);
7424            let agent_name = interner.resolve(*name);
7425            // Generate agent spawn with tokio channel
7426            writeln!(
7427                output,
7428                "{}let {} = tokio::spawn(async move {{ /* {} agent loop */ }});",
7429                indent_str, agent_name, type_name
7430            ).unwrap();
7431        }
7432
7433        // Phase 46: Send message to agent
7434        Stmt::SendMessage { message, destination } => {
7435            let msg_str = codegen_expr_with_async(message, interner, synced_vars, async_functions, ctx.get_variable_types());
7436            let dest_str = codegen_expr_with_async(destination, interner, synced_vars, async_functions, ctx.get_variable_types());
7437            writeln!(
7438                output,
7439                "{}{}.send({}).await.expect(\"Failed to send message\");",
7440                indent_str, dest_str, msg_str
7441            ).unwrap();
7442        }
7443
7444        // Phase 46: Await response from agent
7445        Stmt::AwaitMessage { source, into } => {
7446            let src_str = codegen_expr_with_async(source, interner, synced_vars, async_functions, ctx.get_variable_types());
7447            let var_name = interner.resolve(*into);
7448            writeln!(
7449                output,
7450                "{}let {} = {}.recv().await.expect(\"Failed to receive message\");",
7451                indent_str, var_name, src_str
7452            ).unwrap();
7453        }
7454
7455        // Phase 49: Merge CRDT state
7456        Stmt::MergeCrdt { source, target } => {
7457            let src_str = codegen_expr_with_async(source, interner, synced_vars, async_functions, ctx.get_variable_types());
7458            let tgt_str = codegen_expr_with_async(target, interner, synced_vars, async_functions, ctx.get_variable_types());
7459            writeln!(
7460                output,
7461                "{}{}.merge(&{});",
7462                indent_str, tgt_str, src_str
7463            ).unwrap();
7464        }
7465
7466        // Phase 49: Increment GCounter
7467        // Phase 52: If object is synced, wrap in .mutate() for auto-publish
7468        Stmt::IncreaseCrdt { object, field, amount } => {
7469            let field_name = interner.resolve(*field);
7470            let amount_str = codegen_expr_with_async(amount, interner, synced_vars, async_functions, ctx.get_variable_types());
7471
7472            // Check if the root object is synced
7473            let root_sym = get_root_identifier(object);
7474            if let Some(sym) = root_sym {
7475                if synced_vars.contains(&sym) {
7476                    // Synced: use .mutate() for auto-publish
7477                    let obj_name = interner.resolve(sym);
7478                    writeln!(
7479                        output,
7480                        "{}{}.mutate(|inner| inner.{}.increment({} as u64)).await;",
7481                        indent_str, obj_name, field_name, amount_str
7482                    ).unwrap();
7483                    return output;
7484                }
7485            }
7486
7487            // Not synced: direct access
7488            let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
7489            writeln!(
7490                output,
7491                "{}{}.{}.increment({} as u64);",
7492                indent_str, obj_str, field_name, amount_str
7493            ).unwrap();
7494        }
7495
7496        // Phase 49b: Decrement PNCounter
7497        Stmt::DecreaseCrdt { object, field, amount } => {
7498            let field_name = interner.resolve(*field);
7499            let amount_str = codegen_expr_with_async(amount, interner, synced_vars, async_functions, ctx.get_variable_types());
7500
7501            // Check if the root object is synced
7502            let root_sym = get_root_identifier(object);
7503            if let Some(sym) = root_sym {
7504                if synced_vars.contains(&sym) {
7505                    // Synced: use .mutate() for auto-publish
7506                    let obj_name = interner.resolve(sym);
7507                    writeln!(
7508                        output,
7509                        "{}{}.mutate(|inner| inner.{}.decrement({} as u64)).await;",
7510                        indent_str, obj_name, field_name, amount_str
7511                    ).unwrap();
7512                    return output;
7513                }
7514            }
7515
7516            // Not synced: direct access
7517            let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
7518            writeln!(
7519                output,
7520                "{}{}.{}.decrement({} as u64);",
7521                indent_str, obj_str, field_name, amount_str
7522            ).unwrap();
7523        }
7524
7525        // Phase 49b: Append to SharedSequence (RGA)
7526        Stmt::AppendToSequence { sequence, value } => {
7527            let seq_str = codegen_expr_with_async(sequence, interner, synced_vars, async_functions, ctx.get_variable_types());
7528            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());
7529            writeln!(
7530                output,
7531                "{}{}.append({});",
7532                indent_str, seq_str, val_str
7533            ).unwrap();
7534        }
7535
7536        // Phase 49b: Resolve MVRegister conflicts
7537        Stmt::ResolveConflict { object, field, value } => {
7538            let field_name = interner.resolve(*field);
7539            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());
7540            let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
7541            writeln!(
7542                output,
7543                "{}{}.{}.resolve({});",
7544                indent_str, obj_str, field_name, val_str
7545            ).unwrap();
7546        }
7547
7548        // Escape hatch: emit raw foreign code wrapped in braces for scope isolation
7549        Stmt::Escape { code, .. } => {
7550            let raw_code = interner.resolve(*code);
7551            write!(output, "{}{{\n", indent_str).unwrap();
7552            for line in raw_code.lines() {
7553                write!(output, "{}    {}\n", indent_str, line).unwrap();
7554            }
7555            write!(output, "{}}}\n", indent_str).unwrap();
7556        }
7557
7558        // Dependencies are metadata; no Rust code emitted.
7559        Stmt::Require { .. } => {}
7560
7561        // Phase 63: Theorems are verified at compile-time, no runtime code generated
7562        Stmt::Theorem(_) => {
7563            // Theorems don't generate runtime code - they're processed separately
7564            // by compile_theorem() at the meta-level
7565        }
7566    }
7567
7568    output
7569}
7570
7571/// Phase 52: Extract the root identifier from an expression.
7572/// For `x.field.subfield`, returns `x`.
7573fn get_root_identifier(expr: &Expr) -> Option<Symbol> {
7574    match expr {
7575        Expr::Identifier(sym) => Some(*sym),
7576        Expr::FieldAccess { object, .. } => get_root_identifier(object),
7577        _ => None,
7578    }
7579}
7580
7581/// Check if a type string represents a Copy type (no .clone() needed on indexing).
7582fn is_copy_type(ty: &str) -> bool {
7583    matches!(ty, "i64" | "u64" | "f64" | "i32" | "u32" | "f32" | "bool" | "char" | "u8" | "i8" | "()")
7584}
7585
7586/// Check if a Vec<T> type has a Copy element type.
7587fn has_copy_element_type(vec_type: &str) -> bool {
7588    if let Some(inner) = vec_type.strip_prefix("Vec<").and_then(|s| s.strip_suffix('>')) {
7589        is_copy_type(inner)
7590    } else {
7591        false
7592    }
7593}
7594
7595/// Check if a HashMap<K, V> type has a Copy value type.
7596fn has_copy_value_type(map_type: &str) -> bool {
7597    let inner = map_type.strip_prefix("std::collections::HashMap<")
7598        .or_else(|| map_type.strip_prefix("HashMap<"));
7599    if let Some(inner) = inner.and_then(|s| s.strip_suffix('>')) {
7600        // Split on ", " to get key and value types
7601        if let Some((_key, value)) = inner.split_once(", ") {
7602            return is_copy_type(value);
7603        }
7604    }
7605    false
7606}
7607
7608pub fn codegen_expr(expr: &Expr, interner: &Interner, synced_vars: &HashSet<Symbol>) -> String {
7609    // Use empty registry, boxed_fields, and async_functions for simple expression codegen
7610    let empty_registry = TypeRegistry::new();
7611    let empty_async = HashSet::new();
7612    codegen_expr_boxed(expr, interner, synced_vars, &HashSet::new(), &empty_registry, &empty_async)
7613}
7614
7615/// Phase 54+: Codegen expression with async function tracking.
7616/// Adds .await to async function calls at the expression level, handling nested calls.
7617pub fn codegen_expr_with_async(
7618    expr: &Expr,
7619    interner: &Interner,
7620    synced_vars: &HashSet<Symbol>,
7621    async_functions: &HashSet<Symbol>,
7622    variable_types: &HashMap<Symbol, String>,
7623) -> String {
7624    let empty_registry = TypeRegistry::new();
7625    let empty_strings = HashSet::new();
7626    codegen_expr_boxed_internal(expr, interner, synced_vars, &HashSet::new(), &empty_registry, async_functions, &HashSet::new(), &empty_strings, variable_types)
7627}
7628
7629/// Codegen expression with async support and string variable tracking.
7630fn codegen_expr_with_async_and_strings(
7631    expr: &Expr,
7632    interner: &Interner,
7633    synced_vars: &HashSet<Symbol>,
7634    async_functions: &HashSet<Symbol>,
7635    string_vars: &HashSet<Symbol>,
7636    variable_types: &HashMap<Symbol, String>,
7637) -> String {
7638    let empty_registry = TypeRegistry::new();
7639    codegen_expr_boxed_internal(expr, interner, synced_vars, &HashSet::new(), &empty_registry, async_functions, &HashSet::new(), string_vars, variable_types)
7640}
7641
7642/// Check if an expression is definitely numeric (safe to use + operator).
7643/// This is conservative for Add operations - treats it as string concat only
7644/// when clearly dealing with strings (string literals).
7645fn is_definitely_numeric_expr(expr: &Expr) -> bool {
7646    match expr {
7647        Expr::Literal(Literal::Number(_)) => true,
7648        Expr::Literal(Literal::Float(_)) => true,
7649        Expr::Literal(Literal::Duration(_)) => true,
7650        // Identifiers might be strings, but without a string literal nearby,
7651        // assume numeric (Rust will catch type errors)
7652        Expr::Identifier(_) => true,
7653        // Arithmetic operations are numeric
7654        Expr::BinaryOp { op: BinaryOpKind::Subtract, .. } => true,
7655        Expr::BinaryOp { op: BinaryOpKind::Multiply, .. } => true,
7656        Expr::BinaryOp { op: BinaryOpKind::Divide, .. } => true,
7657        Expr::BinaryOp { op: BinaryOpKind::Modulo, .. } => true,
7658        // Length always returns a number
7659        Expr::Length { .. } => true,
7660        // Add is numeric if both operands seem numeric
7661        Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
7662            is_definitely_numeric_expr(left) && is_definitely_numeric_expr(right)
7663        }
7664        // Function calls - assume numeric (Rust type checker will validate)
7665        Expr::Call { .. } => true,
7666        // Index expressions - assume numeric
7667        Expr::Index { .. } => true,
7668        _ => true,
7669    }
7670}
7671
7672/// Check if an expression is definitely a string (needs format! for concatenation).
7673/// Takes a set of known string variable symbols for identifier lookup.
7674fn is_definitely_string_expr_with_vars(expr: &Expr, string_vars: &HashSet<Symbol>) -> bool {
7675    match expr {
7676        // String literals are definitely strings
7677        Expr::Literal(Literal::Text(_)) => true,
7678        // Variables known to be strings
7679        Expr::Identifier(sym) => string_vars.contains(sym),
7680        // Concat always produces strings
7681        Expr::BinaryOp { op: BinaryOpKind::Concat, .. } => true,
7682        // Add with a string operand produces a string
7683        Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
7684            is_definitely_string_expr_with_vars(left, string_vars)
7685                || is_definitely_string_expr_with_vars(right, string_vars)
7686        }
7687        // WithCapacity wrapping a string value is a string
7688        Expr::WithCapacity { value, .. } => is_definitely_string_expr_with_vars(value, string_vars),
7689        _ => false,
7690    }
7691}
7692
7693/// Check if an expression is definitely a string (without variable tracking).
7694/// This is a fallback for contexts where string_vars isn't available.
7695fn is_definitely_string_expr(expr: &Expr) -> bool {
7696    let empty = HashSet::new();
7697    is_definitely_string_expr_with_vars(expr, &empty)
7698}
7699
7700/// Collect leaf operands from a chain of string Add/Concat operations.
7701///
7702/// Walks left-leaning trees of `+` (on strings) and `Concat` operations,
7703/// collecting all leaf expressions into a flat Vec. This enables emitting
7704/// a single `format!("{}{}{}", a, b, c)` instead of nested
7705/// `format!("{}{}", format!("{}{}", a, b), c)`, avoiding O(n^2) allocation.
7706fn collect_string_concat_operands<'a, 'b>(
7707    expr: &'b Expr<'a>,
7708    string_vars: &HashSet<Symbol>,
7709    operands: &mut Vec<&'b Expr<'a>>,
7710) {
7711    match expr {
7712        Expr::BinaryOp { op: BinaryOpKind::Concat, left, right } => {
7713            collect_string_concat_operands(left, string_vars, operands);
7714            collect_string_concat_operands(right, string_vars, operands);
7715        }
7716        Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
7717            let has_string = is_definitely_string_expr_with_vars(left, string_vars)
7718                || is_definitely_string_expr_with_vars(right, string_vars);
7719            if has_string {
7720                collect_string_concat_operands(left, string_vars, operands);
7721                collect_string_concat_operands(right, string_vars, operands);
7722            } else {
7723                operands.push(expr);
7724            }
7725        }
7726        _ => {
7727            operands.push(expr);
7728        }
7729    }
7730}
7731
7732/// Phase 102: Codegen with boxed field support for recursive enums.
7733/// Phase 103: Added registry for polymorphic enum type inference.
7734/// Phase 54+: Added async_functions for proper .await on nested async calls.
7735fn codegen_expr_boxed(
7736    expr: &Expr,
7737    interner: &Interner,
7738    synced_vars: &HashSet<Symbol>,
7739    boxed_fields: &HashSet<(String, String, String)>,  // (EnumName, VariantName, FieldName)
7740    registry: &TypeRegistry,  // Phase 103: For type annotations on polymorphic enums
7741    async_functions: &HashSet<Symbol>,  // Phase 54+: Functions that are async
7742) -> String {
7743    // Delegate to codegen_expr_full with empty context for boxed bindings and string vars
7744    let empty_boxed = HashSet::new();
7745    let empty_strings = HashSet::new();
7746    let empty_types = HashMap::new();
7747    codegen_expr_boxed_internal(expr, interner, synced_vars, boxed_fields, registry, async_functions, &empty_boxed, &empty_strings, &empty_types)
7748}
7749
7750/// Codegen with string variable tracking for proper string concatenation.
7751fn codegen_expr_boxed_with_strings(
7752    expr: &Expr,
7753    interner: &Interner,
7754    synced_vars: &HashSet<Symbol>,
7755    boxed_fields: &HashSet<(String, String, String)>,
7756    registry: &TypeRegistry,
7757    async_functions: &HashSet<Symbol>,
7758    string_vars: &HashSet<Symbol>,
7759) -> String {
7760    let empty_boxed = HashSet::new();
7761    let empty_types = HashMap::new();
7762    codegen_expr_boxed_internal(expr, interner, synced_vars, boxed_fields, registry, async_functions, &empty_boxed, string_vars, &empty_types)
7763}
7764
7765/// Codegen with variable type tracking for direct collection indexing optimization.
7766fn codegen_expr_boxed_with_types(
7767    expr: &Expr,
7768    interner: &Interner,
7769    synced_vars: &HashSet<Symbol>,
7770    boxed_fields: &HashSet<(String, String, String)>,
7771    registry: &TypeRegistry,
7772    async_functions: &HashSet<Symbol>,
7773    string_vars: &HashSet<Symbol>,
7774    variable_types: &HashMap<Symbol, String>,
7775) -> String {
7776    let empty_boxed = HashSet::new();
7777    codegen_expr_boxed_internal(expr, interner, synced_vars, boxed_fields, registry, async_functions, &empty_boxed, string_vars, variable_types)
7778}
7779
7780/// Internal implementation of codegen_expr_boxed that can handle extra context.
7781fn codegen_expr_boxed_internal(
7782    expr: &Expr,
7783    interner: &Interner,
7784    synced_vars: &HashSet<Symbol>,
7785    boxed_fields: &HashSet<(String, String, String)>,
7786    registry: &TypeRegistry,
7787    async_functions: &HashSet<Symbol>,
7788    boxed_bindings: &HashSet<Symbol>,
7789    string_vars: &HashSet<Symbol>,
7790    variable_types: &HashMap<Symbol, String>,
7791) -> String {
7792    // Helper macro for recursive calls with all context
7793    macro_rules! recurse {
7794        ($e:expr) => {
7795            codegen_expr_boxed_internal($e, interner, synced_vars, boxed_fields, registry, async_functions, boxed_bindings, string_vars, variable_types)
7796        };
7797    }
7798
7799    match expr {
7800        Expr::Literal(lit) => codegen_literal(lit, interner),
7801
7802        Expr::Identifier(sym) => {
7803            let name = interner.resolve(*sym).to_string();
7804            // Dereference boxed bindings from enum destructuring
7805            if boxed_bindings.contains(sym) {
7806                format!("(*{})", name)
7807            } else {
7808                name
7809            }
7810        }
7811
7812        Expr::BinaryOp { op, left, right } => {
7813            // Flatten chained string concat/add into a single format! call.
7814            // Turns O(n^2) nested format! into O(n) single-allocation.
7815            let is_string_concat = matches!(op, BinaryOpKind::Concat)
7816                || (matches!(op, BinaryOpKind::Add)
7817                    && (is_definitely_string_expr_with_vars(left, string_vars)
7818                        || is_definitely_string_expr_with_vars(right, string_vars)));
7819
7820            if is_string_concat {
7821                let mut operands = Vec::new();
7822                collect_string_concat_operands(expr, string_vars, &mut operands);
7823                let placeholders: String = operands.iter().map(|_| "{}").collect::<Vec<_>>().join("");
7824                let values: Vec<String> = operands.iter().map(|e| {
7825                    // String literals can be &str inside format!() — no heap allocation needed
7826                    if let Expr::Literal(Literal::Text(sym)) = e {
7827                        format!("\"{}\"", interner.resolve(*sym))
7828                    } else {
7829                        recurse!(e)
7830                    }
7831                }).collect();
7832                return format!("format!(\"{}\", {})", placeholders, values.join(", "));
7833            }
7834
7835            // Optimize HashMap .get() for equality comparisons to avoid cloning
7836            if matches!(op, BinaryOpKind::Eq | BinaryOpKind::NotEq) {
7837                let neg = matches!(op, BinaryOpKind::NotEq);
7838                // Check if left side is a HashMap index
7839                if let Expr::Index { collection, index } = left {
7840                    if let Expr::Identifier(sym) = collection {
7841                        if let Some(t) = variable_types.get(sym) {
7842                            if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") {
7843                                let coll_str = recurse!(collection);
7844                                let key_str = recurse!(index);
7845                                let val_str = recurse!(right);
7846                                let cmp = if neg { "!=" } else { "==" };
7847                                if has_copy_value_type(t) {
7848                                    return format!("({}.get(&({})).copied() {} Some({}))", coll_str, key_str, cmp, val_str);
7849                                } else {
7850                                    return format!("({}.get(&({})) {} Some(&({})))", coll_str, key_str, cmp, val_str);
7851                                }
7852                            }
7853                        }
7854                    }
7855                }
7856                // Check if right side is a HashMap index
7857                if let Expr::Index { collection, index } = right {
7858                    if let Expr::Identifier(sym) = collection {
7859                        if let Some(t) = variable_types.get(sym) {
7860                            if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") {
7861                                let coll_str = recurse!(collection);
7862                                let key_str = recurse!(index);
7863                                let val_str = recurse!(left);
7864                                let cmp = if neg { "!=" } else { "==" };
7865                                if has_copy_value_type(t) {
7866                                    return format!("(Some({}) {} {}.get(&({})).copied())", val_str, cmp, coll_str, key_str);
7867                                } else {
7868                                    return format!("(Some(&({})) {} {}.get(&({})))", val_str, cmp, coll_str, key_str);
7869                                }
7870                            }
7871                        }
7872                    }
7873                }
7874            }
7875
7876            let left_str = recurse!(left);
7877            let right_str = recurse!(right);
7878            let op_str = match op {
7879                BinaryOpKind::Add => "+",
7880                BinaryOpKind::Subtract => "-",
7881                BinaryOpKind::Multiply => "*",
7882                BinaryOpKind::Divide => "/",
7883                BinaryOpKind::Modulo => "%",
7884                BinaryOpKind::Eq => "==",
7885                BinaryOpKind::NotEq => "!=",
7886                BinaryOpKind::Lt => "<",
7887                BinaryOpKind::Gt => ">",
7888                BinaryOpKind::LtEq => "<=",
7889                BinaryOpKind::GtEq => ">=",
7890                BinaryOpKind::And => "&&",
7891                BinaryOpKind::Or => "||",
7892                BinaryOpKind::Concat => unreachable!(), // Handled above
7893            };
7894            format!("({} {} {})", left_str, op_str, right_str)
7895        }
7896
7897        Expr::Call { function, args } => {
7898            let func_name = escape_rust_ident(interner.resolve(*function));
7899            // Recursively codegen args with full context
7900            let args_str: Vec<String> = args.iter()
7901                .map(|a| recurse!(a))
7902                .collect();
7903            // Add .await if this function is async
7904            if async_functions.contains(function) {
7905                format!("{}({}).await", func_name, args_str.join(", "))
7906            } else {
7907                format!("{}({})", func_name, args_str.join(", "))
7908            }
7909        }
7910
7911        Expr::Index { collection, index } => {
7912            let coll_str = recurse!(collection);
7913            // Direct indexing for known collection types (avoids trait dispatch)
7914            let known_type = if let Expr::Identifier(sym) = collection {
7915                variable_types.get(sym).map(|s| s.as_str())
7916            } else {
7917                None
7918            };
7919            match known_type {
7920                Some(t) if t.starts_with("Vec") => {
7921                    let suffix = if has_copy_element_type(t) { "" } else { ".clone()" };
7922                    // Peephole: simplify (x + 1) - 1 → x for 1-based indexing
7923                    let simplified = if let Expr::BinaryOp { op: BinaryOpKind::Add, left, right } = index {
7924                        if matches!(right, Expr::Literal(Literal::Number(1))) {
7925                            Some(recurse!(left))
7926                        } else if matches!(left, Expr::Literal(Literal::Number(1))) {
7927                            Some(recurse!(right))
7928                        } else {
7929                            None
7930                        }
7931                    } else {
7932                        None
7933                    };
7934                    if let Some(inner_str) = simplified {
7935                        format!("{}[({}) as usize]{}", coll_str, inner_str, suffix)
7936                    } else {
7937                        let index_str = recurse!(index);
7938                        format!("{}[({} - 1) as usize]{}", coll_str, index_str, suffix)
7939                    }
7940                }
7941                Some(t) if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") => {
7942                    let index_str = recurse!(index);
7943                    let suffix = if has_copy_value_type(t) { "" } else { ".clone()" };
7944                    format!("{}[&({})]{}", coll_str, index_str, suffix)
7945                }
7946                _ => {
7947                    let index_str = recurse!(index);
7948                    // Fallback: polymorphic indexing via trait
7949                    format!("LogosIndex::logos_get(&{}, {})", coll_str, index_str)
7950                }
7951            }
7952        }
7953
7954        Expr::Slice { collection, start, end } => {
7955            let coll_str = recurse!(collection);
7956            let start_str = recurse!(start);
7957            let end_str = recurse!(end);
7958            // Phase 43D: 1-indexed inclusive to 0-indexed exclusive
7959            // "items 1 through 3" → &items[0..3] (elements at indices 0, 1, 2)
7960            format!("&{}[({} - 1) as usize..{} as usize]", coll_str, start_str, end_str)
7961        }
7962
7963        Expr::Copy { expr: inner } => {
7964            let expr_str = recurse!(inner);
7965            // Phase 43D: Explicit owned copy — .to_owned() is universal:
7966            // - &[T] (slices) → Vec<T> via [T]: ToOwned<Owned=Vec<T>>
7967            // - Vec<T>, HashMap<K,V>, HashSet<T> → Self via Clone blanket impl
7968            format!("{}.to_owned()", expr_str)
7969        }
7970
7971        Expr::Give { value } => {
7972            // Ownership transfer: emit value without .clone()
7973            // The move semantics are implicit in Rust - no special syntax needed
7974            recurse!(value)
7975        }
7976
7977        Expr::Length { collection } => {
7978            let coll_str = recurse!(collection);
7979            // Phase 43D: Collection length - cast to i64 for LOGOS integer semantics
7980            format!("({}.len() as i64)", coll_str)
7981        }
7982
7983        Expr::Contains { collection, value } => {
7984            let coll_str = recurse!(collection);
7985            let val_str = recurse!(value);
7986            // Use LogosContains trait for unified contains across List, Set, Map, Text
7987            format!("{}.logos_contains(&{})", coll_str, val_str)
7988        }
7989
7990        Expr::Union { left, right } => {
7991            let left_str = recurse!(left);
7992            let right_str = recurse!(right);
7993            format!("{}.union(&{}).cloned().collect::<std::collections::HashSet<_>>()", left_str, right_str)
7994        }
7995
7996        Expr::Intersection { left, right } => {
7997            let left_str = recurse!(left);
7998            let right_str = recurse!(right);
7999            format!("{}.intersection(&{}).cloned().collect::<std::collections::HashSet<_>>()", left_str, right_str)
8000        }
8001
8002        // Phase 48: Sipping Protocol expressions
8003        Expr::ManifestOf { zone } => {
8004            let zone_str = recurse!(zone);
8005            format!("logicaffeine_system::network::FileSipper::from_zone(&{}).manifest()", zone_str)
8006        }
8007
8008        Expr::ChunkAt { index, zone } => {
8009            let zone_str = recurse!(zone);
8010            let index_str = recurse!(index);
8011            // LOGOS uses 1-indexed, Rust uses 0-indexed
8012            format!("logicaffeine_system::network::FileSipper::from_zone(&{}).get_chunk(({} - 1) as usize)", zone_str, index_str)
8013        }
8014
8015        Expr::List(ref items) => {
8016            let item_strs: Vec<String> = items.iter()
8017                .map(|i| recurse!(i))
8018                .collect();
8019            format!("vec![{}]", item_strs.join(", "))
8020        }
8021
8022        Expr::Tuple(ref items) => {
8023            let item_strs: Vec<String> = items.iter()
8024                .map(|i| format!("Value::from({})", recurse!(i)))
8025                .collect();
8026            // Tuples as Vec<Value> for heterogeneous support
8027            format!("vec![{}]", item_strs.join(", "))
8028        }
8029
8030        Expr::Range { start, end } => {
8031            let start_str = recurse!(start);
8032            let end_str = recurse!(end);
8033            format!("({}..={})", start_str, end_str)
8034        }
8035
8036        Expr::FieldAccess { object, field } => {
8037            let field_name = interner.resolve(*field);
8038
8039            // Phase 52: Check if root object is synced - use .get().await
8040            let root_sym = get_root_identifier(object);
8041            if let Some(sym) = root_sym {
8042                if synced_vars.contains(&sym) {
8043                    let obj_name = interner.resolve(sym);
8044                    return format!("{}.get().await.{}", obj_name, field_name);
8045                }
8046            }
8047
8048            let obj_str = recurse!(object);
8049            format!("{}.{}", obj_str, field_name)
8050        }
8051
8052        Expr::New { type_name, type_args, init_fields } => {
8053            let type_str = interner.resolve(*type_name);
8054            if !init_fields.is_empty() {
8055                // Struct initialization with fields: Point { x: 10, y: 20, ..Default::default() }
8056                // Always add ..Default::default() to handle partial initialization (e.g., CRDT fields)
8057                let fields_str = init_fields.iter()
8058                    .map(|(name, value)| {
8059                        let field_name = interner.resolve(*name);
8060                        let value_str = recurse!(value);
8061                        format!("{}: {}", field_name, value_str)
8062                    })
8063                    .collect::<Vec<_>>()
8064                    .join(", ");
8065                format!("{} {{ {}, ..Default::default() }}", type_str, fields_str)
8066            } else if type_args.is_empty() {
8067                format!("{}::default()", type_str)
8068            } else {
8069                // Phase 34: Turbofish syntax for generic instantiation
8070                // Bug fix: Use codegen_type_expr to support nested types like Seq of (Seq of Int)
8071                let args_str = type_args.iter()
8072                    .map(|t| codegen_type_expr(t, interner))
8073                    .collect::<Vec<_>>()
8074                    .join(", ");
8075                format!("{}::<{}>::default()", type_str, args_str)
8076            }
8077        }
8078
8079        Expr::NewVariant { enum_name, variant, fields } => {
8080            let enum_str = interner.resolve(*enum_name);
8081            let variant_str = interner.resolve(*variant);
8082            if fields.is_empty() {
8083                // Unit variant: Shape::Point
8084                format!("{}::{}", enum_str, variant_str)
8085            } else {
8086                // Phase 103: Count identifier usage to handle cloning for reused values
8087                // We need to clone on all uses except the last one
8088                let mut identifier_counts: HashMap<Symbol, usize> = HashMap::new();
8089                for (_, value) in fields.iter() {
8090                    if let Expr::Identifier(sym) = value {
8091                        *identifier_counts.entry(*sym).or_insert(0) += 1;
8092                    }
8093                }
8094
8095                // Track remaining uses for each identifier
8096                let mut remaining_uses: HashMap<Symbol, usize> = identifier_counts.clone();
8097
8098                // Struct variant: Shape::Circle { radius: 10 }
8099                // Phase 102: Check if any field is recursive and needs Box::new()
8100                let fields_str: Vec<String> = fields.iter()
8101                    .map(|(field_name, value)| {
8102                        let name = interner.resolve(*field_name);
8103
8104                        // Phase 103: Clone identifiers that are used multiple times
8105                        // Clone on all uses except the last one (to allow move on final use)
8106                        let val = if let Expr::Identifier(sym) = value {
8107                            let total = identifier_counts.get(sym).copied().unwrap_or(0);
8108                            let remaining = remaining_uses.get_mut(sym);
8109                            let base_name = if boxed_bindings.contains(sym) {
8110                                format!("(*{})", interner.resolve(*sym))
8111                            } else {
8112                                interner.resolve(*sym).to_string()
8113                            };
8114                            if total > 1 {
8115                                if let Some(r) = remaining {
8116                                    *r -= 1;
8117                                    if *r > 0 {
8118                                        // Not the last use, need to clone
8119                                        format!("{}.clone()", base_name)
8120                                    } else {
8121                                        // Last use, can move
8122                                        base_name
8123                                    }
8124                                } else {
8125                                    base_name
8126                                }
8127                            } else {
8128                                base_name
8129                            }
8130                        } else {
8131                            recurse!(value)
8132                        };
8133
8134                        // Check if this field needs to be boxed (recursive type)
8135                        let key = (enum_str.to_string(), variant_str.to_string(), name.to_string());
8136                        if boxed_fields.contains(&key) {
8137                            format!("{}: Box::new({})", name, val)
8138                        } else {
8139                            format!("{}: {}", name, val)
8140                        }
8141                    })
8142                    .collect();
8143                format!("{}::{} {{ {} }}", enum_str, variant_str, fields_str.join(", "))
8144            }
8145        }
8146
8147        Expr::OptionSome { value } => {
8148            format!("Some({})", recurse!(value))
8149        }
8150
8151        Expr::OptionNone => {
8152            "None".to_string()
8153        }
8154
8155        Expr::Escape { code, .. } => {
8156            let raw_code = interner.resolve(*code);
8157            let mut block = String::from("{\n");
8158            for line in raw_code.lines() {
8159                block.push_str("    ");
8160                block.push_str(line);
8161                block.push('\n');
8162            }
8163            block.push('}');
8164            block
8165        }
8166
8167        Expr::WithCapacity { value, capacity } => {
8168            let cap_str = recurse!(capacity);
8169            match value {
8170                // Empty string → String::with_capacity(cap)
8171                Expr::Literal(Literal::Text(sym)) if interner.resolve(*sym).is_empty() => {
8172                    format!("String::with_capacity(({}) as usize)", cap_str)
8173                }
8174                // Non-empty string → { let mut __s = String::with_capacity(cap); __s.push_str("..."); __s }
8175                Expr::Literal(Literal::Text(sym)) => {
8176                    let text = interner.resolve(*sym);
8177                    format!("{{ let mut __s = String::with_capacity(({}) as usize); __s.push_str(\"{}\"); __s }}", cap_str, text)
8178                }
8179                // Collection Expr::New → Type::with_capacity(cap)
8180                Expr::New { type_name, type_args, .. } => {
8181                    let type_str = interner.resolve(*type_name);
8182                    match type_str {
8183                        "Seq" | "List" | "Vec" => {
8184                            let elem = if !type_args.is_empty() {
8185                                codegen_type_expr(&type_args[0], interner)
8186                            } else { "()".to_string() };
8187                            format!("{{ let __v: Vec<{}> = Vec::with_capacity(({}) as usize); __v }}", elem, cap_str)
8188                        }
8189                        "Map" | "HashMap" => {
8190                            let (k, v) = if type_args.len() >= 2 {
8191                                (codegen_type_expr(&type_args[0], interner),
8192                                 codegen_type_expr(&type_args[1], interner))
8193                            } else { ("String".to_string(), "String".to_string()) };
8194                            format!("{{ let __m: std::collections::HashMap<{}, {}> = std::collections::HashMap::with_capacity(({}) as usize); __m }}", k, v, cap_str)
8195                        }
8196                        "Set" | "HashSet" => {
8197                            let elem = if !type_args.is_empty() {
8198                                codegen_type_expr(&type_args[0], interner)
8199                            } else { "()".to_string() };
8200                            format!("{{ let __s: std::collections::HashSet<{}> = std::collections::HashSet::with_capacity(({}) as usize); __s }}", elem, cap_str)
8201                        }
8202                        _ => recurse!(value) // Unknown type — ignore capacity
8203                    }
8204                }
8205                // Other expressions — ignore capacity hint
8206                _ => recurse!(value)
8207            }
8208        }
8209
8210        Expr::Closure { params, body, .. } => {
8211            use crate::ast::stmt::ClosureBody;
8212            let params_str: Vec<String> = params.iter()
8213                .map(|(name, ty)| {
8214                    let param_name = escape_rust_ident(interner.resolve(*name));
8215                    let param_type = codegen_type_expr(ty, interner);
8216                    format!("{}: {}", param_name, param_type)
8217                })
8218                .collect();
8219
8220            match body {
8221                ClosureBody::Expression(expr) => {
8222                    let body_str = recurse!(expr);
8223                    format!("move |{}| {{ {} }}", params_str.join(", "), body_str)
8224                }
8225                ClosureBody::Block(stmts) => {
8226                    let mut body_str = String::new();
8227                    let mut ctx = RefinementContext::new();
8228                    let empty_mutable = collect_mutable_vars(stmts);
8229                    let empty_lww = HashSet::new();
8230                    let empty_mv = HashSet::new();
8231                    let mut empty_synced = HashSet::new();
8232                    let empty_caps = HashMap::new();
8233                    let empty_pipes = HashSet::new();
8234                    let empty_boxed = HashSet::new();
8235                    let empty_registry = TypeRegistry::new();
8236                    for stmt in stmts.iter() {
8237                        body_str.push_str(&codegen_stmt(
8238                            stmt, interner, 2, &empty_mutable, &mut ctx,
8239                            &empty_lww, &empty_mv, &mut empty_synced, &empty_caps,
8240                            async_functions, &empty_pipes, &empty_boxed, &empty_registry,
8241                        ));
8242                    }
8243                    format!("move |{}| {{\n{}{}}}", params_str.join(", "), body_str, "    ")
8244                }
8245            }
8246        }
8247
8248        Expr::CallExpr { callee, args } => {
8249            let callee_str = recurse!(callee);
8250            let args_str: Vec<String> = args.iter().map(|a| recurse!(a)).collect();
8251            format!("({})({})", callee_str, args_str.join(", "))
8252        }
8253    }
8254}
8255
8256fn codegen_literal(lit: &Literal, interner: &Interner) -> String {
8257    match lit {
8258        Literal::Number(n) => n.to_string(),
8259        Literal::Float(f) => format!("{}f64", f),
8260        // String literals are converted to String for consistent Text type handling
8261        Literal::Text(sym) => format!("String::from(\"{}\")", interner.resolve(*sym)),
8262        Literal::Boolean(b) => b.to_string(),
8263        Literal::Nothing => "()".to_string(),
8264        // Character literals
8265        Literal::Char(c) => {
8266            // Handle escape sequences for special characters
8267            match c {
8268                '\n' => "'\\n'".to_string(),
8269                '\t' => "'\\t'".to_string(),
8270                '\r' => "'\\r'".to_string(),
8271                '\\' => "'\\\\'".to_string(),
8272                '\'' => "'\\''".to_string(),
8273                '\0' => "'\\0'".to_string(),
8274                c => format!("'{}'", c),
8275            }
8276        }
8277        // Temporal literals: Duration stored as nanoseconds (i64)
8278        Literal::Duration(nanos) => format!("std::time::Duration::from_nanos({}u64)", nanos),
8279        // Date stored as days since Unix epoch (i32)
8280        Literal::Date(days) => format!("LogosDate({})", days),
8281        // Moment stored as nanoseconds since Unix epoch (i64)
8282        Literal::Moment(nanos) => format!("LogosMoment({})", nanos),
8283        // Span stored as (months, days) - separate because they're incommensurable
8284        Literal::Span { months, days } => format!("LogosSpan::new({}, {})", months, days),
8285        // Time-of-day stored as nanoseconds from midnight
8286        Literal::Time(nanos) => format!("LogosTime({})", nanos),
8287    }
8288}
8289
8290/// Converts a LogicExpr to a Rust boolean expression for debug_assert!().
8291/// Uses RustFormatter to unify all logic-to-Rust translation.
8292pub fn codegen_assertion(expr: &LogicExpr, interner: &Interner) -> String {
8293    let mut registry = SymbolRegistry::new();
8294    let formatter = RustFormatter;
8295    let mut buf = String::new();
8296
8297    match expr.write_logic(&mut buf, &mut registry, interner, &formatter) {
8298        Ok(_) => buf,
8299        Err(_) => "/* error generating assertion */ false".to_string(),
8300    }
8301}
8302
8303pub fn codegen_term(term: &Term, interner: &Interner) -> String {
8304    match term {
8305        Term::Constant(sym) => interner.resolve(*sym).to_string(),
8306        Term::Variable(sym) => interner.resolve(*sym).to_string(),
8307        Term::Value { kind, .. } => match kind {
8308            NumberKind::Integer(n) => n.to_string(),
8309            NumberKind::Real(f) => f.to_string(),
8310            NumberKind::Symbolic(sym) => interner.resolve(*sym).to_string(),
8311        },
8312        Term::Function(name, args) => {
8313            let args_str: Vec<String> = args.iter()
8314                .map(|a| codegen_term(a, interner))
8315                .collect();
8316            format!("{}({})", interner.resolve(*name), args_str.join(", "))
8317        }
8318        Term::Possessed { possessor, possessed } => {
8319            let poss_str = codegen_term(possessor, interner);
8320            format!("{}.{}", poss_str, interner.resolve(*possessed))
8321        }
8322        Term::Group(members) => {
8323            let members_str: Vec<String> = members.iter()
8324                .map(|m| codegen_term(m, interner))
8325                .collect();
8326            format!("({})", members_str.join(", "))
8327        }
8328        _ => "/* unsupported Term */".to_string(),
8329    }
8330}
8331
8332#[cfg(test)]
8333mod tests {
8334    use super::*;
8335
8336    #[test]
8337    fn test_literal_number() {
8338        let interner = Interner::new();
8339        let synced_vars = HashSet::new();
8340        let expr = Expr::Literal(Literal::Number(42));
8341        assert_eq!(codegen_expr(&expr, &interner, &synced_vars), "42");
8342    }
8343
8344    #[test]
8345    fn test_literal_boolean() {
8346        let interner = Interner::new();
8347        let synced_vars = HashSet::new();
8348        assert_eq!(codegen_expr(&Expr::Literal(Literal::Boolean(true)), &interner, &synced_vars), "true");
8349        assert_eq!(codegen_expr(&Expr::Literal(Literal::Boolean(false)), &interner, &synced_vars), "false");
8350    }
8351
8352    #[test]
8353    fn test_literal_nothing() {
8354        let interner = Interner::new();
8355        let synced_vars = HashSet::new();
8356        assert_eq!(codegen_expr(&Expr::Literal(Literal::Nothing), &interner, &synced_vars), "()");
8357    }
8358}