Lines
100 %
Functions
86.67 %
Branches
// Skipped under Miri: these tests compile+run wasm via wasmtime, whose
// Cranelift backend refuses to run under Miri.
#![cfg(not(miri))]
//! Runtime exercise of BLOCK / RETURN-FROM and TAGBODY / GO **inside the
//! Tier 3.2 boundary wrapper**. The wrapper adds three control frames
//! (`block $exit` / `block $handler` / `try_table`) around every
//! host-invoked body, so every lexical `br` target a labelled-exit form
//! computes off `block_depth()` must still resolve correctly through the
//! +3 offset. Codegen tests (`block_forms.rs`, `tagbody_forms.rs`) prove
//! the modules *validate*; these prove they *evaluate to the right value*
//! at runtime — an off-by-one `br` target that still type-checks would be
//! caught here but not by validation alone.
//!
//! `Interpreter::eval` runs through the wrapped `nomi-eval` (anyref) body;
//! `compile_to_wasm` + `run_wasm` runs through the wrapped `process` /
//! `should_apply` (void / i32) entry bodies.
use nms::interpreter::Interpreter;
use scripting::nomiscript::{Fraction, Value};
fn eval_one(src: &str) -> Value {
let mut interp = Interpreter::new(false).unwrap();
let results = interp
.eval(src)
.unwrap_or_else(|e| panic!("eval {src:?}: {e}"));
results
.into_iter()
.next_back()
.unwrap_or_else(|| panic!("eval {src:?} produced no value"))
}
fn n(v: i64) -> Value {
Value::Number(Fraction::from_integer(v))
#[test]
fn return_from_resolves_through_eval_boundary_wrapper() {
assert_eq!(eval_one("(block done (return-from done 7) 99)"), n(7));
fn return_from_inside_if_resolves_through_wrapper() {
assert_eq!(
eval_one("(block done (if (= 1 1) (return-from done 7) 0))"),
n(7)
);
fn nested_block_return_from_outer_skips_inner_remainder() {
eval_one("(block outer (block inner (return-from outer 7)) 99)"),
fn block_falls_through_to_tail_when_no_return_from() {
assert_eq!(eval_one("(block done 1 2 3)"), n(3));
fn block_dead_tail_after_return_from_yields_the_exit_value() {
// The `99` tail is dead code after an unconditional `(return-from)`.
// The block must return the exit value `7`, and the dead tail's
// `unreachable`-sealed value must not corrupt the result at runtime.
fn block_effectful_if_test_runs_exactly_once() {
// BLOCK's emit-time exit discovery classifies divergence on a CLONED
// symbol table, so the `(setf n (+ n 1))` in the IF test is NOT
// double-evaluated. Result must be 1 (one increment), not 2.
eval_one("(let* ((n 0)) (block b (if (begin (setf n (+ n 1)) #t) n 0)))"),
n(1)
fn block_effectful_test_in_non_tail_diverging_form_runs_once() {
// The non-tail `(if … (return-from b n) 0)` diverges conditionally;
// its effectful test must run once even though BLOCK queries
// divergence before compiling. The `99` tail is dead.
eval_one(
"(let* ((n 0)) (block b (if (begin (setf n (+ n 1)) #t) (return-from b n) 0) 99))"
),
fn tagbody_go_loop_resolves_through_wrapper() {
// GO emits `br $tagbody_loop`; its relative depth is computed off
// block_depth() and must clear the +3 wrapper frames.
eval_one("(let* ((x 0)) (tagbody loop (setf x (+ x 1)) (when (< x 3) (go loop))) x)"),
n(3)
fn tagbody_forward_go_skips_intervening_body() {
eval_one("(let* ((x 1)) (tagbody (go skip) (setf x 99) skip) x)"),
fn return_from_resolves_through_process_entry_wrapper() {
// compile_to_wasm + run_wasm drives the wrapped `process` (void) and
// `should_apply` (i32) bodies, a different wrapper-arity pair than the
// eval path above.
let wasm = interp
.compile_to_wasm("(block done (return-from done 7) 99)")
.unwrap();
assert_eq!(interp.run_wasm(&wasm).unwrap(), n(7));
fn tagbody_go_resolves_through_process_entry_wrapper() {
.compile_to_wasm(
"(let* ((x 0)) (tagbody loop (setf x (+ x 1)) (when (< x 3) (go loop))) x)",
)
assert_eq!(interp.run_wasm(&wasm).unwrap(), n(3));