warriors/trisha/rs/tests/struct_literal_layout.rs

use triton_vm::prelude::*;

fn run(source: &str, helper: Option<&str>, input: &[u64], profile: &str) -> Vec<u64> {
    let directory = tempfile::tempdir().unwrap();
    let path = directory.path().join("main.tri");
    std::fs::write(&path, source).unwrap();
    if let Some(helper) = helper {
        std::fs::write(directory.path().join("helper.tri"), helper).unwrap();
    }
    let assembly = trisha_rs::build_tasm(&path, "triton", profile).unwrap();
    VM::run(
        Program::from_code(&assembly).unwrap(),
        PublicInput::new(input.iter().copied().map(BFieldElement::new).collect()),
        NonDeterminism::default(),
    )
    .unwrap()
    .iter()
    .map(|word| word.value())
    .collect()
}

#[test]
fn reordered_literal_uses_declaration_layout_for_locals_calls_and_returns() {
    let source = "program layout
struct Pair { a: Field, b: Field }
fn make() -> Pair { Pair { b: 19, a: 7 } }
fn value(p: Pair) -> Field { p.a * 100 + p.b }
fn main() {
 let p = Pair { b: 19, a: 7 }
 pub_write(p.a * 100 + p.b)
 pub_write(value(p))
 pub_write(value(make()))
}";
    for profile in ["debug", "release"] {
        assert_eq!(run(source, None, &[], profile), [719, 719, 719]);
    }
}

#[test]
fn nested_mixed_width_imported_structs_survive_returns_and_array_indexing() {
    let helper = "module helper
pub struct Pair { pub a: Field, pub b: Field }
pub struct Wide { pub pair: Pair, pub words: [Field; 3], pub tail: Field }
pub fn make(x: Field) -> Wide {
 Wide { tail: 31, words: [17, 19, 23], pair: Pair { b: 11, a: x } }
}
pub fn value(w: Wide) -> Field { w.pair.a * 100 + w.pair.b + w.words[2] + w.tail }
";
    let source = "program imported
use helper
fn main() {
 let mut values = [helper.make(7), helper.make(13)]
 let index = as_u32(pub_read())
 let chosen = values[index]
 pub_write(helper.value(chosen))
 pub_write(chosen.words[0])
}";
    for profile in ["debug", "release"] {
        assert_eq!(run(source, Some(helper), &[0], profile), [765, 17]);
        assert_eq!(run(source, Some(helper), &[1], profile), [1365, 17]);
    }
}

#[test]
fn field_effects_are_evaluated_once_in_recursive_declaration_order() {
    let source = "program effects
struct Pair { a: Field, b: Field }
struct Outer { first: Pair, last: Field }
fn take(tag: Field) -> Field { pub_write(tag)\n pub_read() }
fn main() {
 let p = Outer { last: take(3), first: Pair { b: take(2), a: take(1) } }
 pub_write(p.first.a)
 pub_write(p.first.b)
 pub_write(p.last)
 pub_write(pub_read())
}";
    for profile in ["debug", "release"] {
        assert_eq!(
            run(source, None, &[7, 19, 31, 43], profile),
            [1, 2, 3, 7, 19, 31, 43]
        );
    }
}

#[test]
fn unchecked_builder_errors_cannot_become_executable_stack_values() {
    use trident::tir::builder::TIRBuilder;
    for source in [
        "program bad\nfn main() { pub_write(missing) }",
        "program bad\nconst VALUE: Field = 77\nfn main() { pub_write(missing.VALUE) }",
        "program bad\nstruct Pair { a: Field, b: Field }\nfn main() { let p = Pair { a: 7 } }",
        "program bad\nstruct Pair { a: Field, b: Field }\nfn main() { let p = Pair { a: 7, a: 19 } }",
    ] {
        let file = trident::parse_source_silent(source, "bad.tri").unwrap();
        let ops = TIRBuilder::new(trisha_rs::target::package("triton").unwrap().terrain)
            .build_file(&file);
        let error = trisha_rs::lower::lower_checked(&ops).unwrap_err();
        assert!(error.contains("ERROR:"), "{error}");
    }
}

Graph