1
//! Regression tests for the three `nil`-coercion bugs closed by
2
//! `emit_nil_default` (ADR-0028 Commit A). Before the fix, a `nil` flowing
3
//! into a non-`PairRef`, non-`Bool` typed slot (a DO-var sized to a ref/ratio
4
//! by its step, a FOLD seed whose accumulator is non-pair, a `nil` literal
5
//! passed to a typed closure parameter) emitted an `i32.const 0` `Bool` into
6
//! that slot — malformed wasm, or a hard "type mismatch" compile error. Each
7
//! test routes a `nil` through `emit_nil_default` and asserts the module
8
//! compiles and validates.
9

            
10
use super::common::compile_and_validate;
11

            
12
/// Bug 1: a `DO` var with a `nil` init whose step is a pure-native `Ratio`
13
/// producer is sized `Ratio`; the `nil` init must seed `push_ratio 0/1`, not
14
/// an `i32.const 0`. Runtime loop (`N` is `(entity-count)`).
15
#[test]
16
1
fn do_var_nil_init_sized_ratio_by_step() {
17
1
    compile_and_validate(
18
1
        "(let* ((N (entity-count))) \
19
1
           (do ((I 0 (+ I 1)) (ACC nil (transaction-post-date 0))) ((>= I N) ACC)))",
20
    );
21
1
}
22

            
23
/// Bug 3: a `FOLD` over a runtime list with a `nil` seed whose accumulator type
24
/// probes to `Ratio` (the body returns a ratio). The seed must emit a typed
25
/// ratio zero; previously this errored "FOLD: init type Bool doesn't match
26
/// accumulator type Ratio".
27
#[test]
28
1
fn fold_nil_seed_ratio_accumulator() {
29
1
    compile_and_validate(
30
1
        "(let* ((X (transaction-post-date 0))) \
31
1
           (fold (lambda (acc x) (+ x X)) nil (cons X nil)))",
32
    );
33
1
}
34

            
35
/// Bug 2: a runtime closure (captures the runtime `X`) whose parameter unifies
36
/// to `Ratio` is invoked with a `nil` literal argument. The `nil` must be
37
/// emitted as the parameter's type; previously this errored "closure argument
38
/// type mismatch: expected Ratio, got Bool".
39
#[test]
40
1
fn closure_call_nil_arg_typed_param() {
41
1
    compile_and_validate(
42
1
        "(let* ((X (transaction-post-date 0)) (F (lambda (A) (if (null? A) X A)))) \
43
1
           (F nil))",
44
    );
45
1
}
46

            
47
/// Bug 1, resolved-nil path: a DO var initialized from a SYMBOL bound to `nil`
48
/// (a non-literal init that *resolves* to nil) sized `Ratio` by its step. The
49
/// resolved-nil branch must emit the typed default, not an `i32.const 0` `Bool`.
50
#[test]
51
1
fn do_var_nil_init_via_symbol() {
52
1
    compile_and_validate(
53
1
        "(let* ((N (entity-count)) (Z nil)) \
54
1
           (do ((I 0 (+ I 1)) (ACC Z (transaction-post-date 0))) ((>= I N) ACC)))",
55
    );
56
1
}
57

            
58
/// Bug 2, resolved-nil path: a runtime closure invoked with a SYMBOL bound to
59
/// `nil`. The coercion must resolve the argument, not just match a literal
60
/// `nil`; previously the symbol lowered to `Bool` and hit the type mismatch.
61
#[test]
62
1
fn closure_call_symbol_nil_arg() {
63
1
    compile_and_validate(
64
1
        "(let* ((X (transaction-post-date 0)) (Z nil) (F (lambda (A) (if (null? A) X A)))) \
65
1
           (F Z))",
66
    );
67
1
}
68

            
69
/// Bug 3, resolved-nil path: a FOLD seed that is a SYMBOL bound to `nil`, with a
70
/// `Ratio` accumulator. The resolved-nil seed must emit a typed ratio zero.
71
#[test]
72
1
fn fold_symbol_nil_seed_ratio_accumulator() {
73
1
    compile_and_validate(
74
1
        "(let* ((X (transaction-post-date 0)) (Z nil)) \
75
1
           (fold (lambda (acc x) (+ x X)) Z (cons X nil)))",
76
    );
77
1
}
78

            
79
/// Bug 3, resolved-nil path with a `PairRef` accumulator: a non-literal nil seed
80
/// must route through the resolved-nil branch (which precedes PairRef constant-
81
/// list materialization), emitting the empty `$pair` chain after any seed effects.
82
#[test]
83
1
fn fold_symbol_nil_seed_pair_accumulator() {
84
1
    compile_and_validate(
85
1
        "(let* ((X (transaction-post-date 0)) (Z nil)) \
86
1
           (fold (lambda (acc x) (cons x acc)) Z (cons X nil)))",
87
    );
88
1
}