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
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: swap pattern optimization
4386            if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, i, interner, 1, main_ctx.get_variable_types()) {
4387                output.push_str(&code);
4388                i += 1 + skip;
4389                continue;
4390            }
4391            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));
4392            i += 1;
4393        }
4394    }
4395    writeln!(output, "}}").unwrap();
4396    output
4397}
4398
4399/// Phase 32/38: Generate a function definition.
4400/// Phase 38: Updated for native functions and TypeExpr types.
4401/// Phase 49: Accepts lww_fields for LWWRegister SetField handling.
4402/// Phase 103: Accepts registry for polymorphic enum type inference.
4403fn codegen_function_def(
4404    name: Symbol,
4405    params: &[(Symbol, &TypeExpr)],
4406    body: &[Stmt],
4407    return_type: Option<&TypeExpr>,
4408    is_native: bool,
4409    native_path: Option<Symbol>,
4410    is_exported: bool,
4411    export_target: Option<Symbol>,
4412    interner: &Interner,
4413    lww_fields: &HashSet<(String, String)>,
4414    mv_fields: &HashSet<(String, String)>,  // Phase 49b: MVRegister fields
4415    async_functions: &HashSet<Symbol>,  // Phase 54
4416    boxed_fields: &HashSet<(String, String, String)>,  // Phase 102
4417    registry: &TypeRegistry,  // Phase 103
4418    pure_functions: &HashSet<Symbol>,
4419) -> String {
4420    let mut output = String::new();
4421    let raw_name = interner.resolve(name);
4422    let func_name = escape_rust_ident(raw_name);
4423    let export_target_lower = export_target.map(|s| interner.resolve(s).to_lowercase());
4424
4425    // Phase 54: Detect which parameters are used as pipe senders
4426    let pipe_sender_params = collect_pipe_sender_params(body);
4427
4428    // FFI: Exported functions need special signatures
4429    let is_c_export_early = is_exported && matches!(export_target_lower.as_deref(), None | Some("c"));
4430
4431    // TCE: Detect tail recursion eligibility
4432    let is_tce = !is_native && !is_c_export_early && is_tail_recursive(name, body);
4433    let param_syms: Vec<Symbol> = params.iter().map(|(s, _)| *s).collect();
4434
4435    // Accumulator Introduction: Detect non-tail single-call + / * patterns
4436    let acc_info = if !is_tce && !is_native && !is_c_export_early {
4437        detect_accumulator_pattern(name, body)
4438    } else {
4439        None
4440    };
4441    let is_acc = acc_info.is_some();
4442
4443    // Memoization: Detect pure multi-call recursive functions with hashable params
4444    let is_memo = !is_tce && !is_acc && !is_native && !is_c_export_early
4445        && should_memoize(name, body, params, return_type, pure_functions.contains(&name), interner);
4446
4447    let needs_mut_params = is_tce || is_acc;
4448
4449    // Build parameter list using TypeExpr
4450    let params_str: Vec<String> = params.iter()
4451        .map(|(param_name, param_type)| {
4452            let pname = interner.resolve(*param_name);
4453            let ty = codegen_type_expr(param_type, interner);
4454            // Phase 54: If param is used as a pipe sender, wrap type in Sender<T>
4455            if pipe_sender_params.contains(param_name) {
4456                format!("{}: tokio::sync::mpsc::Sender<{}>", pname, ty)
4457            } else if needs_mut_params {
4458                format!("mut {}: {}", pname, ty)
4459            } else {
4460                format!("{}: {}", pname, ty)
4461            }
4462        })
4463        .collect();
4464
4465    // Get return type string from TypeExpr or infer from body
4466    let return_type_str = return_type
4467        .map(|t| codegen_type_expr(t, interner))
4468        .or_else(|| infer_return_type_from_body(body, interner));
4469
4470    // Phase 51/54: Check if function is async (includes transitive async detection)
4471    let is_async = async_functions.contains(&name);
4472    let fn_keyword = if is_async { "async fn" } else { "fn" };
4473
4474    // FFI: Exported functions need special signatures
4475    let is_c_export = is_c_export_early;
4476
4477    // FFI: Check if C export needs type marshaling
4478    // Triggers for: Text params/return, reference types, Result return, refinement params
4479    let needs_c_marshaling = is_c_export && {
4480        let has_text_param = params.iter().any(|(_, ty)| is_text_type(ty, interner));
4481        let has_text_return = return_type.map_or(false, |ty| is_text_type(ty, interner));
4482        let has_ref_param = params.iter().any(|(_, ty)| {
4483            classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType
4484        });
4485        let has_ref_return = return_type.map_or(false, |ty| {
4486            classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType
4487        });
4488        let has_result_return = return_type.map_or(false, |ty| is_result_type(ty, interner));
4489        let has_refinement_param = params.iter().any(|(_, ty)| {
4490            matches!(ty, TypeExpr::Refinement { .. })
4491        });
4492        has_text_param || has_text_return || has_ref_param || has_ref_return
4493            || has_result_return || has_refinement_param
4494    };
4495
4496    if needs_c_marshaling {
4497        // Generate two-function pattern: inner function + C ABI wrapper
4498        return codegen_c_export_with_marshaling(
4499            name, params, body, return_type, interner,
4500            lww_fields, mv_fields, async_functions, boxed_fields, registry,
4501        );
4502    }
4503
4504    // Build function signature
4505    let (vis_prefix, abi_prefix) = if is_exported {
4506        match export_target_lower.as_deref() {
4507            None | Some("c") => ("pub ", "extern \"C\" "),
4508            Some("wasm") => ("pub ", ""),
4509            _ => ("pub ", ""),
4510        }
4511    } else {
4512        ("", "")
4513    };
4514
4515    let signature = if let Some(ref ret_ty) = return_type_str {
4516        if ret_ty != "()" {
4517            format!("{}{}{} {}({}) -> {}", vis_prefix, abi_prefix, fn_keyword, func_name, params_str.join(", "), ret_ty)
4518        } else {
4519            format!("{}{}{} {}({})", vis_prefix, abi_prefix, fn_keyword, func_name, params_str.join(", "))
4520        }
4521    } else {
4522        format!("{}{}{} {}({})", vis_prefix, abi_prefix, fn_keyword, func_name, params_str.join(", "))
4523    };
4524
4525    // Emit #[inline] for small non-recursive, non-exported functions
4526    if !is_tce && !is_acc && should_inline(name, body, is_native, is_exported, is_async) {
4527        writeln!(output, "#[inline]").unwrap();
4528    }
4529
4530    // FFI: Emit export attributes before the function
4531    if is_exported {
4532        match export_target_lower.as_deref() {
4533            None | Some("c") => {
4534                writeln!(output, "#[export_name = \"logos_{}\"]", raw_name).unwrap();
4535            }
4536            Some("wasm") => {
4537                writeln!(output, "#[wasm_bindgen]").unwrap();
4538            }
4539            _ => {}
4540        }
4541    }
4542
4543    // Phase 38: Handle native functions
4544    if is_native {
4545        let arg_names: Vec<&str> = params.iter()
4546            .map(|(n, _)| interner.resolve(*n))
4547            .collect();
4548
4549        if let Some(path_sym) = native_path {
4550            // User-defined native path: call the Rust path directly
4551            let path = interner.resolve(path_sym);
4552            // Validate path looks like a valid Rust path (identifiers separated by ::)
4553            let is_valid_path = !path.is_empty() && path.split("::").all(|seg| {
4554                !seg.is_empty() && seg.chars().all(|c| c.is_alphanumeric() || c == '_')
4555            });
4556            if is_valid_path {
4557                writeln!(output, "{} {{", signature).unwrap();
4558                writeln!(output, "    {}({})", path, arg_names.join(", ")).unwrap();
4559                writeln!(output, "}}\n").unwrap();
4560            } else {
4561                writeln!(output, "{} {{", signature).unwrap();
4562                writeln!(output, "    compile_error!(\"Invalid native function path: '{}'. Path must be a valid Rust path like \\\"crate::module::function\\\".\")", path).unwrap();
4563                writeln!(output, "}}\n").unwrap();
4564            }
4565        } else {
4566            // Legacy system functions: use map_native_function()
4567            if let Some((module, core_fn)) = map_native_function(raw_name) {
4568                writeln!(output, "{} {{", signature).unwrap();
4569                writeln!(output, "    logicaffeine_system::{}::{}({})", module, core_fn, arg_names.join(", ")).unwrap();
4570                writeln!(output, "}}\n").unwrap();
4571            } else {
4572                writeln!(output, "{} {{", signature).unwrap();
4573                writeln!(output, "    compile_error!(\"Unknown system native function: '{}'. Use `is native \\\"crate::path\\\"` syntax for user-defined native functions.\")", raw_name).unwrap();
4574                writeln!(output, "}}\n").unwrap();
4575            }
4576        }
4577    } else {
4578        // Non-native: emit body (also used for exported functions which have bodies)
4579        // Grand Challenge: Collect mutable vars for this function
4580        let func_mutable_vars = collect_mutable_vars(body);
4581        writeln!(output, "{} {{", signature).unwrap();
4582
4583        // Wrap exported C functions in catch_unwind for panic safety
4584        let wrap_catch_unwind = is_c_export;
4585        if wrap_catch_unwind {
4586            writeln!(output, "    match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {{").unwrap();
4587        }
4588
4589        let mut func_ctx = RefinementContext::new();
4590        let mut func_synced_vars = HashSet::new();  // Phase 52: Track synced variables in function
4591        // Phase 56: Pre-scan for Mount+Sync combinations in function body
4592        let func_var_caps = analyze_variable_capabilities(body, interner);
4593
4594        // Phase 50: Register parameter types for capability Check resolution
4595        for (param_name, param_type) in params {
4596            let type_name = codegen_type_expr(param_type, interner);
4597            func_ctx.register_variable_type(*param_name, type_name);
4598        }
4599
4600        // Phase 54: Functions receive pipe senders as parameters, no local pipe declarations
4601        let func_pipe_vars = HashSet::new();
4602
4603        if is_tce {
4604            // TCE: Wrap body in loop, use TCE-aware statement emitter
4605            writeln!(output, "    loop {{").unwrap();
4606            let stmt_refs: Vec<&Stmt> = body.iter().collect();
4607            let mut si = 0;
4608            while si < stmt_refs.len() {
4609                if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 2) {
4610                    output.push_str(&code);
4611                    si += 1 + skip;
4612                    continue;
4613                }
4614                if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 2, func_ctx.get_variable_types()) {
4615                    output.push_str(&code);
4616                    si += 1 + skip;
4617                    continue;
4618                }
4619                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));
4620                si += 1;
4621            }
4622            writeln!(output, "    }}").unwrap();
4623        } else if let Some(ref acc) = acc_info {
4624            // Accumulator Introduction: Wrap body in loop with accumulator variable
4625            writeln!(output, "    let mut __acc: i64 = {};", acc.identity).unwrap();
4626            writeln!(output, "    loop {{").unwrap();
4627            let stmt_refs: Vec<&Stmt> = body.iter().collect();
4628            let mut si = 0;
4629            while si < stmt_refs.len() {
4630                if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 2) {
4631                    output.push_str(&code);
4632                    si += 1 + skip;
4633                    continue;
4634                }
4635                if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 2, func_ctx.get_variable_types()) {
4636                    output.push_str(&code);
4637                    si += 1 + skip;
4638                    continue;
4639                }
4640                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));
4641                si += 1;
4642            }
4643            writeln!(output, "    }}").unwrap();
4644        } else if is_memo {
4645            // Memoization: Wrap body in closure with thread-local cache
4646            let ret_ty = return_type_str.as_deref().unwrap_or("i64");
4647            let memo_name = format!("__MEMO_{}", func_name.to_uppercase());
4648
4649            // Build key type and key expression
4650            let (key_type, key_expr, copy_method) = if params.len() == 1 {
4651                let ty = codegen_type_expr(params[0].1, interner);
4652                let pname = interner.resolve(params[0].0).to_string();
4653                let copy = if is_copy_type_expr(params[0].1, interner) { "copied" } else { "cloned" };
4654                (ty, pname, copy)
4655            } else {
4656                let types: Vec<String> = params.iter().map(|(_, t)| codegen_type_expr(t, interner)).collect();
4657                let names: Vec<String> = params.iter().map(|(n, _)| interner.resolve(*n).to_string()).collect();
4658                let copy = if params.iter().all(|(_, t)| is_copy_type_expr(t, interner)) { "copied" } else { "cloned" };
4659                (format!("({})", types.join(", ")), format!("({})", names.join(", ")), copy)
4660            };
4661
4662            writeln!(output, "    use std::cell::RefCell;").unwrap();
4663            writeln!(output, "    use std::collections::HashMap;").unwrap();
4664            writeln!(output, "    thread_local! {{").unwrap();
4665            writeln!(output, "        static {}: RefCell<HashMap<{}, {}>> = RefCell::new(HashMap::new());", memo_name, key_type, ret_ty).unwrap();
4666            writeln!(output, "    }}").unwrap();
4667            writeln!(output, "    if let Some(__v) = {}.with(|c| c.borrow().get(&{}).{}()) {{", memo_name, key_expr, copy_method).unwrap();
4668            writeln!(output, "        return __v;").unwrap();
4669            writeln!(output, "    }}").unwrap();
4670            writeln!(output, "    let __memo_result = (|| -> {} {{", ret_ty).unwrap();
4671            let stmt_refs: Vec<&Stmt> = body.iter().collect();
4672            let mut si = 0;
4673            while si < stmt_refs.len() {
4674                if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 2) {
4675                    output.push_str(&code);
4676                    si += 1 + skip;
4677                    continue;
4678                }
4679                if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 2, func_ctx.get_variable_types()) {
4680                    output.push_str(&code);
4681                    si += 1 + skip;
4682                    continue;
4683                }
4684                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));
4685                si += 1;
4686            }
4687            writeln!(output, "    }})();").unwrap();
4688            writeln!(output, "    {}.with(|c| c.borrow_mut().insert({}, __memo_result));", memo_name, key_expr).unwrap();
4689            writeln!(output, "    __memo_result").unwrap();
4690        } else {
4691            let stmt_refs: Vec<&Stmt> = body.iter().collect();
4692            let mut si = 0;
4693            while si < stmt_refs.len() {
4694                if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 1) {
4695                    output.push_str(&code);
4696                    si += 1 + skip;
4697                    continue;
4698                }
4699                if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 1, func_ctx.get_variable_types()) {
4700                    output.push_str(&code);
4701                    si += 1 + skip;
4702                    continue;
4703                }
4704                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));
4705                si += 1;
4706            }
4707        }
4708
4709        if wrap_catch_unwind {
4710            writeln!(output, "    }})) {{").unwrap();
4711            writeln!(output, "        Ok(__v) => __v,").unwrap();
4712            writeln!(output, "        Err(__panic) => {{").unwrap();
4713            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();
4714            writeln!(output, "            logos_set_last_error(__msg);").unwrap();
4715            // Determine default for panic case based on return type
4716            if let Some(ref ret_str) = return_type_str {
4717                if ret_str != "()" {
4718                    writeln!(output, "            Default::default()").unwrap();
4719                }
4720            }
4721            writeln!(output, "        }}").unwrap();
4722            writeln!(output, "    }}").unwrap();
4723        }
4724
4725        writeln!(output, "}}\n").unwrap();
4726    }
4727
4728    output
4729}
4730
4731/// Phase 38: Map native function names to logicaffeine_system module paths.
4732/// For system functions only — user-defined native paths bypass this entirely.
4733/// Returns None for unknown functions (caller emits compile_error!).
4734fn map_native_function(name: &str) -> Option<(&'static str, &'static str)> {
4735    match name {
4736        "read" => Some(("file", "read")),
4737        "write" => Some(("file", "write")),
4738        "now" => Some(("time", "now")),
4739        "sleep" => Some(("time", "sleep")),
4740        "randomInt" => Some(("random", "randomInt")),
4741        "randomFloat" => Some(("random", "randomFloat")),
4742        "get" => Some(("env", "get")),
4743        "args" => Some(("env", "args")),
4744        "parseInt" => Some(("text", "parseInt")),
4745        "parseFloat" => Some(("text", "parseFloat")),
4746        "format" => Some(("fmt", "format")),
4747        _ => None,
4748    }
4749}
4750
4751/// FFI: Check if a TypeExpr resolves to Text/String.
4752fn is_text_type(ty: &TypeExpr, interner: &Interner) -> bool {
4753    match ty {
4754        TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
4755            matches!(interner.resolve(*sym), "Text" | "String")
4756        }
4757        TypeExpr::Refinement { base, .. } => is_text_type(base, interner),
4758        _ => false,
4759    }
4760}
4761
4762/// FFI: Map a TypeExpr to its C ABI representation.
4763/// Primitives pass through; Text becomes raw pointer.
4764fn map_type_to_c_abi(ty: &TypeExpr, interner: &Interner, is_return: bool) -> String {
4765    if is_text_type(ty, interner) {
4766        if is_return {
4767            "*mut std::os::raw::c_char".to_string()
4768        } else {
4769            "*const std::os::raw::c_char".to_string()
4770        }
4771    } else {
4772        codegen_type_expr(ty, interner)
4773    }
4774}
4775
4776/// FFI: Generate a C-exported function with Universal ABI marshaling.
4777///
4778/// Produces: 1) an inner function with normal Rust types, 2) a #[no_mangle] extern "C" wrapper.
4779///
4780/// The wrapper handles:
4781/// - Text param/return marshaling (*const c_char ↔ String)
4782/// - Reference type params/returns via opaque LogosHandle
4783/// - Result<T, E> returns via status code + out-parameter
4784/// - Refinement type boundary guards
4785fn codegen_c_export_with_marshaling(
4786    name: Symbol,
4787    params: &[(Symbol, &TypeExpr)],
4788    body: &[Stmt],
4789    return_type: Option<&TypeExpr>,
4790    interner: &Interner,
4791    lww_fields: &HashSet<(String, String)>,
4792    mv_fields: &HashSet<(String, String)>,
4793    async_functions: &HashSet<Symbol>,
4794    boxed_fields: &HashSet<(String, String, String)>,
4795    registry: &crate::analysis::registry::TypeRegistry,
4796) -> String {
4797    let mut output = String::new();
4798    let raw_name = interner.resolve(name);
4799    // All exported C ABI symbols use the `logos_` prefix to avoid keyword
4800    // collisions in target languages (C, Python, JS, etc.) and to provide
4801    // a consistent namespace for the generated library.
4802    let func_name = format!("logos_{}", raw_name);
4803    let inner_name = escape_rust_ident(raw_name);
4804
4805    // Classify return type
4806    let has_ref_return = return_type.map_or(false, |ty| {
4807        classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType
4808    });
4809    let has_result_return = return_type.map_or(false, |ty| is_result_type(ty, interner));
4810    let has_text_return = return_type.map_or(false, |t| is_text_type(t, interner));
4811
4812    // Determine if we need status-code return pattern
4813    // Status code is needed when the return value requires an out-parameter (ref/text/result)
4814    // or when refinement parameters need validation error paths.
4815    // Ref-type parameters do NOT force status code — catch_unwind handles invalid handle panics.
4816    let uses_status_code = has_ref_return || has_result_return || has_text_return
4817        || params.iter().any(|(_, ty)| matches!(ty, TypeExpr::Refinement { .. }));
4818
4819    // 1) Emit the inner function with normal Rust types
4820    let inner_params: Vec<String> = params.iter()
4821        .map(|(pname, ptype)| {
4822            format!("{}: {}", interner.resolve(*pname), codegen_type_expr(ptype, interner))
4823        })
4824        .collect();
4825    let inner_ret = return_type.map(|t| codegen_type_expr(t, interner));
4826
4827    let inner_sig = if let Some(ref ret) = inner_ret {
4828        if ret != "()" {
4829            format!("fn {}({}) -> {}", inner_name, inner_params.join(", "), ret)
4830        } else {
4831            format!("fn {}({})", inner_name, inner_params.join(", "))
4832        }
4833    } else {
4834        format!("fn {}({})", inner_name, inner_params.join(", "))
4835    };
4836
4837    writeln!(output, "{} {{", inner_sig).unwrap();
4838    let func_mutable_vars = collect_mutable_vars(body);
4839    let mut func_ctx = RefinementContext::new();
4840    let mut func_synced_vars = HashSet::new();
4841    let func_var_caps = analyze_variable_capabilities(body, interner);
4842    for (param_name, param_type) in params {
4843        let type_name = codegen_type_expr(param_type, interner);
4844        func_ctx.register_variable_type(*param_name, type_name);
4845    }
4846    let func_pipe_vars = HashSet::new();
4847    {
4848        let stmt_refs: Vec<&Stmt> = body.iter().collect();
4849        let mut si = 0;
4850        while si < stmt_refs.len() {
4851            if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 1) {
4852                output.push_str(&code);
4853                si += 1 + skip;
4854                continue;
4855            }
4856            if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 1, func_ctx.get_variable_types()) {
4857                output.push_str(&code);
4858                si += 1 + skip;
4859                continue;
4860            }
4861            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));
4862            si += 1;
4863        }
4864    }
4865    writeln!(output, "}}\n").unwrap();
4866
4867    // 2) Build the C ABI wrapper parameters
4868    let mut c_params: Vec<String> = Vec::new();
4869
4870    for (pname, ptype) in params.iter() {
4871        let pn = interner.resolve(*pname);
4872        if classify_type_for_c_abi(ptype, interner, registry) == CAbiClass::ReferenceType {
4873            c_params.push(format!("{}: LogosHandle", pn));
4874        } else if is_text_type(ptype, interner) {
4875            c_params.push(format!("{}: *const std::os::raw::c_char", pn));
4876        } else {
4877            c_params.push(format!("{}: {}", pn, codegen_type_expr(ptype, interner)));
4878        }
4879    }
4880
4881    // Add out-parameter if using status-code pattern with return value
4882    if uses_status_code {
4883        if let Some(ret_ty) = return_type {
4884            if has_result_return {
4885                // Result<T, E>: out param for the Ok(T) type
4886                if let TypeExpr::Generic { params: ref rparams, .. } = ret_ty {
4887                    if !rparams.is_empty() {
4888                        let ok_ty = &rparams[0];
4889                        if classify_type_for_c_abi(ok_ty, interner, registry) == CAbiClass::ReferenceType {
4890                            c_params.push("out: *mut LogosHandle".to_string());
4891                        } else if is_text_type(ok_ty, interner) {
4892                            c_params.push("out: *mut *mut std::os::raw::c_char".to_string());
4893                        } else {
4894                            let ty_str = codegen_type_expr(ok_ty, interner);
4895                            c_params.push(format!("out: *mut {}", ty_str));
4896                        }
4897                    }
4898                }
4899            } else if has_ref_return {
4900                c_params.push("out: *mut LogosHandle".to_string());
4901            } else if has_text_return {
4902                c_params.push("out: *mut *mut std::os::raw::c_char".to_string());
4903            }
4904        }
4905    }
4906
4907    // Build the wrapper signature
4908    let c_sig = if uses_status_code {
4909        format!("pub extern \"C\" fn {}({}) -> LogosStatus", func_name, c_params.join(", "))
4910    } else if has_text_return {
4911        format!("pub extern \"C\" fn {}({}) -> *mut std::os::raw::c_char", func_name, c_params.join(", "))
4912    } else if let Some(ret_ty) = return_type {
4913        let ret_str = codegen_type_expr(ret_ty, interner);
4914        if ret_str != "()" {
4915            format!("pub extern \"C\" fn {}({}) -> {}", func_name, c_params.join(", "), ret_str)
4916        } else {
4917            format!("pub extern \"C\" fn {}({})", func_name, c_params.join(", "))
4918        }
4919    } else {
4920        format!("pub extern \"C\" fn {}({})", func_name, c_params.join(", "))
4921    };
4922
4923    writeln!(output, "#[no_mangle]").unwrap();
4924    writeln!(output, "{} {{", c_sig).unwrap();
4925
4926    // 3) Marshal parameters
4927    let call_args: Vec<String> = params.iter()
4928        .map(|(pname, ptype)| {
4929            let pname_str = interner.resolve(*pname);
4930            if classify_type_for_c_abi(ptype, interner, registry) == CAbiClass::ReferenceType {
4931                // Look up handle in registry, dereference, and clone for inner
4932                let rust_ty = codegen_type_expr(ptype, interner);
4933                writeln!(output, "    let {pn} = {{", pn = pname_str).unwrap();
4934                writeln!(output, "        let __id = {pn} as u64;", pn = pname_str).unwrap();
4935                writeln!(output, "        let __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
4936                writeln!(output, "        let __ptr = __reg.deref(__id).expect(\"InvalidHandle: handle not found in registry\");").unwrap();
4937                writeln!(output, "        drop(__reg);").unwrap();
4938                writeln!(output, "        unsafe {{ &*(__ptr as *const {ty}) }}.clone()", ty = rust_ty).unwrap();
4939                writeln!(output, "    }};").unwrap();
4940            } else if is_text_type(ptype, interner) {
4941                // Null-safety: check for NULL *const c_char before CStr::from_ptr
4942                if uses_status_code {
4943                    writeln!(output, "    if {pn}.is_null() {{ logos_set_last_error(\"NullPointer: text parameter '{pn}' is null\".to_string()); return LogosStatus::NullPointer; }}",
4944                        pn = pname_str).unwrap();
4945                    writeln!(output, "    let {pn} = unsafe {{ std::ffi::CStr::from_ptr({pn}).to_string_lossy().into_owned() }};",
4946                        pn = pname_str).unwrap();
4947                } else {
4948                    // Non-status-code function: substitute empty string for NULL
4949                    writeln!(output, "    let {pn} = if {pn}.is_null() {{ String::new() }} else {{ unsafe {{ std::ffi::CStr::from_ptr({pn}).to_string_lossy().into_owned() }} }};",
4950                        pn = pname_str).unwrap();
4951                }
4952            }
4953            pname_str.to_string()
4954        })
4955        .collect();
4956
4957    // 4) Emit refinement guards for parameters
4958    for (pname, ptype) in params.iter() {
4959        if let TypeExpr::Refinement { base: _, var, predicate } = ptype {
4960            let pname_str = interner.resolve(*pname);
4961            let bound = interner.resolve(*var);
4962            let assertion = codegen_assertion(predicate, interner);
4963            let check = if bound == pname_str {
4964                assertion
4965            } else {
4966                replace_word(&assertion, bound, pname_str)
4967            };
4968            writeln!(output, "    if !({}) {{", check).unwrap();
4969            writeln!(output, "        logos_set_last_error(format!(\"Refinement violation: expected {check}, got {pn} = {{}}\", {pn}));",
4970                check = check, pn = pname_str).unwrap();
4971            writeln!(output, "        return LogosStatus::RefinementViolation;").unwrap();
4972            writeln!(output, "    }}").unwrap();
4973        }
4974    }
4975
4976    // 4b) Null out-parameter check (before catch_unwind to avoid calling inner fn)
4977    if uses_status_code && (has_ref_return || has_text_return || has_result_return) {
4978        writeln!(output, "    if out.is_null() {{ logos_set_last_error(\"NullPointer: output parameter is null\".to_string()); return LogosStatus::NullPointer; }}").unwrap();
4979    }
4980
4981    // 5) Determine panic default for catch_unwind error arm
4982    let panic_default = if uses_status_code {
4983        "LogosStatus::ThreadPanic"
4984    } else if has_text_return {
4985        "std::ptr::null_mut()"
4986    } else if return_type.map_or(false, |t| codegen_type_expr(t, interner) != "()") {
4987        "Default::default()"
4988    } else {
4989        "" // void function
4990    };
4991
4992    // 6) Open catch_unwind panic boundary
4993    writeln!(output, "    match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {{").unwrap();
4994
4995    // 7) Call inner and marshal return (inside catch_unwind closure)
4996    if uses_status_code {
4997        if has_result_return {
4998            // Result<T, E>: match on Ok/Err
4999            writeln!(output, "    match {}({}) {{", inner_name, call_args.join(", ")).unwrap();
5000            writeln!(output, "        Ok(val) => {{").unwrap();
5001
5002            if let Some(TypeExpr::Generic { params: ref rparams, .. }) = return_type {
5003                if !rparams.is_empty() {
5004                    let ok_ty = &rparams[0];
5005                    if classify_type_for_c_abi(ok_ty, interner, registry) == CAbiClass::ReferenceType {
5006                        writeln!(output, "            let __ptr = Box::into_raw(Box::new(val)) as usize;").unwrap();
5007                        writeln!(output, "            let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
5008                        writeln!(output, "            let (__id, _) = __reg.register(__ptr);").unwrap();
5009                        writeln!(output, "            unsafe {{ *out = __id as LogosHandle; }}").unwrap();
5010                    } else if is_text_type(ok_ty, interner) {
5011                        writeln!(output, "            match std::ffi::CString::new(val) {{").unwrap();
5012                        writeln!(output, "                Ok(cstr) => unsafe {{ *out = cstr.into_raw(); }},").unwrap();
5013                        writeln!(output, "                Err(_) => {{").unwrap();
5014                        writeln!(output, "                    logos_set_last_error(\"Return value contains null byte\".to_string());").unwrap();
5015                        writeln!(output, "                    return LogosStatus::ContainsNullByte;").unwrap();
5016                        writeln!(output, "                }}").unwrap();
5017                        writeln!(output, "            }}").unwrap();
5018                    } else {
5019                        writeln!(output, "            unsafe {{ *out = val; }}").unwrap();
5020                    }
5021                }
5022            }
5023
5024            writeln!(output, "            LogosStatus::Ok").unwrap();
5025            writeln!(output, "        }}").unwrap();
5026            writeln!(output, "        Err(e) => {{").unwrap();
5027            writeln!(output, "            logos_set_last_error(format!(\"{{}}\", e));").unwrap();
5028            writeln!(output, "            LogosStatus::Error").unwrap();
5029            writeln!(output, "        }}").unwrap();
5030            writeln!(output, "    }}").unwrap();
5031        } else if has_ref_return {
5032            // Reference type return → box, register in handle registry, and write to out-parameter
5033            writeln!(output, "    let result = {}({});", inner_name, call_args.join(", ")).unwrap();
5034            writeln!(output, "    let __ptr = Box::into_raw(Box::new(result)) as usize;").unwrap();
5035            writeln!(output, "    let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
5036            writeln!(output, "    let (__id, _) = __reg.register(__ptr);").unwrap();
5037            writeln!(output, "    unsafe {{ *out = __id as LogosHandle; }}").unwrap();
5038            writeln!(output, "    LogosStatus::Ok").unwrap();
5039        } else if has_text_return {
5040            // Text return with status code → write to out-parameter
5041            writeln!(output, "    let result = {}({});", inner_name, call_args.join(", ")).unwrap();
5042            writeln!(output, "    match std::ffi::CString::new(result) {{").unwrap();
5043            writeln!(output, "        Ok(cstr) => {{").unwrap();
5044            writeln!(output, "            unsafe {{ *out = cstr.into_raw(); }}").unwrap();
5045            writeln!(output, "            LogosStatus::Ok").unwrap();
5046            writeln!(output, "        }}").unwrap();
5047            writeln!(output, "        Err(_) => {{").unwrap();
5048            writeln!(output, "            logos_set_last_error(\"Return value contains null byte\".to_string());").unwrap();
5049            writeln!(output, "            LogosStatus::ContainsNullByte").unwrap();
5050            writeln!(output, "        }}").unwrap();
5051            writeln!(output, "    }}").unwrap();
5052        } else {
5053            // No return value but status code (e.g., refinement-only)
5054            writeln!(output, "    {}({});", inner_name, call_args.join(", ")).unwrap();
5055            writeln!(output, "    LogosStatus::Ok").unwrap();
5056        }
5057    } else if has_text_return {
5058        // Text-only marshaling (legacy path, no status code)
5059        writeln!(output, "    let result = {}({});", inner_name, call_args.join(", ")).unwrap();
5060        writeln!(output, "    match std::ffi::CString::new(result) {{").unwrap();
5061        writeln!(output, "        Ok(cstr) => cstr.into_raw(),").unwrap();
5062        writeln!(output, "        Err(_) => {{ logos_set_last_error(\"Return value contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
5063        writeln!(output, "    }}").unwrap();
5064    } else if return_type.is_some() {
5065        writeln!(output, "    {}({})", inner_name, call_args.join(", ")).unwrap();
5066    } else {
5067        writeln!(output, "    {}({})", inner_name, call_args.join(", ")).unwrap();
5068    }
5069
5070    // 8) Close catch_unwind with panic handler
5071    writeln!(output, "    }})) {{").unwrap();
5072    writeln!(output, "        Ok(__v) => __v,").unwrap();
5073    writeln!(output, "        Err(__panic) => {{").unwrap();
5074    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();
5075    writeln!(output, "            logos_set_last_error(__msg);").unwrap();
5076    if !panic_default.is_empty() {
5077        writeln!(output, "            {}", panic_default).unwrap();
5078    }
5079    writeln!(output, "        }}").unwrap();
5080    writeln!(output, "    }}").unwrap();
5081
5082    writeln!(output, "}}\n").unwrap();
5083
5084    output
5085}
5086
5087/// Phase 38: Convert TypeExpr to Rust type string.
5088fn codegen_type_expr(ty: &TypeExpr, interner: &Interner) -> String {
5089    match ty {
5090        TypeExpr::Primitive(sym) => {
5091            map_type_to_rust(interner.resolve(*sym))
5092        }
5093        TypeExpr::Named(sym) => {
5094            let name = interner.resolve(*sym);
5095            // Check for common mappings
5096            map_type_to_rust(name)
5097        }
5098        TypeExpr::Generic { base, params } => {
5099            let base_name = interner.resolve(*base);
5100            let params_str: Vec<String> = params.iter()
5101                .map(|p| codegen_type_expr(p, interner))
5102                .collect();
5103
5104            match base_name {
5105                "Result" => {
5106                    if params_str.len() == 2 {
5107                        format!("Result<{}, {}>", params_str[0], params_str[1])
5108                    } else if params_str.len() == 1 {
5109                        format!("Result<{}, String>", params_str[0])
5110                    } else {
5111                        "Result<(), String>".to_string()
5112                    }
5113                }
5114                "Option" | "Maybe" => {
5115                    if !params_str.is_empty() {
5116                        format!("Option<{}>", params_str[0])
5117                    } else {
5118                        "Option<()>".to_string()
5119                    }
5120                }
5121                "Seq" | "List" | "Vec" => {
5122                    if !params_str.is_empty() {
5123                        format!("Vec<{}>", params_str[0])
5124                    } else {
5125                        "Vec<()>".to_string()
5126                    }
5127                }
5128                "Map" | "HashMap" => {
5129                    if params_str.len() >= 2 {
5130                        format!("std::collections::HashMap<{}, {}>", params_str[0], params_str[1])
5131                    } else {
5132                        "std::collections::HashMap<String, String>".to_string()
5133                    }
5134                }
5135                "Set" | "HashSet" => {
5136                    if !params_str.is_empty() {
5137                        format!("std::collections::HashSet<{}>", params_str[0])
5138                    } else {
5139                        "std::collections::HashSet<()>".to_string()
5140                    }
5141                }
5142                other => {
5143                    if params_str.is_empty() {
5144                        other.to_string()
5145                    } else {
5146                        format!("{}<{}>", other, params_str.join(", "))
5147                    }
5148                }
5149            }
5150        }
5151        TypeExpr::Function { inputs, output } => {
5152            let inputs_str: Vec<String> = inputs.iter()
5153                .map(|i| codegen_type_expr(i, interner))
5154                .collect();
5155            let output_str = codegen_type_expr(output, interner);
5156            format!("impl Fn({}) -> {}", inputs_str.join(", "), output_str)
5157        }
5158        // Phase 43C: Refinement types use the base type for Rust type annotation
5159        // The constraint predicate is handled separately via debug_assert!
5160        TypeExpr::Refinement { base, .. } => {
5161            codegen_type_expr(base, interner)
5162        }
5163        // Phase 53: Persistent storage wrapper
5164        TypeExpr::Persistent { inner } => {
5165            let inner_type = codegen_type_expr(inner, interner);
5166            format!("logicaffeine_system::storage::Persistent<{}>", inner_type)
5167        }
5168    }
5169}
5170
5171/// Infer return type from function body by looking at Return statements.
5172fn infer_return_type_from_body(body: &[Stmt], _interner: &Interner) -> Option<String> {
5173    for stmt in body {
5174        if let Stmt::Return { value: Some(_) } = stmt {
5175            // For now, assume i64 for any expression return
5176            // TODO: Implement proper type inference
5177            return Some("i64".to_string());
5178        }
5179    }
5180    None
5181}
5182
5183/// Map LOGOS type names to Rust types.
5184fn map_type_to_rust(ty: &str) -> String {
5185    match ty {
5186        "Int" => "i64".to_string(),
5187        "Nat" => "u64".to_string(),
5188        "Text" => "String".to_string(),
5189        "Bool" | "Boolean" => "bool".to_string(),
5190        "Real" | "Float" => "f64".to_string(),
5191        "Char" => "char".to_string(),
5192        "Byte" => "u8".to_string(),
5193        "Unit" | "()" => "()".to_string(),
5194        "Duration" => "std::time::Duration".to_string(),
5195        other => other.to_string(),
5196    }
5197}
5198
5199/// Generate a single struct definition with derives and visibility.
5200/// Phase 34: Now supports generic type parameters.
5201/// Phase 47: Now supports is_portable for Serialize/Deserialize derives.
5202/// Phase 49: Now supports is_shared for CRDT Merge impl.
5203fn 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 {
5204    let ind = " ".repeat(indent);
5205    let mut output = String::new();
5206
5207    // Build generic parameter string: <T, U> or empty
5208    let generic_str = if generics.is_empty() {
5209        String::new()
5210    } else {
5211        let params: Vec<&str> = generics.iter()
5212            .map(|g| interner.resolve(*g))
5213            .collect();
5214        format!("<{}>", params.join(", "))
5215    };
5216
5217    // Value-type structs used in C ABI exports need #[repr(C)] for stable field layout
5218    if c_abi_value_structs.contains(&name) {
5219        writeln!(output, "{}#[repr(C)]", ind).unwrap();
5220    }
5221
5222    // Phase 47: Add Serialize, Deserialize derives if portable
5223    // Phase 50: Add PartialEq for policy equality comparisons
5224    // Phase 52: Shared types also need Serialize/Deserialize for Synced<T>
5225    // C ABI reference-type structs also need serde for from_json/to_json support
5226    if is_portable || is_shared || c_abi_ref_structs.contains(&name) {
5227        writeln!(output, "{}#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]", ind).unwrap();
5228    } else {
5229        writeln!(output, "{}#[derive(Default, Debug, Clone, PartialEq)]", ind).unwrap();
5230    }
5231    writeln!(output, "{}pub struct {}{} {{", ind, interner.resolve(name), generic_str).unwrap();
5232
5233    for field in fields {
5234        let vis = if field.is_public { "pub " } else { "" };
5235        let rust_type = codegen_field_type(&field.ty, interner);
5236        writeln!(output, "{}    {}{}: {},", ind, vis, interner.resolve(field.name), rust_type).unwrap();
5237    }
5238
5239    writeln!(output, "{}}}\n", ind).unwrap();
5240
5241    // Phase 49: Generate Merge impl for Shared structs
5242    if is_shared {
5243        output.push_str(&codegen_merge_impl(name, fields, generics, interner, indent));
5244    }
5245
5246    output
5247}
5248
5249/// Phase 49: Generate impl Merge for a Shared struct.
5250fn codegen_merge_impl(name: Symbol, fields: &[FieldDef], generics: &[Symbol], interner: &Interner, indent: usize) -> String {
5251    let ind = " ".repeat(indent);
5252    let name_str = interner.resolve(name);
5253    let mut output = String::new();
5254
5255    // Build generic parameter string: <T, U> or empty
5256    let generic_str = if generics.is_empty() {
5257        String::new()
5258    } else {
5259        let params: Vec<&str> = generics.iter()
5260            .map(|g| interner.resolve(*g))
5261            .collect();
5262        format!("<{}>", params.join(", "))
5263    };
5264
5265    writeln!(output, "{}impl{} logicaffeine_data::crdt::Merge for {}{} {{", ind, generic_str, name_str, generic_str).unwrap();
5266    writeln!(output, "{}    fn merge(&mut self, other: &Self) {{", ind).unwrap();
5267
5268    for field in fields {
5269        let field_name = interner.resolve(field.name);
5270        // Only merge fields that implement Merge (CRDT types)
5271        if is_crdt_field_type(&field.ty, interner) {
5272            writeln!(output, "{}        self.{}.merge(&other.{});", ind, field_name, field_name).unwrap();
5273        }
5274    }
5275
5276    writeln!(output, "{}    }}", ind).unwrap();
5277    writeln!(output, "{}}}\n", ind).unwrap();
5278
5279    output
5280}
5281
5282/// Phase 49: Check if a field type is a CRDT type that implements Merge.
5283fn is_crdt_field_type(ty: &FieldType, interner: &Interner) -> bool {
5284    match ty {
5285        FieldType::Named(sym) => {
5286            let name = interner.resolve(*sym);
5287            matches!(name,
5288                "ConvergentCount" | "GCounter" |
5289                "Tally" | "PNCounter"
5290            )
5291        }
5292        FieldType::Generic { base, .. } => {
5293            let name = interner.resolve(*base);
5294            matches!(name,
5295                "LastWriteWins" | "LWWRegister" |
5296                "SharedSet" | "ORSet" | "SharedSet_AddWins" | "SharedSet_RemoveWins" |
5297                "SharedSequence" | "RGA" | "SharedSequence_YATA" | "CollaborativeSequence" |
5298                "SharedMap" | "ORMap" |
5299                "Divergent" | "MVRegister"
5300            )
5301        }
5302        _ => false,
5303    }
5304}
5305
5306/// Phase 33/34: Generate enum definition with optional generic parameters.
5307/// Phase 47: Now supports is_portable for Serialize/Deserialize derives.
5308/// Phase 49: Now accepts is_shared parameter (enums don't generate Merge impl yet).
5309fn codegen_enum_def(name: Symbol, variants: &[VariantDef], generics: &[Symbol], is_portable: bool, _is_shared: bool, interner: &Interner, indent: usize) -> String {
5310    let ind = " ".repeat(indent);
5311    let mut output = String::new();
5312
5313    // Build generic parameter string: <T, U> or empty
5314    let generic_str = if generics.is_empty() {
5315        String::new()
5316    } else {
5317        let params: Vec<&str> = generics.iter()
5318            .map(|g| interner.resolve(*g))
5319            .collect();
5320        format!("<{}>", params.join(", "))
5321    };
5322
5323    // Phase 47: Add Serialize, Deserialize derives if portable
5324    if is_portable {
5325        writeln!(output, "{}#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]", ind).unwrap();
5326    } else {
5327        writeln!(output, "{}#[derive(Debug, Clone, PartialEq)]", ind).unwrap();
5328    }
5329    writeln!(output, "{}pub enum {}{} {{", ind, interner.resolve(name), generic_str).unwrap();
5330
5331    for variant in variants {
5332        let variant_name = interner.resolve(variant.name);
5333        if variant.fields.is_empty() {
5334            // Unit variant
5335            writeln!(output, "{}    {},", ind, variant_name).unwrap();
5336        } else {
5337            // Struct variant with named fields
5338            // Phase 102: Detect and box recursive fields
5339            let enum_name_str = interner.resolve(name);
5340            let fields_str: Vec<String> = variant.fields.iter()
5341                .map(|f| {
5342                    let rust_type = codegen_field_type(&f.ty, interner);
5343                    let field_name = interner.resolve(f.name);
5344                    // Check if this field references the enum itself (recursive type)
5345                    if is_recursive_field(&f.ty, enum_name_str, interner) {
5346                        format!("{}: Box<{}>", field_name, rust_type)
5347                    } else {
5348                        format!("{}: {}", field_name, rust_type)
5349                    }
5350                })
5351                .collect();
5352            writeln!(output, "{}    {} {{ {} }},", ind, variant_name, fields_str.join(", ")).unwrap();
5353        }
5354    }
5355
5356    writeln!(output, "{}}}\n", ind).unwrap();
5357
5358    // Generate Default impl for enum (defaults to first variant)
5359    // This is needed when the enum is used as a struct field and the struct derives Default
5360    // Only for non-generic enums — generic enums can't assume their type params implement Default
5361    if generics.is_empty() {
5362    if let Some(first_variant) = variants.first() {
5363        let enum_name_str = interner.resolve(name);
5364        let first_variant_name = interner.resolve(first_variant.name);
5365        writeln!(output, "{}impl{} Default for {}{} {{", ind, generic_str, enum_name_str, generic_str).unwrap();
5366        writeln!(output, "{}    fn default() -> Self {{", ind).unwrap();
5367        if first_variant.fields.is_empty() {
5368            writeln!(output, "{}        {}::{}", ind, enum_name_str, first_variant_name).unwrap();
5369        } else {
5370            // Default with default field values
5371            let default_fields: Vec<String> = first_variant.fields.iter()
5372                .map(|f| {
5373                    let field_name = interner.resolve(f.name);
5374                    let enum_name_check = interner.resolve(name);
5375                    if is_recursive_field(&f.ty, enum_name_check, interner) {
5376                        format!("{}: Box::new(Default::default())", field_name)
5377                    } else {
5378                        format!("{}: Default::default()", field_name)
5379                    }
5380                })
5381                .collect();
5382            writeln!(output, "{}        {}::{} {{ {} }}", ind, enum_name_str, first_variant_name, default_fields.join(", ")).unwrap();
5383        }
5384        writeln!(output, "{}    }}", ind).unwrap();
5385        writeln!(output, "{}}}\n", ind).unwrap();
5386    }
5387    }
5388
5389    output
5390}
5391
5392/// Convert FieldType to Rust type string.
5393fn codegen_field_type(ty: &FieldType, interner: &Interner) -> String {
5394    match ty {
5395        FieldType::Primitive(sym) => {
5396            match interner.resolve(*sym) {
5397                "Int" => "i64".to_string(),
5398                "Nat" => "u64".to_string(),
5399                "Text" => "String".to_string(),
5400                "Bool" | "Boolean" => "bool".to_string(),
5401                "Real" | "Float" => "f64".to_string(),
5402                "Char" => "char".to_string(),
5403                "Byte" => "u8".to_string(),
5404                "Unit" => "()".to_string(),
5405                "Duration" => "std::time::Duration".to_string(),
5406                other => other.to_string(),
5407            }
5408        }
5409        FieldType::Named(sym) => {
5410            let name = interner.resolve(*sym);
5411            match name {
5412                // Phase 49: CRDT type mapping
5413                "ConvergentCount" => "logicaffeine_data::crdt::GCounter".to_string(),
5414                // Phase 49b: New CRDT types (Wave 5)
5415                "Tally" => "logicaffeine_data::crdt::PNCounter".to_string(),
5416                _ => name.to_string(),
5417            }
5418        }
5419        FieldType::Generic { base, params } => {
5420            let base_name = interner.resolve(*base);
5421            let param_strs: Vec<String> = params.iter()
5422                .map(|p| codegen_field_type(p, interner))
5423                .collect();
5424
5425            // Phase 49c: Handle CRDT types with bias/algorithm modifiers
5426            match base_name {
5427                // SharedSet with explicit bias
5428                "SharedSet_RemoveWins" => {
5429                    return format!("logicaffeine_data::crdt::ORSet<{}, logicaffeine_data::crdt::RemoveWins>", param_strs.join(", "));
5430                }
5431                "SharedSet_AddWins" => {
5432                    return format!("logicaffeine_data::crdt::ORSet<{}, logicaffeine_data::crdt::AddWins>", param_strs.join(", "));
5433                }
5434                // SharedSequence with YATA algorithm
5435                "SharedSequence_YATA" | "CollaborativeSequence" => {
5436                    return format!("logicaffeine_data::crdt::YATA<{}>", param_strs.join(", "));
5437                }
5438                _ => {}
5439            }
5440
5441            let base_str = match base_name {
5442                "List" | "Seq" => "Vec",
5443                "Set" => "std::collections::HashSet",
5444                "Map" => "std::collections::HashMap",
5445                "Option" | "Maybe" => "Option",
5446                "Result" => "Result",
5447                // Phase 49: CRDT generic type
5448                "LastWriteWins" => "logicaffeine_data::crdt::LWWRegister",
5449                // Phase 49b: New CRDT generic types (Wave 5) - default to AddWins for ORSet
5450                "SharedSet" | "ORSet" => "logicaffeine_data::crdt::ORSet",
5451                "SharedSequence" | "RGA" => "logicaffeine_data::crdt::RGA",
5452                "SharedMap" | "ORMap" => "logicaffeine_data::crdt::ORMap",
5453                "Divergent" | "MVRegister" => "logicaffeine_data::crdt::MVRegister",
5454                other => other,
5455            };
5456            format!("{}<{}>", base_str, param_strs.join(", "))
5457        }
5458        // Phase 34: Type parameter reference (T, U, etc.)
5459        FieldType::TypeParam(sym) => interner.resolve(*sym).to_string(),
5460    }
5461}
5462
5463/// Phase 102: Check if a field type references the containing enum (recursive type).
5464/// Recursive types need to be wrapped in Box<T> for Rust to know the size.
5465fn is_recursive_field(ty: &FieldType, enum_name: &str, interner: &Interner) -> bool {
5466    match ty {
5467        FieldType::Primitive(sym) => interner.resolve(*sym) == enum_name,
5468        FieldType::Named(sym) => interner.resolve(*sym) == enum_name,
5469        FieldType::TypeParam(_) => false,
5470        FieldType::Generic { base, params } => {
5471            // Check if base matches or any type parameter contains the enum
5472            interner.resolve(*base) == enum_name ||
5473            params.iter().any(|p| is_recursive_field(p, enum_name, interner))
5474        }
5475    }
5476}
5477
5478/// Phase 103: Infer type annotation for multi-param generic enum variants.
5479/// Returns Some(type_annotation) if the enum has multiple type params, None otherwise.
5480fn infer_variant_type_annotation(
5481    expr: &Expr,
5482    registry: &TypeRegistry,
5483    interner: &Interner,
5484) -> Option<String> {
5485    // Only handle NewVariant expressions
5486    let (enum_name, variant_name, field_values) = match expr {
5487        Expr::NewVariant { enum_name, variant, fields } => (*enum_name, *variant, fields),
5488        _ => return None,
5489    };
5490
5491    // Look up the enum in the registry
5492    let enum_def = registry.get(enum_name)?;
5493    let (generics, variants) = match enum_def {
5494        TypeDef::Enum { generics, variants, .. } => (generics, variants),
5495        _ => return None,
5496    };
5497
5498    // Only generate type annotations for multi-param generics
5499    if generics.len() < 2 {
5500        return None;
5501    }
5502
5503    // Find the variant definition
5504    let variant_def = variants.iter().find(|v| v.name == variant_name)?;
5505
5506    // Collect which type params are bound by which field types
5507    let mut type_param_types: HashMap<Symbol, String> = HashMap::new();
5508    for (field_name, field_value) in field_values {
5509        // Find the field in the variant definition
5510        if let Some(field_def) = variant_def.fields.iter().find(|f| f.name == *field_name) {
5511            // If the field type is a type parameter, infer its type from the value
5512            if let FieldType::TypeParam(type_param) = &field_def.ty {
5513                let inferred = infer_rust_type_from_expr(field_value, interner);
5514                type_param_types.insert(*type_param, inferred);
5515            }
5516        }
5517    }
5518
5519    // Build the type annotation: EnumName<T1, T2, ...>
5520    // For bound params, use the inferred type; for unbound, use ()
5521    let enum_str = interner.resolve(enum_name);
5522    let param_strs: Vec<String> = generics.iter()
5523        .map(|g| {
5524            type_param_types.get(g)
5525                .cloned()
5526                .unwrap_or_else(|| "()".to_string())
5527        })
5528        .collect();
5529
5530    Some(format!("{}<{}>", enum_str, param_strs.join(", ")))
5531}
5532
5533/// Phase 103: Infer Rust type from a LOGOS expression.
5534fn infer_rust_type_from_expr(expr: &Expr, interner: &Interner) -> String {
5535    match expr {
5536        Expr::Literal(lit) => match lit {
5537            Literal::Number(_) => "i64".to_string(),
5538            Literal::Float(_) => "f64".to_string(),
5539            Literal::Text(_) => "String".to_string(),
5540            Literal::Boolean(_) => "bool".to_string(),
5541            Literal::Char(_) => "char".to_string(),
5542            Literal::Nothing => "()".to_string(),
5543            Literal::Duration(_) => "std::time::Duration".to_string(),
5544            Literal::Date(_) => "LogosDate".to_string(),
5545            Literal::Moment(_) => "LogosMoment".to_string(),
5546            Literal::Span { .. } => "LogosSpan".to_string(),
5547            Literal::Time(_) => "LogosTime".to_string(),
5548        },
5549        // For identifiers and complex expressions, let Rust infer
5550        _ => "_".to_string(),
5551    }
5552}
5553
5554/// Peephole optimization: detect `Let vec = new Seq. Let i = 0. While i <= limit: push const to vec, i = i+1`
5555/// and emit `let mut vec: Vec<T> = vec![const; (limit + 1) as usize]` instead.
5556/// Returns (generated_code, number_of_extra_statements_consumed) or None if pattern doesn't match.
5557fn try_emit_vec_fill_pattern<'a>(
5558    stmts: &[&Stmt<'a>],
5559    idx: usize,
5560    interner: &Interner,
5561    indent: usize,
5562) -> Option<(String, usize)> {
5563    if idx + 2 >= stmts.len() {
5564        return None;
5565    }
5566
5567    // Statement 1: Let mutable vec_var be a new Seq of T.
5568    let (vec_sym, elem_type) = match stmts[idx] {
5569        Stmt::Let { var, value, mutable: true, ty, .. } => {
5570            // Check for explicit type annotation like `: Seq of Bool`
5571            let type_from_annotation = if let Some(TypeExpr::Generic { base, params }) = ty {
5572                let base_name = interner.resolve(*base);
5573                if matches!(base_name, "Seq" | "List" | "Vec") && !params.is_empty() {
5574                    Some(codegen_type_expr(&params[0], interner))
5575                } else {
5576                    None
5577                }
5578            } else {
5579                None
5580            };
5581
5582            // Check for `a new Seq of T`
5583            let type_from_new = if let Expr::New { type_name, type_args, init_fields } = value {
5584                let tn = interner.resolve(*type_name);
5585                if matches!(tn, "Seq" | "List" | "Vec") && init_fields.is_empty() {
5586                    if !type_args.is_empty() {
5587                        Some(codegen_type_expr(&type_args[0], interner))
5588                    } else {
5589                        None
5590                    }
5591                } else {
5592                    None
5593                }
5594            } else {
5595                None
5596            };
5597
5598            match type_from_annotation.or(type_from_new) {
5599                Some(t) => (*var, t),
5600                None => return None,
5601            }
5602        }
5603        _ => return None,
5604    };
5605
5606    // Statement 2: Let mutable counter = 0 (or 1).
5607    let (counter_sym, counter_start) = match stmts[idx + 1] {
5608        Stmt::Let { var, value: Expr::Literal(Literal::Number(n)), mutable: true, .. } => {
5609            (*var, *n)
5610        }
5611        _ => return None,
5612    };
5613
5614    // Statement 3: While counter <= limit: Push const_val to vec_var. Set counter to counter + 1.
5615    match stmts[idx + 2] {
5616        Stmt::While { cond, body, .. } => {
5617            // Check condition: counter <= limit
5618            let limit_expr = match cond {
5619                Expr::BinaryOp { op: BinaryOpKind::LtEq, left, right } => {
5620                    if let Expr::Identifier(sym) = left {
5621                        if *sym == counter_sym {
5622                            Some(*right)
5623                        } else {
5624                            None
5625                        }
5626                    } else {
5627                        None
5628                    }
5629                }
5630                _ => None,
5631            }?;
5632
5633            // Body must have exactly 2 statements: Push and Set
5634            if body.len() != 2 {
5635                return None;
5636            }
5637
5638            // First body stmt: Push const_val to vec_var
5639            let push_val = match &body[0] {
5640                Stmt::Push { value, collection } => {
5641                    if let Expr::Identifier(sym) = collection {
5642                        if *sym == vec_sym {
5643                            Some(*value)
5644                        } else {
5645                            None
5646                        }
5647                    } else {
5648                        None
5649                    }
5650                }
5651                _ => None,
5652            }?;
5653
5654            // Push value must be a constant literal
5655            let val_str = match push_val {
5656                Expr::Literal(Literal::Number(n)) => format!("{}", n),
5657                Expr::Literal(Literal::Float(f)) => format!("{:.1}", f),
5658                Expr::Literal(Literal::Boolean(b)) => format!("{}", b),
5659                Expr::Literal(Literal::Char(c)) => format!("'{}'", c),
5660                Expr::Literal(Literal::Text(s)) => format!("{}.to_string()", interner.resolve(*s)),
5661                _ => return None,
5662            };
5663
5664            // Second body stmt: Set counter to counter + 1
5665            match &body[1] {
5666                Stmt::Set { target, value, .. } => {
5667                    if *target != counter_sym {
5668                        return None;
5669                    }
5670                    // Value must be counter + 1
5671                    match value {
5672                        Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
5673                            let is_counter_plus_1 = match (left, right) {
5674                                (Expr::Identifier(s), Expr::Literal(Literal::Number(1))) if *s == counter_sym => true,
5675                                (Expr::Literal(Literal::Number(1)), Expr::Identifier(s)) if *s == counter_sym => true,
5676                                _ => false,
5677                            };
5678                            if !is_counter_plus_1 {
5679                                return None;
5680                            }
5681                        }
5682                        _ => return None,
5683                    }
5684                }
5685                _ => return None,
5686            }
5687
5688            // Pattern matched! Emit optimized code.
5689            let indent_str = "    ".repeat(indent);
5690            let vec_name = interner.resolve(vec_sym);
5691            let limit_str = codegen_expr_simple(limit_expr, interner);
5692
5693            // Calculate count: if counter starts at 0, count = limit + 1; if starts at 1, count = limit
5694            let count_expr = if counter_start == 0 {
5695                format!("({} + 1) as usize", limit_str)
5696            } else if counter_start == 1 {
5697                format!("{} as usize", limit_str)
5698            } else {
5699                format!("({} - {} + 1) as usize", limit_str, counter_start)
5700            };
5701
5702            let mut output = String::new();
5703            writeln!(output, "{}let mut {}: Vec<{}> = vec![{}; {}];",
5704                indent_str, vec_name, elem_type, val_str, count_expr).unwrap();
5705            // Re-emit counter variable declaration (it may be reused after the fill loop)
5706            let counter_name = interner.resolve(counter_sym);
5707            writeln!(output, "{}let mut {} = {};",
5708                indent_str, counter_name, counter_start).unwrap();
5709
5710            Some((output, 2)) // consumed 2 extra statements (counter init + while loop)
5711        }
5712        _ => None,
5713    }
5714}
5715
5716/// Simple expression codegen for peephole patterns (no async/context needed).
5717fn codegen_expr_simple(expr: &Expr, interner: &Interner) -> String {
5718    match expr {
5719        Expr::Literal(Literal::Number(n)) => format!("{}", n),
5720        Expr::Literal(Literal::Float(f)) => format!("{:.1}", f),
5721        Expr::Literal(Literal::Boolean(b)) => format!("{}", b),
5722        Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
5723        Expr::BinaryOp { op, left, right } => {
5724            let l = codegen_expr_simple(left, interner);
5725            let r = codegen_expr_simple(right, interner);
5726            let op_str = match op {
5727                BinaryOpKind::Add => "+",
5728                BinaryOpKind::Subtract => "-",
5729                BinaryOpKind::Multiply => "*",
5730                BinaryOpKind::Divide => "/",
5731                BinaryOpKind::Modulo => "%",
5732                _ => return format!("({})", l),
5733            };
5734            format!("({} {} {})", l, op_str, r)
5735        }
5736        _ => "_".to_string(),
5737    }
5738}
5739
5740/// Check if two expressions are structurally equal (for swap pattern detection).
5741fn exprs_equal(a: &Expr, b: &Expr) -> bool {
5742    match (a, b) {
5743        (Expr::Identifier(s1), Expr::Identifier(s2)) => s1 == s2,
5744        (Expr::Literal(Literal::Number(n1)), Expr::Literal(Literal::Number(n2))) => n1 == n2,
5745        (Expr::BinaryOp { op: op1, left: l1, right: r1 }, Expr::BinaryOp { op: op2, left: l2, right: r2 }) => {
5746            op1 == op2 && exprs_equal(l1, l2) && exprs_equal(r1, r2)
5747        }
5748        _ => false,
5749    }
5750}
5751
5752/// Peephole optimization: detect swap pattern:
5753///   Let a be item j of arr. Let b be item (j+1) of arr.
5754///   If a > b then: Set item j of arr to b. Set item (j+1) of arr to a.
5755/// and emit `arr.swap((j-1) as usize, ((j+1)-1) as usize)` instead.
5756/// Returns (generated_code, number_of_extra_statements_consumed) or None.
5757fn try_emit_swap_pattern<'a>(
5758    stmts: &[&Stmt<'a>],
5759    idx: usize,
5760    interner: &Interner,
5761    indent: usize,
5762    variable_types: &HashMap<Symbol, String>,
5763) -> Option<(String, usize)> {
5764    if idx + 2 >= stmts.len() {
5765        return None;
5766    }
5767
5768    // Statement 1: Let a be item j of arr (index expression)
5769    let (a_sym, arr_sym_1, idx_expr_1) = match stmts[idx] {
5770        Stmt::Let { var, value: Expr::Index { collection, index }, mutable: false, .. } => {
5771            if let Expr::Identifier(coll_sym) = collection {
5772                (*var, *coll_sym, *index)
5773            } else {
5774                return None;
5775            }
5776        }
5777        _ => return None,
5778    };
5779
5780    // Only optimize for known Vec types (direct indexing)
5781    if let Some(t) = variable_types.get(&arr_sym_1) {
5782        if !t.starts_with("Vec") {
5783            return None;
5784        }
5785    } else {
5786        return None;
5787    }
5788
5789    // Statement 2: Let b be item (j+1) of arr (adjacent index)
5790    let (b_sym, arr_sym_2, idx_expr_2) = match stmts[idx + 1] {
5791        Stmt::Let { var, value: Expr::Index { collection, index }, mutable: false, .. } => {
5792            if let Expr::Identifier(coll_sym) = collection {
5793                (*var, *coll_sym, *index)
5794            } else {
5795                return None;
5796            }
5797        }
5798        _ => return None,
5799    };
5800
5801    // Must be the same array
5802    if arr_sym_1 != arr_sym_2 {
5803        return None;
5804    }
5805
5806    // idx_expr_2 must be idx_expr_1 + 1
5807    let is_adjacent = match idx_expr_2 {
5808        Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
5809            (exprs_equal(left, idx_expr_1) && matches!(right, Expr::Literal(Literal::Number(1))))
5810            || (matches!(left, Expr::Literal(Literal::Number(1))) && exprs_equal(right, idx_expr_1))
5811        }
5812        _ => false,
5813    };
5814    if !is_adjacent {
5815        return None;
5816    }
5817
5818    // Statement 3: If a > b (or a < b, etc.) then: SetIndex arr j b, SetIndex arr j+1 a
5819    match stmts[idx + 2] {
5820        Stmt::If { cond, then_block, else_block } => {
5821            // Condition must compare a and b
5822            let compares_a_b = match cond {
5823                Expr::BinaryOp { op, left, right } => {
5824                    matches!(op, BinaryOpKind::Gt | BinaryOpKind::Lt | BinaryOpKind::GtEq | BinaryOpKind::LtEq) &&
5825                    ((matches!(left, Expr::Identifier(s) if *s == a_sym) && matches!(right, Expr::Identifier(s) if *s == b_sym)) ||
5826                     (matches!(left, Expr::Identifier(s) if *s == b_sym) && matches!(right, Expr::Identifier(s) if *s == a_sym)))
5827                }
5828                _ => false,
5829            };
5830            if !compares_a_b {
5831                return None;
5832            }
5833
5834            // Must have no else block
5835            if else_block.is_some() {
5836                return None;
5837            }
5838
5839            // Then block must have exactly 2 SetIndex statements forming a cross-swap
5840            if then_block.len() != 2 {
5841                return None;
5842            }
5843
5844            // Check: SetIndex arr idx1 b, SetIndex arr idx2 a (cross pattern)
5845            let swap_ok = match (&then_block[0], &then_block[1]) {
5846                (
5847                    Stmt::SetIndex { collection: c1, index: i1, value: v1 },
5848                    Stmt::SetIndex { collection: c2, index: i2, value: v2 },
5849                ) => {
5850                    // c1 and c2 must be the same array
5851                    let same_arr = matches!((c1, c2), (Expr::Identifier(s1), Expr::Identifier(s2)) if *s1 == arr_sym_1 && *s2 == arr_sym_1);
5852                    // Cross pattern: set idx1 to b, set idx2 to a
5853                    let cross = exprs_equal(i1, idx_expr_1) && exprs_equal(i2, idx_expr_2) &&
5854                        matches!(v1, Expr::Identifier(s) if *s == b_sym) &&
5855                        matches!(v2, Expr::Identifier(s) if *s == a_sym);
5856                    // Also check reverse: set idx1 to b via idx2/a pattern
5857                    let cross_rev = exprs_equal(i1, idx_expr_2) && exprs_equal(i2, idx_expr_1) &&
5858                        matches!(v1, Expr::Identifier(s) if *s == a_sym) &&
5859                        matches!(v2, Expr::Identifier(s) if *s == b_sym);
5860                    same_arr && (cross || cross_rev)
5861                }
5862                _ => false,
5863            };
5864
5865            if !swap_ok {
5866                return None;
5867            }
5868
5869            // Pattern matched! Emit optimized swap
5870            let indent_str = "    ".repeat(indent);
5871            let arr_name = interner.resolve(arr_sym_1);
5872            let idx1_str = codegen_expr_simple(idx_expr_1, interner);
5873            let idx2_str = codegen_expr_simple(idx_expr_2, interner);
5874
5875            // Emit the condition check + swap
5876            let cond_str = format!("({} {} {})",
5877                codegen_expr_simple(match cond { Expr::BinaryOp { left, .. } => left, _ => unreachable!() }, interner),
5878                match cond { Expr::BinaryOp { op, .. } => match op {
5879                    BinaryOpKind::Gt => ">", BinaryOpKind::Lt => "<",
5880                    BinaryOpKind::GtEq => ">=", BinaryOpKind::LtEq => "<=",
5881                    _ => unreachable!(),
5882                }, _ => unreachable!() },
5883                codegen_expr_simple(match cond { Expr::BinaryOp { right, .. } => right, _ => unreachable!() }, interner),
5884            );
5885
5886            let mut output = String::new();
5887            // Still need to read a and b for the comparison (the swap doesn't need them)
5888            // Actually, we can use direct indexing for the comparison too
5889            writeln!(output, "{}if {}[({} - 1) as usize] {} {}[({} - 1) as usize] {{",
5890                indent_str, arr_name, idx1_str,
5891                match cond { Expr::BinaryOp { op, .. } => match op {
5892                    BinaryOpKind::Gt => ">", BinaryOpKind::Lt => "<",
5893                    BinaryOpKind::GtEq => ">=", BinaryOpKind::LtEq => "<=",
5894                    _ => unreachable!(),
5895                }, _ => unreachable!() },
5896                arr_name, idx2_str,
5897            ).unwrap();
5898            writeln!(output, "{}    {}.swap(({} - 1) as usize, ({} - 1) as usize);",
5899                indent_str, arr_name, idx1_str, idx2_str).unwrap();
5900            writeln!(output, "{}}}", indent_str).unwrap();
5901
5902            Some((output, 2)) // consumed 2 extra statements
5903        }
5904        _ => None,
5905    }
5906}
5907
5908pub fn codegen_stmt<'a>(
5909    stmt: &Stmt<'a>,
5910    interner: &Interner,
5911    indent: usize,
5912    mutable_vars: &HashSet<Symbol>,
5913    ctx: &mut RefinementContext<'a>,
5914    lww_fields: &HashSet<(String, String)>,
5915    mv_fields: &HashSet<(String, String)>,  // Phase 49b: MVRegister fields (no timestamp)
5916    synced_vars: &mut HashSet<Symbol>,  // Phase 52: Track synced variables
5917    var_caps: &HashMap<Symbol, VariableCapabilities>,  // Phase 56: Mount+Sync detection
5918    async_functions: &HashSet<Symbol>,  // Phase 54: Functions that are async
5919    pipe_vars: &HashSet<Symbol>,  // Phase 54: Pipe declarations (have _tx/_rx suffixes)
5920    boxed_fields: &HashSet<(String, String, String)>,  // Phase 102: Recursive enum fields
5921    registry: &TypeRegistry,  // Phase 103: For type annotations on polymorphic enums
5922) -> String {
5923    let indent_str = "    ".repeat(indent);
5924    let mut output = String::new();
5925
5926    match stmt {
5927        Stmt::Let { var, ty, value, mutable } => {
5928            let var_name = interner.resolve(*var);
5929
5930            // Register collection type for direct indexing optimization.
5931            // Check explicit type annotation first, then infer from Expr::New.
5932            if let Some(TypeExpr::Generic { base, params }) = ty {
5933                let base_name = interner.resolve(*base);
5934                match base_name {
5935                    "Seq" | "List" | "Vec" => {
5936                        let rust_type = if !params.is_empty() {
5937                            format!("Vec<{}>", codegen_type_expr(&params[0], interner))
5938                        } else {
5939                            "Vec<()>".to_string()
5940                        };
5941                        ctx.register_variable_type(*var, rust_type);
5942                    }
5943                    "Map" | "HashMap" => {
5944                        let rust_type = if params.len() >= 2 {
5945                            format!("std::collections::HashMap<{}, {}>", codegen_type_expr(&params[0], interner), codegen_type_expr(&params[1], interner))
5946                        } else {
5947                            "std::collections::HashMap<String, String>".to_string()
5948                        };
5949                        ctx.register_variable_type(*var, rust_type);
5950                    }
5951                    _ => {}
5952                }
5953            } else if let Expr::New { type_name, type_args, .. } = value {
5954                let type_str = interner.resolve(*type_name);
5955                match type_str {
5956                    "Seq" | "List" | "Vec" => {
5957                        let rust_type = if !type_args.is_empty() {
5958                            format!("Vec<{}>", codegen_type_expr(&type_args[0], interner))
5959                        } else {
5960                            "Vec<()>".to_string()
5961                        };
5962                        ctx.register_variable_type(*var, rust_type);
5963                    }
5964                    "Map" | "HashMap" => {
5965                        let rust_type = if type_args.len() >= 2 {
5966                            format!("std::collections::HashMap<{}, {}>", codegen_type_expr(&type_args[0], interner), codegen_type_expr(&type_args[1], interner))
5967                        } else {
5968                            "std::collections::HashMap<String, String>".to_string()
5969                        };
5970                        ctx.register_variable_type(*var, rust_type);
5971                    }
5972                    _ => {}
5973                }
5974            } else if matches!(value, Expr::List(_)) {
5975                ctx.register_variable_type(*var, "Vec<_>".to_string());
5976            }
5977
5978            // Phase 54+: Use codegen_expr_boxed with string+type tracking for proper codegen
5979            let value_str = codegen_expr_boxed_with_types(
5980                value, interner, synced_vars, boxed_fields, registry, async_functions,
5981                ctx.get_string_vars(), ctx.get_variable_types()
5982            );
5983
5984            // Phase 103: Get explicit type annotation or infer for multi-param generic enums
5985            let type_annotation = ty.map(|t| codegen_type_expr(t, interner))
5986                .or_else(|| infer_variant_type_annotation(value, registry, interner));
5987
5988            // Grand Challenge: Variable is mutable if explicitly marked OR if it's a Set target
5989            let is_mutable = *mutable || mutable_vars.contains(var);
5990
5991            match (is_mutable, type_annotation) {
5992                (true, Some(t)) => writeln!(output, "{}let mut {}: {} = {};", indent_str, var_name, t, value_str).unwrap(),
5993                (true, None) => writeln!(output, "{}let mut {} = {};", indent_str, var_name, value_str).unwrap(),
5994                (false, Some(t)) => writeln!(output, "{}let {}: {} = {};", indent_str, var_name, t, value_str).unwrap(),
5995                (false, None) => writeln!(output, "{}let {} = {};", indent_str, var_name, value_str).unwrap(),
5996            }
5997
5998            // Track string variables for proper concatenation in subsequent expressions
5999            if is_definitely_string_expr_with_vars(value, ctx.get_string_vars()) {
6000                ctx.register_string_var(*var);
6001            }
6002
6003            // Phase 43C: Handle refinement type
6004            if let Some(TypeExpr::Refinement { base: _, var: bound_var, predicate }) = ty {
6005                emit_refinement_check(var_name, *bound_var, predicate, interner, &indent_str, &mut output);
6006                ctx.register(*var, *bound_var, predicate);
6007            }
6008        }
6009
6010        Stmt::Set { target, value } => {
6011            let target_name = interner.resolve(*target);
6012            let string_vars = ctx.get_string_vars();
6013            let var_types = ctx.get_variable_types();
6014
6015            // Optimization: detect self-append pattern (result = result + x + y)
6016            // and emit write!(result, "{}{}", x, y) instead of result = format!(...).
6017            // This is O(n) amortized (in-place append) vs O(n²) (full copy each iteration).
6018            let used_write = if ctx.is_string_var(*target)
6019                && is_definitely_string_expr_with_vars(value, string_vars)
6020            {
6021                let mut operands = Vec::new();
6022                collect_string_concat_operands(value, string_vars, &mut operands);
6023
6024                // Need at least 2 operands, leftmost must be the target variable
6025                if operands.len() >= 2 && matches!(operands[0], Expr::Identifier(sym) if *sym == *target) {
6026                    // Check no other operand references target (would cause borrow conflict)
6027                    let tail = &operands[1..];
6028                    let mut tail_ids = HashSet::new();
6029                    for op in tail {
6030                        collect_expr_identifiers(op, &mut tail_ids);
6031                    }
6032
6033                    if !tail_ids.contains(target) {
6034                        // Safe to emit write!() — target not referenced in tail operands
6035                        let placeholders: String = tail.iter().map(|_| "{}").collect::<Vec<_>>().join("");
6036                        let values: Vec<String> = tail.iter().map(|e| {
6037                            // String literals can be &str inside write!() — no heap allocation needed
6038                            if let Expr::Literal(Literal::Text(sym)) = e {
6039                                format!("\"{}\"", interner.resolve(*sym))
6040                            } else {
6041                                codegen_expr_boxed_with_types(
6042                                    e, interner, synced_vars, boxed_fields, registry, async_functions,
6043                                    string_vars, var_types
6044                                )
6045                            }
6046                        }).collect();
6047                        writeln!(output, "{}write!({}, \"{}\", {}).unwrap();",
6048                            indent_str, target_name, placeholders, values.join(", ")).unwrap();
6049                        true
6050                    } else {
6051                        false
6052                    }
6053                } else {
6054                    false
6055                }
6056            } else {
6057                false
6058            };
6059
6060            if !used_write {
6061                // Fallback: standard assignment with format!
6062                let value_str = codegen_expr_boxed_with_types(
6063                    value, interner, synced_vars, boxed_fields, registry, async_functions,
6064                    string_vars, var_types
6065                );
6066                writeln!(output, "{}{} = {};", indent_str, target_name, value_str).unwrap();
6067            }
6068
6069            // Phase 43C: Check if this variable has a refinement constraint
6070            if let Some((bound_var, predicate)) = ctx.get_constraint(*target) {
6071                emit_refinement_check(target_name, bound_var, predicate, interner, &indent_str, &mut output);
6072            }
6073        }
6074
6075        Stmt::Call { function, args } => {
6076            let func_name = escape_rust_ident(interner.resolve(*function));
6077            let args_str: Vec<String> = args.iter().map(|a| codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types())).collect();
6078            // Add .await if calling an async function
6079            let await_suffix = if async_functions.contains(function) { ".await" } else { "" };
6080            writeln!(output, "{}{}({}){};", indent_str, func_name, args_str.join(", "), await_suffix).unwrap();
6081        }
6082
6083        Stmt::If { cond, then_block, else_block } => {
6084            let cond_str = codegen_expr_with_async(cond, interner, synced_vars, async_functions, ctx.get_variable_types());
6085            writeln!(output, "{}if {} {{", indent_str, cond_str).unwrap();
6086            ctx.push_scope();
6087            for stmt in *then_block {
6088                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));
6089            }
6090            ctx.pop_scope();
6091            if let Some(else_stmts) = else_block {
6092                writeln!(output, "{}}} else {{", indent_str).unwrap();
6093                ctx.push_scope();
6094                for stmt in *else_stmts {
6095                    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));
6096                }
6097                ctx.pop_scope();
6098            }
6099            writeln!(output, "{}}}", indent_str).unwrap();
6100        }
6101
6102        Stmt::While { cond, body, decreasing: _ } => {
6103            // decreasing is compile-time only, ignored at runtime
6104            let cond_str = codegen_expr_with_async(cond, interner, synced_vars, async_functions, ctx.get_variable_types());
6105            writeln!(output, "{}while {} {{", indent_str, cond_str).unwrap();
6106            ctx.push_scope();
6107            // Peephole: process body statements with swap pattern detection
6108            let body_refs: Vec<&Stmt> = body.iter().collect();
6109            let mut bi = 0;
6110            while bi < body_refs.len() {
6111                if let Some((code, skip)) = try_emit_swap_pattern(&body_refs, bi, interner, indent + 1, ctx.get_variable_types()) {
6112                    output.push_str(&code);
6113                    bi += 1 + skip;
6114                    continue;
6115                }
6116                if let Some((code, skip)) = try_emit_vec_fill_pattern(&body_refs, bi, interner, indent + 1) {
6117                    output.push_str(&code);
6118                    bi += 1 + skip;
6119                    continue;
6120                }
6121                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));
6122                bi += 1;
6123            }
6124            ctx.pop_scope();
6125            writeln!(output, "{}}}", indent_str).unwrap();
6126        }
6127
6128        Stmt::Repeat { pattern, iterable, body } => {
6129            use crate::ast::stmt::Pattern;
6130
6131            // Generate pattern string for Rust code
6132            let pattern_str = match pattern {
6133                Pattern::Identifier(sym) => interner.resolve(*sym).to_string(),
6134                Pattern::Tuple(syms) => {
6135                    let names = syms.iter()
6136                        .map(|s| interner.resolve(*s))
6137                        .collect::<Vec<_>>()
6138                        .join(", ");
6139                    format!("({})", names)
6140                }
6141            };
6142
6143            let iter_str = codegen_expr_with_async(iterable, interner, synced_vars, async_functions, ctx.get_variable_types());
6144
6145            // Check if body contains async operations - if so, use while-let pattern
6146            // because standard for loops cannot contain .await
6147            let body_has_async = body.iter().any(|s| {
6148                requires_async_stmt(s) || calls_async_function(s, async_functions)
6149            });
6150
6151            if body_has_async {
6152                // Use while-let with explicit iterator for async compatibility
6153                writeln!(output, "{}let mut __iter = ({}).into_iter();", indent_str, iter_str).unwrap();
6154                writeln!(output, "{}while let Some({}) = __iter.next() {{", indent_str, pattern_str).unwrap();
6155            } else {
6156                // Clone the collection before iterating to avoid moving it.
6157                // This allows the collection to be reused after the loop.
6158                // Works for Vec, HashMap, HashSet, and any Clone collection.
6159                writeln!(output, "{}for {} in {}.clone() {{", indent_str, pattern_str, iter_str).unwrap();
6160            }
6161            ctx.push_scope();
6162            // Peephole: process body statements with swap pattern detection
6163            {
6164                let body_refs: Vec<&Stmt> = body.iter().collect();
6165                let mut bi = 0;
6166                while bi < body_refs.len() {
6167                    if let Some((code, skip)) = try_emit_swap_pattern(&body_refs, bi, interner, indent + 1, ctx.get_variable_types()) {
6168                        output.push_str(&code);
6169                        bi += 1 + skip;
6170                        continue;
6171                    }
6172                    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));
6173                    bi += 1;
6174                }
6175            }
6176            ctx.pop_scope();
6177            writeln!(output, "{}}}", indent_str).unwrap();
6178        }
6179
6180        Stmt::Return { value } => {
6181            if let Some(v) = value {
6182                let value_str = codegen_expr_with_async(v, interner, synced_vars, async_functions, ctx.get_variable_types());
6183                writeln!(output, "{}return {};", indent_str, value_str).unwrap();
6184            } else {
6185                writeln!(output, "{}return;", indent_str).unwrap();
6186            }
6187        }
6188
6189        Stmt::Assert { proposition } => {
6190            let condition = codegen_assertion(proposition, interner);
6191            writeln!(output, "{}debug_assert!({});", indent_str, condition).unwrap();
6192        }
6193
6194        // Phase 35: Trust with documented justification
6195        Stmt::Trust { proposition, justification } => {
6196            let reason = interner.resolve(*justification);
6197            // Strip quotes if present (string literals include their quotes)
6198            let reason_clean = reason.trim_matches('"');
6199            writeln!(output, "{}// TRUST: {}", indent_str, reason_clean).unwrap();
6200            let condition = codegen_assertion(proposition, interner);
6201            writeln!(output, "{}debug_assert!({});", indent_str, condition).unwrap();
6202        }
6203
6204        Stmt::RuntimeAssert { condition } => {
6205            let cond_str = codegen_expr_with_async(condition, interner, synced_vars, async_functions, ctx.get_variable_types());
6206            writeln!(output, "{}debug_assert!({});", indent_str, cond_str).unwrap();
6207        }
6208
6209        // Phase 50: Security Check - mandatory runtime guard (NEVER optimized out)
6210        Stmt::Check { subject, predicate, is_capability, object, source_text, span } => {
6211            let subj_name = interner.resolve(*subject);
6212            let pred_name = interner.resolve(*predicate).to_lowercase();
6213
6214            let call = if *is_capability {
6215                let obj_sym = object.expect("capability must have object");
6216                let obj_word = interner.resolve(obj_sym);
6217
6218                // Phase 50: Type-based resolution
6219                // "Check that user can publish the document" -> find variable of type Document
6220                // First try to find a variable whose type matches the object word
6221                let obj_name = ctx.find_variable_by_type(obj_word, interner)
6222                    .unwrap_or_else(|| obj_word.to_string());
6223
6224                format!("{}.can_{}(&{})", subj_name, pred_name, obj_name)
6225            } else {
6226                format!("{}.is_{}()", subj_name, pred_name)
6227            };
6228
6229            writeln!(output, "{}if !({}) {{", indent_str, call).unwrap();
6230            writeln!(output, "{}    logicaffeine_system::panic_with(\"Security Check Failed at line {}: {}\");",
6231                     indent_str, span.start, source_text).unwrap();
6232            writeln!(output, "{}}}", indent_str).unwrap();
6233        }
6234
6235        // Phase 51: P2P Networking - Listen on network address
6236        Stmt::Listen { address } => {
6237            let addr_str = codegen_expr_with_async(address, interner, synced_vars, async_functions, ctx.get_variable_types());
6238            // Pass &str instead of String
6239            writeln!(output, "{}logicaffeine_system::network::listen(&{}).await.expect(\"Failed to listen\");",
6240                     indent_str, addr_str).unwrap();
6241        }
6242
6243        // Phase 51: P2P Networking - Connect to remote peer
6244        Stmt::ConnectTo { address } => {
6245            let addr_str = codegen_expr_with_async(address, interner, synced_vars, async_functions, ctx.get_variable_types());
6246            // Pass &str instead of String
6247            writeln!(output, "{}logicaffeine_system::network::connect(&{}).await.expect(\"Failed to connect\");",
6248                     indent_str, addr_str).unwrap();
6249        }
6250
6251        // Phase 51: P2P Networking - Create PeerAgent remote handle
6252        Stmt::LetPeerAgent { var, address } => {
6253            let var_name = interner.resolve(*var);
6254            let addr_str = codegen_expr_with_async(address, interner, synced_vars, async_functions, ctx.get_variable_types());
6255            // Pass &str instead of String
6256            writeln!(output, "{}let {} = logicaffeine_system::network::PeerAgent::new(&{}).expect(\"Invalid address\");",
6257                     indent_str, var_name, addr_str).unwrap();
6258        }
6259
6260        // Phase 51: Sleep - supports Duration literals or milliseconds
6261        Stmt::Sleep { milliseconds } => {
6262            let expr_str = codegen_expr_with_async(milliseconds, interner, synced_vars, async_functions, ctx.get_variable_types());
6263            let inferred_type = infer_rust_type_from_expr(milliseconds, interner);
6264
6265            if inferred_type == "std::time::Duration" {
6266                // Duration type: use directly (already a std::time::Duration)
6267                writeln!(output, "{}tokio::time::sleep({}).await;",
6268                         indent_str, expr_str).unwrap();
6269            } else {
6270                // Assume milliseconds (integer) - legacy behavior
6271                writeln!(output, "{}tokio::time::sleep(std::time::Duration::from_millis({} as u64)).await;",
6272                         indent_str, expr_str).unwrap();
6273            }
6274        }
6275
6276        // Phase 52/56: Sync CRDT variable on topic
6277        Stmt::Sync { var, topic } => {
6278            let var_name = interner.resolve(*var);
6279            let topic_str = codegen_expr_with_async(topic, interner, synced_vars, async_functions, ctx.get_variable_types());
6280
6281            // Phase 56: Check if this variable is also mounted
6282            if let Some(caps) = var_caps.get(var) {
6283                if caps.mounted {
6284                    // Both Mount and Sync: use Distributed<T>
6285                    // Mount statement will handle the Distributed::mount call
6286                    // Here we just track it as synced
6287                    synced_vars.insert(*var);
6288                    return output;  // Skip - Mount will emit Distributed<T>
6289                }
6290            }
6291
6292            // Sync-only: use Synced<T>
6293            writeln!(
6294                output,
6295                "{}let {} = logicaffeine_system::crdt::Synced::new({}, &{}).await;",
6296                indent_str, var_name, var_name, topic_str
6297            ).unwrap();
6298            synced_vars.insert(*var);
6299        }
6300
6301        // Phase 53/56: Mount persistent CRDT from journal
6302        Stmt::Mount { var, path } => {
6303            let var_name = interner.resolve(*var);
6304            let path_str = codegen_expr_with_async(path, interner, synced_vars, async_functions, ctx.get_variable_types());
6305
6306            // Phase 56: Check if this variable is also synced
6307            if let Some(caps) = var_caps.get(var) {
6308                if caps.synced {
6309                    // Both Mount and Sync: use Distributed<T>
6310                    let topic_str = caps.sync_topic.as_ref()
6311                        .map(|s| s.as_str())
6312                        .unwrap_or("\"default\"");
6313                    writeln!(
6314                        output,
6315                        "{}let {} = logicaffeine_system::distributed::Distributed::mount(std::sync::Arc::new(vfs.clone()), &{}, Some({}.to_string())).await.expect(\"Failed to mount\");",
6316                        indent_str, var_name, path_str, topic_str
6317                    ).unwrap();
6318                    synced_vars.insert(*var);
6319                    return output;
6320                }
6321            }
6322
6323            // Mount-only: use Persistent<T>
6324            writeln!(
6325                output,
6326                "{}let {} = logicaffeine_system::storage::Persistent::mount(&vfs, &{}).await.expect(\"Failed to mount\");",
6327                indent_str, var_name, path_str
6328            ).unwrap();
6329            synced_vars.insert(*var);
6330        }
6331
6332        // =====================================================================
6333        // Phase 54: Go-like Concurrency Codegen
6334        // =====================================================================
6335
6336        Stmt::LaunchTask { function, args } => {
6337            let fn_name = escape_rust_ident(interner.resolve(*function));
6338            // Phase 54: When passing a pipe variable, pass the sender (_tx)
6339            let args_str: Vec<String> = args.iter()
6340                .map(|a| {
6341                    if let Expr::Identifier(sym) = a {
6342                        if pipe_vars.contains(sym) {
6343                            return format!("{}_tx.clone()", interner.resolve(*sym));
6344                        }
6345                    }
6346                    codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types())
6347                })
6348                .collect();
6349            // Phase 54: Add .await only if the function is async
6350            let await_suffix = if async_functions.contains(function) { ".await" } else { "" };
6351            writeln!(
6352                output,
6353                "{}tokio::spawn(async move {{ {}({}){await_suffix}; }});",
6354                indent_str, fn_name, args_str.join(", ")
6355            ).unwrap();
6356        }
6357
6358        Stmt::LaunchTaskWithHandle { handle, function, args } => {
6359            let handle_name = interner.resolve(*handle);
6360            let fn_name = escape_rust_ident(interner.resolve(*function));
6361            // Phase 54: When passing a pipe variable, pass the sender (_tx)
6362            let args_str: Vec<String> = args.iter()
6363                .map(|a| {
6364                    if let Expr::Identifier(sym) = a {
6365                        if pipe_vars.contains(sym) {
6366                            return format!("{}_tx.clone()", interner.resolve(*sym));
6367                        }
6368                    }
6369                    codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types())
6370                })
6371                .collect();
6372            // Phase 54: Add .await only if the function is async
6373            let await_suffix = if async_functions.contains(function) { ".await" } else { "" };
6374            writeln!(
6375                output,
6376                "{}let {} = tokio::spawn(async move {{ {}({}){await_suffix} }});",
6377                indent_str, handle_name, fn_name, args_str.join(", ")
6378            ).unwrap();
6379        }
6380
6381        Stmt::CreatePipe { var, element_type, capacity } => {
6382            let var_name = interner.resolve(*var);
6383            let type_name = interner.resolve(*element_type);
6384            let cap = capacity.unwrap_or(32);
6385            // Map LOGOS types to Rust types
6386            let rust_type = match type_name {
6387                "Int" => "i64",
6388                "Nat" => "u64",
6389                "Text" => "String",
6390                "Bool" => "bool",
6391                _ => type_name,
6392            };
6393            writeln!(
6394                output,
6395                "{}let ({}_tx, mut {}_rx) = tokio::sync::mpsc::channel::<{}>({});",
6396                indent_str, var_name, var_name, rust_type, cap
6397            ).unwrap();
6398        }
6399
6400        Stmt::SendPipe { value, pipe } => {
6401            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());
6402            let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6403            // Phase 54: Check if pipe is a local declaration (has _tx suffix) or parameter (no suffix)
6404            let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6405                pipe_vars.contains(sym)
6406            } else {
6407                false
6408            };
6409            if is_local_pipe {
6410                writeln!(
6411                    output,
6412                    "{}{}_tx.send({}).await.expect(\"pipe send failed\");",
6413                    indent_str, pipe_str, val_str
6414                ).unwrap();
6415            } else {
6416                writeln!(
6417                    output,
6418                    "{}{}.send({}).await.expect(\"pipe send failed\");",
6419                    indent_str, pipe_str, val_str
6420                ).unwrap();
6421            }
6422        }
6423
6424        Stmt::ReceivePipe { var, pipe } => {
6425            let var_name = interner.resolve(*var);
6426            let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6427            // Phase 54: Check if pipe is a local declaration (has _rx suffix) or parameter (no suffix)
6428            let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6429                pipe_vars.contains(sym)
6430            } else {
6431                false
6432            };
6433            if is_local_pipe {
6434                writeln!(
6435                    output,
6436                    "{}let {} = {}_rx.recv().await.expect(\"pipe closed\");",
6437                    indent_str, var_name, pipe_str
6438                ).unwrap();
6439            } else {
6440                writeln!(
6441                    output,
6442                    "{}let {} = {}.recv().await.expect(\"pipe closed\");",
6443                    indent_str, var_name, pipe_str
6444                ).unwrap();
6445            }
6446        }
6447
6448        Stmt::TrySendPipe { value, pipe, result } => {
6449            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());
6450            let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6451            // Phase 54: Check if pipe is a local declaration
6452            let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6453                pipe_vars.contains(sym)
6454            } else {
6455                false
6456            };
6457            let suffix = if is_local_pipe { "_tx" } else { "" };
6458            if let Some(res) = result {
6459                let res_name = interner.resolve(*res);
6460                writeln!(
6461                    output,
6462                    "{}let {} = {}{}.try_send({}).is_ok();",
6463                    indent_str, res_name, pipe_str, suffix, val_str
6464                ).unwrap();
6465            } else {
6466                writeln!(
6467                    output,
6468                    "{}let _ = {}{}.try_send({});",
6469                    indent_str, pipe_str, suffix, val_str
6470                ).unwrap();
6471            }
6472        }
6473
6474        Stmt::TryReceivePipe { var, pipe } => {
6475            let var_name = interner.resolve(*var);
6476            let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6477            // Phase 54: Check if pipe is a local declaration
6478            let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6479                pipe_vars.contains(sym)
6480            } else {
6481                false
6482            };
6483            let suffix = if is_local_pipe { "_rx" } else { "" };
6484            writeln!(
6485                output,
6486                "{}let {} = {}{}.try_recv().ok();",
6487                indent_str, var_name, pipe_str, suffix
6488            ).unwrap();
6489        }
6490
6491        Stmt::StopTask { handle } => {
6492            let handle_str = codegen_expr_with_async(handle, interner, synced_vars, async_functions, ctx.get_variable_types());
6493            writeln!(output, "{}{}.abort();", indent_str, handle_str).unwrap();
6494        }
6495
6496        Stmt::Select { branches } => {
6497            use crate::ast::stmt::SelectBranch;
6498
6499            writeln!(output, "{}tokio::select! {{", indent_str).unwrap();
6500            for branch in branches {
6501                match branch {
6502                    SelectBranch::Receive { var, pipe, body } => {
6503                        let var_name = interner.resolve(*var);
6504                        let pipe_str = codegen_expr_with_async(pipe, interner, synced_vars, async_functions, ctx.get_variable_types());
6505                        // Check if pipe is a local declaration (has _rx suffix) or a parameter (no suffix)
6506                        let is_local_pipe = if let Expr::Identifier(sym) = pipe {
6507                            pipe_vars.contains(sym)
6508                        } else {
6509                            false
6510                        };
6511                        let suffix = if is_local_pipe { "_rx" } else { "" };
6512                        writeln!(
6513                            output,
6514                            "{}    {} = {}{}.recv() => {{",
6515                            indent_str, var_name, pipe_str, suffix
6516                        ).unwrap();
6517                        writeln!(
6518                            output,
6519                            "{}        if let Some({}) = {} {{",
6520                            indent_str, var_name, var_name
6521                        ).unwrap();
6522                        for stmt in *body {
6523                            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);
6524                            write!(output, "{}", stmt_code).unwrap();
6525                        }
6526                        writeln!(output, "{}        }}", indent_str).unwrap();
6527                        writeln!(output, "{}    }}", indent_str).unwrap();
6528                    }
6529                    SelectBranch::Timeout { milliseconds, body } => {
6530                        let ms_str = codegen_expr_with_async(milliseconds, interner, synced_vars, async_functions, ctx.get_variable_types());
6531                        // Convert seconds to milliseconds if the value looks like seconds
6532                        writeln!(
6533                            output,
6534                            "{}    _ = tokio::time::sleep(std::time::Duration::from_secs({} as u64)) => {{",
6535                            indent_str, ms_str
6536                        ).unwrap();
6537                        for stmt in *body {
6538                            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);
6539                            write!(output, "{}", stmt_code).unwrap();
6540                        }
6541                        writeln!(output, "{}    }}", indent_str).unwrap();
6542                    }
6543                }
6544            }
6545            writeln!(output, "{}}}", indent_str).unwrap();
6546        }
6547
6548        Stmt::Give { object, recipient } => {
6549            // Move semantics: pass ownership without borrowing
6550            let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
6551            let recv_str = codegen_expr_with_async(recipient, interner, synced_vars, async_functions, ctx.get_variable_types());
6552            writeln!(output, "{}{}({});", indent_str, recv_str, obj_str).unwrap();
6553        }
6554
6555        Stmt::Show { object, recipient } => {
6556            // Borrow semantics: pass immutable reference
6557            // Use string_vars for proper concatenation of string variables
6558            let obj_str = codegen_expr_with_async_and_strings(object, interner, synced_vars, async_functions, ctx.get_string_vars(), ctx.get_variable_types());
6559            let recv_str = codegen_expr_with_async(recipient, interner, synced_vars, async_functions, ctx.get_variable_types());
6560            writeln!(output, "{}{}(&{});", indent_str, recv_str, obj_str).unwrap();
6561        }
6562
6563        Stmt::SetField { object, field, value } => {
6564            let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
6565            let field_name = interner.resolve(*field);
6566            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());
6567
6568            // Phase 49: Check if this field is an LWWRegister or MVRegister
6569            // LWW needs .set(value, timestamp), MV needs .set(value)
6570            let is_lww = lww_fields.iter().any(|(_, f)| f == field_name);
6571            let is_mv = mv_fields.iter().any(|(_, f)| f == field_name);
6572            if is_lww {
6573                // LWWRegister needs a timestamp - use current system time in microseconds
6574                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();
6575            } else if is_mv {
6576                // MVRegister just needs the value
6577                writeln!(output, "{}{}.{}.set({});", indent_str, obj_str, field_name, value_str).unwrap();
6578            } else {
6579                writeln!(output, "{}{}.{} = {};", indent_str, obj_str, field_name, value_str).unwrap();
6580            }
6581        }
6582
6583        Stmt::StructDef { .. } => {
6584            // Struct definitions are handled in codegen_program, not here
6585        }
6586
6587        Stmt::FunctionDef { .. } => {
6588            // Function definitions are handled in codegen_program, not here
6589        }
6590
6591        Stmt::Inspect { target, arms, .. } => {
6592            let target_str = codegen_expr_with_async(target, interner, synced_vars, async_functions, ctx.get_variable_types());
6593
6594            // Phase 102: Track which bindings come from boxed fields for inner Inspects
6595            // Use NAMES (strings) not symbols, because parser may create different symbols
6596            // for the same identifier in different syntactic positions.
6597            let mut inner_boxed_binding_names: HashSet<String> = HashSet::new();
6598
6599            writeln!(output, "{}match {} {{", indent_str, target_str).unwrap();
6600
6601            for arm in arms {
6602                if let Some(variant) = arm.variant {
6603                    let variant_name = interner.resolve(variant);
6604                    // Get the enum name from the arm, or fallback to just variant name
6605                    let enum_name_str = arm.enum_name.map(|e| interner.resolve(e));
6606                    let enum_prefix = enum_name_str
6607                        .map(|e| format!("{}::", e))
6608                        .unwrap_or_default();
6609
6610                    if arm.bindings.is_empty() {
6611                        // Unit variant pattern
6612                        writeln!(output, "{}    {}{} => {{", indent_str, enum_prefix, variant_name).unwrap();
6613                    } else {
6614                        // Pattern with bindings
6615                        // Phase 102: Check which bindings are from boxed fields
6616                        let bindings_str: Vec<String> = arm.bindings.iter()
6617                            .map(|(field, binding)| {
6618                                let field_name = interner.resolve(*field);
6619                                let binding_name = interner.resolve(*binding);
6620
6621                                // Check if this field is boxed
6622                                if let Some(enum_name) = enum_name_str {
6623                                    let key = (enum_name.to_string(), variant_name.to_string(), field_name.to_string());
6624                                    if boxed_fields.contains(&key) {
6625                                        inner_boxed_binding_names.insert(binding_name.to_string());
6626                                    }
6627                                }
6628
6629                                if field_name == binding_name {
6630                                    field_name.to_string()
6631                                } else {
6632                                    format!("{}: {}", field_name, binding_name)
6633                                }
6634                            })
6635                            .collect();
6636                        writeln!(output, "{}    {}{} {{ {} }} => {{", indent_str, enum_prefix, variant_name, bindings_str.join(", ")).unwrap();
6637                    }
6638                } else {
6639                    // Otherwise (wildcard) pattern
6640                    writeln!(output, "{}    _ => {{", indent_str).unwrap();
6641                }
6642
6643                ctx.push_scope();
6644
6645                // Generate explicit dereferences for boxed bindings at the start of the arm
6646                // This makes them usable as regular values in the rest of the body
6647                for binding_name in &inner_boxed_binding_names {
6648                    writeln!(output, "{}        let {} = (*{}).clone();", indent_str, binding_name, binding_name).unwrap();
6649                }
6650
6651                for stmt in arm.body {
6652                    // Phase 102: Handle inner Inspect statements with boxed bindings
6653                    // Note: Since we now dereference boxed bindings at the start of the arm,
6654                    // inner matches don't need the `*` dereference operator.
6655                    let inner_stmt_code = if let Stmt::Inspect { target: inner_target, .. } = stmt {
6656                        // Check if the inner target is a boxed binding (already dereferenced above)
6657                        // Use name comparison since symbols may differ between binding and reference
6658                        if let Expr::Identifier(sym) = inner_target {
6659                            let target_name = interner.resolve(*sym);
6660                            if inner_boxed_binding_names.contains(target_name) {
6661                                // Generate match (binding was already dereferenced at arm start)
6662                                let mut inner_output = String::new();
6663                                writeln!(inner_output, "{}match {} {{", "    ".repeat(indent + 2), target_name).unwrap();
6664
6665                                if let Stmt::Inspect { arms: inner_arms, .. } = stmt {
6666                                    for inner_arm in inner_arms.iter() {
6667                                        if let Some(v) = inner_arm.variant {
6668                                            let v_name = interner.resolve(v);
6669                                            let inner_enum_prefix = inner_arm.enum_name
6670                                                .map(|e| format!("{}::", interner.resolve(e)))
6671                                                .unwrap_or_default();
6672
6673                                            if inner_arm.bindings.is_empty() {
6674                                                writeln!(inner_output, "{}    {}{} => {{", "    ".repeat(indent + 2), inner_enum_prefix, v_name).unwrap();
6675                                            } else {
6676                                                let bindings: Vec<String> = inner_arm.bindings.iter()
6677                                                    .map(|(f, b)| {
6678                                                        let fn_name = interner.resolve(*f);
6679                                                        let bn_name = interner.resolve(*b);
6680                                                        if fn_name == bn_name { fn_name.to_string() }
6681                                                        else { format!("{}: {}", fn_name, bn_name) }
6682                                                    })
6683                                                    .collect();
6684                                                writeln!(inner_output, "{}    {}{} {{ {} }} => {{", "    ".repeat(indent + 2), inner_enum_prefix, v_name, bindings.join(", ")).unwrap();
6685                                            }
6686                                        } else {
6687                                            writeln!(inner_output, "{}    _ => {{", "    ".repeat(indent + 2)).unwrap();
6688                                        }
6689
6690                                        ctx.push_scope();
6691                                        for inner_stmt in inner_arm.body {
6692                                            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));
6693                                        }
6694                                        ctx.pop_scope();
6695                                        writeln!(inner_output, "{}    }}", "    ".repeat(indent + 2)).unwrap();
6696                                    }
6697                                }
6698                                writeln!(inner_output, "{}}}", "    ".repeat(indent + 2)).unwrap();
6699                                inner_output
6700                            } else {
6701                                codegen_stmt(stmt, interner, indent + 2, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
6702                            }
6703                        } else {
6704                            codegen_stmt(stmt, interner, indent + 2, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
6705                        }
6706                    } else {
6707                        codegen_stmt(stmt, interner, indent + 2, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry)
6708                    };
6709                    output.push_str(&inner_stmt_code);
6710                }
6711                ctx.pop_scope();
6712                writeln!(output, "{}    }}", indent_str).unwrap();
6713            }
6714
6715            writeln!(output, "{}}}", indent_str).unwrap();
6716        }
6717
6718        Stmt::Push { value, collection } => {
6719            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());
6720            let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
6721            writeln!(output, "{}{}.push({});", indent_str, coll_str, val_str).unwrap();
6722        }
6723
6724        Stmt::Pop { collection, into } => {
6725            let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
6726            match into {
6727                Some(var) => {
6728                    let var_name = interner.resolve(*var);
6729                    // Unwrap the Option returned by pop() - panics if empty
6730                    writeln!(output, "{}let {} = {}.pop().expect(\"Pop from empty collection\");", indent_str, var_name, coll_str).unwrap();
6731                }
6732                None => {
6733                    writeln!(output, "{}{}.pop();", indent_str, coll_str).unwrap();
6734                }
6735            }
6736        }
6737
6738        Stmt::Add { value, collection } => {
6739            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());
6740            let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
6741            writeln!(output, "{}{}.insert({});", indent_str, coll_str, val_str).unwrap();
6742        }
6743
6744        Stmt::Remove { value, collection } => {
6745            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());
6746            let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
6747            writeln!(output, "{}{}.remove(&{});", indent_str, coll_str, val_str).unwrap();
6748        }
6749
6750        Stmt::SetIndex { collection, index, value } => {
6751            let coll_str = codegen_expr_with_async(collection, interner, synced_vars, async_functions, ctx.get_variable_types());
6752            let index_str = codegen_expr_with_async(index, interner, synced_vars, async_functions, ctx.get_variable_types());
6753            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());
6754
6755            // Direct indexing for known collection types (avoids trait dispatch)
6756            let known_type = if let Expr::Identifier(sym) = collection {
6757                ctx.get_variable_types().get(sym).map(|s| s.as_str())
6758            } else {
6759                None
6760            };
6761
6762            match known_type {
6763                Some(t) if t.starts_with("Vec") => {
6764                    // Evaluate value first if it references the same collection (borrow safety)
6765                    if value_str.contains(&coll_str) {
6766                        writeln!(output, "{}let __set_tmp = {};", indent_str, value_str).unwrap();
6767                        writeln!(output, "{}{}[({} - 1) as usize] = __set_tmp;", indent_str, coll_str, index_str).unwrap();
6768                    } else {
6769                        writeln!(output, "{}{}[({} - 1) as usize] = {};", indent_str, coll_str, index_str, value_str).unwrap();
6770                    }
6771                }
6772                Some(t) if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") => {
6773                    writeln!(output, "{}{}.insert({}, {});", indent_str, coll_str, index_str, value_str).unwrap();
6774                }
6775                _ => {
6776                    // Fallback: polymorphic indexing via trait
6777                    if value_str.contains("logos_get") && value_str.contains(&coll_str) {
6778                        writeln!(output, "{}let __set_tmp = {};", indent_str, value_str).unwrap();
6779                        writeln!(output, "{}LogosIndexMut::logos_set(&mut {}, {}, __set_tmp);", indent_str, coll_str, index_str).unwrap();
6780                    } else {
6781                        writeln!(output, "{}LogosIndexMut::logos_set(&mut {}, {}, {});", indent_str, coll_str, index_str, value_str).unwrap();
6782                    }
6783                }
6784            }
6785        }
6786
6787        // Phase 8.5: Zone (memory arena) block
6788        Stmt::Zone { name, capacity, source_file, body } => {
6789            let zone_name = interner.resolve(*name);
6790
6791            // Generate zone creation based on type
6792            if let Some(path_sym) = source_file {
6793                // Memory-mapped file zone
6794                let path = interner.resolve(*path_sym);
6795                writeln!(
6796                    output,
6797                    "{}let {} = logicaffeine_system::memory::Zone::new_mapped(\"{}\").expect(\"Failed to map file\");",
6798                    indent_str, zone_name, path
6799                ).unwrap();
6800            } else {
6801                // Heap arena zone
6802                let cap = capacity.unwrap_or(4096); // Default 4KB
6803                writeln!(
6804                    output,
6805                    "{}let {} = logicaffeine_system::memory::Zone::new_heap({});",
6806                    indent_str, zone_name, cap
6807                ).unwrap();
6808            }
6809
6810            // Open block scope
6811            writeln!(output, "{}{{", indent_str).unwrap();
6812            ctx.push_scope();
6813
6814            // Generate body statements
6815            for stmt in *body {
6816                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));
6817            }
6818
6819            ctx.pop_scope();
6820            writeln!(output, "{}}}", indent_str).unwrap();
6821        }
6822
6823        // Phase 9: Concurrent execution block (async, I/O-bound)
6824        // Generates tokio::join! for concurrent task execution
6825        // Phase 51: Variables used across multiple tasks are cloned to avoid move issues
6826        Stmt::Concurrent { tasks } => {
6827            // Collect Let statements to generate tuple destructuring
6828            let let_bindings: Vec<_> = tasks.iter().filter_map(|s| {
6829                if let Stmt::Let { var, .. } = s {
6830                    Some(interner.resolve(*var).to_string())
6831                } else {
6832                    None
6833                }
6834            }).collect();
6835
6836            // Collect variables DEFINED in this block (to exclude from cloning)
6837            let defined_vars: HashSet<Symbol> = tasks.iter().filter_map(|s| {
6838                if let Stmt::Let { var, .. } = s {
6839                    Some(*var)
6840                } else {
6841                    None
6842                }
6843            }).collect();
6844
6845            // Check if there are intra-block dependencies (a later task uses a var from earlier task)
6846            // If so, fall back to sequential execution
6847            let mut has_intra_dependency = false;
6848            let mut seen_defs: HashSet<Symbol> = HashSet::new();
6849            for s in *tasks {
6850                // Check if this task uses any variable defined by previous tasks in this block
6851                let mut used_in_task: HashSet<Symbol> = HashSet::new();
6852                collect_stmt_identifiers(s, &mut used_in_task);
6853                for used_var in &used_in_task {
6854                    if seen_defs.contains(used_var) {
6855                        has_intra_dependency = true;
6856                        break;
6857                    }
6858                }
6859                // Track variables defined by this task
6860                if let Stmt::Let { var, .. } = s {
6861                    seen_defs.insert(*var);
6862                }
6863                if has_intra_dependency {
6864                    break;
6865                }
6866            }
6867
6868            // Collect ALL variables used in task expressions (not just Call args)
6869            // Exclude variables defined within this block
6870            let mut used_syms: HashSet<Symbol> = HashSet::new();
6871            for s in *tasks {
6872                collect_stmt_identifiers(s, &mut used_syms);
6873            }
6874            // Remove variables that are defined in this block
6875            for def_var in &defined_vars {
6876                used_syms.remove(def_var);
6877            }
6878            let used_vars: HashSet<String> = used_syms.iter()
6879                .map(|sym| interner.resolve(*sym).to_string())
6880                .collect();
6881
6882            // If there are intra-block dependencies, execute sequentially
6883            if has_intra_dependency {
6884                // Generate sequential Let bindings
6885                for stmt in *tasks {
6886                    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));
6887                }
6888            } else {
6889                // Generate concurrent execution with tokio::join!
6890                if !let_bindings.is_empty() {
6891                    // Generate tuple destructuring for concurrent Let bindings
6892                    writeln!(output, "{}let ({}) = tokio::join!(", indent_str, let_bindings.join(", ")).unwrap();
6893                } else {
6894                    writeln!(output, "{}tokio::join!(", indent_str).unwrap();
6895                }
6896
6897                for (i, stmt) in tasks.iter().enumerate() {
6898                    // For Let statements, generate only the VALUE so the async block returns it
6899                    // For Call statements, generate the call with .await
6900                    let inner_code = match stmt {
6901                        Stmt::Let { value, .. } => {
6902                            // Return the value expression directly (not "let x = value;")
6903                            // Phase 54+: Use codegen_expr_with_async to handle all nested async calls
6904                            codegen_expr_with_async(value, interner, synced_vars, async_functions, ctx.get_variable_types())
6905                        }
6906                        Stmt::Call { function, args } => {
6907                            let func_name = interner.resolve(*function);
6908                            let args_str: Vec<String> = args.iter()
6909                                .map(|a| codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types()))
6910                                .collect();
6911                            // Only add .await for async functions
6912                            let await_suffix = if async_functions.contains(function) { ".await" } else { "" };
6913                            format!("{}({}){}", func_name, args_str.join(", "), await_suffix)
6914                        }
6915                        _ => {
6916                            // Fallback for other statement types
6917                            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);
6918                            inner.trim().to_string()
6919                        }
6920                    };
6921
6922                    // For tasks that use shared variables, wrap in a block that clones them
6923                    if !used_vars.is_empty() && i < tasks.len() - 1 {
6924                        // Clone variables for all tasks except the last one
6925                        let clones: Vec<String> = used_vars.iter()
6926                            .map(|v| format!("let {} = {}.clone();", v, v))
6927                            .collect();
6928                        write!(output, "{}    {{ {} async move {{ {} }} }}",
6929                               indent_str, clones.join(" "), inner_code).unwrap();
6930                    } else {
6931                        // Last task can use original variables
6932                        write!(output, "{}    async {{ {} }}", indent_str, inner_code).unwrap();
6933                    }
6934
6935                    if i < tasks.len() - 1 {
6936                        writeln!(output, ",").unwrap();
6937                    } else {
6938                        writeln!(output).unwrap();
6939                    }
6940                }
6941
6942                writeln!(output, "{});", indent_str).unwrap();
6943            }
6944        }
6945
6946        // Phase 9: Parallel execution block (CPU-bound)
6947        // Generates rayon::join for two tasks, or thread::spawn for 3+ tasks
6948        Stmt::Parallel { tasks } => {
6949            // Collect Let statements to generate tuple destructuring
6950            let let_bindings: Vec<_> = tasks.iter().filter_map(|s| {
6951                if let Stmt::Let { var, .. } = s {
6952                    Some(interner.resolve(*var).to_string())
6953                } else {
6954                    None
6955                }
6956            }).collect();
6957
6958            if tasks.len() == 2 {
6959                // Use rayon::join for exactly 2 tasks
6960                if !let_bindings.is_empty() {
6961                    writeln!(output, "{}let ({}) = rayon::join(", indent_str, let_bindings.join(", ")).unwrap();
6962                } else {
6963                    writeln!(output, "{}rayon::join(", indent_str).unwrap();
6964                }
6965
6966                for (i, stmt) in tasks.iter().enumerate() {
6967                    // For Let statements, generate only the VALUE so the closure returns it
6968                    let inner_code = match stmt {
6969                        Stmt::Let { value, .. } => {
6970                            // Return the value expression directly (not "let x = value;")
6971                            codegen_expr(value, interner, synced_vars)
6972                        }
6973                        Stmt::Call { function, args } => {
6974                            let func_name = interner.resolve(*function);
6975                            let args_str: Vec<String> = args.iter()
6976                                .map(|a| codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types()))
6977                                .collect();
6978                            format!("{}({})", func_name, args_str.join(", "))
6979                        }
6980                        _ => {
6981                            // Fallback for other statement types
6982                            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);
6983                            inner.trim().to_string()
6984                        }
6985                    };
6986                    write!(output, "{}    || {{ {} }}", indent_str, inner_code).unwrap();
6987                    if i == 0 {
6988                        writeln!(output, ",").unwrap();
6989                    } else {
6990                        writeln!(output).unwrap();
6991                    }
6992                }
6993                writeln!(output, "{});", indent_str).unwrap();
6994            } else {
6995                // For 3+ tasks, use thread::spawn pattern
6996                writeln!(output, "{}{{", indent_str).unwrap();
6997                writeln!(output, "{}    let handles: Vec<_> = vec![", indent_str).unwrap();
6998                for stmt in *tasks {
6999                    // For Let statements, generate only the VALUE so the closure returns it
7000                    let inner_code = match stmt {
7001                        Stmt::Let { value, .. } => {
7002                            codegen_expr(value, interner, synced_vars)
7003                        }
7004                        Stmt::Call { function, args } => {
7005                            let func_name = interner.resolve(*function);
7006                            let args_str: Vec<String> = args.iter()
7007                                .map(|a| codegen_expr_with_async(a, interner, synced_vars, async_functions, ctx.get_variable_types()))
7008                                .collect();
7009                            format!("{}({})", func_name, args_str.join(", "))
7010                        }
7011                        _ => {
7012                            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);
7013                            inner.trim().to_string()
7014                        }
7015                    };
7016                    writeln!(output, "{}        std::thread::spawn(move || {{ {} }}),",
7017                             indent_str, inner_code).unwrap();
7018                }
7019                writeln!(output, "{}    ];", indent_str).unwrap();
7020                writeln!(output, "{}    for h in handles {{ h.join().unwrap(); }}", indent_str).unwrap();
7021                writeln!(output, "{}}}", indent_str).unwrap();
7022            }
7023        }
7024
7025        // Phase 10: Read from console or file
7026        // Phase 53: File reads now use async VFS
7027        Stmt::ReadFrom { var, source } => {
7028            let var_name = interner.resolve(*var);
7029            match source {
7030                ReadSource::Console => {
7031                    writeln!(output, "{}let {} = logicaffeine_system::io::read_line();", indent_str, var_name).unwrap();
7032                }
7033                ReadSource::File(path_expr) => {
7034                    let path_str = codegen_expr_with_async(path_expr, interner, synced_vars, async_functions, ctx.get_variable_types());
7035                    // Phase 53: Use VFS with async
7036                    writeln!(
7037                        output,
7038                        "{}let {} = vfs.read_to_string(&{}).await.expect(\"Failed to read file\");",
7039                        indent_str, var_name, path_str
7040                    ).unwrap();
7041                }
7042            }
7043        }
7044
7045        // Phase 10: Write to file
7046        // Phase 53: File writes now use async VFS
7047        Stmt::WriteFile { content, path } => {
7048            let content_str = codegen_expr_with_async(content, interner, synced_vars, async_functions, ctx.get_variable_types());
7049            let path_str = codegen_expr_with_async(path, interner, synced_vars, async_functions, ctx.get_variable_types());
7050            // Phase 53: Use VFS with async
7051            writeln!(
7052                output,
7053                "{}vfs.write(&{}, {}.as_bytes()).await.expect(\"Failed to write file\");",
7054                indent_str, path_str, content_str
7055            ).unwrap();
7056        }
7057
7058        // Phase 46: Spawn an agent
7059        Stmt::Spawn { agent_type, name } => {
7060            let type_name = interner.resolve(*agent_type);
7061            let agent_name = interner.resolve(*name);
7062            // Generate agent spawn with tokio channel
7063            writeln!(
7064                output,
7065                "{}let {} = tokio::spawn(async move {{ /* {} agent loop */ }});",
7066                indent_str, agent_name, type_name
7067            ).unwrap();
7068        }
7069
7070        // Phase 46: Send message to agent
7071        Stmt::SendMessage { message, destination } => {
7072            let msg_str = codegen_expr_with_async(message, interner, synced_vars, async_functions, ctx.get_variable_types());
7073            let dest_str = codegen_expr_with_async(destination, interner, synced_vars, async_functions, ctx.get_variable_types());
7074            writeln!(
7075                output,
7076                "{}{}.send({}).await.expect(\"Failed to send message\");",
7077                indent_str, dest_str, msg_str
7078            ).unwrap();
7079        }
7080
7081        // Phase 46: Await response from agent
7082        Stmt::AwaitMessage { source, into } => {
7083            let src_str = codegen_expr_with_async(source, interner, synced_vars, async_functions, ctx.get_variable_types());
7084            let var_name = interner.resolve(*into);
7085            writeln!(
7086                output,
7087                "{}let {} = {}.recv().await.expect(\"Failed to receive message\");",
7088                indent_str, var_name, src_str
7089            ).unwrap();
7090        }
7091
7092        // Phase 49: Merge CRDT state
7093        Stmt::MergeCrdt { source, target } => {
7094            let src_str = codegen_expr_with_async(source, interner, synced_vars, async_functions, ctx.get_variable_types());
7095            let tgt_str = codegen_expr_with_async(target, interner, synced_vars, async_functions, ctx.get_variable_types());
7096            writeln!(
7097                output,
7098                "{}{}.merge(&{});",
7099                indent_str, tgt_str, src_str
7100            ).unwrap();
7101        }
7102
7103        // Phase 49: Increment GCounter
7104        // Phase 52: If object is synced, wrap in .mutate() for auto-publish
7105        Stmt::IncreaseCrdt { object, field, amount } => {
7106            let field_name = interner.resolve(*field);
7107            let amount_str = codegen_expr_with_async(amount, interner, synced_vars, async_functions, ctx.get_variable_types());
7108
7109            // Check if the root object is synced
7110            let root_sym = get_root_identifier(object);
7111            if let Some(sym) = root_sym {
7112                if synced_vars.contains(&sym) {
7113                    // Synced: use .mutate() for auto-publish
7114                    let obj_name = interner.resolve(sym);
7115                    writeln!(
7116                        output,
7117                        "{}{}.mutate(|inner| inner.{}.increment({} as u64)).await;",
7118                        indent_str, obj_name, field_name, amount_str
7119                    ).unwrap();
7120                    return output;
7121                }
7122            }
7123
7124            // Not synced: direct access
7125            let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
7126            writeln!(
7127                output,
7128                "{}{}.{}.increment({} as u64);",
7129                indent_str, obj_str, field_name, amount_str
7130            ).unwrap();
7131        }
7132
7133        // Phase 49b: Decrement PNCounter
7134        Stmt::DecreaseCrdt { object, field, amount } => {
7135            let field_name = interner.resolve(*field);
7136            let amount_str = codegen_expr_with_async(amount, interner, synced_vars, async_functions, ctx.get_variable_types());
7137
7138            // Check if the root object is synced
7139            let root_sym = get_root_identifier(object);
7140            if let Some(sym) = root_sym {
7141                if synced_vars.contains(&sym) {
7142                    // Synced: use .mutate() for auto-publish
7143                    let obj_name = interner.resolve(sym);
7144                    writeln!(
7145                        output,
7146                        "{}{}.mutate(|inner| inner.{}.decrement({} as u64)).await;",
7147                        indent_str, obj_name, field_name, amount_str
7148                    ).unwrap();
7149                    return output;
7150                }
7151            }
7152
7153            // Not synced: direct access
7154            let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
7155            writeln!(
7156                output,
7157                "{}{}.{}.decrement({} as u64);",
7158                indent_str, obj_str, field_name, amount_str
7159            ).unwrap();
7160        }
7161
7162        // Phase 49b: Append to SharedSequence (RGA)
7163        Stmt::AppendToSequence { sequence, value } => {
7164            let seq_str = codegen_expr_with_async(sequence, interner, synced_vars, async_functions, ctx.get_variable_types());
7165            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());
7166            writeln!(
7167                output,
7168                "{}{}.append({});",
7169                indent_str, seq_str, val_str
7170            ).unwrap();
7171        }
7172
7173        // Phase 49b: Resolve MVRegister conflicts
7174        Stmt::ResolveConflict { object, field, value } => {
7175            let field_name = interner.resolve(*field);
7176            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());
7177            let obj_str = codegen_expr_with_async(object, interner, synced_vars, async_functions, ctx.get_variable_types());
7178            writeln!(
7179                output,
7180                "{}{}.{}.resolve({});",
7181                indent_str, obj_str, field_name, val_str
7182            ).unwrap();
7183        }
7184
7185        // Escape hatch: emit raw foreign code wrapped in braces for scope isolation
7186        Stmt::Escape { code, .. } => {
7187            let raw_code = interner.resolve(*code);
7188            write!(output, "{}{{\n", indent_str).unwrap();
7189            for line in raw_code.lines() {
7190                write!(output, "{}    {}\n", indent_str, line).unwrap();
7191            }
7192            write!(output, "{}}}\n", indent_str).unwrap();
7193        }
7194
7195        // Dependencies are metadata; no Rust code emitted.
7196        Stmt::Require { .. } => {}
7197
7198        // Phase 63: Theorems are verified at compile-time, no runtime code generated
7199        Stmt::Theorem(_) => {
7200            // Theorems don't generate runtime code - they're processed separately
7201            // by compile_theorem() at the meta-level
7202        }
7203    }
7204
7205    output
7206}
7207
7208/// Phase 52: Extract the root identifier from an expression.
7209/// For `x.field.subfield`, returns `x`.
7210fn get_root_identifier(expr: &Expr) -> Option<Symbol> {
7211    match expr {
7212        Expr::Identifier(sym) => Some(*sym),
7213        Expr::FieldAccess { object, .. } => get_root_identifier(object),
7214        _ => None,
7215    }
7216}
7217
7218/// Check if a type string represents a Copy type (no .clone() needed on indexing).
7219fn is_copy_type(ty: &str) -> bool {
7220    matches!(ty, "i64" | "u64" | "f64" | "i32" | "u32" | "f32" | "bool" | "char" | "u8" | "i8" | "()")
7221}
7222
7223/// Check if a Vec<T> type has a Copy element type.
7224fn has_copy_element_type(vec_type: &str) -> bool {
7225    if let Some(inner) = vec_type.strip_prefix("Vec<").and_then(|s| s.strip_suffix('>')) {
7226        is_copy_type(inner)
7227    } else {
7228        false
7229    }
7230}
7231
7232/// Check if a HashMap<K, V> type has a Copy value type.
7233fn has_copy_value_type(map_type: &str) -> bool {
7234    let inner = map_type.strip_prefix("std::collections::HashMap<")
7235        .or_else(|| map_type.strip_prefix("HashMap<"));
7236    if let Some(inner) = inner.and_then(|s| s.strip_suffix('>')) {
7237        // Split on ", " to get key and value types
7238        if let Some((_key, value)) = inner.split_once(", ") {
7239            return is_copy_type(value);
7240        }
7241    }
7242    false
7243}
7244
7245pub fn codegen_expr(expr: &Expr, interner: &Interner, synced_vars: &HashSet<Symbol>) -> String {
7246    // Use empty registry, boxed_fields, and async_functions for simple expression codegen
7247    let empty_registry = TypeRegistry::new();
7248    let empty_async = HashSet::new();
7249    codegen_expr_boxed(expr, interner, synced_vars, &HashSet::new(), &empty_registry, &empty_async)
7250}
7251
7252/// Phase 54+: Codegen expression with async function tracking.
7253/// Adds .await to async function calls at the expression level, handling nested calls.
7254pub fn codegen_expr_with_async(
7255    expr: &Expr,
7256    interner: &Interner,
7257    synced_vars: &HashSet<Symbol>,
7258    async_functions: &HashSet<Symbol>,
7259    variable_types: &HashMap<Symbol, String>,
7260) -> String {
7261    let empty_registry = TypeRegistry::new();
7262    let empty_strings = HashSet::new();
7263    codegen_expr_boxed_internal(expr, interner, synced_vars, &HashSet::new(), &empty_registry, async_functions, &HashSet::new(), &empty_strings, variable_types)
7264}
7265
7266/// Codegen expression with async support and string variable tracking.
7267fn codegen_expr_with_async_and_strings(
7268    expr: &Expr,
7269    interner: &Interner,
7270    synced_vars: &HashSet<Symbol>,
7271    async_functions: &HashSet<Symbol>,
7272    string_vars: &HashSet<Symbol>,
7273    variable_types: &HashMap<Symbol, String>,
7274) -> String {
7275    let empty_registry = TypeRegistry::new();
7276    codegen_expr_boxed_internal(expr, interner, synced_vars, &HashSet::new(), &empty_registry, async_functions, &HashSet::new(), string_vars, variable_types)
7277}
7278
7279/// Check if an expression is definitely numeric (safe to use + operator).
7280/// This is conservative for Add operations - treats it as string concat only
7281/// when clearly dealing with strings (string literals).
7282fn is_definitely_numeric_expr(expr: &Expr) -> bool {
7283    match expr {
7284        Expr::Literal(Literal::Number(_)) => true,
7285        Expr::Literal(Literal::Float(_)) => true,
7286        Expr::Literal(Literal::Duration(_)) => true,
7287        // Identifiers might be strings, but without a string literal nearby,
7288        // assume numeric (Rust will catch type errors)
7289        Expr::Identifier(_) => true,
7290        // Arithmetic operations are numeric
7291        Expr::BinaryOp { op: BinaryOpKind::Subtract, .. } => true,
7292        Expr::BinaryOp { op: BinaryOpKind::Multiply, .. } => true,
7293        Expr::BinaryOp { op: BinaryOpKind::Divide, .. } => true,
7294        Expr::BinaryOp { op: BinaryOpKind::Modulo, .. } => true,
7295        // Length always returns a number
7296        Expr::Length { .. } => true,
7297        // Add is numeric if both operands seem numeric
7298        Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
7299            is_definitely_numeric_expr(left) && is_definitely_numeric_expr(right)
7300        }
7301        // Function calls - assume numeric (Rust type checker will validate)
7302        Expr::Call { .. } => true,
7303        // Index expressions - assume numeric
7304        Expr::Index { .. } => true,
7305        _ => true,
7306    }
7307}
7308
7309/// Check if an expression is definitely a string (needs format! for concatenation).
7310/// Takes a set of known string variable symbols for identifier lookup.
7311fn is_definitely_string_expr_with_vars(expr: &Expr, string_vars: &HashSet<Symbol>) -> bool {
7312    match expr {
7313        // String literals are definitely strings
7314        Expr::Literal(Literal::Text(_)) => true,
7315        // Variables known to be strings
7316        Expr::Identifier(sym) => string_vars.contains(sym),
7317        // Concat always produces strings
7318        Expr::BinaryOp { op: BinaryOpKind::Concat, .. } => true,
7319        // Add with a string operand produces a string
7320        Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
7321            is_definitely_string_expr_with_vars(left, string_vars)
7322                || is_definitely_string_expr_with_vars(right, string_vars)
7323        }
7324        // WithCapacity wrapping a string value is a string
7325        Expr::WithCapacity { value, .. } => is_definitely_string_expr_with_vars(value, string_vars),
7326        _ => false,
7327    }
7328}
7329
7330/// Check if an expression is definitely a string (without variable tracking).
7331/// This is a fallback for contexts where string_vars isn't available.
7332fn is_definitely_string_expr(expr: &Expr) -> bool {
7333    let empty = HashSet::new();
7334    is_definitely_string_expr_with_vars(expr, &empty)
7335}
7336
7337/// Collect leaf operands from a chain of string Add/Concat operations.
7338///
7339/// Walks left-leaning trees of `+` (on strings) and `Concat` operations,
7340/// collecting all leaf expressions into a flat Vec. This enables emitting
7341/// a single `format!("{}{}{}", a, b, c)` instead of nested
7342/// `format!("{}{}", format!("{}{}", a, b), c)`, avoiding O(n^2) allocation.
7343fn collect_string_concat_operands<'a, 'b>(
7344    expr: &'b Expr<'a>,
7345    string_vars: &HashSet<Symbol>,
7346    operands: &mut Vec<&'b Expr<'a>>,
7347) {
7348    match expr {
7349        Expr::BinaryOp { op: BinaryOpKind::Concat, left, right } => {
7350            collect_string_concat_operands(left, string_vars, operands);
7351            collect_string_concat_operands(right, string_vars, operands);
7352        }
7353        Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
7354            let has_string = is_definitely_string_expr_with_vars(left, string_vars)
7355                || is_definitely_string_expr_with_vars(right, string_vars);
7356            if has_string {
7357                collect_string_concat_operands(left, string_vars, operands);
7358                collect_string_concat_operands(right, string_vars, operands);
7359            } else {
7360                operands.push(expr);
7361            }
7362        }
7363        _ => {
7364            operands.push(expr);
7365        }
7366    }
7367}
7368
7369/// Phase 102: Codegen with boxed field support for recursive enums.
7370/// Phase 103: Added registry for polymorphic enum type inference.
7371/// Phase 54+: Added async_functions for proper .await on nested async calls.
7372fn codegen_expr_boxed(
7373    expr: &Expr,
7374    interner: &Interner,
7375    synced_vars: &HashSet<Symbol>,
7376    boxed_fields: &HashSet<(String, String, String)>,  // (EnumName, VariantName, FieldName)
7377    registry: &TypeRegistry,  // Phase 103: For type annotations on polymorphic enums
7378    async_functions: &HashSet<Symbol>,  // Phase 54+: Functions that are async
7379) -> String {
7380    // Delegate to codegen_expr_full with empty context for boxed bindings and string vars
7381    let empty_boxed = HashSet::new();
7382    let empty_strings = HashSet::new();
7383    let empty_types = HashMap::new();
7384    codegen_expr_boxed_internal(expr, interner, synced_vars, boxed_fields, registry, async_functions, &empty_boxed, &empty_strings, &empty_types)
7385}
7386
7387/// Codegen with string variable tracking for proper string concatenation.
7388fn codegen_expr_boxed_with_strings(
7389    expr: &Expr,
7390    interner: &Interner,
7391    synced_vars: &HashSet<Symbol>,
7392    boxed_fields: &HashSet<(String, String, String)>,
7393    registry: &TypeRegistry,
7394    async_functions: &HashSet<Symbol>,
7395    string_vars: &HashSet<Symbol>,
7396) -> String {
7397    let empty_boxed = HashSet::new();
7398    let empty_types = HashMap::new();
7399    codegen_expr_boxed_internal(expr, interner, synced_vars, boxed_fields, registry, async_functions, &empty_boxed, string_vars, &empty_types)
7400}
7401
7402/// Codegen with variable type tracking for direct collection indexing optimization.
7403fn codegen_expr_boxed_with_types(
7404    expr: &Expr,
7405    interner: &Interner,
7406    synced_vars: &HashSet<Symbol>,
7407    boxed_fields: &HashSet<(String, String, String)>,
7408    registry: &TypeRegistry,
7409    async_functions: &HashSet<Symbol>,
7410    string_vars: &HashSet<Symbol>,
7411    variable_types: &HashMap<Symbol, String>,
7412) -> String {
7413    let empty_boxed = HashSet::new();
7414    codegen_expr_boxed_internal(expr, interner, synced_vars, boxed_fields, registry, async_functions, &empty_boxed, string_vars, variable_types)
7415}
7416
7417/// Internal implementation of codegen_expr_boxed that can handle extra context.
7418fn codegen_expr_boxed_internal(
7419    expr: &Expr,
7420    interner: &Interner,
7421    synced_vars: &HashSet<Symbol>,
7422    boxed_fields: &HashSet<(String, String, String)>,
7423    registry: &TypeRegistry,
7424    async_functions: &HashSet<Symbol>,
7425    boxed_bindings: &HashSet<Symbol>,
7426    string_vars: &HashSet<Symbol>,
7427    variable_types: &HashMap<Symbol, String>,
7428) -> String {
7429    // Helper macro for recursive calls with all context
7430    macro_rules! recurse {
7431        ($e:expr) => {
7432            codegen_expr_boxed_internal($e, interner, synced_vars, boxed_fields, registry, async_functions, boxed_bindings, string_vars, variable_types)
7433        };
7434    }
7435
7436    match expr {
7437        Expr::Literal(lit) => codegen_literal(lit, interner),
7438
7439        Expr::Identifier(sym) => {
7440            let name = interner.resolve(*sym).to_string();
7441            // Dereference boxed bindings from enum destructuring
7442            if boxed_bindings.contains(sym) {
7443                format!("(*{})", name)
7444            } else {
7445                name
7446            }
7447        }
7448
7449        Expr::BinaryOp { op, left, right } => {
7450            // Flatten chained string concat/add into a single format! call.
7451            // Turns O(n^2) nested format! into O(n) single-allocation.
7452            let is_string_concat = matches!(op, BinaryOpKind::Concat)
7453                || (matches!(op, BinaryOpKind::Add)
7454                    && (is_definitely_string_expr_with_vars(left, string_vars)
7455                        || is_definitely_string_expr_with_vars(right, string_vars)));
7456
7457            if is_string_concat {
7458                let mut operands = Vec::new();
7459                collect_string_concat_operands(expr, string_vars, &mut operands);
7460                let placeholders: String = operands.iter().map(|_| "{}").collect::<Vec<_>>().join("");
7461                let values: Vec<String> = operands.iter().map(|e| {
7462                    // String literals can be &str inside format!() — no heap allocation needed
7463                    if let Expr::Literal(Literal::Text(sym)) = e {
7464                        format!("\"{}\"", interner.resolve(*sym))
7465                    } else {
7466                        recurse!(e)
7467                    }
7468                }).collect();
7469                return format!("format!(\"{}\", {})", placeholders, values.join(", "));
7470            }
7471
7472            // Optimize HashMap .get() for equality comparisons to avoid cloning
7473            if matches!(op, BinaryOpKind::Eq | BinaryOpKind::NotEq) {
7474                let neg = matches!(op, BinaryOpKind::NotEq);
7475                // Check if left side is a HashMap index
7476                if let Expr::Index { collection, index } = left {
7477                    if let Expr::Identifier(sym) = collection {
7478                        if let Some(t) = variable_types.get(sym) {
7479                            if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") {
7480                                let coll_str = recurse!(collection);
7481                                let key_str = recurse!(index);
7482                                let val_str = recurse!(right);
7483                                let cmp = if neg { "!=" } else { "==" };
7484                                if has_copy_value_type(t) {
7485                                    return format!("({}.get(&({})).copied() {} Some({}))", coll_str, key_str, cmp, val_str);
7486                                } else {
7487                                    return format!("({}.get(&({})) {} Some(&({})))", coll_str, key_str, cmp, val_str);
7488                                }
7489                            }
7490                        }
7491                    }
7492                }
7493                // Check if right side is a HashMap index
7494                if let Expr::Index { collection, index } = right {
7495                    if let Expr::Identifier(sym) = collection {
7496                        if let Some(t) = variable_types.get(sym) {
7497                            if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") {
7498                                let coll_str = recurse!(collection);
7499                                let key_str = recurse!(index);
7500                                let val_str = recurse!(left);
7501                                let cmp = if neg { "!=" } else { "==" };
7502                                if has_copy_value_type(t) {
7503                                    return format!("(Some({}) {} {}.get(&({})).copied())", val_str, cmp, coll_str, key_str);
7504                                } else {
7505                                    return format!("(Some(&({})) {} {}.get(&({})))", val_str, cmp, coll_str, key_str);
7506                                }
7507                            }
7508                        }
7509                    }
7510                }
7511            }
7512
7513            let left_str = recurse!(left);
7514            let right_str = recurse!(right);
7515            let op_str = match op {
7516                BinaryOpKind::Add => "+",
7517                BinaryOpKind::Subtract => "-",
7518                BinaryOpKind::Multiply => "*",
7519                BinaryOpKind::Divide => "/",
7520                BinaryOpKind::Modulo => "%",
7521                BinaryOpKind::Eq => "==",
7522                BinaryOpKind::NotEq => "!=",
7523                BinaryOpKind::Lt => "<",
7524                BinaryOpKind::Gt => ">",
7525                BinaryOpKind::LtEq => "<=",
7526                BinaryOpKind::GtEq => ">=",
7527                BinaryOpKind::And => "&&",
7528                BinaryOpKind::Or => "||",
7529                BinaryOpKind::Concat => unreachable!(), // Handled above
7530            };
7531            format!("({} {} {})", left_str, op_str, right_str)
7532        }
7533
7534        Expr::Call { function, args } => {
7535            let func_name = escape_rust_ident(interner.resolve(*function));
7536            // Recursively codegen args with full context
7537            let args_str: Vec<String> = args.iter()
7538                .map(|a| recurse!(a))
7539                .collect();
7540            // Add .await if this function is async
7541            if async_functions.contains(function) {
7542                format!("{}({}).await", func_name, args_str.join(", "))
7543            } else {
7544                format!("{}({})", func_name, args_str.join(", "))
7545            }
7546        }
7547
7548        Expr::Index { collection, index } => {
7549            let coll_str = recurse!(collection);
7550            let index_str = recurse!(index);
7551            // Direct indexing for known collection types (avoids trait dispatch)
7552            let known_type = if let Expr::Identifier(sym) = collection {
7553                variable_types.get(sym).map(|s| s.as_str())
7554            } else {
7555                None
7556            };
7557            match known_type {
7558                Some(t) if t.starts_with("Vec") => {
7559                    let suffix = if has_copy_element_type(t) { "" } else { ".clone()" };
7560                    format!("{}[({} - 1) as usize]{}", coll_str, index_str, suffix)
7561                }
7562                Some(t) if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") => {
7563                    let suffix = if has_copy_value_type(t) { "" } else { ".clone()" };
7564                    format!("{}[&({})]{}", coll_str, index_str, suffix)
7565                }
7566                _ => {
7567                    // Fallback: polymorphic indexing via trait
7568                    format!("LogosIndex::logos_get(&{}, {})", coll_str, index_str)
7569                }
7570            }
7571        }
7572
7573        Expr::Slice { collection, start, end } => {
7574            let coll_str = recurse!(collection);
7575            let start_str = recurse!(start);
7576            let end_str = recurse!(end);
7577            // Phase 43D: 1-indexed inclusive to 0-indexed exclusive
7578            // "items 1 through 3" → &items[0..3] (elements at indices 0, 1, 2)
7579            format!("&{}[({} - 1) as usize..{} as usize]", coll_str, start_str, end_str)
7580        }
7581
7582        Expr::Copy { expr: inner } => {
7583            let expr_str = recurse!(inner);
7584            // Phase 43D: Explicit owned copy — .to_owned() is universal:
7585            // - &[T] (slices) → Vec<T> via [T]: ToOwned<Owned=Vec<T>>
7586            // - Vec<T>, HashMap<K,V>, HashSet<T> → Self via Clone blanket impl
7587            format!("{}.to_owned()", expr_str)
7588        }
7589
7590        Expr::Give { value } => {
7591            // Ownership transfer: emit value without .clone()
7592            // The move semantics are implicit in Rust - no special syntax needed
7593            recurse!(value)
7594        }
7595
7596        Expr::Length { collection } => {
7597            let coll_str = recurse!(collection);
7598            // Phase 43D: Collection length - cast to i64 for LOGOS integer semantics
7599            format!("({}.len() as i64)", coll_str)
7600        }
7601
7602        Expr::Contains { collection, value } => {
7603            let coll_str = recurse!(collection);
7604            let val_str = recurse!(value);
7605            // Use LogosContains trait for unified contains across List, Set, Map, Text
7606            format!("{}.logos_contains(&{})", coll_str, val_str)
7607        }
7608
7609        Expr::Union { left, right } => {
7610            let left_str = recurse!(left);
7611            let right_str = recurse!(right);
7612            format!("{}.union(&{}).cloned().collect::<std::collections::HashSet<_>>()", left_str, right_str)
7613        }
7614
7615        Expr::Intersection { left, right } => {
7616            let left_str = recurse!(left);
7617            let right_str = recurse!(right);
7618            format!("{}.intersection(&{}).cloned().collect::<std::collections::HashSet<_>>()", left_str, right_str)
7619        }
7620
7621        // Phase 48: Sipping Protocol expressions
7622        Expr::ManifestOf { zone } => {
7623            let zone_str = recurse!(zone);
7624            format!("logicaffeine_system::network::FileSipper::from_zone(&{}).manifest()", zone_str)
7625        }
7626
7627        Expr::ChunkAt { index, zone } => {
7628            let zone_str = recurse!(zone);
7629            let index_str = recurse!(index);
7630            // LOGOS uses 1-indexed, Rust uses 0-indexed
7631            format!("logicaffeine_system::network::FileSipper::from_zone(&{}).get_chunk(({} - 1) as usize)", zone_str, index_str)
7632        }
7633
7634        Expr::List(ref items) => {
7635            let item_strs: Vec<String> = items.iter()
7636                .map(|i| recurse!(i))
7637                .collect();
7638            format!("vec![{}]", item_strs.join(", "))
7639        }
7640
7641        Expr::Tuple(ref items) => {
7642            let item_strs: Vec<String> = items.iter()
7643                .map(|i| format!("Value::from({})", recurse!(i)))
7644                .collect();
7645            // Tuples as Vec<Value> for heterogeneous support
7646            format!("vec![{}]", item_strs.join(", "))
7647        }
7648
7649        Expr::Range { start, end } => {
7650            let start_str = recurse!(start);
7651            let end_str = recurse!(end);
7652            format!("({}..={})", start_str, end_str)
7653        }
7654
7655        Expr::FieldAccess { object, field } => {
7656            let field_name = interner.resolve(*field);
7657
7658            // Phase 52: Check if root object is synced - use .get().await
7659            let root_sym = get_root_identifier(object);
7660            if let Some(sym) = root_sym {
7661                if synced_vars.contains(&sym) {
7662                    let obj_name = interner.resolve(sym);
7663                    return format!("{}.get().await.{}", obj_name, field_name);
7664                }
7665            }
7666
7667            let obj_str = recurse!(object);
7668            format!("{}.{}", obj_str, field_name)
7669        }
7670
7671        Expr::New { type_name, type_args, init_fields } => {
7672            let type_str = interner.resolve(*type_name);
7673            if !init_fields.is_empty() {
7674                // Struct initialization with fields: Point { x: 10, y: 20, ..Default::default() }
7675                // Always add ..Default::default() to handle partial initialization (e.g., CRDT fields)
7676                let fields_str = init_fields.iter()
7677                    .map(|(name, value)| {
7678                        let field_name = interner.resolve(*name);
7679                        let value_str = recurse!(value);
7680                        format!("{}: {}", field_name, value_str)
7681                    })
7682                    .collect::<Vec<_>>()
7683                    .join(", ");
7684                format!("{} {{ {}, ..Default::default() }}", type_str, fields_str)
7685            } else if type_args.is_empty() {
7686                format!("{}::default()", type_str)
7687            } else {
7688                // Phase 34: Turbofish syntax for generic instantiation
7689                // Bug fix: Use codegen_type_expr to support nested types like Seq of (Seq of Int)
7690                let args_str = type_args.iter()
7691                    .map(|t| codegen_type_expr(t, interner))
7692                    .collect::<Vec<_>>()
7693                    .join(", ");
7694                format!("{}::<{}>::default()", type_str, args_str)
7695            }
7696        }
7697
7698        Expr::NewVariant { enum_name, variant, fields } => {
7699            let enum_str = interner.resolve(*enum_name);
7700            let variant_str = interner.resolve(*variant);
7701            if fields.is_empty() {
7702                // Unit variant: Shape::Point
7703                format!("{}::{}", enum_str, variant_str)
7704            } else {
7705                // Phase 103: Count identifier usage to handle cloning for reused values
7706                // We need to clone on all uses except the last one
7707                let mut identifier_counts: HashMap<Symbol, usize> = HashMap::new();
7708                for (_, value) in fields.iter() {
7709                    if let Expr::Identifier(sym) = value {
7710                        *identifier_counts.entry(*sym).or_insert(0) += 1;
7711                    }
7712                }
7713
7714                // Track remaining uses for each identifier
7715                let mut remaining_uses: HashMap<Symbol, usize> = identifier_counts.clone();
7716
7717                // Struct variant: Shape::Circle { radius: 10 }
7718                // Phase 102: Check if any field is recursive and needs Box::new()
7719                let fields_str: Vec<String> = fields.iter()
7720                    .map(|(field_name, value)| {
7721                        let name = interner.resolve(*field_name);
7722
7723                        // Phase 103: Clone identifiers that are used multiple times
7724                        // Clone on all uses except the last one (to allow move on final use)
7725                        let val = if let Expr::Identifier(sym) = value {
7726                            let total = identifier_counts.get(sym).copied().unwrap_or(0);
7727                            let remaining = remaining_uses.get_mut(sym);
7728                            let base_name = if boxed_bindings.contains(sym) {
7729                                format!("(*{})", interner.resolve(*sym))
7730                            } else {
7731                                interner.resolve(*sym).to_string()
7732                            };
7733                            if total > 1 {
7734                                if let Some(r) = remaining {
7735                                    *r -= 1;
7736                                    if *r > 0 {
7737                                        // Not the last use, need to clone
7738                                        format!("{}.clone()", base_name)
7739                                    } else {
7740                                        // Last use, can move
7741                                        base_name
7742                                    }
7743                                } else {
7744                                    base_name
7745                                }
7746                            } else {
7747                                base_name
7748                            }
7749                        } else {
7750                            recurse!(value)
7751                        };
7752
7753                        // Check if this field needs to be boxed (recursive type)
7754                        let key = (enum_str.to_string(), variant_str.to_string(), name.to_string());
7755                        if boxed_fields.contains(&key) {
7756                            format!("{}: Box::new({})", name, val)
7757                        } else {
7758                            format!("{}: {}", name, val)
7759                        }
7760                    })
7761                    .collect();
7762                format!("{}::{} {{ {} }}", enum_str, variant_str, fields_str.join(", "))
7763            }
7764        }
7765
7766        Expr::OptionSome { value } => {
7767            format!("Some({})", recurse!(value))
7768        }
7769
7770        Expr::OptionNone => {
7771            "None".to_string()
7772        }
7773
7774        Expr::Escape { code, .. } => {
7775            let raw_code = interner.resolve(*code);
7776            let mut block = String::from("{\n");
7777            for line in raw_code.lines() {
7778                block.push_str("    ");
7779                block.push_str(line);
7780                block.push('\n');
7781            }
7782            block.push('}');
7783            block
7784        }
7785
7786        Expr::WithCapacity { value, capacity } => {
7787            let cap_str = recurse!(capacity);
7788            match value {
7789                // Empty string → String::with_capacity(cap)
7790                Expr::Literal(Literal::Text(sym)) if interner.resolve(*sym).is_empty() => {
7791                    format!("String::with_capacity(({}) as usize)", cap_str)
7792                }
7793                // Non-empty string → { let mut __s = String::with_capacity(cap); __s.push_str("..."); __s }
7794                Expr::Literal(Literal::Text(sym)) => {
7795                    let text = interner.resolve(*sym);
7796                    format!("{{ let mut __s = String::with_capacity(({}) as usize); __s.push_str(\"{}\"); __s }}", cap_str, text)
7797                }
7798                // Collection Expr::New → Type::with_capacity(cap)
7799                Expr::New { type_name, type_args, .. } => {
7800                    let type_str = interner.resolve(*type_name);
7801                    match type_str {
7802                        "Seq" | "List" | "Vec" => {
7803                            let elem = if !type_args.is_empty() {
7804                                codegen_type_expr(&type_args[0], interner)
7805                            } else { "()".to_string() };
7806                            format!("{{ let __v: Vec<{}> = Vec::with_capacity(({}) as usize); __v }}", elem, cap_str)
7807                        }
7808                        "Map" | "HashMap" => {
7809                            let (k, v) = if type_args.len() >= 2 {
7810                                (codegen_type_expr(&type_args[0], interner),
7811                                 codegen_type_expr(&type_args[1], interner))
7812                            } else { ("String".to_string(), "String".to_string()) };
7813                            format!("{{ let __m: std::collections::HashMap<{}, {}> = std::collections::HashMap::with_capacity(({}) as usize); __m }}", k, v, cap_str)
7814                        }
7815                        "Set" | "HashSet" => {
7816                            let elem = if !type_args.is_empty() {
7817                                codegen_type_expr(&type_args[0], interner)
7818                            } else { "()".to_string() };
7819                            format!("{{ let __s: std::collections::HashSet<{}> = std::collections::HashSet::with_capacity(({}) as usize); __s }}", elem, cap_str)
7820                        }
7821                        _ => recurse!(value) // Unknown type — ignore capacity
7822                    }
7823                }
7824                // Other expressions — ignore capacity hint
7825                _ => recurse!(value)
7826            }
7827        }
7828
7829        Expr::Closure { params, body, .. } => {
7830            use crate::ast::stmt::ClosureBody;
7831            let params_str: Vec<String> = params.iter()
7832                .map(|(name, ty)| {
7833                    let param_name = escape_rust_ident(interner.resolve(*name));
7834                    let param_type = codegen_type_expr(ty, interner);
7835                    format!("{}: {}", param_name, param_type)
7836                })
7837                .collect();
7838
7839            match body {
7840                ClosureBody::Expression(expr) => {
7841                    let body_str = recurse!(expr);
7842                    format!("move |{}| {{ {} }}", params_str.join(", "), body_str)
7843                }
7844                ClosureBody::Block(stmts) => {
7845                    let mut body_str = String::new();
7846                    let mut ctx = RefinementContext::new();
7847                    let empty_mutable = collect_mutable_vars(stmts);
7848                    let empty_lww = HashSet::new();
7849                    let empty_mv = HashSet::new();
7850                    let mut empty_synced = HashSet::new();
7851                    let empty_caps = HashMap::new();
7852                    let empty_pipes = HashSet::new();
7853                    let empty_boxed = HashSet::new();
7854                    let empty_registry = TypeRegistry::new();
7855                    for stmt in stmts.iter() {
7856                        body_str.push_str(&codegen_stmt(
7857                            stmt, interner, 2, &empty_mutable, &mut ctx,
7858                            &empty_lww, &empty_mv, &mut empty_synced, &empty_caps,
7859                            async_functions, &empty_pipes, &empty_boxed, &empty_registry,
7860                        ));
7861                    }
7862                    format!("move |{}| {{\n{}{}}}", params_str.join(", "), body_str, "    ")
7863                }
7864            }
7865        }
7866
7867        Expr::CallExpr { callee, args } => {
7868            let callee_str = recurse!(callee);
7869            let args_str: Vec<String> = args.iter().map(|a| recurse!(a)).collect();
7870            format!("({})({})", callee_str, args_str.join(", "))
7871        }
7872    }
7873}
7874
7875fn codegen_literal(lit: &Literal, interner: &Interner) -> String {
7876    match lit {
7877        Literal::Number(n) => n.to_string(),
7878        Literal::Float(f) => format!("{}f64", f),
7879        // String literals are converted to String for consistent Text type handling
7880        Literal::Text(sym) => format!("String::from(\"{}\")", interner.resolve(*sym)),
7881        Literal::Boolean(b) => b.to_string(),
7882        Literal::Nothing => "()".to_string(),
7883        // Character literals
7884        Literal::Char(c) => {
7885            // Handle escape sequences for special characters
7886            match c {
7887                '\n' => "'\\n'".to_string(),
7888                '\t' => "'\\t'".to_string(),
7889                '\r' => "'\\r'".to_string(),
7890                '\\' => "'\\\\'".to_string(),
7891                '\'' => "'\\''".to_string(),
7892                '\0' => "'\\0'".to_string(),
7893                c => format!("'{}'", c),
7894            }
7895        }
7896        // Temporal literals: Duration stored as nanoseconds (i64)
7897        Literal::Duration(nanos) => format!("std::time::Duration::from_nanos({}u64)", nanos),
7898        // Date stored as days since Unix epoch (i32)
7899        Literal::Date(days) => format!("LogosDate({})", days),
7900        // Moment stored as nanoseconds since Unix epoch (i64)
7901        Literal::Moment(nanos) => format!("LogosMoment({})", nanos),
7902        // Span stored as (months, days) - separate because they're incommensurable
7903        Literal::Span { months, days } => format!("LogosSpan::new({}, {})", months, days),
7904        // Time-of-day stored as nanoseconds from midnight
7905        Literal::Time(nanos) => format!("LogosTime({})", nanos),
7906    }
7907}
7908
7909/// Converts a LogicExpr to a Rust boolean expression for debug_assert!().
7910/// Uses RustFormatter to unify all logic-to-Rust translation.
7911pub fn codegen_assertion(expr: &LogicExpr, interner: &Interner) -> String {
7912    let mut registry = SymbolRegistry::new();
7913    let formatter = RustFormatter;
7914    let mut buf = String::new();
7915
7916    match expr.write_logic(&mut buf, &mut registry, interner, &formatter) {
7917        Ok(_) => buf,
7918        Err(_) => "/* error generating assertion */ false".to_string(),
7919    }
7920}
7921
7922pub fn codegen_term(term: &Term, interner: &Interner) -> String {
7923    match term {
7924        Term::Constant(sym) => interner.resolve(*sym).to_string(),
7925        Term::Variable(sym) => interner.resolve(*sym).to_string(),
7926        Term::Value { kind, .. } => match kind {
7927            NumberKind::Integer(n) => n.to_string(),
7928            NumberKind::Real(f) => f.to_string(),
7929            NumberKind::Symbolic(sym) => interner.resolve(*sym).to_string(),
7930        },
7931        Term::Function(name, args) => {
7932            let args_str: Vec<String> = args.iter()
7933                .map(|a| codegen_term(a, interner))
7934                .collect();
7935            format!("{}({})", interner.resolve(*name), args_str.join(", "))
7936        }
7937        Term::Possessed { possessor, possessed } => {
7938            let poss_str = codegen_term(possessor, interner);
7939            format!("{}.{}", poss_str, interner.resolve(*possessed))
7940        }
7941        Term::Group(members) => {
7942            let members_str: Vec<String> = members.iter()
7943                .map(|m| codegen_term(m, interner))
7944                .collect();
7945            format!("({})", members_str.join(", "))
7946        }
7947        _ => "/* unsupported Term */".to_string(),
7948    }
7949}
7950
7951#[cfg(test)]
7952mod tests {
7953    use super::*;
7954
7955    #[test]
7956    fn test_literal_number() {
7957        let interner = Interner::new();
7958        let synced_vars = HashSet::new();
7959        let expr = Expr::Literal(Literal::Number(42));
7960        assert_eq!(codegen_expr(&expr, &interner, &synced_vars), "42");
7961    }
7962
7963    #[test]
7964    fn test_literal_boolean() {
7965        let interner = Interner::new();
7966        let synced_vars = HashSet::new();
7967        assert_eq!(codegen_expr(&Expr::Literal(Literal::Boolean(true)), &interner, &synced_vars), "true");
7968        assert_eq!(codegen_expr(&Expr::Literal(Literal::Boolean(false)), &interner, &synced_vars), "false");
7969    }
7970
7971    #[test]
7972    fn test_literal_nothing() {
7973        let interner = Interner::new();
7974        let synced_vars = HashSet::new();
7975        assert_eq!(codegen_expr(&Expr::Literal(Literal::Nothing), &interner, &synced_vars), "()");
7976    }
7977}