Skip to main content

logicaffeine_compile/codegen/
expr.rs

1use std::collections::{HashMap, HashSet};
2
3use crate::analysis::registry::TypeRegistry;
4use crate::analysis::types::RustNames;
5use crate::ast::logic::{LogicExpr, NumberKind, Term};
6use crate::ast::stmt::{BinaryOpKind, Expr, Literal, Stmt, TypeExpr};
7use crate::formatter::RustFormatter;
8use crate::intern::{Interner, Symbol};
9use crate::registry::SymbolRegistry;
10
11use super::context::RefinementContext;
12use super::detection::{collect_mutable_vars, expr_debug_prefix, parse_aos_tag};
13use super::i64_map::is_logos_map_type;
14use super::types::{codegen_type_expr, infer_logos_type, infer_numeric_type};
15use super::{
16    codegen_stmt, get_root_identifier, has_copy_element_type, has_copy_value_type, is_copy_type,
17};
18
19use std::sync::LazyLock;
20
21// Stable empty contexts so a sparsely-populated [`ExprCtx`] can borrow `'static`
22// placeholders instead of stack temporaries at every call site.
23static EMPTY_SYMS: LazyLock<HashSet<Symbol>> = LazyLock::new(HashSet::new);
24static EMPTY_TYPES: LazyLock<HashMap<Symbol, String>> = LazyLock::new(HashMap::new);
25static EMPTY_BOXED_FIELDS: LazyLock<HashSet<(String, String, String)>> =
26    LazyLock::new(HashSet::new);
27static EMPTY_REGISTRY: LazyLock<TypeRegistry> = LazyLock::new(TypeRegistry::new);
28
29/// All borrowed context an expression lowering threads through its recursion.
30///
31/// This bundles what were eight positional parameters passed by hand through
32/// every recursive `codegen_expr` call. It is `Copy` (every field is a shared
33/// reference), so recursion passes `ctx` unchanged and a sub-scope that needs to
34/// vary one field uses struct-update: `ExprCtx { boxed_bindings: &b, ..*ctx }`.
35/// New cross-cutting context becomes one field here instead of a parameter on
36/// hundreds of call sites.
37#[derive(Clone, Copy)]
38pub(crate) struct ExprCtx<'a> {
39    pub interner: &'a Interner,
40    pub synced_vars: &'a HashSet<Symbol>,
41    pub boxed_fields: &'a HashSet<(String, String, String)>,
42    pub registry: &'a TypeRegistry,
43    pub async_functions: &'a HashSet<Symbol>,
44    pub boxed_bindings: &'a HashSet<Symbol>,
45    pub string_vars: &'a HashSet<Symbol>,
46    pub variable_types: &'a HashMap<Symbol, String>,
47    /// Divisor symbol → precomputed `LogosDivU64` helper variable name, for the
48    /// loop-invariant libdivide rewrite of `% n` / `/ n` (empty when inactive).
49    pub fast_div: &'a HashMap<Symbol, String>,
50    /// Bounds-elision oracle: when present, an `item E of arr` read the oracle
51    /// proves in range lowers to `get_unchecked` (no bounds branch) instead of
52    /// checked indexing. `None` ⟹ every access keeps its check. Threaded only
53    /// from the statement arms that lower indexed reads (`Let`/`Set`/`If`/
54    /// `SetIndex` values), where the `RefinementContext`'s oracle is in hand.
55    pub oracle: Option<&'a crate::optimize::OracleFacts>,
56    /// Overflow ruling v2 (stage 2): when true, an unproven Int op may emit the
57    /// bare checked-exact helper (result `LogosInt` — the promoting boxed
58    /// path); when false the result NARROWS back to i64 with a loud canonical
59    /// error if it no longer fits (never a silent wrap). True under `Show` and
60    /// down the operand chains of exactable ops; false at i64 storage sinks.
61    pub int_exact_tolerant: bool,
62    /// True while codegen'ing an INDEX subexpression (`arr[<here>]`). Index
63    /// arithmetic is a `usize` computation the interpreter also requires to fit
64    /// i64 (a promoted BigInt can't index) — so it stays raw, never exact-wrapped.
65    pub int_index_context: bool,
66    /// True while codegen'ing the argument of a `wordN(...)` truncation. The
67    /// result keeps only the low N bits, so Int add/sub/mul lower to raw i64
68    /// `wrapping_*` (identical low bits to the checked-exact helper, matching the
69    /// crate's wrapping word semantics) instead of `logos_*_exact`. Propagates
70    /// through the additive chain but is RESET at a call boundary (a call's
71    /// argument is a full-width value, not truncated).
72    pub int_wrapping: bool,
73}
74
75impl<'a> ExprCtx<'a> {
76    /// The minimal context: just the interner and synced vars, every richer
77    /// field empty. Richer wrappers layer their fields on with struct-update.
78    pub(crate) fn bare(interner: &'a Interner, synced_vars: &'a HashSet<Symbol>) -> Self {
79        ExprCtx {
80            interner,
81            synced_vars,
82            boxed_fields: &EMPTY_BOXED_FIELDS,
83            registry: &EMPTY_REGISTRY,
84            async_functions: &EMPTY_SYMS,
85            boxed_bindings: &EMPTY_SYMS,
86            string_vars: &EMPTY_SYMS,
87            variable_types: &EMPTY_TYPES,
88            fast_div: &EMPTY_TYPES,
89            oracle: None,
90            int_exact_tolerant: false,
91            int_index_context: false,
92            int_wrapping: false,
93        }
94    }
95}
96
97/// True when the bounds-elision oracle proves `index` in range for `collection`,
98/// so the access can lower to `get_unchecked`. The `LOGOS_ORACLE_UNCHECKED=0`
99/// kill switch forces the checked form (A/B against the `assert_unchecked` hint
100/// path). Soundness rests on the kernel-LIA-certified proof plus the caller's
101/// entry precondition guard; a `debug_assert!` (emitted by the statement-level
102/// hint pass) traps an unsound proof in debug builds.
103/// A compile-time Int op on two literals (overflow ruling v2, stage 2).
104thread_local! {
105    /// The whole-program set of `Int`-returning functions whose value can exceed i64, so their
106    /// return type is the promoting `LogosInt` (`bigint_promote::bigint_returning_fns`). Set once
107    /// per program at codegen entry by [`set_bigint_returning_fns`]; read by `mentions_bigint_var`
108    /// so an INLINE call to such a function (e.g. `mlkemBit(a) + mlkemBit(b)`) is recognised as a
109    /// promoted operand and routes through the exact helper. A bare `LogosInt + LogosInt` has no
110    /// operator impl and would not compile — this closes the call-operand hole the variable-only
111    /// detector left open. Codegen is single-threaded per program (nextest gives each test its own
112    /// thread), and each program overwrites the set, so no state leaks across programs or tests.
113    static BIGINT_RETURNING_FNS: std::cell::RefCell<HashSet<Symbol>> =
114        std::cell::RefCell::new(HashSet::new());
115}
116
117/// Record the program's bignum-returning function set for the current codegen (see
118/// [`BIGINT_RETURNING_FNS`]). Called once at the top of `generate_rust_code`.
119pub(crate) fn set_bigint_returning_fns(fns: &HashSet<Symbol>) {
120    BIGINT_RETURNING_FNS.with(|c| *c.borrow_mut() = fns.clone());
121}
122
123fn is_bigint_returning_call(function: Symbol) -> bool {
124    BIGINT_RETURNING_FNS.with(|c| c.borrow().contains(&function))
125}
126
127/// Does `e` PRODUCE a value stored as the overflow-promoting `LogosInt`? — a variable typed with
128/// the `|__bigint` sentinel, or an inline call to a bignum-returning function. Such an operand
129/// forces the exact-helper path so the result stays `LogosInt` rather than a raw `i64` operator
130/// (or `as u64` cast) that `LogosInt` has no impl for.
131fn mentions_bigint_var(e: &Expr, variable_types: &HashMap<Symbol, String>) -> bool {
132    match e {
133        Expr::Identifier(s) => variable_types.get(s).map_or(false, |t| t.contains("__bigint")),
134        Expr::Call { function, .. } => is_bigint_returning_call(*function),
135        Expr::BinaryOp { left, right, .. } => {
136            mentions_bigint_var(left, variable_types) || mentions_bigint_var(right, variable_types)
137        }
138        Expr::Not { operand } => mentions_bigint_var(operand, variable_types),
139        _ => false,
140    }
141}
142
143enum ConstExact {
144    /// The exact result fits i64 — emit today's raw text, byte-identical.
145    InRange,
146    /// The exact result escapes i64 — the promoted `LogosInt` literal.
147    Promoted(String),
148}
149
150/// Evaluate `a op b` exactly at compile time (i128 covers any single i64 op).
151/// `None` = not decidable here (a zero divisor falls to the runtime helper,
152/// which raises the canonical catchable error).
153fn const_exact_int(op: BinaryOpKind, a: i64, b: i64) -> Option<ConstExact> {
154    let exact: i128 = match op {
155        BinaryOpKind::Add => a as i128 + b as i128,
156        BinaryOpKind::Subtract => a as i128 - b as i128,
157        BinaryOpKind::Multiply => a as i128 * b as i128,
158        BinaryOpKind::Divide => {
159            if b == 0 {
160                return None;
161            }
162            a as i128 / b as i128
163        }
164        BinaryOpKind::Modulo => {
165            if b == 0 {
166                return None;
167            }
168            a as i128 % b as i128
169        }
170        _ => return None,
171    };
172    Some(if i64::try_from(exact).is_ok() {
173        ConstExact::InRange
174    } else {
175        ConstExact::Promoted(format!("LogosInt::from_literal(\"{}\")", exact))
176    })
177}
178
179/// True when the range-analysis Oracle bounds BOTH operands such that the
180/// exact result provably fits i64 — the license for raw (unchecked) i64
181/// emission. Add/Sub are monotone in each operand; Mul takes the corner
182/// products. Div/Mod never qualify here (their rims are divisor-shaped,
183/// not range-shaped) — they go through the checked helper when non-const.
184fn oracle_proves_int_op_in_range(
185    ecx: &ExprCtx,
186    whole: &Expr,
187    op: BinaryOpKind,
188    left: &Expr,
189    right: &Expr,
190) -> bool {
191    // A constructing pass (e.g. `try_defer_modulus`) may have PROVEN this very
192    // node in-range when it built it — its version guard is the proof the
193    // interval fixpoint cannot re-derive (accumulators widen to ±∞ by design).
194    if crate::optimize::expr_proven_raw_int_op(whole) {
195        return true;
196    }
197    let Some(o) = ecx.oracle else { return false };
198    // If the oracle bounds the WHOLE expression to an i64 range (the affine
199    // index proof `prev[w - wi]` gives `w - wi ∈ [0, len)`), it cannot
200    // overflow — raw, no promotion. This keeps proven index arithmetic clean.
201    if let Some((lo, hi)) = o.expr_int_range(whole) {
202        let _ = (lo, hi); // in range by construction (i64 bounds)
203        return true;
204    }
205    let (Some((la, lb)), Some((ra, rb))) = (o.expr_int_range(left), o.expr_int_range(right))
206    else {
207        return false;
208    };
209    let (la, lb, ra, rb) = (la as i128, lb as i128, ra as i128, rb as i128);
210    let (lo, hi) = match op {
211        BinaryOpKind::Add => (la + ra, lb + rb),
212        BinaryOpKind::Subtract => (la - rb, lb - ra),
213        BinaryOpKind::Multiply => {
214            let c = [la * ra, la * rb, lb * ra, lb * rb];
215            (*c.iter().min().unwrap(), *c.iter().max().unwrap())
216        }
217        _ => return false,
218    };
219    lo >= i64::MIN as i128 && hi <= i64::MAX as i128
220}
221
222/// Is `e` an Int-exactable arithmetic interior node — the shape the tolerant
223/// `LogosInt` chain would own?
224fn is_exact_arith_node(e: &Expr) -> bool {
225    matches!(
226        e,
227        Expr::BinaryOp {
228            op: BinaryOpKind::Add
229                | BinaryOpKind::Subtract
230                | BinaryOpKind::Multiply
231                | BinaryOpKind::Divide
232                | BinaryOpKind::Modulo,
233            ..
234        }
235    )
236}
237
238/// Worst-case MAGNITUDE of an exact Int chain — the i128-lowering gate. A
239/// literal costs its own magnitude, every other PROVEN-i64 leaf costs the
240/// full `2^63` (covers `i64::MIN`); add/sub sum magnitudes, mul multiplies
241/// them, div keeps the dividend's, rem is bounded by both. Exact tracking
242/// (not bit-widths, which compound over-estimates: `(i·n + j) + 1` is
243/// `2^126 + 2^63 + 1`, comfortably inside i128, where width arithmetic
244/// rounds every step up). `None` = some node is not provably an i64-valued
245/// Int-chain member, or the bound escapes u128 — only the promoting
246/// `LogosInt` chain is exact there.
247fn exact_chain_max_magnitude(e: &Expr, ecx: &ExprCtx) -> Option<u128> {
248    if mentions_bigint_var(e, ecx.variable_types) {
249        return None;
250    }
251    match e {
252        Expr::Literal(Literal::Number(n)) => Some(n.unsigned_abs() as u128),
253        Expr::BinaryOp { op, left, right } if is_exact_arith_node(e) => {
254            let l = exact_chain_max_magnitude(left, ecx)?;
255            let r = exact_chain_max_magnitude(right, ecx)?;
256            match op {
257                BinaryOpKind::Add | BinaryOpKind::Subtract => l.checked_add(r),
258                BinaryOpKind::Multiply => l.checked_mul(r),
259                BinaryOpKind::Divide => Some(l),
260                BinaryOpKind::Modulo => Some(r.min(l)),
261                _ => unreachable!("is_exact_arith_node covers exactly these five"),
262            }
263        }
264        _ => (infer_numeric_type(e, ecx.interner, ecx.variable_types) == "i64")
265            .then_some(1u128 << 63),
266    }
267}
268
269/// Emit the i128 form of a width-bounded exact chain: leaves cast to i128,
270/// interiors native (`/`/`%` keep the canonical zero-divisor error via the
271/// guarded helpers unless the divisor is a nonzero literal).
272fn emit_exact_chain_i128(e: &Expr, ecx: &ExprCtx) -> String {
273    match e {
274        Expr::Literal(Literal::Number(n)) => format!("{}i128", n),
275        Expr::BinaryOp { op, left, right } if is_exact_arith_node(e) => {
276            let l = emit_exact_chain_i128(left, ecx);
277            let r = emit_exact_chain_i128(right, ecx);
278            let nonzero_lit = matches!(&**right, Expr::Literal(Literal::Number(d)) if *d != 0);
279            match op {
280                BinaryOpKind::Add => format!("({} + {})", l, r),
281                BinaryOpKind::Subtract => format!("({} - {})", l, r),
282                BinaryOpKind::Multiply => format!("({} * {})", l, r),
283                BinaryOpKind::Divide if nonzero_lit => format!("({} / {})", l, r),
284                BinaryOpKind::Divide => format!("logos_div_i128({}, {})", l, r),
285                BinaryOpKind::Modulo if nonzero_lit => format!("({} % {})", l, r),
286                BinaryOpKind::Modulo => format!("logos_rem_i128({}, {})", l, r),
287                _ => unreachable!("is_exact_arith_node covers exactly these five"),
288            }
289        }
290        _ => {
291            let plain = ExprCtx { int_exact_tolerant: false, ..*ecx };
292            format!("(({}) as i128)", codegen_expr_ctx(e, &plain))
293        }
294    }
295}
296
297/// The register-shaped lowerings for a root-NARROWED exact Int op. Both
298/// tiers preserve the interpreter's semantics bit for bit — same values,
299/// same canonical error text:
300///
301///  - **depth-1** (no exact-chain interior): one fused checked machine op
302///    (`logos_add_i64` …), overflow to the cold canonical panic;
303///  - **width-bounded chain**: native i128 with a single root narrow — every
304///    intermediate is exact by construction, so an oversized intermediate
305///    that returns to range still succeeds, exactly like the `LogosInt`
306///    chain it replaces.
307///
308/// `None`: the chain's worst case may exceed 127 bits — the promoting
309/// `LogosInt` chain is the only exact lowering.
310fn try_fuse_narrowed_int_op(
311    op: BinaryOpKind,
312    left: &Expr,
313    right: &Expr,
314    ecx: &ExprCtx,
315) -> Option<String> {
316    let plain = ExprCtx { int_exact_tolerant: false, ..*ecx };
317    if !is_exact_arith_node(left) && !is_exact_arith_node(right) {
318        let helper = match op {
319            BinaryOpKind::Add => "logos_add_i64",
320            BinaryOpKind::Subtract => "logos_sub_i64",
321            BinaryOpKind::Multiply => "logos_mul_i64",
322            BinaryOpKind::Divide => "logos_div_i64",
323            BinaryOpKind::Modulo => "logos_rem_i64",
324            _ => return None,
325        };
326        let l = codegen_expr_ctx(left, &plain);
327        let r = codegen_expr_ctx(right, &plain);
328        return Some(format!("{}({}, {})", helper, l, r));
329    }
330    None
331}
332
333/// Tier 3 — the width-bounded i128 chain (see [`try_fuse_narrowed_int_op`]
334/// for tier 1, [`try_guarded_dual_chain`] for the preferred tier 2 on deeper
335/// chains: raw i64 behind one predictable leaf guard beats branchless i128
336/// wherever it applies, so this runs LAST).
337fn try_i128_chain(
338    op: BinaryOpKind,
339    left: &Expr,
340    right: &Expr,
341    ecx: &ExprCtx,
342) -> Option<String> {
343    let l = exact_chain_max_magnitude(left, ecx)?;
344    let r = exact_chain_max_magnitude(right, ecx)?;
345    let root_magnitude = match op {
346        BinaryOpKind::Add | BinaryOpKind::Subtract => l.checked_add(r)?,
347        BinaryOpKind::Multiply => l.checked_mul(r)?,
348        BinaryOpKind::Divide => l,
349        BinaryOpKind::Modulo => r.min(l),
350        _ => return None,
351    };
352    if root_magnitude > (i128::MAX as u128) {
353        return None;
354    }
355    let le = emit_exact_chain_i128(left, ecx);
356    let re = emit_exact_chain_i128(right, ecx);
357    let nonzero_lit = matches!(right, Expr::Literal(Literal::Number(d)) if *d != 0);
358    let chain = match op {
359        BinaryOpKind::Add => format!("({} + {})", le, re),
360        BinaryOpKind::Subtract => format!("({} - {})", le, re),
361        BinaryOpKind::Multiply => format!("({} * {})", le, re),
362        BinaryOpKind::Divide if nonzero_lit => format!("({} / {})", le, re),
363        BinaryOpKind::Divide => format!("logos_div_i128({}, {})", le, re),
364        BinaryOpKind::Modulo if nonzero_lit => format!("({} % {})", le, re),
365        BinaryOpKind::Modulo => format!("logos_rem_i128({}, {})", le, re),
366        _ => unreachable!("root op matched above"),
367    };
368    Some(format!("logos_narrow_i128({})", chain))
369}
370
371/// Chain magnitude with every non-literal leaf capped at `bound` — the
372/// solver's evaluation function for the guarded-dual tier.
373fn chain_magnitude_with_leaf_bound(e: &Expr, bound: u128) -> Option<u128> {
374    match e {
375        Expr::Literal(Literal::Number(n)) => Some(n.unsigned_abs() as u128),
376        Expr::BinaryOp { op, left, right } if is_exact_arith_node(e) => {
377            let l = chain_magnitude_with_leaf_bound(left, bound)?;
378            let r = chain_magnitude_with_leaf_bound(right, bound)?;
379            match op {
380                BinaryOpKind::Add | BinaryOpKind::Subtract => l.checked_add(r),
381                BinaryOpKind::Multiply => l.checked_mul(r),
382                BinaryOpKind::Divide => Some(l),
383                BinaryOpKind::Modulo => Some(r.min(l)),
384                _ => unreachable!("is_exact_arith_node covers exactly these five"),
385            }
386        }
387        _ => Some(bound),
388    }
389}
390
391/// Emit a chain over PRE-BOUND leaf names. `raw` picks plain i64 ops (the
392/// guarded fast branch — proven in range by the leaf-bound guard); otherwise
393/// the promoting `LogosInt` helpers (the exact slow branch). `/`/`%` keep
394/// their canonical zero-divisor errors in both forms.
395fn emit_chain_with_bound_leaves(
396    e: &Expr,
397    names: &HashMap<usize, String>,
398    raw: bool,
399) -> String {
400    match e {
401        Expr::Literal(Literal::Number(n)) => format!("{}i64", n),
402        Expr::BinaryOp { op, left, right } if is_exact_arith_node(e) => {
403            let l = emit_chain_with_bound_leaves(left, names, raw);
404            let r = emit_chain_with_bound_leaves(right, names, raw);
405            let nonzero_lit = matches!(&**right, Expr::Literal(Literal::Number(d)) if *d != 0);
406            if raw {
407                match op {
408                    BinaryOpKind::Add => format!("({} + {})", l, r),
409                    BinaryOpKind::Subtract => format!("({} - {})", l, r),
410                    BinaryOpKind::Multiply => format!("({} * {})", l, r),
411                    BinaryOpKind::Divide if nonzero_lit => format!("({} / {})", l, r),
412                    BinaryOpKind::Divide => format!("logos_div_i64({}, {})", l, r),
413                    BinaryOpKind::Modulo if nonzero_lit => format!("({} % {})", l, r),
414                    BinaryOpKind::Modulo => format!("logos_rem_i64({}, {})", l, r),
415                    _ => unreachable!("is_exact_arith_node covers exactly these five"),
416                }
417            } else {
418                let helper = match op {
419                    BinaryOpKind::Add => "logos_add_exact",
420                    BinaryOpKind::Subtract => "logos_sub_exact",
421                    BinaryOpKind::Multiply => "logos_mul_exact",
422                    BinaryOpKind::Divide => "logos_div_exact",
423                    BinaryOpKind::Modulo => "logos_rem_exact",
424                    _ => unreachable!("is_exact_arith_node covers exactly these five"),
425                };
426                format!("{}({}, {})", helper, l, r)
427            }
428        }
429        Expr::Identifier(s) => names[&(usize::MAX - s.index())].clone(),
430        _ => names[&(e as *const Expr as usize)].clone(),
431    }
432}
433
434/// Tier 3.5 — the GUARDED DUAL for a narrowed chain the i128 gate refused:
435/// bind every leaf once (evaluation order preserved), test them against the
436/// largest bound `B` under which the whole chain provably fits i64, and run
437/// the RAW i64 chain under the guard — the exact `LogosInt` chain otherwise.
438/// Value- and error-identical to the exact chain (the raw branch is proven
439/// overflow-free; the slow branch IS the exact chain); what it buys is a
440/// register-shaped, vectorizable hot path behind one predictable branch.
441fn try_guarded_dual_chain<'e>(
442    op: BinaryOpKind,
443    left: &'e Expr<'e>,
444    right: &'e Expr<'e>,
445    whole_ecx: &ExprCtx,
446) -> Option<String> {
447    fn walk<'e>(
448        e: &'e Expr<'e>,
449        ecx: &ExprCtx,
450        addrs: &mut Vec<usize>,
451        exprs: &mut Vec<&'e Expr<'e>>,
452        seen: &mut std::collections::HashSet<usize>,
453    ) -> Option<()> {
454        if mentions_bigint_var(e, ecx.variable_types) {
455            return None;
456        }
457        match e {
458            Expr::Literal(Literal::Number(_)) => Some(()),
459            Expr::BinaryOp { left, right, .. } if is_exact_arith_node(e) => {
460                walk(left, ecx, addrs, exprs, seen)?;
461                walk(right, ecx, addrs, exprs, seen)
462            }
463            _ => {
464                if infer_numeric_type(e, ecx.interner, ecx.variable_types) != "i64" {
465                    return None;
466                }
467                // A bare identifier is pure — no binding needed, and every
468                // occurrence shares one guard term (dedupe by symbol via the
469                // seen-key). Anything else binds once per node.
470                let addr = match e {
471                    Expr::Identifier(s) => usize::MAX - s.index(),
472                    _ => e as *const Expr as usize,
473                };
474                if seen.insert(addr) {
475                    addrs.push(addr);
476                    exprs.push(e);
477                }
478                Some(())
479            }
480        }
481    }
482    // Bound the emission: tiny chains fused earlier; huge ones would bloat
483    // the guard past its value.
484    let mut leaves: Vec<usize> = Vec::new();
485    let mut seen = std::collections::HashSet::new();
486    let mut leaf_exprs: Vec<&Expr> = Vec::new();
487    walk(left, whole_ecx, &mut leaves, &mut leaf_exprs, &mut seen)?;
488    walk(right, whole_ecx, &mut leaves, &mut leaf_exprs, &mut seen)?;
489    if leaves.is_empty() || leaves.len() > 6 {
490        return None;
491    }
492    // Solve for the largest power-of-two leaf bound that keeps the chain in
493    // i64 (descending scan; the chain magnitude is monotone in the bound).
494    let root_mag = |b: u128| -> Option<u128> {
495        let l = chain_magnitude_with_leaf_bound(left, b)?;
496        let r = chain_magnitude_with_leaf_bound(right, b)?;
497        match op {
498            BinaryOpKind::Add | BinaryOpKind::Subtract => l.checked_add(r),
499            BinaryOpKind::Multiply => l.checked_mul(r),
500            BinaryOpKind::Divide => Some(l),
501            BinaryOpKind::Modulo => Some(r.min(l)),
502            _ => None,
503        }
504    };
505    let mut bound: u128 = 0;
506    for exp in (16..=62).rev() {
507        let b = 1u128 << exp;
508        if root_mag(b).is_some_and(|m| m <= i64::MAX as u128) {
509            bound = b;
510            break;
511        }
512    }
513    if bound == 0 {
514        return None;
515    }
516    let plain = ExprCtx { int_exact_tolerant: false, ..*whole_ecx };
517    let mut names: HashMap<usize, String> = HashMap::new();
518    let mut binds = String::new();
519    for (k, (addr, le)) in leaves.iter().zip(leaf_exprs.iter()).enumerate() {
520        // A bare identifier needs no binding — its emitted name is pure and
521        // the guard/chain reference it directly (one guard term per SYMBOL).
522        if matches!(le, Expr::Identifier(_)) {
523            names.insert(*addr, codegen_expr_ctx(le, &plain));
524            continue;
525        }
526        let name = format!("__chain_l{}", k);
527        binds.push_str(&format!("let {} = {}; ", name, codegen_expr_ctx(le, &plain)));
528        names.insert(*addr, name);
529    }
530    // Guard terms in LEAF ORDER (the deduped evaluation order) — HashMap
531    // iteration would make the emitted text nondeterministic across compiles.
532    // A leaf the oracle proves non-negative needs only the UPPER bound: its
533    // `>= -bound` term is vacuous, so we drop it. This halves the per-iteration
534    // guard on hot multiply chains whose operand is an inductive `>= 0` (e.g.
535    // collatz's `3*k+1`, where `k`'s loop range fixes to `[0, ∞)`).
536    let guard = leaves
537        .iter()
538        .zip(leaf_exprs.iter())
539        .map(|(addr, le)| {
540            let n = &names[addr];
541            let b = bound as i64;
542            let nonneg = whole_ecx
543                .oracle
544                .and_then(|o| o.expr_int_range(le))
545                .is_some_and(|(lo, _)| lo >= 0);
546            if nonneg {
547                format!("({n} <= {b}i64)")
548            } else {
549                format!("({n} >= -{b}i64 && {n} <= {b}i64)")
550            }
551        })
552        .collect::<Vec<_>>()
553        .join(" && ");
554    let root = |raw: bool| -> String {
555        let l = emit_chain_with_bound_leaves(left, &names, raw);
556        let r = emit_chain_with_bound_leaves(right, &names, raw);
557        let nonzero_lit = matches!(right, Expr::Literal(Literal::Number(d)) if *d != 0);
558        if raw {
559            match op {
560                BinaryOpKind::Add => format!("({} + {})", l, r),
561                BinaryOpKind::Subtract => format!("({} - {})", l, r),
562                BinaryOpKind::Multiply => format!("({} * {})", l, r),
563                BinaryOpKind::Divide if nonzero_lit => format!("({} / {})", l, r),
564                BinaryOpKind::Divide => format!("logos_div_i64({}, {})", l, r),
565                BinaryOpKind::Modulo if nonzero_lit => format!("({} % {})", l, r),
566                BinaryOpKind::Modulo => format!("logos_rem_i64({}, {})", l, r),
567                _ => unreachable!(),
568            }
569        } else {
570            let helper = match op {
571                BinaryOpKind::Add => "logos_add_exact",
572                BinaryOpKind::Subtract => "logos_sub_exact",
573                BinaryOpKind::Multiply => "logos_mul_exact",
574                BinaryOpKind::Divide => "logos_div_exact",
575                BinaryOpKind::Modulo => "logos_rem_exact",
576                _ => unreachable!(),
577            };
578            format!("{}({}, {}).expect_i64(\"Int\")", helper, l, r)
579        }
580    };
581    Some(format!(
582        "{{ {binds}if {guard} {{ {fast} }} else {{ {slow} }} }}",
583        fast = root(true),
584        slow = root(false),
585    ))
586}
587
588fn oracle_proves_index(ecx: &ExprCtx, collection: &Expr, index: &Expr) -> bool {
589    if !crate::optimize::active_config().is_on(crate::optimization::Opt::Unchecked) {
590        return false;
591    }
592    let proven = ecx.oracle.map_or(false, |o| o.index_provably_in_bounds(collection, index));
593    if proven {
594        crate::optimize::mark_fired(crate::optimization::Opt::Unchecked);
595    }
596    proven
597}
598
599/// [`codegen_expr_with_async`] plus the bounds-elision oracle, so proven indexed
600/// reads in the lowered expression become `get_unchecked`. Used by the `If`-cond
601/// statement arm.
602pub(crate) fn codegen_expr_with_async_oracle<'a>(
603    expr: &Expr,
604    interner: &'a Interner,
605    synced_vars: &'a HashSet<Symbol>,
606    async_functions: &'a HashSet<Symbol>,
607    variable_types: &'a HashMap<Symbol, String>,
608    oracle: Option<&'a crate::optimize::OracleFacts>,
609) -> String {
610    codegen_expr_ctx(
611        expr,
612        &ExprCtx { async_functions, variable_types, oracle, ..ExprCtx::bare(interner, synced_vars) },
613    )
614}
615
616/// [`codegen_expr_boxed_with_types`] plus the bounds-elision oracle, so proven
617/// indexed reads in the lowered expression become `get_unchecked`. Used by the
618/// `Let`/`Set`/`SetIndex` value statement arms.
619#[allow(clippy::too_many_arguments)]
620pub(crate) fn codegen_expr_boxed_with_types_oracle<'a>(
621    expr: &Expr,
622    interner: &'a Interner,
623    synced_vars: &'a HashSet<Symbol>,
624    boxed_fields: &'a HashSet<(String, String, String)>,
625    registry: &'a TypeRegistry,
626    async_functions: &'a HashSet<Symbol>,
627    string_vars: &'a HashSet<Symbol>,
628    variable_types: &'a HashMap<Symbol, String>,
629    fast_div: &'a HashMap<Symbol, String>,
630    oracle: Option<&'a crate::optimize::OracleFacts>,
631) -> String {
632    codegen_expr_ctx(
633        expr,
634        &ExprCtx {
635            boxed_fields,
636            registry,
637            async_functions,
638            string_vars,
639            variable_types,
640            fast_div,
641            oracle,
642            ..ExprCtx::bare(interner, synced_vars)
643        },
644    )
645}
646
647/// Like [`codegen_expr_boxed_with_types_oracle`] but TOLERANT of overflow: the
648/// root exact-arithmetic result stays a `LogosInt` (no `.expect_i64` narrow).
649/// Used for the RHS of a binding whose target is stored as `LogosInt`.
650#[allow(clippy::too_many_arguments)]
651pub(crate) fn codegen_expr_boxed_with_types_oracle_tolerant<'a>(
652    expr: &Expr,
653    interner: &'a Interner,
654    synced_vars: &'a HashSet<Symbol>,
655    boxed_fields: &'a HashSet<(String, String, String)>,
656    registry: &'a TypeRegistry,
657    async_functions: &'a HashSet<Symbol>,
658    string_vars: &'a HashSet<Symbol>,
659    variable_types: &'a HashMap<Symbol, String>,
660    fast_div: &'a HashMap<Symbol, String>,
661    oracle: Option<&'a crate::optimize::OracleFacts>,
662) -> String {
663    codegen_expr_ctx(
664        expr,
665        &ExprCtx {
666            boxed_fields,
667            registry,
668            async_functions,
669            string_vars,
670            variable_types,
671            fast_div,
672            oracle,
673            int_exact_tolerant: true,
674            ..ExprCtx::bare(interner, synced_vars)
675        },
676    )
677}
678
679pub fn codegen_expr(expr: &Expr, interner: &Interner, synced_vars: &HashSet<Symbol>) -> String {
680    codegen_expr_ctx(expr, &ExprCtx::bare(interner, synced_vars))
681}
682
683/// Phase 54+: Codegen expression with async function tracking.
684/// Adds .await to async function calls at the expression level, handling nested calls.
685pub(crate) fn codegen_expr_with_async(
686    expr: &Expr,
687    interner: &Interner,
688    synced_vars: &HashSet<Symbol>,
689    async_functions: &HashSet<Symbol>,
690    variable_types: &HashMap<Symbol, String>,
691) -> String {
692    codegen_expr_ctx(
693        expr,
694        &ExprCtx { async_functions, variable_types, ..ExprCtx::bare(interner, synced_vars) },
695    )
696}
697
698/// Codegen expression with async support, string variable tracking, and the
699/// loop-invariant libdivide map (`fast_div`).
700pub(crate) fn codegen_expr_with_async_and_strings(
701    expr: &Expr,
702    interner: &Interner,
703    synced_vars: &HashSet<Symbol>,
704    async_functions: &HashSet<Symbol>,
705    string_vars: &HashSet<Symbol>,
706    variable_types: &HashMap<Symbol, String>,
707    fast_div: &HashMap<Symbol, String>,
708) -> String {
709    codegen_expr_ctx(
710        expr,
711        &ExprCtx {
712            async_functions,
713            string_vars,
714            variable_types,
715            fast_div,
716            // Every caller is a `Show` sink (plain object or an interpolated
717            // part): a display accepts the promoting `LogosInt`, so overflow
718            // prints the EXACT value instead of narrowing.
719            int_exact_tolerant: true,
720            ..ExprCtx::bare(interner, synced_vars)
721        },
722    )
723}
724
725/// Check if an expression is definitely numeric (safe to use + operator).
726/// This is conservative for Add operations - treats it as string concat only
727/// when clearly dealing with strings (string literals).
728pub(crate) fn is_definitely_numeric_expr(expr: &Expr) -> bool {
729    match expr {
730        Expr::Literal(Literal::Number(_)) => true,
731        Expr::Literal(Literal::Float(_)) => true,
732        Expr::Literal(Literal::Duration(_)) => true,
733        // Identifiers might be strings, but without a string literal nearby,
734        // assume numeric (Rust will catch type errors)
735        Expr::Identifier(_) => true,
736        // Arithmetic operations are numeric
737        Expr::BinaryOp { op: BinaryOpKind::Subtract, .. } => true,
738        Expr::BinaryOp { op: BinaryOpKind::Multiply, .. } => true,
739        Expr::BinaryOp { op: BinaryOpKind::Divide, .. } => true,
740        Expr::BinaryOp { op: BinaryOpKind::Modulo, .. } => true,
741        // Length always returns a number
742        Expr::Length { .. } => true,
743        // Add is numeric if both operands seem numeric
744        Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
745            is_definitely_numeric_expr(left) && is_definitely_numeric_expr(right)
746        }
747        // Function calls - assume numeric (Rust type checker will validate)
748        Expr::Call { .. } => true,
749        // Index expressions - assume numeric
750        Expr::Index { .. } => true,
751        _ => true,
752    }
753}
754
755/// Check if an expression is definitely a string (needs format! for concatenation).
756/// Takes a set of known string variable symbols for identifier lookup.
757pub(crate) fn is_definitely_string_expr_with_vars(expr: &Expr, string_vars: &HashSet<Symbol>) -> bool {
758    match expr {
759        // String literals are definitely strings
760        Expr::Literal(Literal::Text(_)) => true,
761        // Variables known to be strings
762        Expr::Identifier(sym) => string_vars.contains(sym),
763        // Concat always produces strings
764        Expr::BinaryOp { op: BinaryOpKind::Concat, .. } => true,
765        // Add with a string operand produces a string
766        Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
767            is_definitely_string_expr_with_vars(left, string_vars)
768                || is_definitely_string_expr_with_vars(right, string_vars)
769        }
770        // WithCapacity wrapping a string value is a string
771        Expr::WithCapacity { value, .. } => is_definitely_string_expr_with_vars(value, string_vars),
772        // Interpolated strings always produce strings
773        Expr::InterpolatedString(_) => true,
774        _ => false,
775    }
776}
777
778/// Check if an expression is definitely a string (without variable tracking).
779/// This is a fallback for contexts where string_vars isn't available.
780pub(crate) fn is_definitely_string_expr(expr: &Expr) -> bool {
781    let empty = HashSet::new();
782    is_definitely_string_expr_with_vars(expr, &empty)
783}
784
785/// True when the expression is definitely a SEQUENCE — a list literal, a
786/// Seq-typed variable, a slice, a concat/repeat result, or a fill. Drives the
787/// `+` (concat) and `*` (repeat) operator overloads onto the sequence
788/// emissions instead of the numeric ones.
789fn is_definitely_seq_expr(
790    expr: &Expr,
791    variable_types: &HashMap<Symbol, String>,
792    interner: &Interner,
793) -> bool {
794    match expr {
795        Expr::List(_) | Expr::Slice { .. } => true,
796        Expr::Identifier(sym) => variable_types
797            .get(sym)
798            .map(|t| {
799                let t = t.split("|__hl:").next().unwrap_or(t.as_str());
800                t.starts_with("LogosSeq") || t.starts_with("Vec<") || t.starts_with("&[")
801            })
802            .unwrap_or(false),
803        Expr::BinaryOp { op: BinaryOpKind::SeqConcat, .. } => true,
804        Expr::BinaryOp { op: BinaryOpKind::Add | BinaryOpKind::Multiply, left, right } => {
805            is_definitely_seq_expr(left, variable_types, interner)
806                || is_definitely_seq_expr(right, variable_types, interner)
807        }
808        Expr::Call { function, .. } => interner.resolve(*function) == "repeatSeq",
809        Expr::New { type_name, .. } => interner.resolve(*type_name) == "Seq",
810        _ => false,
811    }
812}
813
814/// Collect leaf operands from a chain of string Add/Concat operations.
815///
816/// Walks left-leaning trees of `+` (on strings) and `Concat` operations,
817/// collecting all leaf expressions into a flat Vec. This enables emitting
818/// a single `format!("{}{}{}", a, b, c)` instead of nested
819/// `format!("{}{}", format!("{}{}", a, b), c)`, avoiding O(n^2) allocation.
820pub(crate) fn collect_string_concat_operands<'a, 'b>(
821    expr: &'b Expr<'a>,
822    string_vars: &HashSet<Symbol>,
823    operands: &mut Vec<&'b Expr<'a>>,
824) {
825    match expr {
826        Expr::BinaryOp { op: BinaryOpKind::Concat, left, right } => {
827            collect_string_concat_operands(left, string_vars, operands);
828            collect_string_concat_operands(right, string_vars, operands);
829        }
830        Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
831            let has_string = is_definitely_string_expr_with_vars(left, string_vars)
832                || is_definitely_string_expr_with_vars(right, string_vars);
833            if has_string {
834                collect_string_concat_operands(left, string_vars, operands);
835                collect_string_concat_operands(right, string_vars, operands);
836            } else {
837                operands.push(expr);
838            }
839        }
840        _ => {
841            operands.push(expr);
842        }
843    }
844}
845
846/// Phase 102: Codegen with boxed field support for recursive enums.
847/// Phase 103: Added registry for polymorphic enum type inference.
848/// Phase 54+: Added async_functions for proper .await on nested async calls.
849pub(crate) fn codegen_expr_boxed(
850    expr: &Expr,
851    interner: &Interner,
852    synced_vars: &HashSet<Symbol>,
853    boxed_fields: &HashSet<(String, String, String)>,  // (EnumName, VariantName, FieldName)
854    registry: &TypeRegistry,  // Phase 103: For type annotations on polymorphic enums
855    async_functions: &HashSet<Symbol>,  // Phase 54+: Functions that are async
856) -> String {
857    codegen_expr_ctx(
858        expr,
859        &ExprCtx {
860            boxed_fields,
861            registry,
862            async_functions,
863            ..ExprCtx::bare(interner, synced_vars)
864        },
865    )
866}
867
868/// Codegen with string variable tracking for proper string concatenation.
869pub(crate) fn codegen_expr_boxed_with_strings(
870    expr: &Expr,
871    interner: &Interner,
872    synced_vars: &HashSet<Symbol>,
873    boxed_fields: &HashSet<(String, String, String)>,
874    registry: &TypeRegistry,
875    async_functions: &HashSet<Symbol>,
876    string_vars: &HashSet<Symbol>,
877) -> String {
878    codegen_expr_ctx(
879        expr,
880        &ExprCtx {
881            boxed_fields,
882            registry,
883            async_functions,
884            string_vars,
885            ..ExprCtx::bare(interner, synced_vars)
886        },
887    )
888}
889
890/// Codegen with variable type tracking for direct collection indexing
891/// optimization, plus the loop-invariant libdivide map (`fast_div`) so `% n` /
892/// `/ n` sites can lower to a precomputed magic multiply.
893pub(crate) fn codegen_expr_boxed_with_types(
894    expr: &Expr,
895    interner: &Interner,
896    synced_vars: &HashSet<Symbol>,
897    boxed_fields: &HashSet<(String, String, String)>,
898    registry: &TypeRegistry,
899    async_functions: &HashSet<Symbol>,
900    string_vars: &HashSet<Symbol>,
901    variable_types: &HashMap<Symbol, String>,
902    fast_div: &HashMap<Symbol, String>,
903) -> String {
904    codegen_expr_ctx(
905        expr,
906        &ExprCtx {
907            boxed_fields,
908            registry,
909            async_functions,
910            string_vars,
911            variable_types,
912            fast_div,
913            ..ExprCtx::bare(interner, synced_vars)
914        },
915    )
916}
917
918/// The expression-lowering workhorse. Takes the whole [`ExprCtx`] and unpacks it
919/// into the locals the match arms use, so the body reads exactly as it did when
920/// these were eight positional parameters; recursion just forwards `ctx`.
921/// Parse an `__affine_array:coeff:offset:trip` type tag into `(coeff, offset)`,
922/// the constants of the deleted CSR array's closed form `A[p] = coeff*p+offset`.
923fn affine_array_coeff_offset(ty: Option<&String>) -> Option<(i64, i64)> {
924    let rest = ty?.strip_prefix("__affine_array:")?;
925    let mut parts = rest.splitn(3, ':');
926    let coeff: i64 = parts.next()?.parse().ok()?;
927    let offset: i64 = parts.next()?.parse().ok()?;
928    Some((coeff, offset))
929}
930
931/// The trip-count Rust expression (the value of `length of A`) from the tag.
932fn affine_array_trip(ty: Option<&String>) -> Option<String> {
933    let rest = ty?.strip_prefix("__affine_array:")?;
934    let mut parts = rest.splitn(3, ':');
935    parts.next()?; // coeff
936    parts.next()?; // offset
937    Some(parts.next()?.to_string())
938}
939
940/// Does `expr` evaluate to a `LogosRational` at runtime? True for an `ExactDivide`, for
941/// `+ − ×` over a Rational operand, and for an identifier bound to a `LogosRational`.
942/// Mirrors `resolve_division::Cx::is_rat_expr` at the codegen level so operand coercion
943/// matches the resolve pass that produced the `ExactDivide` in the first place.
944pub(super) fn is_rational_expr(expr: &Expr, variable_types: &HashMap<Symbol, String>) -> bool {
945    match expr {
946        Expr::BinaryOp { op: BinaryOpKind::ExactDivide, .. } => true,
947        Expr::BinaryOp {
948            op: BinaryOpKind::Add | BinaryOpKind::Subtract | BinaryOpKind::Multiply,
949            left,
950            right,
951        } => is_rational_expr(left, variable_types) || is_rational_expr(right, variable_types),
952        Expr::Identifier(sym) => {
953            variable_types.get(sym).map_or(false, |t| t == "LogosRational")
954        }
955        _ => false,
956    }
957}
958
959/// A `LogosRational`-producing Rust string for an operand of exact arithmetic: a value
960/// that is already Rational is used verbatim; an integer operand is wrapped as
961/// `LogosRational::from_i64(..)` so the exact methods type-check.
962fn rational_operand(expr: &Expr, code: &str, variable_types: &HashMap<Symbol, String>) -> String {
963    if is_rational_expr(expr, variable_types) {
964        code.to_string()
965    } else {
966        format!("LogosRational::from_i64(({}) as i64)", code)
967    }
968}
969
970/// Does `expr` evaluate to a `LogosDecimal` at runtime? True for a `decimal(..)` call, for
971/// `+ − ×` over a Decimal operand, and for an identifier bound to a `LogosDecimal`. Mirrors
972/// `is_rational_expr` so operand coercion lines up with the inferred `Decimal` type.
973pub(super) fn is_decimal_expr(
974    expr: &Expr,
975    variable_types: &HashMap<Symbol, String>,
976    interner: &Interner,
977) -> bool {
978    match expr {
979        Expr::Call { function, .. } => interner.resolve(*function) == "decimal",
980        Expr::BinaryOp {
981            op: BinaryOpKind::Add | BinaryOpKind::Subtract | BinaryOpKind::Multiply,
982            left,
983            right,
984        } => {
985            is_decimal_expr(left, variable_types, interner)
986                || is_decimal_expr(right, variable_types, interner)
987        }
988        Expr::Identifier(sym) => variable_types.get(sym).map_or(false, |t| t == "LogosDecimal"),
989        _ => false,
990    }
991}
992
993/// A `LogosDecimal`-producing Rust string for an operand of exact decimal arithmetic: a
994/// value that is already a Decimal is used verbatim; an integer operand is widened with
995/// `LogosDecimal::from_i64(..)` so the exact methods type-check (`price * 3`).
996fn decimal_operand(
997    expr: &Expr,
998    code: &str,
999    variable_types: &HashMap<Symbol, String>,
1000    interner: &Interner,
1001) -> String {
1002    if is_decimal_expr(expr, variable_types, interner) {
1003        code.to_string()
1004    } else {
1005        format!("LogosDecimal::from_i64(({}) as i64)", code)
1006    }
1007}
1008
1009/// Does `expr` evaluate to a `LogosComplex`? True for a `complex(..)` call, for `+ − × ÷`
1010/// over a Complex operand, and for an identifier bound to a `LogosComplex`.
1011pub(super) fn is_complex_expr(
1012    expr: &Expr,
1013    variable_types: &HashMap<Symbol, String>,
1014    interner: &Interner,
1015) -> bool {
1016    match expr {
1017        Expr::Call { function, .. } => interner.resolve(*function) == "complex",
1018        Expr::BinaryOp {
1019            op: BinaryOpKind::Add | BinaryOpKind::Subtract | BinaryOpKind::Multiply | BinaryOpKind::Divide,
1020            left,
1021            right,
1022        } => {
1023            is_complex_expr(left, variable_types, interner)
1024                || is_complex_expr(right, variable_types, interner)
1025        }
1026        Expr::Identifier(sym) => variable_types.get(sym).map_or(false, |t| t == "LogosComplex"),
1027        _ => false,
1028    }
1029}
1030
1031/// A `LogosComplex`-producing operand: a value already Complex is used verbatim; an integer
1032/// is embedded as `LogosComplex::from_i64(..)` (a real `re + 0i`).
1033fn complex_operand(
1034    expr: &Expr,
1035    code: &str,
1036    variable_types: &HashMap<Symbol, String>,
1037    interner: &Interner,
1038) -> String {
1039    if is_complex_expr(expr, variable_types, interner) {
1040        code.to_string()
1041    } else {
1042        format!("LogosComplex::from_i64(({}) as i64)", code)
1043    }
1044}
1045
1046/// Does `expr` evaluate to a `LogosModular`? True for a `modular(..)` call, for `+ − × ÷` over
1047/// two Modular operands, and for an identifier bound to a `LogosModular`. (No auto-lift: a bare
1048/// integer has no modulus, so modular arithmetic fires only when BOTH operands are modular.)
1049pub(super) fn is_modular_expr(
1050    expr: &Expr,
1051    variable_types: &HashMap<Symbol, String>,
1052    interner: &Interner,
1053) -> bool {
1054    match expr {
1055        Expr::Call { function, .. } => interner.resolve(*function) == "modular",
1056        Expr::BinaryOp {
1057            op: BinaryOpKind::Add | BinaryOpKind::Subtract | BinaryOpKind::Multiply | BinaryOpKind::Divide,
1058            left,
1059            right,
1060        } => {
1061            is_modular_expr(left, variable_types, interner)
1062                && is_modular_expr(right, variable_types, interner)
1063        }
1064        Expr::Identifier(sym) => variable_types.get(sym).map_or(false, |t| t == "LogosModular"),
1065        _ => false,
1066    }
1067}
1068
1069/// Does `expr` evaluate to a `LogosQuantity`? True for a `quantity(..)`/`convert(..)` call, an
1070/// identifier bound to a `LogosQuantity`, `+ −` over two quantities, and `× ÷` where AT LEAST one
1071/// operand is a quantity (the other scales it). Mirrors the interpreter's quantity dispatch.
1072pub(super) fn is_quantity_expr(
1073    expr: &Expr,
1074    variable_types: &HashMap<Symbol, String>,
1075    interner: &Interner,
1076) -> bool {
1077    match expr {
1078        Expr::Call { function, .. } => {
1079            matches!(interner.resolve(*function), "quantity" | "convert")
1080        }
1081        Expr::BinaryOp { op: BinaryOpKind::Add | BinaryOpKind::Subtract, left, right } => {
1082            is_quantity_expr(left, variable_types, interner)
1083                && is_quantity_expr(right, variable_types, interner)
1084        }
1085        Expr::BinaryOp { op: BinaryOpKind::Multiply | BinaryOpKind::Divide, left, right } => {
1086            is_quantity_expr(left, variable_types, interner)
1087                || is_quantity_expr(right, variable_types, interner)
1088        }
1089        Expr::Identifier(sym) => variable_types.get(sym).map_or(false, |t| t == "LogosQuantity"),
1090        _ => false,
1091    }
1092}
1093
1094/// Does `expr` evaluate to a `LogosMoney`? True for a `money(..)` call, an identifier bound to a
1095/// `LogosMoney`, `+ −` over two monies, and `× ÷` where AT LEAST one operand is money (the other
1096/// scales it). Mirrors the interpreter's money dispatch.
1097pub(super) fn is_money_expr(
1098    expr: &Expr,
1099    variable_types: &HashMap<Symbol, String>,
1100    interner: &Interner,
1101) -> bool {
1102    match expr {
1103        Expr::Call { function, .. } => interner.resolve(*function) == "money",
1104        Expr::BinaryOp { op: BinaryOpKind::Add | BinaryOpKind::Subtract, left, right } => {
1105            is_money_expr(left, variable_types, interner)
1106                && is_money_expr(right, variable_types, interner)
1107        }
1108        Expr::BinaryOp { op: BinaryOpKind::Multiply | BinaryOpKind::Divide, left, right } => {
1109            is_money_expr(left, variable_types, interner)
1110                || is_money_expr(right, variable_types, interner)
1111        }
1112        Expr::Identifier(sym) => variable_types.get(sym).map_or(false, |t| t == "LogosMoney"),
1113        _ => false,
1114    }
1115}
1116
1117fn codegen_expr_ctx(expr: &Expr, ecx: &ExprCtx) -> String {
1118    // Bind the fields the match arms read directly; `registry` rides along in
1119    // `ecx` and reaches recursion via the `recurse!` macro.
1120    let ExprCtx {
1121        interner,
1122        synced_vars,
1123        boxed_fields,
1124        async_functions,
1125        boxed_bindings,
1126        string_vars,
1127        variable_types,
1128        fast_div,
1129        ..
1130    } = *ecx;
1131    let names = RustNames::new(interner);
1132    // Helper macro for recursive calls with all context
1133    macro_rules! recurse {
1134        ($e:expr) => {
1135            codegen_expr_ctx($e, ecx)
1136        };
1137    }
1138    // Recurse into an INDEX subexpression: arithmetic there is a `usize`
1139    // computation (raw i64, never exact-promoted). The flag propagates through
1140    // nested arithmetic automatically.
1141    macro_rules! irecurse {
1142        ($e:expr) => {
1143            codegen_expr_ctx($e, &ExprCtx { int_index_context: true, ..*ecx })
1144        };
1145    }
1146
1147    match expr {
1148        Expr::Literal(lit) => codegen_literal(lit, interner),
1149
1150        Expr::Identifier(sym) => {
1151            let name = names.ident(*sym);
1152            // Dereference boxed bindings from enum destructuring
1153            let base = if boxed_bindings.contains(sym) {
1154                format!("(*{})", name)
1155            } else {
1156                name
1157            };
1158            // An overflow-promoting `LogosInt` variable: in an INDEX position it must narrow to
1159            // an i64 (`.expect_i64`, then `- 1 as usize` downstream); in any other value position
1160            // it is read by value, so `.clone()` avoids a move (LogosInt is not Copy).
1161            if variable_types.get(sym).map_or(false, |t| t.contains("__bigint")) {
1162                if ecx.int_index_context {
1163                    format!("{}.expect_i64(\"Int\")", base)
1164                } else {
1165                    format!("{}.clone()", base)
1166                }
1167            } else {
1168                base
1169            }
1170        }
1171
1172        Expr::BinaryOp { op, left, right } => {
1173            // O9 libdivide: a loop-invariant POSITIVE divisor lowers to a
1174            // precomputed `LogosDivU64` magic multiply (`helper.rem(x)` /
1175            // `helper.div(x)`) instead of a hardware `div`/`idiv`. `detect_fast_div`
1176            // already proved the divisor immutable and `>= 1` and the dividend
1177            // `>= 0` at every site, so the `i64`→`u64` reinterpretation is
1178            // value-preserving.
1179            // A promoted (`LogosInt`) dividend can hold a value beyond i64, so the `i64`→`u64`
1180            // libdivide reinterpretation is neither valid (no `as` cast on `LogosInt`) nor correct;
1181            // it falls through to the exact helper below.
1182            if matches!(op, BinaryOpKind::Modulo | BinaryOpKind::Divide)
1183                && !mentions_bigint_var(left, variable_types)
1184            {
1185                if let Expr::Identifier(n) = right {
1186                    if let Some(helper) = fast_div.get(n) {
1187                        let dividend = recurse!(left);
1188                        let method = if matches!(op, BinaryOpKind::Modulo) { "rem" } else { "div" };
1189                        return format!("({}.{}(({}) as u64) as i64)", helper, method, dividend);
1190                    }
1191                }
1192            }
1193
1194            // Flatten chained string concat/add into a single format! call.
1195            // Turns O(n^2) nested format! into O(n) single-allocation.
1196            let is_string_concat = matches!(op, BinaryOpKind::Concat)
1197                || (matches!(op, BinaryOpKind::Add)
1198                    && (is_definitely_string_expr_with_vars(left, string_vars)
1199                        || is_definitely_string_expr_with_vars(right, string_vars)));
1200
1201            if is_string_concat {
1202                let mut operands = Vec::new();
1203                collect_string_concat_operands(expr, string_vars, &mut operands);
1204                let placeholders: String = operands.iter().map(|_| "{}").collect::<Vec<_>>().join("");
1205                let values: Vec<String> = operands.iter().map(|e| {
1206                    // String literals can be &str inside format!() — no heap allocation needed
1207                    if let Expr::Literal(Literal::Text(sym)) = e {
1208                        format!("\"{}\"", interner.resolve(*sym))
1209                    } else {
1210                        recurse!(e)
1211                    }
1212                }).collect();
1213                return format!("format!(\"{}\", {})", placeholders, values.join(", "));
1214            }
1215
1216            // `a followed by b` — merge two sequences into one fresh `LogosSeq` (the default repr
1217            // for a `Seq of T`, matching list literals and slices). `.to_vec()` clones from either
1218            // representation (a plain `Vec<T>` or a `LogosSeq<T>`), so the operands are
1219            // representation-agnostic.
1220            if matches!(op, BinaryOpKind::SeqConcat) {
1221                let l = recurse!(left);
1222                let r = recurse!(right);
1223                return format!(
1224                    "LogosSeq::from_vec({{ let mut __sc = ({}).to_vec(); __sc.extend(({}).to_vec()); __sc }})",
1225                    l, r
1226                );
1227            }
1228
1229            // `a is approximately b` — the ONE shared isclose definition
1230            // (both operands promoted to f64; approximation is inherently
1231            // tolerant, so the lossy view is correct here).
1232            if matches!(op, BinaryOpKind::ApproxEq) {
1233                let l = recurse!(left);
1234                let r = recurse!(right);
1235                return format!("logos_approx_eq(({}) as f64, ({}) as f64)", l, r);
1236            }
1237
1238            // `| & ^ -` on SET operands are the set operations (insertion-
1239            // ordered IndexSet methods; `-` reaches here only for Sets — the
1240            // numeric subtract keeps the plain emission below).
1241            if matches!(op, BinaryOpKind::BitAnd | BinaryOpKind::BitOr | BinaryOpKind::BitXor | BinaryOpKind::Subtract) {
1242                let set_typed = |e: &Expr| -> bool {
1243                    matches!(e, Expr::Identifier(sym)
1244                        if variable_types.get(sym).is_some_and(|t| t.starts_with("Set<") || t.starts_with("FxHashSet<")))
1245                        || matches!(e, Expr::Call { function, .. } if names.raw(*function) == "setOf")
1246                };
1247                if set_typed(left) || set_typed(right) {
1248                    let l = recurse!(left);
1249                    let r = recurse!(right);
1250                    let method = match op {
1251                        BinaryOpKind::BitAnd => "intersection",
1252                        BinaryOpKind::BitOr => "union",
1253                        BinaryOpKind::BitXor => "symmetric_difference",
1254                        _ => "difference",
1255                    };
1256                    return format!("({}).{}(&({})).cloned().collect::<Set<_>>()", l, method, r);
1257                }
1258            }
1259
1260            // `xs + ys` on sequences IS `followed by` — the same fresh-merge emission.
1261            if matches!(op, BinaryOpKind::Add)
1262                && (is_definitely_seq_expr(left, variable_types, interner)
1263                    || is_definitely_seq_expr(right, variable_types, interner))
1264            {
1265                let l = recurse!(left);
1266                let r = recurse!(right);
1267                return format!(
1268                    "LogosSeq::from_vec({{ let mut __sc = ({}).to_vec(); __sc.extend(({}).to_vec()); __sc }})",
1269                    l, r
1270                );
1271            }
1272
1273            // `xs * n` / `n * xs` — repeat into a fresh sequence; every slot is
1274            // an independent `fill_clone` of its element (n rows, never n
1275            // aliases of one). n ≤ 0 is empty, matching the tree-walker.
1276            if matches!(op, BinaryOpKind::Multiply) {
1277                let seq_first = is_definitely_seq_expr(left, variable_types, interner);
1278                let seq_second = is_definitely_seq_expr(right, variable_types, interner);
1279                if seq_first || seq_second {
1280                    let (seq_e, n_e) = if seq_first { (left, right) } else { (right, left) };
1281                    let s = recurse!(seq_e);
1282                    let n = recurse!(n_e);
1283                    return format!(
1284                        "{{ let __rp_src = ({}).to_vec(); let __rp_n = (({}) as i64).max(0) as usize; \
1285                         let mut __rp = Vec::with_capacity(__rp_src.len() * __rp_n); \
1286                         for _ in 0..__rp_n {{ __rp.extend(__rp_src.iter().map(|__e| __e.fill_clone())); }} \
1287                         LogosSeq::from_vec(__rp) }}",
1288                        s, n
1289                    );
1290                }
1291            }
1292
1293            // Optimize HashMap .get() for equality comparisons to avoid cloning
1294            if matches!(op, BinaryOpKind::Eq | BinaryOpKind::NotEq) {
1295                let neg = matches!(op, BinaryOpKind::NotEq);
1296                // Check if left side is a HashMap/LogosMap index
1297                if let Expr::Index { collection, index } = left {
1298                    if let Expr::Identifier(sym) = collection {
1299                        if let Some(t) = variable_types.get(sym) {
1300                            if is_logos_map_type(t) {
1301                                let coll_str = recurse!(collection);
1302                                let key_str = irecurse!(index);
1303                                let val_str = recurse!(right);
1304                                let cmp = if neg { "!=" } else { "==" };
1305                                return format!("({}.get(&({})) {} Some({}))", coll_str, key_str, cmp, val_str);
1306                            } else if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") || t.starts_with("rustc_hash::FxHashMap") || t.starts_with("FxHashMap") {
1307                                let coll_str = recurse!(collection);
1308                                let key_str = irecurse!(index);
1309                                let val_str = recurse!(right);
1310                                let cmp = if neg { "!=" } else { "==" };
1311                                if has_copy_value_type(t) {
1312                                    return format!("({}.get(&({})).copied() {} Some({}))", coll_str, key_str, cmp, val_str);
1313                                } else {
1314                                    return format!("({}.get(&({})) {} Some(&({})))", coll_str, key_str, cmp, val_str);
1315                                }
1316                            }
1317                        }
1318                    }
1319                }
1320                // Check if right side is a HashMap/LogosMap index
1321                if let Expr::Index { collection, index } = right {
1322                    if let Expr::Identifier(sym) = collection {
1323                        if let Some(t) = variable_types.get(sym) {
1324                            if is_logos_map_type(t) {
1325                                let coll_str = recurse!(collection);
1326                                let key_str = irecurse!(index);
1327                                let val_str = recurse!(left);
1328                                let cmp = if neg { "!=" } else { "==" };
1329                                return format!("(Some({}) {} {}.get(&({})))", val_str, cmp, coll_str, key_str);
1330                            } else if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") || t.starts_with("rustc_hash::FxHashMap") || t.starts_with("FxHashMap") {
1331                                let coll_str = recurse!(collection);
1332                                let key_str = irecurse!(index);
1333                                let val_str = recurse!(left);
1334                                let cmp = if neg { "!=" } else { "==" };
1335                                if has_copy_value_type(t) {
1336                                    return format!("(Some({}) {} {}.get(&({})).copied())", val_str, cmp, coll_str, key_str);
1337                                } else {
1338                                    return format!("(Some(&({})) {} {}.get(&({})))", val_str, cmp, coll_str, key_str);
1339                                }
1340                            }
1341                        }
1342                    }
1343                }
1344
1345                // Optimize string-index-vs-string-index comparison to use direct
1346                // byte comparison via as_bytes() instead of logos_get_char().
1347                // Byte equality is correct for UTF-8: two characters are equal
1348                // iff their byte representations are equal, and the logos_get_char
1349                // function already uses byte-level indexing for the ASCII fast path.
1350                if let (Expr::Index { collection: left_coll, index: left_idx },
1351                        Expr::Index { collection: right_coll, index: right_idx }) = (left, right) {
1352                    let left_is_string = if let Expr::Identifier(sym) = left_coll {
1353                        string_vars.contains(sym) || variable_types.get(sym).map_or(false, |t| t == "String")
1354                    } else { false };
1355                    let right_is_string = if let Expr::Identifier(sym) = right_coll {
1356                        string_vars.contains(sym) || variable_types.get(sym).map_or(false, |t| t == "String")
1357                    } else { false };
1358                    if left_is_string && right_is_string {
1359                        let cmp = if neg { "!=" } else { "==" };
1360                        let left_coll_str = recurse!(left_coll);
1361                        let right_coll_str = recurse!(right_coll);
1362                        let left_idx_simplified = super::peephole::simplify_1based_index(left_idx, interner, true, variable_types);
1363                        let right_idx_simplified = super::peephole::simplify_1based_index(right_idx, interner, true, variable_types);
1364                        return format!("({}.as_bytes()[{}] {} {}.as_bytes()[{}])",
1365                            left_coll_str, left_idx_simplified, cmp, right_coll_str, right_idx_simplified);
1366                    }
1367                }
1368
1369                // Optimize string-index-vs-single-char-literal comparison to use
1370                // logos_get_char() == 'c' instead of LogosIndex::logos_get() == String::from("c").
1371                // Avoids two heap allocations per comparison in hot loops.
1372                let is_string_index = |expr: &Expr| -> bool {
1373                    if let Expr::Index { collection, .. } = expr {
1374                        if let Expr::Identifier(sym) = collection {
1375                            return string_vars.contains(sym) || variable_types.get(sym).map_or(false, |t| t == "String");
1376                        }
1377                    }
1378                    false
1379                };
1380                let single_char_literal = |expr: &Expr| -> Option<char> {
1381                    if let Expr::Literal(Literal::Text(sym)) = expr {
1382                        let s = interner.resolve(*sym);
1383                        let mut chars = s.chars();
1384                        if let Some(c) = chars.next() {
1385                            if chars.next().is_none() {
1386                                return Some(c);
1387                            }
1388                        }
1389                    }
1390                    None
1391                };
1392
1393                // Left is string index, right is single-char literal
1394                if is_string_index(left) {
1395                    if let Some(ch) = single_char_literal(right) {
1396                        if let Expr::Index { collection, index } = left {
1397                            let coll_str = recurse!(collection);
1398                            let idx_str = irecurse!(index);
1399                            let cmp = if neg { "!=" } else { "==" };
1400                            let ch_escaped = match ch {
1401                                '\'' => "\\'".to_string(),
1402                                '\\' => "\\\\".to_string(),
1403                                '\n' => "\\n".to_string(),
1404                                '\t' => "\\t".to_string(),
1405                                '\r' => "\\r".to_string(),
1406                                _ => ch.to_string(),
1407                            };
1408                            return format!("({}.logos_get_char({}) {} '{}')",
1409                                coll_str, idx_str, cmp, ch_escaped);
1410                        }
1411                    }
1412                }
1413                // Right is string index, left is single-char literal
1414                if is_string_index(right) {
1415                    if let Some(ch) = single_char_literal(left) {
1416                        if let Expr::Index { collection, index } = right {
1417                            let coll_str = recurse!(collection);
1418                            let idx_str = irecurse!(index);
1419                            let cmp = if neg { "!=" } else { "==" };
1420                            let ch_escaped = match ch {
1421                                '\'' => "\\'".to_string(),
1422                                '\\' => "\\\\".to_string(),
1423                                '\n' => "\\n".to_string(),
1424                                '\t' => "\\t".to_string(),
1425                                '\r' => "\\r".to_string(),
1426                                _ => ch.to_string(),
1427                            };
1428                            return format!("('{}' {} {}.logos_get_char({}))",
1429                                ch_escaped, cmp, coll_str, idx_str);
1430                        }
1431                    }
1432                }
1433            }
1434
1435            // OPT-8b: Zero-based counter in comparison.
1436            // When a __zero_based_i64 counter appears as a bare operand in a comparison,
1437            // emit (counter + 1) to compensate for the 0-based range shift.
1438            // E.g., `If i > 3` with 0-based `i` becomes `if (i + 1) > 3`.
1439            if matches!(op, BinaryOpKind::Lt | BinaryOpKind::LtEq | BinaryOpKind::Gt
1440                | BinaryOpKind::GtEq | BinaryOpKind::Eq | BinaryOpKind::NotEq)
1441            {
1442                let left_zb = if let Expr::Identifier(sym) = left {
1443                    variable_types.get(sym).map_or(false, |t| t == "__zero_based_i64")
1444                } else { false };
1445                let right_zb = if let Expr::Identifier(sym) = right {
1446                    variable_types.get(sym).map_or(false, |t| t == "__zero_based_i64")
1447                } else { false };
1448                if left_zb || right_zb {
1449                    let left_str = if left_zb {
1450                        format!("({} + 1)", recurse!(left))
1451                    } else { recurse!(left) };
1452                    let right_str = if right_zb {
1453                        format!("({} + 1)", recurse!(right))
1454                    } else { recurse!(right) };
1455                    let op_str = match op {
1456                        BinaryOpKind::Lt => "<", BinaryOpKind::LtEq => "<=",
1457                        BinaryOpKind::Gt => ">", BinaryOpKind::GtEq => ">=",
1458                        BinaryOpKind::Eq => "==", BinaryOpKind::NotEq => "!=",
1459                        _ => unreachable!(),
1460                    };
1461                    return format!("({} {} {})", left_str, op_str, right_str);
1462                }
1463            }
1464
1465            let left_str = recurse!(left);
1466            let right_str = recurse!(right);
1467
1468            // And/Or are logical: truthiness in, Bool out, short-circuit (Rust's
1469            // `&&`/`||` keep the right operand lazy). Bool×Bool skips the trait call.
1470            if matches!(op, BinaryOpKind::And | BinaryOpKind::Or) {
1471                let op_str = match op {
1472                    BinaryOpKind::And => "&&",
1473                    BinaryOpKind::Or => "||",
1474                    _ => unreachable!(),
1475                };
1476                let bools = matches!(
1477                    infer_logos_type(left, interner, variable_types),
1478                    crate::analysis::types::LogosType::Bool
1479                ) && matches!(
1480                    infer_logos_type(right, interner, variable_types),
1481                    crate::analysis::types::LogosType::Bool
1482                );
1483                return if bools {
1484                    format!("({} {} {})", left_str, op_str, right_str)
1485                } else {
1486                    format!(
1487                        "(logos_truthy(&({})) {} logos_truthy(&({})))",
1488                        left_str, op_str, right_str
1489                    )
1490                };
1491            }
1492
1493            // Dimensioned quantity arithmetic. `+ −` need two quantities (same dimension, checked
1494            // at runtime), `× ÷` combine dimensions (two quantities) or scale a quantity by an
1495            // integer; magnitudes stay exact on the rational tower. Comparison/equality use the
1496            // derived PartialOrd/PartialEq (same-dimension; physical equality).
1497            if matches!(
1498                op,
1499                BinaryOpKind::Add | BinaryOpKind::Subtract | BinaryOpKind::Multiply | BinaryOpKind::Divide
1500                    | BinaryOpKind::Lt | BinaryOpKind::Gt | BinaryOpKind::LtEq | BinaryOpKind::GtEq
1501                    | BinaryOpKind::Eq | BinaryOpKind::NotEq
1502            ) && (is_quantity_expr(left, variable_types, interner)
1503                || is_quantity_expr(right, variable_types, interner))
1504            {
1505                let l_q = is_quantity_expr(left, variable_types, interner);
1506                let r_q = is_quantity_expr(right, variable_types, interner);
1507                let code: Option<std::string::String> = match (op, l_q, r_q) {
1508                    (BinaryOpKind::Add, true, true) => Some(format!("{}.add(&{})", left_str, right_str)),
1509                    (BinaryOpKind::Subtract, true, true) => Some(format!("{}.sub(&{})", left_str, right_str)),
1510                    (BinaryOpKind::Multiply, true, true) => Some(format!("{}.mul(&{})", left_str, right_str)),
1511                    (BinaryOpKind::Divide, true, true) => Some(format!("{}.div_exact(&{})", left_str, right_str)),
1512                    // Scale a quantity by an integer (the common case), commuting for ×.
1513                    (BinaryOpKind::Multiply, true, false) => Some(format!("{}.scale_int({})", left_str, right_str)),
1514                    (BinaryOpKind::Multiply, false, true) => Some(format!("{}.scale_int({})", right_str, left_str)),
1515                    (BinaryOpKind::Divide, true, false) => Some(format!("{}.div_int({})", left_str, right_str)),
1516                    // Comparison / equality over two quantities (PartialOrd / physical PartialEq).
1517                    (BinaryOpKind::Lt, true, true) => Some(format!("({} < {})", left_str, right_str)),
1518                    (BinaryOpKind::Gt, true, true) => Some(format!("({} > {})", left_str, right_str)),
1519                    (BinaryOpKind::LtEq, true, true) => Some(format!("({} <= {})", left_str, right_str)),
1520                    (BinaryOpKind::GtEq, true, true) => Some(format!("({} >= {})", left_str, right_str)),
1521                    (BinaryOpKind::Eq, true, true) => Some(format!("({} == {})", left_str, right_str)),
1522                    (BinaryOpKind::NotEq, true, true) => Some(format!("({} != {})", left_str, right_str)),
1523                    _ => None,
1524                };
1525                if let Some(c) = code {
1526                    return c;
1527                }
1528            }
1529
1530            // Money arithmetic. `+ −` require the same currency, `× ÷` scale by an integer, and a
1531            // same-currency `Money ÷ Money` is the dimensionless ratio. Mirrors the interpreter.
1532            if matches!(
1533                op,
1534                BinaryOpKind::Add | BinaryOpKind::Subtract | BinaryOpKind::Multiply | BinaryOpKind::Divide
1535                    | BinaryOpKind::Lt | BinaryOpKind::Gt | BinaryOpKind::LtEq | BinaryOpKind::GtEq
1536                    | BinaryOpKind::Eq | BinaryOpKind::NotEq
1537            ) && (is_money_expr(left, variable_types, interner)
1538                || is_money_expr(right, variable_types, interner))
1539            {
1540                let l_m = is_money_expr(left, variable_types, interner);
1541                let r_m = is_money_expr(right, variable_types, interner);
1542                let code: Option<std::string::String> = match (op, l_m, r_m) {
1543                    (BinaryOpKind::Add, true, true) => Some(format!("{}.add(&{})", left_str, right_str)),
1544                    (BinaryOpKind::Subtract, true, true) => Some(format!("{}.sub(&{})", left_str, right_str)),
1545                    // Same-currency ratio → an exact Rational.
1546                    (BinaryOpKind::Divide, true, true) => Some(format!("{}.ratio(&{})", left_str, right_str)),
1547                    // Scale money by an integer (commutes for ×); split money by an integer.
1548                    (BinaryOpKind::Multiply, true, false) => Some(format!("{}.scale_int({})", left_str, right_str)),
1549                    (BinaryOpKind::Multiply, false, true) => Some(format!("{}.scale_int({})", right_str, left_str)),
1550                    (BinaryOpKind::Divide, true, false) => Some(format!("{}.div_int({})", left_str, right_str)),
1551                    (BinaryOpKind::Lt, true, true) => Some(format!("({} < {})", left_str, right_str)),
1552                    (BinaryOpKind::Gt, true, true) => Some(format!("({} > {})", left_str, right_str)),
1553                    (BinaryOpKind::LtEq, true, true) => Some(format!("({} <= {})", left_str, right_str)),
1554                    (BinaryOpKind::GtEq, true, true) => Some(format!("({} >= {})", left_str, right_str)),
1555                    (BinaryOpKind::Eq, true, true) => Some(format!("({} == {})", left_str, right_str)),
1556                    (BinaryOpKind::NotEq, true, true) => Some(format!("({} != {})", left_str, right_str)),
1557                    _ => None,
1558                };
1559                if let Some(c) = code {
1560                    return c;
1561                }
1562            }
1563
1564            // Modular (ℤ/nℤ) arithmetic. `+ − × ÷` wrap in the ring when BOTH operands are
1565            // modular (same modulus; no auto-lift). `÷` multiplies by the modular inverse.
1566            if matches!(
1567                op,
1568                BinaryOpKind::Add | BinaryOpKind::Subtract | BinaryOpKind::Multiply | BinaryOpKind::Divide
1569            ) && is_modular_expr(left, variable_types, interner)
1570                && is_modular_expr(right, variable_types, interner)
1571            {
1572                let method = match op {
1573                    BinaryOpKind::Add => "add",
1574                    BinaryOpKind::Subtract => "sub",
1575                    BinaryOpKind::Multiply => "mul",
1576                    BinaryOpKind::Divide => "div_exact",
1577                    _ => unreachable!(),
1578                };
1579                return format!("{}.{}(&{})", left_str, method, right_str);
1580            }
1581
1582            // Exact Complex arithmetic. `+ − × ÷` stay an exact `LogosComplex` (the field is
1583            // closed) the moment one operand is Complex; an integer operand embeds as `re + 0i`.
1584            if matches!(
1585                op,
1586                BinaryOpKind::Add | BinaryOpKind::Subtract | BinaryOpKind::Multiply | BinaryOpKind::Divide
1587            ) && (is_complex_expr(left, variable_types, interner)
1588                || is_complex_expr(right, variable_types, interner))
1589            {
1590                let l = complex_operand(left, &left_str, variable_types, interner);
1591                let r = complex_operand(right, &right_str, variable_types, interner);
1592                let method = match op {
1593                    BinaryOpKind::Add => "add",
1594                    BinaryOpKind::Subtract => "sub",
1595                    BinaryOpKind::Multiply => "mul",
1596                    BinaryOpKind::Divide => "div_exact",
1597                    _ => unreachable!(),
1598                };
1599                return format!("{}.{}(&{})", l, method, r);
1600            }
1601
1602            // Exact Decimal (money) arithmetic. `+ − ×` stay an exact `LogosDecimal` the
1603            // moment one operand is a Decimal (scale preserved); an integer operand is widened.
1604            // (`÷` widening to Rational is a separate follow-up; not emitted here.)
1605            if matches!(op, BinaryOpKind::Add | BinaryOpKind::Subtract | BinaryOpKind::Multiply)
1606                && (is_decimal_expr(left, variable_types, interner)
1607                    || is_decimal_expr(right, variable_types, interner))
1608            {
1609                let l = decimal_operand(left, &left_str, variable_types, interner);
1610                let r = decimal_operand(right, &right_str, variable_types, interner);
1611                let method = match op {
1612                    BinaryOpKind::Add => "add",
1613                    BinaryOpKind::Subtract => "sub",
1614                    BinaryOpKind::Multiply => "mul",
1615                    _ => unreachable!(),
1616                };
1617                return format!("{}.{}(&{})", l, method, r);
1618            }
1619
1620            // Decimal division widens to an exact `LogosRational` (base-10 division need not
1621            // terminate) — `19.99 / 3` is exact, never a lossy float or a floored int.
1622            if matches!(op, BinaryOpKind::Divide)
1623                && (is_decimal_expr(left, variable_types, interner)
1624                    || is_decimal_expr(right, variable_types, interner))
1625            {
1626                let l = decimal_operand(left, &left_str, variable_types, interner);
1627                let r = decimal_operand(right, &right_str, variable_types, interner);
1628                return format!("{}.to_rational().div_exact(&{}.to_rational())", l, r);
1629            }
1630
1631            // Decimal comparison / equality: coerce each operand to `LogosDecimal` (which
1632            // derives Ord/Eq) so `price > 10` and `a == b` compile exactly, cross-type included.
1633            if matches!(
1634                op,
1635                BinaryOpKind::Lt | BinaryOpKind::Gt | BinaryOpKind::LtEq | BinaryOpKind::GtEq
1636                    | BinaryOpKind::Eq | BinaryOpKind::NotEq
1637            ) && (is_decimal_expr(left, variable_types, interner)
1638                || is_decimal_expr(right, variable_types, interner))
1639            {
1640                let l = decimal_operand(left, &left_str, variable_types, interner);
1641                let r = decimal_operand(right, &right_str, variable_types, interner);
1642                let op_str = match op {
1643                    BinaryOpKind::Lt => "<",
1644                    BinaryOpKind::Gt => ">",
1645                    BinaryOpKind::LtEq => "<=",
1646                    BinaryOpKind::GtEq => ">=",
1647                    BinaryOpKind::Eq => "==",
1648                    BinaryOpKind::NotEq => "!=",
1649                    _ => unreachable!(),
1650                };
1651                return format!("({} {} {})", l, op_str, r);
1652            }
1653
1654            // Complex equality: coerce each operand to `LogosComplex` (derives Eq). Complex
1655            // has NO order, so relational `< >` are deliberately left to fail to compile.
1656            if matches!(op, BinaryOpKind::Eq | BinaryOpKind::NotEq)
1657                && (is_complex_expr(left, variable_types, interner)
1658                    || is_complex_expr(right, variable_types, interner))
1659            {
1660                let l = complex_operand(left, &left_str, variable_types, interner);
1661                let r = complex_operand(right, &right_str, variable_types, interner);
1662                let op_str = if matches!(op, BinaryOpKind::Eq) { "==" } else { "!=" };
1663                return format!("({} {} {})", l, op_str, r);
1664            }
1665
1666            // Exact Rational arithmetic. `ExactDivide` only ever appears in a Rational
1667            // context (the `resolve_divisions` invariant), and `+ − ×` become Rational the
1668            // moment one operand is. Coerce each operand to `LogosRational` and call the
1669            // exact method, so `Let x: Rational be 7 / 2` compiles to `7/2`, not floored `3`.
1670            if matches!(op, BinaryOpKind::ExactDivide)
1671                || (matches!(
1672                    op,
1673                    BinaryOpKind::Add | BinaryOpKind::Subtract | BinaryOpKind::Multiply
1674                ) && (is_rational_expr(left, variable_types)
1675                    || is_rational_expr(right, variable_types)))
1676            {
1677                let l = rational_operand(left, &left_str, variable_types);
1678                let r = rational_operand(right, &right_str, variable_types);
1679                let method = match op {
1680                    BinaryOpKind::Add => "add",
1681                    BinaryOpKind::Subtract => "sub",
1682                    BinaryOpKind::Multiply => "mul",
1683                    BinaryOpKind::ExactDivide => "div_exact",
1684                    _ => unreachable!(),
1685                };
1686                return format!("{}.{}(&{})", l, method, r);
1687            }
1688
1689            // `**` exponentiation has no native Rust operator, so it never
1690            // reaches the operator-string match below. Integer power is EXACT —
1691            // overflow promotes to BigInt via `logos_pow_exact` (a negative Int
1692            // exponent panics loudly, the interp's error). Any Float operand
1693            // switches to `powf`, widening the Int side to f64. Mirrors
1694            // `semantics::arith::power`.
1695            if matches!(op, BinaryOpKind::Pow) {
1696                let lt = infer_numeric_type(left, interner, variable_types);
1697                let rt = infer_numeric_type(right, interner, variable_types);
1698                if lt == "f64" || rt == "f64" {
1699                    let l = if lt == "f64" { left_str.clone() } else { format!("(({}) as f64)", left_str) };
1700                    let r = if rt == "f64" { right_str.clone() } else { format!("(({}) as f64)", right_str) };
1701                    return format!("({}).powf({})", l, r);
1702                }
1703                // Both operands constant: fold at compile time. The i64 fast path
1704                // (`checked_pow`) emits a plain literal; overflow or a negative/oversized
1705                // exponent falls through to the runtime helper (BigInt promotion, or the
1706                // loud negative-exponent error), never a giant compile-time BigInt.
1707                if let (Expr::Literal(Literal::Number(a)), Expr::Literal(Literal::Number(b))) = (left, right) {
1708                    if let Ok(e) = u32::try_from(*b) {
1709                        if let Some(v) = a.checked_pow(e) {
1710                            return format!("{}i64", v);
1711                        }
1712                    }
1713                }
1714                let tol = ExprCtx { int_exact_tolerant: true, ..*ecx };
1715                let l = codegen_expr_ctx(left, &tol);
1716                let r = codegen_expr_ctx(right, &tol);
1717                // A bare integer-literal operand needs its `i64` suffix: only `i64: Into<LogosInt>`,
1718                // so a bare `{integer}` (default i32) would fail the `logos_pow_exact` bound.
1719                let l = if matches!(left, Expr::Literal(Literal::Number(_))) { format!("{}i64", l) } else { l };
1720                let r = if matches!(right, Expr::Literal(Literal::Number(_))) { format!("{}i64", r) } else { r };
1721                let inner = format!("logos_pow_exact({}, {})", l, r);
1722                return if ecx.int_exact_tolerant {
1723                    inner
1724                } else {
1725                    format!("{}.expect_i64(\"Int\")", inner)
1726                };
1727            }
1728
1729            // `//` floor division rounds toward NEGATIVE INFINITY, which Rust's `/`
1730            // (truncation) does not, so it never reaches the operator-string match
1731            // below. Integer floor is EXACT — overflow promotes to BigInt via
1732            // `logos_floordiv_exact` (a zero divisor panics loudly, the interp's
1733            // error). Any Float operand floors the float quotient, staying f64.
1734            // Mirrors `semantics::arith::floor_divide`.
1735            if matches!(op, BinaryOpKind::FloorDivide) {
1736                let lt = infer_numeric_type(left, interner, variable_types);
1737                let rt = infer_numeric_type(right, interner, variable_types);
1738                if lt == "f64" || rt == "f64" {
1739                    let l = if lt == "f64" { left_str.clone() } else { format!("(({}) as f64)", left_str) };
1740                    let r = if rt == "f64" { right_str.clone() } else { format!("(({}) as f64)", right_str) };
1741                    return format!("({} / {}).floor()", l, r);
1742                }
1743                // Both operands constant: fold the floored quotient at compile time when
1744                // it fits i64 (a zero divisor or the `i64::MIN // -1` overflow falls
1745                // through to the promoting runtime helper).
1746                if let (Expr::Literal(Literal::Number(a)), Expr::Literal(Literal::Number(b))) = (left, right) {
1747                    if *b != 0 {
1748                        if let (Some(q), Some(r)) = (a.checked_div(*b), a.checked_rem(*b)) {
1749                            let floored = if r != 0 && (r < 0) != (*b < 0) { q - 1 } else { q };
1750                            return format!("{}i64", floored);
1751                        }
1752                    }
1753                }
1754                let tol = ExprCtx { int_exact_tolerant: true, ..*ecx };
1755                let l = codegen_expr_ctx(left, &tol);
1756                let r = codegen_expr_ctx(right, &tol);
1757                let l = if matches!(left, Expr::Literal(Literal::Number(_))) { format!("{}i64", l) } else { l };
1758                let r = if matches!(right, Expr::Literal(Literal::Number(_))) { format!("{}i64", r) } else { r };
1759                let inner = format!("logos_floordiv_exact({}, {})", l, r);
1760                return if ecx.int_exact_tolerant {
1761                    inner
1762                } else {
1763                    format!("{}.expect_i64(\"Int\")", inner)
1764                };
1765            }
1766
1767            let op_str = match op {
1768                BinaryOpKind::Add => "+",
1769                BinaryOpKind::Subtract => "-",
1770                BinaryOpKind::Multiply => "*",
1771                // Floor division (the integer default); `ExactDivide` is handled above.
1772                BinaryOpKind::Divide | BinaryOpKind::ExactDivide => "/",
1773                BinaryOpKind::Modulo => "%",
1774                BinaryOpKind::Eq => "==",
1775                BinaryOpKind::NotEq => "!=",
1776                BinaryOpKind::Lt => "<",
1777                BinaryOpKind::Gt => ">",
1778                BinaryOpKind::LtEq => "<=",
1779                BinaryOpKind::GtEq => ">=",
1780                BinaryOpKind::And | BinaryOpKind::Or => unreachable!(), // handled above
1781                BinaryOpKind::Concat => unreachable!(), // handled above
1782                BinaryOpKind::SeqConcat => unreachable!(), // handled above
1783                BinaryOpKind::ApproxEq => unreachable!(), // handled above
1784                BinaryOpKind::Pow => unreachable!(), // handled above
1785                BinaryOpKind::FloorDivide => unreachable!(), // handled above
1786                BinaryOpKind::BitXor => "^",
1787                BinaryOpKind::BitAnd => "&",
1788                BinaryOpKind::BitOr => "|",
1789                BinaryOpKind::Shl => "<<",
1790                BinaryOpKind::Shr => ">>",
1791            };
1792
1793            // Mixed Float*Int handling. ARITHMETIC promotes the Int side to
1794            // f64 (float math is the result type). COMPARISON is EXACT —
1795            // mathematical values via `logos_cmp_i64_f64`, never a lossy cast
1796            // (`i as f64` rounds above 2^53, which would call
1797            // `9007199254740993` equal to the float `9007199254740992.0`).
1798            // Same-type compares are untouched: hot loops pay nothing.
1799            if !matches!(op, BinaryOpKind::And | BinaryOpKind::Or) {
1800                let left_type = infer_numeric_type(left, interner, variable_types);
1801                let right_type = infer_numeric_type(right, interner, variable_types);
1802                let is_cmp = matches!(
1803                    op,
1804                    BinaryOpKind::Eq
1805                        | BinaryOpKind::NotEq
1806                        | BinaryOpKind::Lt
1807                        | BinaryOpKind::Gt
1808                        | BinaryOpKind::LtEq
1809                        | BinaryOpKind::GtEq
1810                );
1811                // `(int_expr, float_expr)` operand order for the exact helper;
1812                // `flip` marks that the FLOAT was on the left, so the ordering
1813                // reverses (`f < i` ⟺ `cmp(i, f) == Greater`).
1814                let mixed_cmp = if is_cmp && left_type == "f64" && right_type == "i64" {
1815                    Some((right_str.clone(), left_str.clone(), true))
1816                } else if is_cmp && left_type == "i64" && right_type == "f64" {
1817                    Some((left_str.clone(), right_str.clone(), false))
1818                } else {
1819                    None
1820                };
1821                if let Some((int_e, float_e, flip)) = mixed_cmp {
1822                    let pat = match (op, flip) {
1823                        (BinaryOpKind::Lt, false) | (BinaryOpKind::Gt, true) => "Some(core::cmp::Ordering::Less)",
1824                        (BinaryOpKind::Gt, false) | (BinaryOpKind::Lt, true) => "Some(core::cmp::Ordering::Greater)",
1825                        (BinaryOpKind::LtEq, false) | (BinaryOpKind::GtEq, true) => {
1826                            "Some(core::cmp::Ordering::Less | core::cmp::Ordering::Equal)"
1827                        }
1828                        (BinaryOpKind::GtEq, false) | (BinaryOpKind::LtEq, true) => {
1829                            "Some(core::cmp::Ordering::Greater | core::cmp::Ordering::Equal)"
1830                        }
1831                        (BinaryOpKind::Eq, _) => {
1832                            return format!("logos_i64_eq_f64({}, {})", int_e, float_e);
1833                        }
1834                        (BinaryOpKind::NotEq, _) => {
1835                            return format!("(!logos_i64_eq_f64({}, {}))", int_e, float_e);
1836                        }
1837                        _ => unreachable!("is_cmp covers exactly the six comparison ops"),
1838                    };
1839                    return format!("matches!(logos_cmp_i64_f64({}, {}), {})", int_e, float_e, pat);
1840                }
1841                if left_type == "f64" && right_type != "f64" {
1842                    return format!("({} {} (({}) as f64))", left_str, op_str, right_str);
1843                } else if right_type == "f64" && left_type != "f64" {
1844                    return format!("((({}) as f64) {} {})", left_str, op_str, right_str);
1845                }
1846
1847                // Under a `wordN(...)` truncation the result is mod 2^N, so Int add/sub/mul lower to raw
1848                // i64 `wrapping_*` — identical low bits to the checked-exact helper, at a fraction of the
1849                // cost (the MD5 byte→word schedule was ~half the runtime as `logos_*_exact`). The flag
1850                // propagates down the additive chain (recurse with `ecx`) and is reset at call boundaries.
1851                if ecx.int_wrapping
1852                    && left_type == "i64"
1853                    && right_type == "i64"
1854                    && !ecx.int_index_context
1855                    && matches!(op, BinaryOpKind::Add | BinaryOpKind::Subtract | BinaryOpKind::Multiply)
1856                {
1857                    let method = match op {
1858                        BinaryOpKind::Add => "wrapping_add",
1859                        BinaryOpKind::Subtract => "wrapping_sub",
1860                        BinaryOpKind::Multiply => "wrapping_mul",
1861                        _ => unreachable!("matches! guards exactly these three"),
1862                    };
1863                    let l = codegen_expr_ctx(left, ecx);
1864                    let r = codegen_expr_ctx(right, ecx);
1865                    // Pin the receiver to `i64`: the codegen already proved both sides are i64, but a
1866                    // syntactically-unconstrained operand (e.g. a loop counter `for i in 1..=8` whose
1867                    // only use is this op) is an ambiguous `{integer}` in the emitted Rust, and
1868                    // `wrapping_add` exists on every integer type → E0689. `as i64` is a no-op on an
1869                    // already-i64 value and resolves the method. The result is i64, as before.
1870                    return format!("(({}) as i64).{}({})", l, method, r);
1871                }
1872
1873                // Overflow ruling v2 (stage 2): compiled Int arithmetic is EXACT.
1874                // Constant operands promote at COMPILE time; Oracle-proven-in-range
1875                // arithmetic stays raw i64 (hot loops pay nothing); everything else
1876                // emits the checked-exact helper — an inlined checked op whose
1877                // overflow spills to the promoting `LogosInt`. `%` rides along for
1878                // its own rims (zero divisor message, `i64::MIN % -1`).
1879                let is_exactable = matches!(
1880                    op,
1881                    BinaryOpKind::Add
1882                        | BinaryOpKind::Subtract
1883                        | BinaryOpKind::Multiply
1884                        | BinaryOpKind::Divide
1885                        | BinaryOpKind::Modulo
1886                );
1887                // A `LogosInt`-typed (promoted) operand forces the exact helper even when the
1888                // OTHER operand's type is unknown (e.g. a `while`→`for` loop counter that was never
1889                // registered): the result must stay `LogosInt`, and a raw `LogosInt * i64` has no
1890                // operator. `logos_*_exact` takes `impl Into<LogosInt>`, so a bare `i64` flows in.
1891                let has_bigint_operand = mentions_bigint_var(left, variable_types)
1892                    || mentions_bigint_var(right, variable_types);
1893                if is_exactable
1894                    && (left_type == "i64" && right_type == "i64" || has_bigint_operand)
1895                    && !ecx.int_index_context
1896                {
1897                    let narrow = |e: String| {
1898                        if ecx.int_exact_tolerant {
1899                            e
1900                        } else {
1901                            format!("{}.expect_i64(\"Int\")", e)
1902                        }
1903                    };
1904                    // `/` and `%` by a NONZERO literal can never overflow i64
1905                    // (a remainder is bounded by the divisor; a quotient shrinks
1906                    // — the sole exception `i64::MIN / -1` is excluded) and can
1907                    // never divide by zero, so raw matches the interpreter and
1908                    // keeps the mod-pow2 strength reduction (`x % 1024 → x & 1023`)
1909                    // legible. A zero (or `-1` for `/`) literal falls through to
1910                    // the checked helper for the canonical loud error.
1911                    if let Expr::Literal(Literal::Number(d)) = right {
1912                        let safe_div = matches!(op, BinaryOpKind::Divide) && *d != 0 && *d != -1;
1913                        let safe_mod = matches!(op, BinaryOpKind::Modulo) && *d != 0;
1914                        // A promoted (`LogosInt`) dividend has no raw `/`/`%` operator and may exceed
1915                        // i64, so it must use the exact helper below rather than this raw fast-path.
1916                        if (safe_div || safe_mod) && !has_bigint_operand {
1917                            // The raw i64 `/`/`%` needs an i64 DIVIDEND. In a tolerant context `left_str` is
1918                            // a promoted `LogosInt` (no `Div<i64>`/`Rem<i64>`) — e.g. `(a + b) / 64` inside
1919                            // an exact op — so recompute the dividend as i64; the i64 result flows back into
1920                            // the enclosing exact op via `Into`. (The divisor `d` is a literal, always i64.)
1921                            let l = if ecx.int_exact_tolerant {
1922                                codegen_expr_ctx(left, &ExprCtx { int_exact_tolerant: false, ..*ecx })
1923                            } else {
1924                                left_str.clone()
1925                            };
1926                            return format!("({} {} {})", l, op_str, right_str);
1927                        }
1928                    }
1929                    if let (Expr::Literal(Literal::Number(a)), Expr::Literal(Literal::Number(b))) =
1930                        (left, right)
1931                    {
1932                        match const_exact_int(*op, *a, *b) {
1933                            Some(ConstExact::InRange) => {
1934                                return format!("({} {} {})", left_str, op_str, right_str);
1935                            }
1936                            Some(ConstExact::Promoted(lit)) => return narrow(lit),
1937                            // A constant zero divisor falls to the runtime
1938                            // helper: the loud canonical error, the interp's.
1939                            None => {}
1940                        }
1941                    } else if !has_bigint_operand && oracle_proves_int_op_in_range(ecx, expr, *op, left, right) {
1942                        // A raw `left op right` needs both sides to be i64. With a promoted operand
1943                        // (`LogosInt`, no arithmetic operators) the exact helper below is the only
1944                        // valid lowering, even when the result provably fits i64.
1945                        return format!("({} {} {})", left_str, op_str, right_str);
1946                    }
1947                    // A root-NARROWED op (non-tolerant, no promoted operand)
1948                    // has two register-shaped exact lowerings that skip the
1949                    // `LogosInt` round-trip entirely; a TOLERANT root must
1950                    // stay on the promoting chain so an oversized
1951                    // intermediate flows exact into the enclosing op.
1952                    if !ecx.int_exact_tolerant && !has_bigint_operand {
1953                        if let Some(fused) = try_fuse_narrowed_int_op(*op, left, right, ecx) {
1954                            return fused;
1955                        }
1956                        if let Some(dual) = try_guarded_dual_chain(*op, left, right, ecx) {
1957                            return dual;
1958                        }
1959                        if let Some(chain) = try_i128_chain(*op, left, right, ecx) {
1960                            return chain;
1961                        }
1962                    }
1963                    let helper = match op {
1964                        BinaryOpKind::Add => "logos_add_exact",
1965                        BinaryOpKind::Subtract => "logos_sub_exact",
1966                        BinaryOpKind::Multiply => "logos_mul_exact",
1967                        BinaryOpKind::Divide => "logos_div_exact",
1968                        BinaryOpKind::Modulo => "logos_rem_exact",
1969                        _ => unreachable!("is_exactable covers exactly these five"),
1970                    };
1971                    // Exactness flows DOWN the operand chain: intermediates stay
1972                    // `LogosInt` (Into-chained), narrowing happens once at the root.
1973                    let tol = ExprCtx { int_exact_tolerant: true, ..*ecx };
1974                    let l = codegen_expr_ctx(left, &tol);
1975                    let r = codegen_expr_ctx(right, &tol);
1976                    return narrow(format!("{}({}, {})", helper, l, r));
1977                }
1978            }
1979
1980            format!("({} {} {})", left_str, op_str, right_str)
1981        }
1982
1983        Expr::Call { function, args } => {
1984            let func_name = names.ident(*function);
1985            let raw_name = names.raw(*function);
1986            // Callee param-role indices (packed one slot per function): a readonly
1987            // borrow (`&[T]`) and a value-semantics `mutable` collection param can
1988            // BOTH be present, so read each role independently rather than assuming
1989            // one clobbers the other.
1990            let callee_slot = variable_types.get(function);
1991            let callee_borrow_indices: HashSet<usize> =
1992                super::fn_role_indices(callee_slot, super::FnRole::Borrow);
1993            // `mutable` collection params (value semantics): pass the caller's
1994            // collection by shared reference (no clone) so the callee mutates it
1995            // in place — the explicit propagation escape hatch.
1996            let callee_value_mutable: HashSet<usize> =
1997                super::fn_role_indices(callee_slot, super::FnRole::ValueMutable);
1998            // Recursively codegen args with full context.
1999            // Borrow params: pass &name (or pass through if already a slice).
2000            // Non-borrow params: clone non-Copy identifiers to avoid move-after-use.
2001            // A `wordN(...)` argument is codegen'd in `int_wrapping` mode (its Int arithmetic is mod 2^N);
2002            // every OTHER call resets the flag — a callee's argument is a full-width value, not truncated,
2003            // so `wordN(f(a * b))` must NOT wrap `a * b`.
2004            let arg_wrapping = matches!(raw_name, "word8" | "word16" | "word32" | "word64");
2005            let args_str: Vec<String> = args.iter()
2006                .enumerate()
2007                .map(|(i, a)| {
2008                    let s = codegen_expr_ctx(a, &ExprCtx { int_wrapping: arg_wrapping, ..*ecx });
2009                    if callee_value_mutable.contains(&i) {
2010                        // Pass the caller's collection by shared reference (no clone)
2011                        // so the callee mutates it in place. If the arg is already a
2012                        // reference (a `mutable` param threaded through a nested call),
2013                        // pass it as-is rather than re-referencing.
2014                        if let Expr::Identifier(sym) = a {
2015                            if variable_types.get(sym).is_some_and(|t| t.starts_with('&')) {
2016                                return s;
2017                            }
2018                        }
2019                        return format!("&{}", s);
2020                    }
2021                    if callee_borrow_indices.contains(&i) {
2022                        // Borrow param: pass &[T] reference instead of cloning
2023                        if let Expr::Identifier(sym) = a {
2024                            if let Some(ty) = variable_types.get(sym) {
2025                                let ty = ty.split("|__hl:").next().unwrap_or(ty.as_str());
2026                                if ty.starts_with("&[") || ty.starts_with("&mut [") {
2027                                    return s; // Already a slice — pass through
2028                                }
2029                                if ty.starts_with("Vec<") {
2030                                    return format!("&{}", s); // Vec<T> derefs to &[T]
2031                                }
2032                                if ty.starts_with('[') {
2033                                    return format!("&{}", s); // fixed array [T; N] coerces to &[T]
2034                                }
2035                                if ty.starts_with("LogosSeq") {
2036                                    return format!("&*{}.borrow()", s);
2037                                }
2038                            }
2039                            // Unknown type at borrow position — default to LogosSeq conversion.
2040                            // All LOGOS Seq variables are LogosSeq<T> unless materialized as Vec<T>
2041                            // (which is always tracked). Safe because borrow positions only accept Seq<T>.
2042                            return format!("&*{}.borrow()", s);
2043                        }
2044                        // Non-identifier at borrow position (e.g. list literal, function call).
2045                        // The expression evaluates to LogosSeq<T>; borrow the temporary.
2046                        format!("&*{}.borrow()", s)
2047                    } else {
2048                        // Regular param: clone non-Copy identifiers
2049                        if let Expr::Identifier(sym) = a {
2050                            // A `LogosInt` (promoted) var passed to a scalar `i64` param narrows to
2051                            // the param's declared width — a loud panic if it exceeds i64 (the
2052                            // i64-param contract). Functions do not yet take `LogosInt` params.
2053                            if variable_types.get(sym).map_or(false, |t| t.contains("__bigint")) {
2054                                return format!("{}.expect_i64(\"Int\")", names.ident(*sym));
2055                            }
2056                            if let Some(ty) = variable_types.get(sym) {
2057                                if !is_copy_type(ty) {
2058                                    return format!("{}.clone()", s);
2059                                }
2060                            } else {
2061                                // Unknown type (e.g. pattern-bound variable from Inspect):
2062                                // clone defensively to avoid move-after-use in loops
2063                                return format!("{}.clone()", s);
2064                            }
2065                        }
2066                        s
2067                    }
2068                })
2069                .collect();
2070            // Builtin math functions → Rust method call syntax
2071            match raw_name {
2072                "sqrt" if args_str.len() == 1 => {
2073                    format!("(({}) as f64).sqrt()", args_str[0])
2074                }
2075                // `{k: v, …}` map literal — flat pairs into an insertion-ordered
2076                // LogosMap (block expression; `insert` takes `&self`).
2077                "mapOf" if !args_str.is_empty() && args_str.len() % 2 == 0 => {
2078                    let mut s = String::from("{ let __map_lit = LogosMap::new(); ");
2079                    for pair in args_str.chunks(2) {
2080                        s.push_str(&format!("__map_lit.insert({}, {}); ", pair[0], pair[1]));
2081                    }
2082                    s.push_str("__map_lit }");
2083                    s
2084                }
2085                // `repeatSeq(x, n)` — the `n copies of x` fill: n independent
2086                // `fill_clone` slots (the element evaluates once).
2087                "repeatSeq" if args_str.len() == 2 => {
2088                    format!(
2089                        "{{ let __rp_x = ({}); let __rp_n = (({}) as i64).max(0) as usize; \
2090                         let mut __rp = Vec::with_capacity(__rp_n); \
2091                         for _ in 0..__rp_n {{ __rp.push(__rp_x.fill_clone()); }} \
2092                         LogosSeq::from_vec(__rp) }}",
2093                        args_str[0], args_str[1]
2094                    )
2095                }
2096                // `{a, b, …}` set literal — elements into the Set repr
2097                // (hash-set semantics: dedup by value equality).
2098                "setOf" if !args_str.is_empty() => {
2099                    let mut s = String::from("{ let mut __set_lit = Set::default(); ");
2100                    for a in &args_str {
2101                        s.push_str(&format!("__set_lit.insert({}); ", a));
2102                    }
2103                    s.push_str("__set_lit }");
2104                    s
2105                }
2106                // Exact base-10 money: parse the literal text into a `LogosDecimal`. The
2107                // `.to_string()` accepts the arg whether it lowered to `&str` or `String`.
2108                "decimal" if args_str.len() == 1 => {
2109                    format!("LogosDecimal::parse(&({}).to_string())", args_str[0])
2110                }
2111                // Exact complex `re + im·i` from two exact reals (integers embed as `n/1`).
2112                "complex" if args_str.len() == 2 => {
2113                    let re = rational_operand(&args[0], &args_str[0], variable_types);
2114                    let im = rational_operand(&args[1], &args_str[1], variable_types);
2115                    format!("LogosComplex::new({}, {})", re, im)
2116                }
2117                // A ℤ/nℤ element from a value and a modulus (both integers).
2118                "modular" if args_str.len() == 2 => {
2119                    format!("LogosModular::new(({}) as i64, ({}) as i64)", args_str[0], args_str[1])
2120                }
2121                // A dimensioned quantity from an exact magnitude and a unit name (`quantity(2, "inch")`).
2122                // The magnitude rides the rational tower (integers embed as `n/1`), so it never truncates.
2123                "quantity" if args_str.len() == 2 => {
2124                    let mag = rational_operand(&args[0], &args_str[0], variable_types);
2125                    format!("LogosQuantity::from_rational({}, &({}).to_string())", mag, args_str[1])
2126                }
2127                // Re-express a quantity in another unit of the same dimension (`convert(q, "foot")`).
2128                "convert" if args_str.len() == 2 => {
2129                    format!("({}).convert(&({}).to_string())", args_str[0], args_str[1])
2130                }
2131                // Construct money: a Decimal amount → `LogosMoney::of`, an Int amount → `from_i64`.
2132                "money" if args_str.len() == 2 => {
2133                    if is_decimal_expr(&args[0], variable_types, interner) {
2134                        let amt = decimal_operand(&args[0], &args_str[0], variable_types, interner);
2135                        format!("LogosMoney::of({}, &({}).to_string())", amt, args_str[1])
2136                    } else {
2137                        format!("LogosMoney::from_i64({}, &({}).to_string())", args_str[0], args_str[1])
2138                    }
2139                }
2140                // UUID — parse, the two special ids, the well-known namespaces, name-based v3/v5, and
2141                // the version accessor. All lower to `LogosUuid` associated functions.
2142                "uuid" if args_str.len() == 1 => {
2143                    format!("LogosUuid::parse(&({}).to_string())", args_str[0])
2144                }
2145                "uuid_nil" if args_str.is_empty() => "LogosUuid::nil()".to_string(),
2146                "uuid_max" if args_str.is_empty() => "LogosUuid::max()".to_string(),
2147                "uuid_dns" if args_str.is_empty() => "LogosUuid::namespace_dns()".to_string(),
2148                "uuid_url" if args_str.is_empty() => "LogosUuid::namespace_url()".to_string(),
2149                "uuid_oid" if args_str.is_empty() => "LogosUuid::namespace_oid()".to_string(),
2150                "uuid_x500" if args_str.is_empty() => "LogosUuid::namespace_x500()".to_string(),
2151                "uuid_version" if args_str.len() == 1 => format!("({}).version()", args_str[0]),
2152                // Byte-level primitives backing the Logos-written uuid.lg constructors. (`md5`/`sha1`/
2153                // `uuid_v3`/`uuid_v5` are Logos stdlib functions now, emitted as normal calls.)
2154                "text_bytes" if args_str.len() == 1 => format!("text_bytes(&({}))", args_str[0]),
2155                "text_from_bytes" if args_str.len() == 1 => format!("text_from_bytes(&({}))", args_str[0]),
2156                "readWireProgram" if args_str.is_empty() => {
2157                    "{ use std::io::Read as _; let mut __len = [0u8; 4]; if std::io::stdin().read_exact(&mut __len).is_err() { std::process::exit(0); } let __n = u32::from_le_bytes(__len) as usize; let mut __wb = vec![0u8; __n]; std::io::stdin().read_exact(&mut __wb).expect(\"readWireProgram: frame\"); <CProgram as logicaffeine_data::wire::WireDecode>::wire_decode(&__wb, &mut 0usize).expect(\"readWireProgram: decode\") }".to_string()
2158                }
2159                "writeWireResidual" if args_str.len() == 1 => {
2160                    format!("{{ use std::io::Write as _; let __s: String = ({}).into(); let __b = __s.as_bytes(); let __o = std::io::stdout(); let mut __h = __o.lock(); __h.write_all(&(__b.len() as u32).to_le_bytes()).unwrap(); __h.write_all(__b).unwrap(); __h.flush().unwrap(); __b.len() as i64 }}", args_str[0])
2161                }
2162                "uuid_bytes" if args_str.len() == 1 => format!("({}).byte_seq()", args_str[0]),
2163                "uuid_from_bytes" if args_str.len() == 1 => {
2164                    format!("LogosUuid::from_byte_seq(&({}))", args_str[0])
2165                }
2166                // `chr(code)` → a one-char String (the char code → text; used building the canonical
2167                // UUID hex in Logos). Fully qualified — it lives in the `text` submodule, not the glob.
2168                "chr" if args_str.len() == 1 => {
2169                    format!("logicaffeine_system::text::chr(({}) as i64)", args_str[0])
2170                }
2171                // Parse an RFC 3339 timestamp into a `LogosMoment` (delegates to base::temporal).
2172                "parse_timestamp" if args_str.len() == 1 => {
2173                    format!("LogosMoment::parse_rfc3339(&({}).to_string())", args_str[0])
2174                }
2175                // Render a `LogosMoment` as an RFC 3339 string.
2176                "format_timestamp" if args_str.len() == 1 => {
2177                    format!("({}).format_rfc3339()", args_str[0])
2178                }
2179                // UTC calendar component extractors on a `LogosMoment`.
2180                "year_of" if args_str.len() == 1 => format!("({}).year()", args_str[0]),
2181                "month_of" if args_str.len() == 1 => format!("({}).month()", args_str[0]),
2182                "day_of" if args_str.len() == 1 => format!("({}).day()", args_str[0]),
2183                "weekday_of" if args_str.len() == 1 => format!("({}).weekday()", args_str[0]),
2184                "hour_of" if args_str.len() == 1 => format!("({}).hour()", args_str[0]),
2185                "minute_of" if args_str.len() == 1 => format!("({}).minute()", args_str[0]),
2186                "second_of" if args_str.len() == 1 => format!("({}).second()", args_str[0]),
2187                // The ISO-8601 week number (1..=53) of the Moment/Date.
2188                "week_of" if args_str.len() == 1 => format!("({}).iso_week()", args_str[0]),
2189                // The calendar quarter (1..=4) of the Moment/Date.
2190                "quarter_of" if args_str.len() == 1 => format!("({}).quarter()", args_str[0]),
2191                // The calendar day (a `LogosDate`) the Moment falls on.
2192                "date_of" if args_str.len() == 1 => format!("({}).date()", args_str[0]),
2193                // The wall-clock time-of-day (a `LogosTime`) of the Moment.
2194                "time_of" if args_str.len() == 1 => format!("({}).time_of_day()", args_str[0]),
2195                // Moment arithmetic.
2196                "seconds_between" if args_str.len() == 2 => {
2197                    format!("({}).seconds_until(&({}))", args_str[0], args_str[1])
2198                }
2199                "months_between" if args_str.len() == 2 => {
2200                    format!("({}).months_until(&({}))", args_str[0], args_str[1])
2201                }
2202                "years_between" if args_str.len() == 2 => {
2203                    format!("({}).years_until(&({}))", args_str[0], args_str[1])
2204                }
2205                "add_seconds" if args_str.len() == 2 => {
2206                    format!("({}).add_seconds({})", args_str[0], args_str[1])
2207                }
2208                "in_zone" if args_str.len() == 2 => {
2209                    format!("({}).in_zone(&({}).to_string())", args_str[0], args_str[1])
2210                }
2211                "local_instant" if args_str.len() == 2 => {
2212                    format!("({}).local_instant(&({}).to_string())", args_str[0], args_str[1])
2213                }
2214                // SHA-1 SHA-NI lane (`Lanes4Word32` = one `__m128i`): pack/unpack + the four SHA ops,
2215                // which lower to the `sha1rnds4`/`sha1msg1/2`/`sha1nexte` hardware instructions.
2216                "lanes4Word32" if args_str.len() == 1 => {
2217                    format!("lanes4_word32(&({}))", args_str[0])
2218                }
2219                "lanes4Of" if args_str.len() == 4 => {
2220                    format!("lanes4_of({}, {}, {}, {})", args_str[0], args_str[1], args_str[2], args_str[3])
2221                }
2222                // Byte-shuffle lane (`Lanes16Word8`): pshufb + per-byte shift + interleaves — the SIMD
2223                // hex codec written in Logos lowers to these.
2224                "lanes16Word8" if args_str.len() == 1 => format!("lanes16_word8(&({}))", args_str[0]),
2225                "seqOfLanes16W8" if args_str.len() == 1 => format!("seq_of_lanes16w8({})", args_str[0]),
2226                "splat16Word8" if args_str.len() == 1 => format!("splat16_word8({})", args_str[0]),
2227                "shuffle16" if args_str.len() == 2 => {
2228                    format!("shuffle16({}, {})", args_str[0], args_str[1])
2229                }
2230                "shrBytes16" if args_str.len() == 2 => {
2231                    format!("shr_bytes16({}, {})", args_str[0], args_str[1])
2232                }
2233                "interleaveLo16" if args_str.len() == 2 => {
2234                    format!("interleave_lo16({}, {})", args_str[0], args_str[1])
2235                }
2236                "interleaveHi16" if args_str.len() == 2 => {
2237                    format!("interleave_hi16({}, {})", args_str[0], args_str[1])
2238                }
2239                "byteAdd16" if args_str.len() == 2 => {
2240                    format!("byte_add16({}, {})", args_str[0], args_str[1])
2241                }
2242                "maddubs16" if args_str.len() == 2 => {
2243                    format!("maddubs16({}, {})", args_str[0], args_str[1])
2244                }
2245                "packus16" if args_str.len() == 2 => {
2246                    format!("packus16({}, {})", args_str[0], args_str[1])
2247                }
2248                "seqOfLanes4W32" if args_str.len() == 1 => {
2249                    format!("seq_of_lanes4w32({})", args_str[0])
2250                }
2251                "sha1rnds4" if args_str.len() == 3 => {
2252                    format!("sha1rnds4({}, {}, {})", args_str[0], args_str[1], args_str[2])
2253                }
2254                "sha1msg1" if args_str.len() == 2 => {
2255                    format!("sha1msg1({}, {})", args_str[0], args_str[1])
2256                }
2257                "sha1msg2" if args_str.len() == 2 => {
2258                    format!("sha1msg2({}, {})", args_str[0], args_str[1])
2259                }
2260                "sha1nexte" if args_str.len() == 2 => {
2261                    format!("sha1nexte({}, {})", args_str[0], args_str[1])
2262                }
2263                // SIMD lane vector: pack a Seq of Word32 into one `__m256i`, or read its lanes back.
2264                "lanes8Word32" if args_str.len() == 1 => {
2265                    format!("lanes8_word32(&({}))", args_str[0])
2266                }
2267                "seqOfLanes8" if args_str.len() == 1 => {
2268                    format!("seq_of_lanes8({})", args_str[0])
2269                }
2270                "splat8Word32" if args_str.len() == 1 => {
2271                    format!("splat8_word32({})", args_str[0])
2272                }
2273                "intOfWord32" if args_str.len() == 1 => {
2274                    format!("int_of_word32({})", args_str[0])
2275                }
2276                "intOfWord64" if args_str.len() == 1 => {
2277                    format!("int_of_word64({})", args_str[0])
2278                }
2279                "word64Shl" if args_str.len() == 2 => {
2280                    format!("word64_shl({}, {})", args_str[0], args_str[1])
2281                }
2282                "word64Shr" if args_str.len() == 2 => {
2283                    format!("word64_shr({}, {})", args_str[0], args_str[1])
2284                }
2285                "word32Shr" if args_str.len() == 2 => {
2286                    format!("word32_shr({}, {})", args_str[0], args_str[1])
2287                }
2288                "word64And" if args_str.len() == 2 => {
2289                    format!("word64_and({}, {})", args_str[0], args_str[1])
2290                }
2291                "word16" if args_str.len() == 1 => {
2292                    format!("word16({})", args_str[0])
2293                }
2294                "intOfWord16" if args_str.len() == 1 => {
2295                    format!("int_of_word16({})", args_str[0])
2296                }
2297                "lanes4Word64" if args_str.len() == 1 => {
2298                    format!("lanes4_word64(&({}))", args_str[0])
2299                }
2300                "seqOfLanes4" if args_str.len() == 1 => {
2301                    format!("seq_of_lanes4({})", args_str[0])
2302                }
2303                "mul32x32to64" if args_str.len() == 2 => {
2304                    format!("mul32x32to64({}, {})", args_str[0], args_str[1])
2305                }
2306                "hsumLanes4" if args_str.len() == 1 => {
2307                    format!("hsum_lanes4({})", args_str[0])
2308                }
2309                "splat4Word64" if args_str.len() == 1 => {
2310                    format!("splat4_word64({})", args_str[0])
2311                }
2312                "andNot4" if args_str.len() == 2 => {
2313                    format!("and_not4({}, {})", args_str[0], args_str[1])
2314                }
2315                "lanes16Word16" if args_str.len() == 1 => {
2316                    format!("lanes16_word16(&({}))", args_str[0])
2317                }
2318                "seqOfLanes16" if args_str.len() == 1 => {
2319                    format!("seq_of_lanes16({})", args_str[0])
2320                }
2321                "splat16Word16" if args_str.len() == 1 => {
2322                    format!("splat16_word16({})", args_str[0])
2323                }
2324                "mulhi16" if args_str.len() == 2 => {
2325                    format!("mulhi16({}, {})", args_str[0], args_str[1])
2326                }
2327                "montmul32" if args_str.len() == 4 => {
2328                    format!("montmul32({}, {}, {}, {})", args_str[0], args_str[1], args_str[2], args_str[3])
2329                }
2330                // Method calls so Rust resolves the right lane impl by type (Lanes16Word16 i16 NTT
2331                // strides 8/4/2; Lanes8Word32 i32 NTT strides 4/2/1).
2332                "nttBcastLo" if args_str.len() == 2 => {
2333                    format!("({}).ntt_bcast_lo(({}) as usize)", args_str[0], args_str[1])
2334                }
2335                "nttBcastHi" if args_str.len() == 2 => {
2336                    format!("({}).ntt_bcast_hi(({}) as usize)", args_str[0], args_str[1])
2337                }
2338                "nttBlend" if args_str.len() == 3 => {
2339                    format!("({}).ntt_blend({}, ({}) as usize)", args_str[0], args_str[1], args_str[2])
2340                }
2341                // Modular exponentiation: `pow(m, e)` where the base is a ℤ/nℤ element.
2342                "pow" if args_str.len() == 2 && is_modular_expr(&args[0], variable_types, interner) => {
2343                    format!("({}).pow(({}) as u64)", args_str[0], args_str[1])
2344                }
2345                // A Rational argument takes the EXACT path (BigInt num/den), never a
2346                // lossy `as f64`: `|·|` stays rational; floor/ceil/round give the exact Int.
2347                "abs" if args_str.len() == 1 && is_rational_expr(&args[0], variable_types) => {
2348                    format!("({}).abs()", args_str[0])
2349                }
2350                "floor" if args_str.len() == 1 && is_rational_expr(&args[0], variable_types) => {
2351                    format!("({}).floor()", args_str[0])
2352                }
2353                "ceil" if args_str.len() == 1 && is_rational_expr(&args[0], variable_types) => {
2354                    format!("({}).ceil()", args_str[0])
2355                }
2356                "round" if args_str.len() == 1 && is_rational_expr(&args[0], variable_types) => {
2357                    format!("({}).round()", args_str[0])
2358                }
2359                "abs" if args_str.len() == 1 => {
2360                    let arg_type = infer_numeric_type(&args[0], interner, variable_types);
2361                    if arg_type == "f64" {
2362                        format!("(({}) as f64).abs()", args_str[0])
2363                    } else {
2364                        format!("(({}) as i64).abs()", args_str[0])
2365                    }
2366                }
2367                "floor" if args_str.len() == 1 => {
2368                    format!("((({}) as f64).floor() as i64)", args_str[0])
2369                }
2370                "ceil" if args_str.len() == 1 => {
2371                    format!("((({}) as f64).ceil() as i64)", args_str[0])
2372                }
2373                "round" if args_str.len() == 1 => {
2374                    format!("((({}) as f64).round() as i64)", args_str[0])
2375                }
2376                "pow" if args_str.len() == 2 => {
2377                    format!("((({}) as f64).powf(({}) as f64))", args_str[0], args_str[1])
2378                }
2379                "min" if args_str.len() == 2 => {
2380                    format!("({}).min({})", args_str[0], args_str[1])
2381                }
2382                "max" if args_str.len() == 2 => {
2383                    format!("({}).max({})", args_str[0], args_str[1])
2384                }
2385                _ => {
2386                    // Add .await if this function is async
2387                    if async_functions.contains(function) {
2388                        format!("{}({}).await", func_name, args_str.join(", "))
2389                    } else {
2390                        format!("{}({})", func_name, args_str.join(", "))
2391                    }
2392                }
2393            }
2394        }
2395
2396        // Affine read-only array (deleted CSR offset array): `item k of A` is the
2397        // closed form `coeff * (k-1) + offset` — no load, no array. The 1-based→
2398        // 0-based `-1` cancels a `+1` in the index, so `item (v+1) of A` with
2399        // `A[p]=5*p` becomes `v * 5` (C's `v*MAX_EDGES`).
2400        Expr::Index { collection: Expr::Identifier(sym), index }
2401            if affine_array_coeff_offset(variable_types.get(sym)).is_some() =>
2402        {
2403            let (coeff, offset) = affine_array_coeff_offset(variable_types.get(sym)).unwrap();
2404            enum Pos {
2405                Const(i64),
2406                Expr(String),
2407            }
2408            let pos = match index {
2409                Expr::Literal(Literal::Number(n)) => Pos::Const(n - 1),
2410                Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => match (left, right) {
2411                    (_, Expr::Literal(Literal::Number(1))) => Pos::Expr(recurse!(left)),
2412                    (Expr::Literal(Literal::Number(1)), _) => Pos::Expr(recurse!(right)),
2413                    (_, Expr::Literal(Literal::Number(k))) if *k > 1 => {
2414                        Pos::Expr(format!("({} + {})", recurse!(left), k - 1))
2415                    }
2416                    (Expr::Literal(Literal::Number(k)), _) if *k > 1 => {
2417                        Pos::Expr(format!("({} + {})", recurse!(right), k - 1))
2418                    }
2419                    _ => Pos::Expr(format!("(({}) - 1)", irecurse!(index))),
2420                },
2421                _ => Pos::Expr(format!("(({}) - 1)", irecurse!(index))),
2422            };
2423            match pos {
2424                Pos::Const(p) => format!("{}i64", coeff.wrapping_mul(p).wrapping_add(offset)),
2425                Pos::Expr(_) if coeff == 0 => format!("{}i64", offset),
2426                Pos::Expr(p) => {
2427                    let base = if coeff == 1 { p } else { format!("(({}) * {})", p, coeff) };
2428                    if offset == 0 {
2429                        base
2430                    } else {
2431                        format!("({} + {})", base, offset)
2432                    }
2433                }
2434            }
2435        }
2436
2437        // AoS interleaving: `item i of member` reads column `col` of the fused
2438        // backing's `(i-1)`-th row — `backing[(i-1)][col]`. Adjacent columns are
2439        // memory-adjacent, so LLVM packs them (C's struct-array load).
2440        Expr::Index { collection: Expr::Identifier(sym), index }
2441            if parse_aos_tag(variable_types.get(sym)).is_some() =>
2442        {
2443            let tag = parse_aos_tag(variable_types.get(sym)).unwrap();
2444            let row = match index {
2445                Expr::Literal(Literal::Number(1)) => "0".to_string(),
2446                Expr::Literal(Literal::Number(n)) => format!("{}", n - 1),
2447                Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => match (left, right) {
2448                    (_, Expr::Literal(Literal::Number(1))) => format!("({}) as usize", recurse!(left)),
2449                    (Expr::Literal(Literal::Number(1)), _) => format!("({}) as usize", recurse!(right)),
2450                    _ => format!("(({}) - 1) as usize", irecurse!(index)),
2451                },
2452                _ => format!("(({}) - 1) as usize", irecurse!(index)),
2453            };
2454            format!("{}[{}][{}]", tag.backing, row, tag.col)
2455        }
2456
2457        Expr::Index { collection, index } => {
2458            let coll_str = recurse!(collection);
2459            // Direct indexing for known collection types (avoids trait dispatch)
2460            // Strip |__hl: hoisting suffix so type parsing (strip_suffix, etc.) works correctly.
2461            let known_type = if let Expr::Identifier(sym) = collection {
2462                variable_types.get(sym).map(|s| s.split("|__hl:").next().unwrap_or(s.as_str()))
2463            } else {
2464                None
2465            };
2466            // A NEGATIVE literal index is end-relative (`item -1 of xs` is the
2467            // last element). The direct-`[...]` fast paths below assume a
2468            // positive 1-based index and would compute `(-1 - 1) as usize` — a
2469            // giant offset. Route it through the `LogosIndex` trait, whose
2470            // `resolve_logos_index` carries the one end-relative rule shared
2471            // with the interpreter. (A narrowed `Vec<i32>` sign-extends.)
2472            if let Expr::Literal(Literal::Number(n)) = index {
2473                if *n < 0 {
2474                    if let Some(t) = known_type {
2475                        let idx_capable = t.starts_with("LogosSeq")
2476                            || t.starts_with("Vec")
2477                            || t.starts_with("&[")
2478                            || t.starts_with("&mut [")
2479                            || t.starts_with('[')
2480                            || t == "String";
2481                        if idx_capable {
2482                            let read = format!(
2483                                "logicaffeine_data::LogosIndex::logos_get(&{}, {}i64)",
2484                                coll_str, n
2485                            );
2486                            return if t == "Vec<i32>" {
2487                                format!("(({}) as i64)", read)
2488                            } else {
2489                                read
2490                            };
2491                        }
2492                    }
2493                }
2494            }
2495            match known_type {
2496                Some(t) if t.starts_with("LogosSeq") || t.starts_with("Vec") => {
2497                    let is_logos_seq = t.starts_with("LogosSeq");
2498                    let suffix = if has_copy_element_type(t) { "" } else { ".clone()" };
2499                    // OPT-8: Check if index is a zero-based counter (already 0-based, no -1 needed)
2500                    let is_zero_based_counter = if let Expr::Identifier(idx_sym) = index {
2501                        variable_types.get(idx_sym).map_or(false, |t| t == "__zero_based_i64")
2502                    } else {
2503                        false
2504                    };
2505                    let index_part = if is_zero_based_counter {
2506                        let idx_name = irecurse!(index);
2507                        format!("{} as usize", idx_name)
2508                    } else { match index {
2509                        // Literal(1) → 0
2510                        Expr::Literal(Literal::Number(1)) => "0".to_string(),
2511                        // Literal(N) → N-1
2512                        Expr::Literal(Literal::Number(n)) => format!("{}", n - 1),
2513                        // (X + K) patterns: +1 cancels the -1 from 1-based indexing
2514                        Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
2515                            match (left, right) {
2516                                (_, Expr::Literal(Literal::Number(1))) => {
2517                                    let left_str = irecurse!(left);
2518                                    if matches!(left, Expr::Identifier(_)) {
2519                                        format!("{} as usize", left_str)
2520                                    } else {
2521                                        format!("({}) as usize", left_str)
2522                                    }
2523                                }
2524                                (Expr::Literal(Literal::Number(1)), _) => {
2525                                    let right_str = irecurse!(right);
2526                                    if matches!(right, Expr::Identifier(_)) {
2527                                        format!("{} as usize", right_str)
2528                                    } else {
2529                                        format!("({}) as usize", right_str)
2530                                    }
2531                                }
2532                                (_, Expr::Literal(Literal::Number(k))) if *k > 1 => {
2533                                    format!("({} + {}) as usize", irecurse!(left), k - 1)
2534                                }
2535                                (Expr::Literal(Literal::Number(k)), _) if *k > 1 => {
2536                                    format!("({} + {}) as usize", irecurse!(right), k - 1)
2537                                }
2538                                _ => {
2539                                    format!("({} - 1) as usize", irecurse!(index))
2540                                }
2541                            }
2542                        }
2543                        _ => {
2544                            format!("({} - 1) as usize", irecurse!(index))
2545                        }
2546                    } };
2547                    // BCE: when the oracle proves this index in range, emit an
2548                    // unchecked load (no bounds branch). The statement-level hint
2549                    // pass emits a `debug_assert!` net for debug builds.
2550                    let read = if oracle_proves_index(ecx, collection, index) {
2551                        match (is_logos_seq, suffix.is_empty()) {
2552                            (true, true) => format!("unsafe {{ *{}.borrow().get_unchecked({}) }}", coll_str, index_part),
2553                            (true, false) => format!("unsafe {{ {}.borrow().get_unchecked({}){} }}", coll_str, index_part, suffix),
2554                            (false, true) => format!("unsafe {{ *{}.get_unchecked({}) }}", coll_str, index_part),
2555                            (false, false) => format!("unsafe {{ {}.get_unchecked({}){} }}", coll_str, index_part, suffix),
2556                        }
2557                    } else if is_logos_seq {
2558                        format!("{}.borrow()[{}]{}", coll_str, index_part, suffix)
2559                    } else {
2560                        format!("{}[{}]{}", coll_str, index_part, suffix)
2561                    };
2562                    // A narrowed (`Vec<i32>`) read sign-extends to the i64 the rest
2563                    // of the program expects (lossless).
2564                    if t == "Vec<i32>" {
2565                        format!("(({}) as i64)", read)
2566                    } else {
2567                        read
2568                    }
2569                }
2570                Some(t) if t.starts_with("&[") || t.starts_with("&mut [") || t.starts_with('[') => {
2571                    // Slice (&[T] / &mut [T]) or O3 fixed array [T; N] — direct
2572                    // indexing with the same 1-based simplification.
2573                    let elem = if t.starts_with('[') {
2574                        t.trim_start_matches('[').split("; ").next().unwrap_or("_")
2575                    } else {
2576                        t.strip_prefix("&mut [")
2577                            .or_else(|| t.strip_prefix("&["))
2578                            .and_then(|s| s.strip_suffix(']'))
2579                            .unwrap_or("_")
2580                    };
2581                    let suffix = if is_copy_type(elem) { "" } else { ".clone()" };
2582                    // OPT-8: Check if index is a zero-based counter
2583                    let is_zero_based_counter = if let Expr::Identifier(idx_sym) = index {
2584                        variable_types.get(idx_sym).map_or(false, |t| t == "__zero_based_i64")
2585                    } else {
2586                        false
2587                    };
2588                    let index_part = if is_zero_based_counter {
2589                        let idx_name = irecurse!(index);
2590                        format!("{} as usize", idx_name)
2591                    } else { match index {
2592                        Expr::Literal(Literal::Number(1)) => "0".to_string(),
2593                        Expr::Literal(Literal::Number(n)) => format!("{}", n - 1),
2594                        Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
2595                            match (left, right) {
2596                                (_, Expr::Literal(Literal::Number(1))) => {
2597                                    let left_str = irecurse!(left);
2598                                    if matches!(left, Expr::Identifier(_)) {
2599                                        format!("{} as usize", left_str)
2600                                    } else {
2601                                        format!("({}) as usize", left_str)
2602                                    }
2603                                }
2604                                (Expr::Literal(Literal::Number(1)), _) => {
2605                                    let right_str = irecurse!(right);
2606                                    if matches!(right, Expr::Identifier(_)) {
2607                                        format!("{} as usize", right_str)
2608                                    } else {
2609                                        format!("({}) as usize", right_str)
2610                                    }
2611                                }
2612                                (_, Expr::Literal(Literal::Number(k))) if *k > 1 => {
2613                                    format!("({} + {}) as usize", irecurse!(left), k - 1)
2614                                }
2615                                (Expr::Literal(Literal::Number(k)), _) if *k > 1 => {
2616                                    format!("({} + {}) as usize", irecurse!(right), k - 1)
2617                                }
2618                                _ => {
2619                                    format!("({} - 1) as usize", irecurse!(index))
2620                                }
2621                            }
2622                        }
2623                        _ => {
2624                            format!("({} - 1) as usize", irecurse!(index))
2625                        }
2626                    } };
2627                    // BCE: oracle-proven index → unchecked load (no bounds branch).
2628                    let read = if oracle_proves_index(ecx, collection, index) {
2629                        if suffix.is_empty() {
2630                            format!("unsafe {{ *{}.get_unchecked({}) }}", coll_str, index_part)
2631                        } else {
2632                            format!("unsafe {{ {}.get_unchecked({}){} }}", coll_str, index_part, suffix)
2633                        }
2634                    } else {
2635                        format!("{}[{}]{}", coll_str, index_part, suffix)
2636                    };
2637                    // A narrowed slice (`&[i32]`/`&mut [i32]`, from a hoisted
2638                    // `Vec<i32>`) sign-extends to i64 (lossless).
2639                    if elem == "i32" {
2640                        format!("(({}) as i64)", read)
2641                    } else {
2642                        read
2643                    }
2644                }
2645                Some(t) if is_logos_map_type(t) => {
2646                    let index_str = irecurse!(index);
2647                    // Use .get() which borrows the key (avoids moving String keys)
2648                    format!("{}.get(&({})).expect(\"Key not found in map\")", coll_str, index_str)
2649                }
2650                Some(t) if t.starts_with("std::collections::HashMap") || t.starts_with("HashMap") || t.starts_with("rustc_hash::FxHashMap") || t.starts_with("FxHashMap") => {
2651                    let index_str = irecurse!(index);
2652                    let suffix = if has_copy_value_type(t) { "" } else { ".clone()" };
2653                    format!("{}[&({})]{}", coll_str, index_str, suffix)
2654                }
2655                Some("String") => {
2656                    let index_str = irecurse!(index);
2657                    format!("LogosIndex::logos_get(&{}, {})", coll_str, index_str)
2658                }
2659                _ => {
2660                    let index_str = irecurse!(index);
2661                    format!("LogosIndex::logos_get(&{}, {})", coll_str, index_str)
2662                }
2663            }
2664        }
2665
2666        Expr::Slice { collection, start, end } => {
2667            let coll_str = recurse!(collection);
2668            let start_str = recurse!(start);
2669            let end_str = recurse!(end);
2670            // For LogosSeq, need to borrow the inner Vec first
2671            let known_type = if let Expr::Identifier(sym) = collection {
2672                variable_types.get(sym).map(|s| s.split("|__hl:").next().unwrap_or(s.as_str()))
2673            } else {
2674                None
2675            };
2676            if matches!(known_type, Some(t) if t.starts_with("LogosSeq")) {
2677                format!("&{}.borrow()[({} - 1) as usize..{} as usize]", coll_str, start_str, end_str)
2678            } else {
2679                format!("&{}[({} - 1) as usize..{} as usize]", coll_str, start_str, end_str)
2680            }
2681        }
2682
2683        Expr::Copy { expr: inner } => {
2684            // Special case: Copy of Slice → emit arr[range].to_vec() wrapped in LogosSeq
2685            if let Expr::Slice { collection, start, end } = inner {
2686                let coll_str = recurse!(collection);
2687                let start_str = recurse!(start);
2688                let end_str = recurse!(end);
2689                let known_type = if let Expr::Identifier(sym) = collection {
2690                    variable_types.get(sym).map(|s| s.split("|__hl:").next().unwrap_or(s.as_str()))
2691                } else {
2692                    None
2693                };
2694                if matches!(known_type, Some(t) if t.starts_with("LogosSeq")) {
2695                    format!("LogosSeq::from_vec({}.borrow()[({} - 1) as usize..{} as usize].to_vec())", coll_str, start_str, end_str)
2696                } else if matches!(known_type, Some(t) if t.starts_with("&[") || t.starts_with("Vec<")) {
2697                    format!("LogosSeq::from_vec({}[({} - 1) as usize..{} as usize].to_vec())", coll_str, start_str, end_str)
2698                } else {
2699                    format!("{}[({} - 1) as usize..{} as usize].to_vec()", coll_str, start_str, end_str)
2700                }
2701            } else {
2702                // Check if the inner expression is a LogosSeq/LogosMap → deep_clone()
2703                let known_type = if let Expr::Identifier(sym) = inner {
2704                    variable_types.get(sym).map(|s| s.split("|__hl:").next().unwrap_or(s.as_str()))
2705                } else {
2706                    None
2707                };
2708                let expr_str = recurse!(inner);
2709                if matches!(known_type, Some(t) if t.starts_with("Vec<")) {
2710                    // Slice variable stored as Vec<T> — wrap with LogosSeq::from_vec.
2711                    // One copy (.clone()) + zero-cost wrap, same as old pre-ref-semantics .to_vec().
2712                    format!("LogosSeq::from_vec({}.clone())", expr_str)
2713                } else if matches!(known_type, Some(t) if t.starts_with("&[")) {
2714                    format!("LogosSeq::from_vec({}.to_vec())", expr_str)
2715                } else if matches!(known_type, Some(t) if t.starts_with("LogosSeq") || t.starts_with("LogosMap")) {
2716                    format!("{}.deep_clone()", expr_str)
2717                } else {
2718                    format!("{}.to_owned()", expr_str)
2719                }
2720            }
2721        }
2722
2723        Expr::Give { value } => {
2724            // Ownership transfer: emit value without .clone()
2725            // The move semantics are implicit in Rust - no special syntax needed
2726            recurse!(value)
2727        }
2728
2729        // Affine read-only array: `length of A` is its trip count (the array is
2730        // deleted, so there is no `.len()` to read).
2731        Expr::Length { collection: Expr::Identifier(sym) }
2732            if affine_array_trip(variable_types.get(sym)).is_some() =>
2733        {
2734            format!("(({}) as i64)", affine_array_trip(variable_types.get(sym)).unwrap())
2735        }
2736
2737        // AoS-interleaved member: its length is the backing's fixed row count.
2738        Expr::Length { collection: Expr::Identifier(sym) }
2739            if parse_aos_tag(variable_types.get(sym)).is_some() =>
2740        {
2741            format!("{}i64", parse_aos_tag(variable_types.get(sym)).unwrap().len)
2742        }
2743
2744        Expr::Length { collection } => {
2745            if let Expr::Identifier(sym) = collection {
2746                if let Some(t) = variable_types.get(sym) {
2747                    if let Some(pos) = t.find("|__hl:") {
2748                        return t[pos + "|__hl:".len()..].to_string();
2749                    }
2750                }
2751            }
2752            let coll_str = recurse!(collection);
2753            // Phase 43D: Collection length - cast to i64 for LOGOS integer semantics
2754            format!("({}.len() as i64)", coll_str)
2755        }
2756
2757        Expr::Contains { collection, value } => {
2758            let coll_str = recurse!(collection);
2759            let val_str = recurse!(value);
2760            // Numeric-unified map key: a Float indexing an `Int`-keyed map coerces to its Int
2761            // (`1.0` → key `1`; a non-integral `1.5` matches nothing), mirroring the interpreter's
2762            // `1 == 1.0` key rule. An `i64`-keyed `logos_contains` otherwise rejects the `f64` operand.
2763            if let crate::analysis::types::LogosType::Map(k, _) =
2764                super::types::infer_logos_type(collection, interner, variable_types)
2765            {
2766                if matches!(*k, crate::analysis::types::LogosType::Int)
2767                    && super::types::infer_numeric_type(value, interner, variable_types) == "f64"
2768                {
2769                    return format!(
2770                        "logicaffeine_data::logos_i64_key_of_f64({}).map_or(false, |__k| {}.logos_contains(&__k))",
2771                        val_str, coll_str
2772                    );
2773                }
2774            }
2775            // Use LogosContains trait for unified contains across List, Set, Map, Text
2776            format!("{}.logos_contains(&{})", coll_str, val_str)
2777        }
2778
2779        Expr::Union { left, right } => {
2780            let left_str = recurse!(left);
2781            let right_str = recurse!(right);
2782            format!("{}.union(&{}).cloned().collect::<Set<_>>()", left_str, right_str)
2783        }
2784
2785        Expr::Intersection { left, right } => {
2786            let left_str = recurse!(left);
2787            let right_str = recurse!(right);
2788            format!("{}.intersection(&{}).cloned().collect::<Set<_>>()", left_str, right_str)
2789        }
2790
2791        // Phase 48: Sipping Protocol expressions
2792        Expr::ManifestOf { zone } => {
2793            let zone_str = recurse!(zone);
2794            format!("logicaffeine_system::network::FileSipper::from_zone(&{}).manifest()", zone_str)
2795        }
2796
2797        Expr::ChunkAt { index, zone } => {
2798            let zone_str = recurse!(zone);
2799            let index_str = irecurse!(index);
2800            // LOGOS uses 1-indexed, Rust uses 0-indexed
2801            format!("logicaffeine_system::network::FileSipper::from_zone(&{}).get_chunk(({} - 1) as usize)", zone_str, index_str)
2802        }
2803
2804        Expr::List(ref items) => {
2805            let item_strs: Vec<String> = items.iter()
2806                .map(|i| recurse!(i))
2807                .collect();
2808            format!("LogosSeq::from_vec(vec![{}])", item_strs.join(", "))
2809        }
2810
2811        Expr::Tuple(ref items) => {
2812            let item_strs: Vec<String> = items.iter()
2813                .map(|i| format!("Value::from({})", recurse!(i)))
2814                .collect();
2815            // Tuples as Vec<Value> for heterogeneous support
2816            format!("vec![{}]", item_strs.join(", "))
2817        }
2818
2819        Expr::Range { start, end } => {
2820            let start_str = recurse!(start);
2821            let end_str = recurse!(end);
2822            format!("({}..={})", start_str, end_str)
2823        }
2824
2825        Expr::FieldAccess { object, field } => {
2826            let field_name = interner.resolve(*field);
2827
2828            // Phase 52: Check if root object is synced - use .get().await
2829            let root_sym = get_root_identifier(object);
2830            if let Some(sym) = root_sym {
2831                if synced_vars.contains(&sym) {
2832                    let obj_name = interner.resolve(sym);
2833                    return format!("{}.get().await.{}", obj_name, field_name);
2834                }
2835            }
2836
2837            let obj_str = recurse!(object);
2838            format!("{}.{}", obj_str, field_name)
2839        }
2840
2841        Expr::New { type_name, type_args, init_fields } => {
2842            let type_str = interner.resolve(*type_name);
2843            if !init_fields.is_empty() {
2844                // Struct initialization with fields: Point { x: 10, y: 20, ..Default::default() }
2845                // Always add ..Default::default() to handle partial initialization (e.g., CRDT fields)
2846                let fields_str = init_fields.iter()
2847                    .map(|(name, value)| {
2848                        let field_name = interner.resolve(*name);
2849                        let value_str = recurse!(value);
2850                        format!("{}: {}", field_name, value_str)
2851                    })
2852                    .collect::<Vec<_>>()
2853                    .join(", ");
2854                format!("{} {{ {}, ..Default::default() }}", type_str, fields_str)
2855            } else if type_args.is_empty() {
2856                format!("{}::default()", type_str)
2857            } else {
2858                // Phase 34: Turbofish syntax for generic instantiation
2859                // Bug fix: Use codegen_type_expr to support nested types like Seq of (Seq of Int)
2860                let args_str = type_args.iter()
2861                    .map(|t| codegen_type_expr(t, interner))
2862                    .collect::<Vec<_>>()
2863                    .join(", ");
2864                format!("{}::<{}>::default()", type_str, args_str)
2865            }
2866        }
2867
2868        Expr::NewVariant { enum_name, variant, fields } => {
2869            let enum_str = interner.resolve(*enum_name);
2870            let variant_str = interner.resolve(*variant);
2871            if fields.is_empty() {
2872                // Unit variant: Shape::Point
2873                format!("{}::{}", enum_str, variant_str)
2874            } else {
2875                // Phase 103: Count identifier usage to handle cloning for reused values
2876                // We need to clone on all uses except the last one
2877                let mut identifier_counts: HashMap<Symbol, usize> = HashMap::new();
2878                for (_, value) in fields.iter() {
2879                    if let Expr::Identifier(sym) = value {
2880                        *identifier_counts.entry(*sym).or_insert(0) += 1;
2881                    }
2882                }
2883
2884                // Track remaining uses for each identifier
2885                let mut remaining_uses: HashMap<Symbol, usize> = identifier_counts.clone();
2886
2887                // Struct variant: Shape::Circle { radius: 10 }
2888                // Phase 102: Check if any field is recursive and needs Box::new()
2889                let fields_str: Vec<String> = fields.iter()
2890                    .map(|(field_name, value)| {
2891                        let name = interner.resolve(*field_name);
2892
2893                        // Phase 103: Clone identifiers that are used multiple times
2894                        // Clone on all uses except the last one (to allow move on final use)
2895                        let val = if let Expr::Identifier(sym) = value {
2896                            let total = identifier_counts.get(sym).copied().unwrap_or(0);
2897                            let remaining = remaining_uses.get_mut(sym);
2898                            let base_name = if boxed_bindings.contains(sym) {
2899                                format!("(*{})", interner.resolve(*sym))
2900                            } else {
2901                                interner.resolve(*sym).to_string()
2902                            };
2903                            if total > 1 {
2904                                if let Some(r) = remaining {
2905                                    *r -= 1;
2906                                    if *r > 0 {
2907                                        // Not the last use, need to clone
2908                                        format!("{}.clone()", base_name)
2909                                    } else {
2910                                        // Last use, can move
2911                                        base_name
2912                                    }
2913                                } else {
2914                                    base_name
2915                                }
2916                            } else {
2917                                base_name
2918                            }
2919                        } else {
2920                            recurse!(value)
2921                        };
2922
2923                        // Check if this field needs to be boxed (recursive type)
2924                        let key = (enum_str.to_string(), variant_str.to_string(), name.to_string());
2925                        if boxed_fields.contains(&key) {
2926                            format!("{}: Box::new({})", name, val)
2927                        } else {
2928                            format!("{}: {}", name, val)
2929                        }
2930                    })
2931                    .collect();
2932                format!("{}::{} {{ {} }}", enum_str, variant_str, fields_str.join(", "))
2933            }
2934        }
2935
2936        Expr::OptionSome { value } => {
2937            format!("Some({})", recurse!(value))
2938        }
2939
2940        Expr::OptionNone => {
2941            "None".to_string()
2942        }
2943
2944        Expr::Escape { code, .. } => {
2945            let raw_code = interner.resolve(*code);
2946            let mut block = String::from("{\n");
2947            for line in raw_code.lines() {
2948                block.push_str("    ");
2949                block.push_str(line);
2950                block.push('\n');
2951            }
2952            block.push('}');
2953            block
2954        }
2955
2956        Expr::WithCapacity { value, capacity } => {
2957            let cap_str = recurse!(capacity);
2958            match value {
2959                // Empty string → String::with_capacity(cap)
2960                Expr::Literal(Literal::Text(sym)) if interner.resolve(*sym).is_empty() => {
2961                    format!("String::with_capacity(({}) as usize)", cap_str)
2962                }
2963                // Non-empty string → { let mut __s = String::with_capacity(cap); __s.push_str("..."); __s }
2964                Expr::Literal(Literal::Text(sym)) => {
2965                    let text = interner.resolve(*sym);
2966                    format!("{{ let mut __s = String::with_capacity(({}) as usize); __s.push_str(\"{}\"); __s }}", cap_str, text)
2967                }
2968                // Collection Expr::New → Type::with_capacity(cap)
2969                Expr::New { type_name, type_args, .. } => {
2970                    let type_str = interner.resolve(*type_name);
2971                    match type_str {
2972                        "Seq" | "List" | "Vec" => {
2973                            let elem = if !type_args.is_empty() {
2974                                codegen_type_expr(&type_args[0], interner)
2975                            } else { "()".to_string() };
2976                            format!("LogosSeq::<{}>::with_capacity(({}) as usize)", elem, cap_str)
2977                        }
2978                        "Map" | "HashMap" => {
2979                            let (k, v) = if type_args.len() >= 2 {
2980                                (codegen_type_expr(&type_args[0], interner),
2981                                 codegen_type_expr(&type_args[1], interner))
2982                            } else { ("String".to_string(), "String".to_string()) };
2983                            format!("LogosMap::<{}, {}>::with_capacity(({}) as usize)", k, v, cap_str)
2984                        }
2985                        "Set" | "HashSet" => {
2986                            let elem = if !type_args.is_empty() {
2987                                codegen_type_expr(&type_args[0], interner)
2988                            } else { "()".to_string() };
2989                            format!("{{ let __s: Set<{}> = Set::with_capacity_and_hasher(({}) as usize, Default::default()); __s }}", elem, cap_str)
2990                        }
2991                        _ => recurse!(value) // Unknown type — ignore capacity
2992                    }
2993                }
2994                // Other expressions — ignore capacity hint
2995                _ => recurse!(value)
2996            }
2997        }
2998
2999        Expr::Closure { params, body, .. } => {
3000            use crate::ast::stmt::ClosureBody;
3001            let params_str: Vec<String> = params.iter()
3002                .map(|(name, ty)| {
3003                    let param_name = names.ident(*name);
3004                    let param_type = codegen_type_expr(ty, interner);
3005                    format!("{}: {}", param_name, param_type)
3006                })
3007                .collect();
3008
3009            match body {
3010                ClosureBody::Expression(expr) => {
3011                    let body_str = recurse!(expr);
3012                    format!("move |{}| {{ {} }}", params_str.join(", "), body_str)
3013                }
3014                ClosureBody::Block(stmts) => {
3015                    let mut body_str = String::new();
3016                    let mut ctx = RefinementContext::new();
3017                    let empty_mutable = collect_mutable_vars(stmts);
3018                    let empty_lww = HashSet::new();
3019                    let empty_mv = HashSet::new();
3020                    let mut empty_synced = HashSet::new();
3021                    let empty_caps = HashMap::new();
3022                    let empty_pipes = HashSet::new();
3023                    let empty_boxed = HashSet::new();
3024                    let empty_registry = TypeRegistry::new();
3025                    let type_env = crate::analysis::types::TypeEnv::new();
3026                    for stmt in stmts.iter() {
3027                        body_str.push_str(&codegen_stmt(
3028                            stmt, interner, 2, &empty_mutable, &mut ctx,
3029                            &empty_lww, &empty_mv, &mut empty_synced, &empty_caps,
3030                            async_functions, &empty_pipes, &empty_boxed, &empty_registry,
3031                            &type_env,
3032                        ));
3033                    }
3034                    format!("move |{}| {{\n{}{}}}", params_str.join(", "), body_str, "    ")
3035                }
3036            }
3037        }
3038
3039        Expr::CallExpr { callee, args } => {
3040            let callee_str = recurse!(callee);
3041            let args_str: Vec<String> = args.iter().map(|a| recurse!(a)).collect();
3042            format!("({})({})", callee_str, args_str.join(", "))
3043        }
3044
3045        Expr::InterpolatedString(parts) => {
3046            codegen_interpolated_string(parts, ecx)
3047        }
3048
3049        Expr::Not { operand } => {
3050            // Logical negation of truthiness (`~` is the bitwise complement).
3051            let operand_str = recurse!(operand);
3052            if matches!(
3053                infer_logos_type(operand, interner, variable_types),
3054                crate::analysis::types::LogosType::Bool
3055            ) {
3056                format!("!({})", operand_str)
3057            } else {
3058                format!("(!logos_truthy(&({})))", operand_str)
3059            }
3060        }
3061    }
3062}
3063
3064pub(crate) fn codegen_interpolated_string(
3065    parts: &[crate::ast::stmt::StringPart],
3066    ecx: &ExprCtx,
3067) -> String {
3068    use crate::ast::stmt::StringPart;
3069    let interner = ecx.interner;
3070
3071    let mut fmt_str = String::new();
3072    let mut args = Vec::new();
3073
3074    for part in parts {
3075        match part {
3076            StringPart::Literal(sym) => {
3077                let text = interner.resolve(*sym);
3078                // Escape braces and special chars in the format string
3079                for ch in text.chars() {
3080                    match ch {
3081                        '{' => fmt_str.push_str("{{"),
3082                        '}' => fmt_str.push_str("}}"),
3083                        '\n' => fmt_str.push_str("\\n"),
3084                        '\t' => fmt_str.push_str("\\t"),
3085                        '\r' => fmt_str.push_str("\\r"),
3086                        _ => fmt_str.push(ch),
3087                    }
3088                }
3089            }
3090            StringPart::Expr { value, format_spec, debug } => {
3091                if *debug {
3092                    let debug_prefix = expr_debug_prefix(value, interner);
3093                    for ch in debug_prefix.chars() {
3094                        match ch {
3095                            '{' => fmt_str.push_str("{{"),
3096                            '}' => fmt_str.push_str("}}"),
3097                            _ => fmt_str.push(ch),
3098                        }
3099                    }
3100                    fmt_str.push('=');
3101                }
3102                let needs_float_cast = if let Some(spec) = format_spec {
3103                    let spec_str = interner.resolve(*spec);
3104                    if spec_str == "$" {
3105                        fmt_str.push('$');
3106                        fmt_str.push_str("{:.2}");
3107                        true
3108                    } else if spec_str.starts_with('.') {
3109                        fmt_str.push_str(&format!("{{:{}}}", spec_str));
3110                        true
3111                    } else {
3112                        fmt_str.push_str(&format!("{{:{}}}", spec_str));
3113                        false
3114                    }
3115                } else {
3116                    fmt_str.push_str("{}");
3117                    false
3118                };
3119                let arg_str = codegen_expr_ctx(value, ecx);
3120                if needs_float_cast {
3121                    args.push(format!("{} as f64", arg_str));
3122                } else {
3123                    args.push(arg_str);
3124                }
3125            }
3126        }
3127    }
3128
3129    if args.is_empty() {
3130        // No holes — emit raw String::from (no format! escaping needed).
3131        // Reconstruct the raw text from parts without brace escaping.
3132        let mut raw = String::new();
3133        for part in parts {
3134            if let StringPart::Literal(sym) = part {
3135                let text = interner.resolve(*sym);
3136                for ch in text.chars() {
3137                    match ch {
3138                        '\n' => raw.push_str("\\n"),
3139                        '\t' => raw.push_str("\\t"),
3140                        '\r' => raw.push_str("\\r"),
3141                        '"' => raw.push_str("\\\""),
3142                        '\\' => raw.push_str("\\\\"),
3143                        _ => raw.push(ch),
3144                    }
3145                }
3146            }
3147        }
3148        format!("String::from(\"{}\")", raw)
3149    } else {
3150        format!("format!(\"{}\"{})", fmt_str, args.iter().map(|a| format!(", {}", a)).collect::<String>())
3151    }
3152}
3153
3154pub(crate) fn codegen_literal(lit: &Literal, interner: &Interner) -> String {
3155    match lit {
3156        Literal::Number(n) => {
3157            if *n > i32::MAX as i64 || *n < i32::MIN as i64 {
3158                format!("{}_i64", n)
3159            } else {
3160                n.to_string()
3161            }
3162        }
3163        // Non-finite values have no numeric-literal spelling in Rust
3164        // (`Display` gives "inf"/"NaN", which would emit invalid tokens).
3165        Literal::Float(f) if f.is_nan() => "f64::NAN".to_string(),
3166        Literal::Float(f) if f.is_infinite() && *f > 0.0 => "f64::INFINITY".to_string(),
3167        Literal::Float(f) if f.is_infinite() => "f64::NEG_INFINITY".to_string(),
3168        Literal::Float(f) => format!("{}f64", f),
3169        // String literals are converted to String for consistent Text type handling
3170        Literal::Text(sym) => {
3171            let raw = interner.resolve(*sym);
3172            let escaped: String = raw.chars().map(|c| match c {
3173                '\n' => "\\n".to_string(),
3174                '\r' => "\\r".to_string(),
3175                '\t' => "\\t".to_string(),
3176                '\\' => "\\\\".to_string(),
3177                '"' => "\\\"".to_string(),
3178                other => other.to_string(),
3179            }).collect();
3180            format!("String::from(\"{}\")", escaped)
3181        }
3182        Literal::Boolean(b) => b.to_string(),
3183        Literal::Nothing => "()".to_string(),
3184        // Character literals
3185        Literal::Char(c) => {
3186            // Handle escape sequences for special characters
3187            match c {
3188                '\n' => "'\\n'".to_string(),
3189                '\t' => "'\\t'".to_string(),
3190                '\r' => "'\\r'".to_string(),
3191                '\\' => "'\\\\'".to_string(),
3192                '\'' => "'\\''".to_string(),
3193                '\0' => "'\\0'".to_string(),
3194                c => format!("'{}'", c),
3195            }
3196        }
3197        // Temporal literals: Duration stored as nanoseconds (i64)
3198        Literal::Duration(nanos) => format!("std::time::Duration::from_nanos({}u64)", nanos),
3199        // Date stored as days since Unix epoch (i32)
3200        Literal::Date(days) => format!("LogosDate({})", days),
3201        // Moment stored as nanoseconds since Unix epoch (i64)
3202        Literal::Moment(nanos) => format!("LogosMoment({})", nanos),
3203        // Span stored as (months, days) - separate because they're incommensurable
3204        Literal::Span { months, days } => format!("LogosSpan::new({}, {})", months, days),
3205        // Time-of-day stored as nanoseconds from midnight
3206        Literal::Time(nanos) => format!("LogosTime({})", nanos),
3207    }
3208}
3209
3210/// Converts a LogicExpr to a Rust boolean expression for debug_assert!().
3211/// Uses RustFormatter to unify all logic-to-Rust translation.
3212pub fn codegen_assertion(expr: &LogicExpr, interner: &Interner) -> String {
3213    let mut registry = SymbolRegistry::new();
3214    let formatter = RustFormatter;
3215    let mut buf = String::new();
3216
3217    match expr.write_logic(&mut buf, &mut registry, interner, &formatter) {
3218        Ok(_) => buf,
3219        Err(_) => "/* error generating assertion */ false".to_string(),
3220    }
3221}
3222
3223pub fn codegen_term(term: &Term, interner: &Interner) -> String {
3224    match term {
3225        Term::Constant(sym) => interner.resolve(*sym).to_string(),
3226        Term::Variable(sym) => interner.resolve(*sym).to_string(),
3227        Term::Value { kind, .. } => match kind {
3228            NumberKind::Integer(n) => n.to_string(),
3229            NumberKind::Real(f) => f.to_string(),
3230            NumberKind::Symbolic(sym) => interner.resolve(*sym).to_string(),
3231        },
3232        Term::Function(name, args) => {
3233            let args_str: Vec<String> = args.iter()
3234                .map(|a| codegen_term(a, interner))
3235                .collect();
3236            format!("{}({})", interner.resolve(*name), args_str.join(", "))
3237        }
3238        Term::Possessed { possessor, possessed } => {
3239            let poss_str = codegen_term(possessor, interner);
3240            format!("{}.{}", poss_str, interner.resolve(*possessed))
3241        }
3242        Term::Group(members) => {
3243            let members_str: Vec<String> = members.iter()
3244                .map(|m| codegen_term(m, interner))
3245                .collect();
3246            format!("({})", members_str.join(", "))
3247        }
3248        _ => "/* unsupported Term */".to_string(),
3249    }
3250}
3251
3252#[cfg(test)]
3253mod tests {
3254    use super::*;
3255
3256    #[test]
3257    fn test_literal_number() {
3258        let interner = Interner::new();
3259        let synced_vars = HashSet::new();
3260        let expr = Expr::Literal(Literal::Number(42));
3261        assert_eq!(codegen_expr(&expr, &interner, &synced_vars), "42");
3262    }
3263
3264    #[test]
3265    fn test_literal_boolean() {
3266        let interner = Interner::new();
3267        let synced_vars = HashSet::new();
3268        assert_eq!(codegen_expr(&Expr::Literal(Literal::Boolean(true)), &interner, &synced_vars), "true");
3269        assert_eq!(codegen_expr(&Expr::Literal(Literal::Boolean(false)), &interner, &synced_vars), "false");
3270    }
3271
3272    #[test]
3273    fn test_literal_nothing() {
3274        let interner = Interner::new();
3275        let synced_vars = HashSet::new();
3276        assert_eq!(codegen_expr(&Expr::Literal(Literal::Nothing), &interner, &synced_vars), "()");
3277    }
3278}