vyre-foundation 0.6.3

Foundation layer: IR, type system, memory model, wire format. Zero application semantics. Part of the vyre GPU compiler.
Documentation
use super::*;

#[test]
fn partial_eq_ignores_buffer_declaration_order() {
    let left = Program::wrapped(
        vec![
            BufferDecl::output("out", 0, DataType::U32).with_count(1),
            BufferDecl::read("input", 1, DataType::U32).with_count(1),
        ],
        [1, 1, 1],
        sample_body(),
    );
    let right = Program::wrapped(
        vec![
            BufferDecl::read("input", 1, DataType::U32).with_count(1),
            BufferDecl::output("out", 0, DataType::U32).with_count(1),
        ],
        [1, 1, 1],
        sample_body(),
    );

    assert_eq!(
        left, right,
        "Fix: Program equality must ignore buffer declaration order."
    );
    assert!(
        left.structural_eq(&right),
        "Fix: structural_eq must agree with PartialEq on reordered buffers."
    );
}

#[test]
fn structural_eq_rejects_semantic_entry_differences() {
    let left = Program::wrapped(
        vec![BufferDecl::output("out", 0, DataType::U32).with_count(1)],
        [1, 1, 1],
        vec![Node::store("out", Expr::u32(0), Expr::u32(7)), Node::Return],
    );
    let right = Program::wrapped(
        vec![BufferDecl::output("out", 0, DataType::U32).with_count(1)],
        [1, 1, 1],
        vec![Node::store("out", Expr::u32(0), Expr::u32(9)), Node::Return],
    );

    assert!(
        !left.structural_eq(&right),
        "Fix: structural_eq must reject programs whose observable writes differ."
    );
}

#[test]
fn canonical_fingerprint_normalizes_commutative_literal_order() {
    let left = Program::wrapped(
        vec![BufferDecl::output("out", 0, DataType::U32).with_count(1)],
        [1, 1, 1],
        vec![Node::store(
            "out",
            Expr::u32(0),
            Expr::add(Expr::u32(7), Expr::var("x")),
        )],
    );
    let right = Program::wrapped(
        vec![BufferDecl::output("out", 0, DataType::U32).with_count(1)],
        [1, 1, 1],
        vec![Node::store(
            "out",
            Expr::u32(0),
            Expr::add(Expr::var("x"), Expr::u32(7)),
        )],
    );

    assert_ne!(
        left.to_wire().expect("Fix: left fixture must encode"),
        right.to_wire().expect("Fix: right fixture must encode"),
        "Fix: this regression test must exercise distinct author wire forms."
    );
    assert_eq!(
        left.fingerprint(),
        right.fingerprint(),
        "Fix: canonical Program fingerprint must ignore commutative literal spelling."
    );
}

#[test]
fn canonical_fingerprint_normalizes_safe_commutative_nonliteral_order() {
    let left = Program::wrapped(
        vec![BufferDecl::output("out", 0, DataType::U32).with_count(1)],
        [1, 1, 1],
        vec![Node::store(
            "out",
            Expr::u32(0),
            Expr::bitxor(Expr::var("a"), Expr::var("b")),
        )],
    );
    let right = Program::wrapped(
        vec![BufferDecl::output("out", 0, DataType::U32).with_count(1)],
        [1, 1, 1],
        vec![Node::store(
            "out",
            Expr::u32(0),
            Expr::bitxor(Expr::var("b"), Expr::var("a")),
        )],
    );

    assert_eq!(
        left.fingerprint(),
        right.fingerprint(),
        "Fix: canonical Program fingerprint must sort safe commutative operands."
    );
}

