logicaffeine-compile 0.10.1

LOGOS compilation pipeline - codegen and interpreter
Documentation
//! Code generation from LOGOS AST to Rust source code.
//!
//! This module transforms the parsed and type-checked LOGOS program into
//! idiomatic Rust code. The generated code uses `logicaffeine_data` types
//! for runtime values and integrates with the kernel for proof verification.
//!
//! # Pipeline Position
//!
//! ```text
//! ┌─────────────────────────────────────────────────────────┐
//! │  LOGOS Source → Lexer → Parser → AST → Analysis → HERE │
//! └─────────────────────────────────────────────────────────┘
//!//!                                               Rust Source
//! ```
//!
//! # Code Generation Rules
//!
//! | LOGOS Statement | Rust Output |
//! |-----------------|-------------|
//! | `Let x be 5.` | `let x = 5;` |
//! | `Set x to 10.` | `x = 10;` |
//! | `Give x to y.` | `let y = x;` (move) |
//! | `Show x to show.` | `println!("{}", x);` |
//! | `If x > 0 then...` | `if x > 0 { ... }` |
//! | `Repeat while x > 0...` | `while x > 0 { ... }` |
//! | `Zone "name"...` | `{ /* zone scope */ }` |
//! | `Mount x at "path".` | `x.mount(vfs, "path").await;` |
//! | `Sync x on "topic".` | `x.subscribe("topic").await;` |
//!
//! # Key Features
//!
//! - **Refinement Types**: Generates `debug_assert!` for type predicates
//! - **Policy Enforcement**: Emits capability checks for access control
//! - **Zone Safety**: Translates memory zones to Rust scopes
//! - **CRDT Mutability**: Uses `.set()` for LWWRegister/MVRegister fields
//! - **Async Detection**: Adds `#[tokio::main]` when async operations are present
//! - **VFS Detection**: Injects `get_platform_vfs()` for file operations (io_uring on Linux, NativeVfs elsewhere)
//! - **Mount+Sync Detection**: Uses `Distributed<T>` for combined persistence/sync
//!
//! # Refinement Context
//!
//! The [`RefinementContext`] tracks type predicates across scopes:
//!
//! ```text
//! Let x: { it: Int | it > 0 } be 5.   ←  Register constraint
//!//! debug_assert!(5 > 0);               ←  Check at definition
//!//! Set x to 10.                        ←  Re-check on mutation
//!//! debug_assert!(10 > 0);              ←  Re-emit assertion
//! ```
//!
//! - When a variable with a refinement type is defined, its constraint is registered
//! - When that variable is mutated, the assertion is re-emitted
//! - Variable types are tracked for capability resolution
//!
//! # Entry Point
//!
//! The main entry point is [`codegen_program`], which generates a complete Rust
//! program from a list of statements.

// Module declarations
pub(crate) mod bigint_promote;
pub(crate) mod context;
pub(crate) mod detection;
pub(crate) mod types;
mod policy;
pub(crate) mod ffi;
pub(crate) mod bindings;
pub(crate) mod tce;
pub(crate) mod marshal;
pub(crate) mod peephole;
pub(crate) mod hoist;
pub(crate) mod stmt;
pub(crate) mod expr;
pub(crate) mod program;
pub(crate) mod worklist;
pub(crate) mod affine_array;
pub(crate) mod narrow;
pub(crate) mod i64_map;
pub(crate) mod fast_div;
pub(crate) mod entry_guard;
pub(crate) mod strsearch;
// Per-function codegen slicing for the AOT-native tier (HOTSWAP Axis-3 / P14a).
pub(crate) mod slice;

// ─── External API re-exports ────────────────────────────────────────────────
// These preserve the public API used by compile.rs, ui_bridge.rs, and test files.

pub use context::{RefinementContext, VariableCapabilities, empty_var_caps};
pub use detection::{collect_async_functions, collect_pipe_sender_params, collect_pipe_vars};
pub use program::{codegen_program, codegen_program_mapped, codegen_program_with_proven};
pub use slice::function_slice;
pub use marshal::codegen_native_tier_export;
pub use hoist::force_disable_for_test as force_disable_borrow_hoist_for_test;
pub use ffi::generate_c_header;
pub use bindings::{generate_python_bindings, generate_typescript_bindings};
pub use expr::{codegen_expr, codegen_assertion, codegen_term};
pub use stmt::codegen_stmt;

// ─── Mode-B (deterministic-replay) codegen gate ─────────────────────────────
//
// A compile-global flag: when set, a `Select` emits the *seeded* winner-pick
// (sharing the interpreter's choice function) instead of a raw `tokio::select!`.
// The DEFAULT (Mode A) is off, so default emission stays byte-identical to today
// — the one bit Phase 8 adds. Codegen is single-threaded per compile, so a
// thread-local is the natural home; `compile::compile_to_rust_deterministic`
// sets it for the extent of one compile and restores it after.

thread_local! {
    static SEEDED_SELECT: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
}

/// Is the deterministic-replay (Mode-B) `Select` lowering active for this compile?
pub(crate) fn seeded_select_enabled() -> bool {
    SEEDED_SELECT.with(|c| c.get())
}

/// Run `f` with Mode-B `Select` lowering enabled, restoring the previous mode
/// afterwards (panic-safe). Returns `f`'s result.
pub fn with_seeded_select<R>(f: impl FnOnce() -> R) -> R {
    struct Restore(bool);
    impl Drop for Restore {
        fn drop(&mut self) {
            SEEDED_SELECT.with(|c| c.set(self.0));
        }
    }
    let _guard = SEEDED_SELECT.with(|c| {
        let prev = c.get();
        c.set(true);
        Restore(prev)
    });
    f()
}

