Lines
100 %
Functions
Branches
//! Regression tests for the three `nil`-coercion bugs closed by
//! `emit_nil_default` (ADR-0028 Commit A). Before the fix, a `nil` flowing
//! into a non-`PairRef`, non-`Bool` typed slot (a DO-var sized to a ref/ratio
//! by its step, a FOLD seed whose accumulator is non-pair, a `nil` literal
//! passed to a typed closure parameter) emitted an `i32.const 0` `Bool` into
//! that slot — malformed wasm, or a hard "type mismatch" compile error. Each
//! test routes a `nil` through `emit_nil_default` and asserts the module
//! compiles and validates.
use super::common::compile_and_validate;
/// Bug 1: a `DO` var with a `nil` init whose step is a pure-native `Ratio`
/// producer is sized `Ratio`; the `nil` init must seed `push_ratio 0/1`, not
/// an `i32.const 0`. Runtime loop (`N` is `(entity-count)`).
#[test]
fn do_var_nil_init_sized_ratio_by_step() {
compile_and_validate(
"(let* ((N (entity-count))) \
(do ((I 0 (+ I 1)) (ACC nil (transaction-post-date 0))) ((>= I N) ACC)))",
);
}
/// Bug 3: a `FOLD` over a runtime list with a `nil` seed whose accumulator type
/// probes to `Ratio` (the body returns a ratio). The seed must emit a typed
/// ratio zero; previously this errored "FOLD: init type Bool doesn't match
/// accumulator type Ratio".
fn fold_nil_seed_ratio_accumulator() {
"(let* ((X (transaction-post-date 0))) \
(fold (lambda (acc x) (+ x X)) nil (cons X nil)))",
/// Bug 2: a runtime closure (captures the runtime `X`) whose parameter unifies
/// to `Ratio` is invoked with a `nil` literal argument. The `nil` must be
/// emitted as the parameter's type; previously this errored "closure argument
/// type mismatch: expected Ratio, got Bool".
fn closure_call_nil_arg_typed_param() {
"(let* ((X (transaction-post-date 0)) (F (lambda (A) (if (null? A) X A)))) \
(F nil))",
/// Bug 1, resolved-nil path: a DO var initialized from a SYMBOL bound to `nil`
/// (a non-literal init that *resolves* to nil) sized `Ratio` by its step. The
/// resolved-nil branch must emit the typed default, not an `i32.const 0` `Bool`.
fn do_var_nil_init_via_symbol() {
"(let* ((N (entity-count)) (Z nil)) \
(do ((I 0 (+ I 1)) (ACC Z (transaction-post-date 0))) ((>= I N) ACC)))",
/// Bug 2, resolved-nil path: a runtime closure invoked with a SYMBOL bound to
/// `nil`. The coercion must resolve the argument, not just match a literal
/// `nil`; previously the symbol lowered to `Bool` and hit the type mismatch.
fn closure_call_symbol_nil_arg() {
"(let* ((X (transaction-post-date 0)) (Z nil) (F (lambda (A) (if (null? A) X A)))) \
(F Z))",
/// Bug 3, resolved-nil path: a FOLD seed that is a SYMBOL bound to `nil`, with a
/// `Ratio` accumulator. The resolved-nil seed must emit a typed ratio zero.
fn fold_symbol_nil_seed_ratio_accumulator() {
"(let* ((X (transaction-post-date 0)) (Z nil)) \
(fold (lambda (acc x) (+ x X)) Z (cons X nil)))",
/// Bug 3, resolved-nil path with a `PairRef` accumulator: a non-literal nil seed
/// must route through the resolved-nil branch (which precedes PairRef constant-
/// list materialization), emitting the empty `$pair` chain after any seed effects.
fn fold_symbol_nil_seed_pair_accumulator() {
(fold (lambda (acc x) (cons x acc)) Z (cons X nil)))",