#[test]
fn canonical_fingerprint_preserves_float_sensitive_nonliteral_order() {
    for (left_value, right_value) in [
        (
            Expr::add(Expr::var("a"), Expr::var("b")),
            Expr::add(Expr::var("b"), Expr::var("a")),
        ),
        (
            Expr::mul(Expr::var("a"), Expr::var("b")),
            Expr::mul(Expr::var("b"), Expr::var("a")),
        ),
        (
            Expr::min(Expr::var("a"), Expr::var("b")),
            Expr::min(Expr::var("b"), Expr::var("a")),
        ),
        (
            Expr::max(Expr::var("a"), Expr::var("b")),
            Expr::max(Expr::var("b"), Expr::var("a")),
        ),
    ] {
        let left = Program::wrapped(
            vec![BufferDecl::output("out", 0, DataType::U32).with_count(1)],
            [1, 1, 1],
            vec![Node::store("out", Expr::u32(0), left_value)],
        );
        let right = Program::wrapped(
            vec![BufferDecl::output("out", 0, DataType::U32).with_count(1)],
            [1, 1, 1],
            vec![Node::store("out", Expr::u32(0), right_value)],
        );

        assert_ne!(
            left.fingerprint(),
            right.fingerprint(),
            "Fix: canonical Program fingerprint must preserve order for float-sensitive ops."
        );
    }
}

#[test]
fn canonical_wire_hash_is_blake3_of_canonical_wire_bytes() {
    let program = Program::wrapped(
        vec![
            BufferDecl::read("input", 1, DataType::U32).with_count(1),
            BufferDecl::output("out", 0, DataType::U32).with_count(1),
        ],
        [1, 1, 1],
        vec![Node::store(
            "out",
            Expr::u32(0),
            Expr::bitxor(Expr::var("b"), Expr::var("a")),
        )],
    );
    let canonical_wire = program
        .canonical_wire_bytes()
        .expect("Fix: canonical fixture must encode");
    let expected = *blake3::hash(&canonical_wire).as_bytes();

    assert_eq!(program.fingerprint(), expected);
    assert_eq!(
        *program
            .canonical_wire_hash()
            .expect("Fix: canonical fixture must hash")
            .as_bytes(),
        expected
    );
}

#[test]
fn canonical_wire_hash_normalizes_float_payload_noise() {
    let nan_a = Program::wrapped(
        Vec::new(),
        [1, 1, 1],
        vec![Node::let_bind("x", Expr::f32(f32::from_bits(0x7FC1_2345)))],
    );
    let nan_b = Program::wrapped(
        Vec::new(),
        [1, 1, 1],
        vec![Node::let_bind("x", Expr::f32(f32::from_bits(0x7FA0_0001)))],
    );
    let subnormal = Program::wrapped(
        Vec::new(),
        [1, 1, 1],
        vec![Node::let_bind("x", Expr::f32(f32::from_bits(1)))],
    );
    let zero = Program::wrapped(
        Vec::new(),
        [1, 1, 1],
        vec![Node::let_bind("x", Expr::f32(0.0))],
    );

    assert_eq!(nan_a.fingerprint(), nan_b.fingerprint());
    assert_eq!(subnormal.fingerprint(), zero.fingerprint());
}

#[test]
fn canonical_fingerprint_flattens_binding_free_nested_blocks() {
    let nested = Program::wrapped(
        vec![BufferDecl::output("out", 0, DataType::U32).with_count(1)],
        [1, 1, 1],
        vec![Node::block(vec![Node::block(vec![Node::store(
            "out",
            Expr::u32(0),
            Expr::u32(1),
        )])])],
    );
    let flat = Program::wrapped(
        vec![BufferDecl::output("out", 0, DataType::U32).with_count(1)],
        [1, 1, 1],
        vec![Node::store("out", Expr::u32(0), Expr::u32(1))],
    );

    assert_eq!(
        nested.fingerprint(),
        flat.fingerprint(),
        "Fix: canonical Program fingerprint must flatten binding-free Block wrappers."
    );
}

#[test]
fn canonical_fingerprint_preserves_binding_block_scope() {
    let scoped = Program::wrapped(
        vec![BufferDecl::output("out", 0, DataType::U32).with_count(1)],
        [1, 1, 1],
        vec![Node::block(vec![Node::let_bind("x", Expr::u32(1))])],
    );
    let leaked = Program::wrapped(
        vec![BufferDecl::output("out", 0, DataType::U32).with_count(1)],
        [1, 1, 1],
        vec![Node::let_bind("x", Expr::u32(1))],
    );

    assert_ne!(
        scoped.fingerprint(),
        leaked.fingerprint(),
        "Fix: canonicalization must not flatten Blocks that own local bindings."
    );
}