//! FROZEN ABI byte-pin — see design/contracts/0006-frozen-value-layout.md.
//! Pins the heap (@ailang_rc_alloc) box layout a boundary-crossing
//! single-ctor scalar record takes: size = 8 + n*8, tag i64 @ offset
//! 0, fields i64-strided from offset 8. Goes RED if codegen moves an
//! offset — the M3 freeze made enforceable, not aspirational.
//!
//! The carrier (`embed_record_layout_carrier.ail`) is a non-export
//! exe-target program whose `mk` returns a `Pt` consumed by an
//! `(own (con Pt))` param `sum_pt`, forcing the escaping heap box
//! (NOT the `alloca` non-escape path the pre-existing
//! `iter17a_local_box_alloca` pins — that is a different path and not
//! a substitute for this one). Lowered under `AllocStrategy::Rc`
//! (the path a crossing M3 record actually takes), mirroring the
//! workspace-load + lower incantation of `embed_staticlib_lowering.rs`.

use ailang_core::Workspace;
use std::path::PathBuf;

fn load(file: &str) -> Workspace {
    let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .parent().unwrap().parent().unwrap()
        .join("examples").join(file);
    ailang_surface::load_workspace(&path).unwrap()
}

fn lower_carrier_ir() -> String {
    let ws = load("embed_record_layout_carrier.ail");
    let mir = ailang_check::elaborate_workspace(&ws).expect("elaborates");
    ailang_codegen::lower_workspace_with_alloc(
        &mir, ailang_codegen::AllocStrategy::Rc).unwrap()
}

#[test]
fn heap_box_layout_is_frozen_8_plus_n_times_8_tag_at_0_fields_from_8() {
    let ir = lower_carrier_ir();
    // Pt has 2 Int fields → payload size 8 (tag) + 2*8 (fields) = 24,
    // on the @ailang_rc_alloc heap path (record crosses the boundary).
    assert!(ir.contains("call ptr @ailang_rc_alloc(i64 24)"),
        "frozen: heap box payload size = 8 + n*8 (n=2 → 24); IR:\n{ir}");
    // constructor tag written at offset 0 (single-ctor → 0; no elision)
    assert!(ir.contains("store i64 0, ptr"),
        "frozen: ctor tag i64 at offset 0; IR:\n{ir}");
    // field 0 at offset 8, field 1 at offset 16 (i64-strided from 8)
    assert!(ir.contains("getelementptr inbounds i8, ptr") && ir.contains("i64 8"),
        "frozen: field 0 at payload offset 8; IR:\n{ir}");
    assert!(ir.contains("i64 16"),
        "frozen: field 1 at payload offset 16 (8 + 1*8); IR:\n{ir}");
}
