Lines
100 %
Functions
Branches
//! Arithmetic with a runtime Ratio operand. Each test wraps its
//! expression in a `let*` that binds `X` to a real runtime ratio via
//! `(transaction-post-date 0)` — the same shape production scripts
//! use to flow host-fn return values into arithmetic. The legacy
//! `Symbol::new("X").with_value(Expr::WasmRuntime(Ratio))` pattern
//! was a workaround for the silent `compile_for_stack(WasmRuntime)`
//! path and is gone.
use super::common::{
compile_and_validate, compile_expect_error, wrap_with_runtime_i32, wrap_with_runtime_ratio,
};
#[test]
fn index_arithmetic_over_runtime_count_compiles() {
// ADR-0028: `(entity-count)` is a runtime Index (I32); Index arithmetic
// accepts it, and the integer literal `1` stays Index. `(+ idx 1)` lowers
// to a raw `i32.add` (it used to be refused as "ratio vs indices").
compile_and_validate("(let* ((idx (entity-count))) (+ idx 1))");
}
fn index_scalar_mix_is_refused() {
// A runtime Index cannot mix with a Scalar — only an explicit bridge
// crosses the strata.
let err = compile_expect_error("(let* ((idx (entity-count))) (+ idx 1/2))");
assert!(
err.contains("mix dimensions") || err.contains("index"),
"expected a strata-mix error, got: {err}",
);
fn runtime_addition() {
compile_and_validate(&wrap_with_runtime_ratio("(+ X (/ 1 3))"));
fn runtime_subtraction() {
compile_and_validate(&wrap_with_runtime_ratio("(- X 1)"));
fn runtime_multiplication() {
compile_and_validate(&wrap_with_runtime_ratio("(* X 2)"));
fn runtime_division() {
compile_and_validate(&wrap_with_runtime_ratio("(/ X 3)"));
fn runtime_nested_arithmetic() {
compile_and_validate(&wrap_with_runtime_ratio("(+ (* X 2) (/ 1 3))"));
fn index_div_scalar_literal_mix_is_refused_on_both_surfaces() {
// eval↔codegen agreement: `(/ IDX 1/2)` (runtime Index ÷ fractional literal)
// must be refused, not eval-predicted as I32 then codegen-rejected.
let err = compile_expect_error(&wrap_with_runtime_i32("(/ IDX 1/2)"));
err.contains("mix") || err.contains("index") || err.contains("scalar"),
fn index_mod_scalar_literal_mix_is_refused_on_both_surfaces() {
let err = compile_expect_error(&wrap_with_runtime_i32("(mod IDX 1/2)"));
fn index_to_scalar_bridges_runtime_count_into_scalar_arithmetic() {
// ADR-0028 sanctioned crossing: a runtime Index (`(entity-count)`) can be
// lifted to a Scalar explicitly, then mixed with a Scalar literal — the mix
// that `index_scalar_mix_is_refused` forbids without the bridge.
compile_and_validate(&wrap_with_runtime_i32("(+ (index->scalar IDX) 1/2)"));
fn scalar_to_index_bridges_runtime_ratio_into_index_arithmetic() {
// The reverse crossing: a runtime Scalar narrows to an Index (truncating
// toward zero), then participates in Index arithmetic with the literal `1`.
compile_and_validate(&wrap_with_runtime_ratio("(+ (scalar->index X) 1)"));
fn bridge_round_trip_compiles() {
compile_and_validate(&wrap_with_runtime_i32(
"(scalar->index (index->scalar IDX))",
));
fn index_to_scalar_rejects_fractional_constant() {
// A constant fractional value is not an index; the bridge refuses it rather
// than silently truncating.
let err = compile_expect_error("(index->scalar 1/2)");
err.contains("integer index") || err.contains("index"),
"expected an index-domain error, got: {err}",
fn scalar_to_index_folds_constant_toward_zero() {
// `7/2 → 3`: a constant scalar folds to an integer Index at compile time.
compile_and_validate("(scalar->index 7/2)");
fn index_to_scalar_rejects_out_of_i32_range_constant() {
// An Index is an i32; a constant above i32::MAX is not an index. eval and
// codegen agree on rejecting it (no eval-accepts/codegen-rejects drift).
let err = compile_expect_error("(index->scalar 2147483648)");
err.contains("range"),
"expected an i32-range error, got: {err}"
fn scalar_to_index_rejects_out_of_i32_range_constant() {
// A constant scalar whose truncation overflows i32 is a compile error on
// both surfaces — never a silent `i32.wrap_i64` narrowing.
let err = compile_expect_error("(scalar->index 4294967296)");
fn runtime_division_emits_div_by_zero_guard() {
// `ratio_new` (the chokepoint `ratio_div` funnels through) guards a zero
// denominator with a catchable `division-by-zero` throw; validate that the
// guarded body is well-formed wasm (throw structure, stack-polymorphism
// past the throw).
compile_and_validate(&wrap_with_runtime_ratio("(/ X X)"));