// ─── Internal cross-module re-exports ───────────────────────────────────────
// These allow sibling submodules to use `use super::item` for items
// that moved out of mod.rs into submodule files.

pub(crate) use ffi::{
    CAbiClass, classify_type_for_c_abi, has_wasm_exports, has_c_exports, has_c_exports_with_text,
    codegen_c_accessors, collect_c_export_ref_structs, collect_c_export_reference_types,
    collect_c_export_value_type_structs, codegen_logos_runtime_preamble, mangle_type_for_c,
    map_type_to_c_header, map_field_type_to_c,
};
pub(crate) use expr::{
    codegen_expr_with_async, codegen_expr_boxed, codegen_expr_boxed_with_strings,
    codegen_expr_boxed_with_types, codegen_interpolated_string, codegen_literal,
    codegen_expr_with_async_and_strings, is_definitely_string_expr_with_vars,
    is_definitely_string_expr, is_definitely_numeric_expr, collect_string_concat_operands,
};
pub(crate) use stmt::{get_root_identifier, is_copy_type, has_copy_element_type, has_copy_value_type};
pub(crate) use peephole::{
    try_emit_for_range_pattern, try_emit_vec_fill_pattern, try_emit_swap_pattern,
    try_emit_prefix_reverse,
    try_emit_seq_copy_pattern, try_emit_seq_from_slice_pattern,
    try_emit_bare_slice_push_pattern,
    try_emit_vec_with_capacity_pattern, try_emit_merge_capacity_pattern,
    try_emit_string_with_capacity_pattern,
    try_emit_rotate_left_pattern,
    try_emit_buffer_reuse_while,
    body_mutates_collection, body_modifies_var, exprs_equal,
};
pub(crate) use types::is_recursive_field;

/// Check if a name is a Rust keyword (needs `r#` escaping in generated code).
pub(crate) fn is_rust_keyword(name: &str) -> bool {
    matches!(name,
        "as" | "async" | "await" | "break" | "const" | "continue" | "crate" |
        "dyn" | "else" | "enum" | "extern" | "false" | "fn" | "for" | "if" |
        "impl" | "in" | "let" | "loop" | "match" | "mod" | "move" | "mut" |
        "pub" | "ref" | "return" | "self" | "Self" | "static" | "struct" |
        "super" | "trait" | "true" | "type" | "unsafe" | "use" | "where" |
        "while" | "abstract" | "become" | "box" | "do" | "final" | "macro" |
        "override" | "priv" | "try" | "typeof" | "unsized" | "virtual" | "yield"
    )
}

/// Escape a name as a Rust raw identifier if it's a keyword.
/// e.g., "move" → "r#move", "add" → "add"
pub(crate) fn escape_rust_ident(name: &str) -> String {
    if is_rust_keyword(name) {
        format!("r#{}", name)
    } else {
        name.to_string()
    }
}

/// A call-site ABI role a function parameter can carry, keyed by parameter index.
/// A single function may play SEVERAL at once — the canonical case is a readonly
/// `Seq` param (de-Rc'd to `&[T]`) alongside a value-semantics `mutable` collection
/// param (passed as `&LogosSeq`/`&LogosMap`).
#[derive(Clone, Copy)]
pub(crate) enum FnRole {
    /// Readonly borrow (`&[T]`) — the `borrow_params_map` opt.
    Borrow,
    /// Element-mutating borrow (`&mut [T]`) — the `mut_borrow_params_map` opt.
    MutBorrow,
    /// Value-semantics `mutable` collection (`&LogosSeq`/`&LogosMap`).
    ValueMutable,
}

/// Pack the per-parameter role index sets a function plays into ONE `variable_types`
/// slot. The slot is single-valued, so registering each role as its own
/// `fn_borrow:` / `fn_value_mutable:` string used to make them clobber each other —
/// a function with both a readonly-borrow param and a `mutable` param would keep
/// only the last-registered role, and every call site then mis-lowered the other
/// (cloning an owned `LogosSeq` into a `&[T]` slot). This keeps all roles together.
pub(crate) fn encode_fn_roles(
    borrow: &std::collections::HashSet<usize>,
    mut_borrow: &std::collections::HashSet<usize>,
    value_mutable: &std::collections::HashSet<usize>,
) -> String {
    fn csv(s: &std::collections::HashSet<usize>) -> String {
        let mut v: Vec<usize> = s.iter().copied().collect();
        v.sort_unstable();
        v.iter().map(|i| i.to_string()).collect::<Vec<_>>().join(",")
    }
    format!(
        "fn_roles|b={}|m={}|v={}",
        csv(borrow),
        csv(mut_borrow),
        csv(value_mutable)
    )
}

/// The parameter indices for one [`FnRole`] out of a slot written by
/// [`encode_fn_roles`]. Empty when the slot is absent or is an ordinary variable
/// type rather than a role encoding.
pub(crate) fn fn_role_indices(
    encoded: Option<&String>,
    role: FnRole,
) -> std::collections::HashSet<usize> {
    let empty = std::collections::HashSet::new();
    let Some(rest) = encoded.and_then(|s| s.strip_prefix("fn_roles|")) else {
        return empty;
    };
    let key = match role {
        FnRole::Borrow => "b=",
        FnRole::MutBorrow => "m=",
        FnRole::ValueMutable => "v=",
    };
    for section in rest.split('|') {
        if let Some(csv) = section.strip_prefix(key) {
            return csv.split(',').filter_map(|i| i.parse().ok()).collect();
        }
    }
    empty
}

/// True when a `variable_types` slot is a [`encode_fn_roles`] encoding (a function
/// symbol's ABI role) rather than an ordinary variable type.
pub(crate) fn is_fn_role_slot(ty: &str) -> bool {
    ty.starts_with("fn_roles|")
}