1
//! DO / DO* with runtime-valued bounds. `N` is bound to a real
2
//! runtime Ratio via the host fn; the loop test exercises the
3
//! compile_do_runtime path that allocates a wasm local for `I` and
4
//! emits the step/test wasm.
5

            
6
use super::common::{compile_and_validate, compile_expect_error};
7

            
8
/// Wraps a do-loop body so `N` is a runtime Ratio.
9
7
fn wrap_with_runtime_n(body: &str) -> String {
10
    // N is a loop bound — a count — so it is a runtime Index (I32). ADR-0028:
11
    // an integer counter loop stays in the Index stratum end to end.
12
7
    format!("(let* ((N (entity-count))) {body})")
13
7
}
14

            
15
#[test]
16
1
fn runtime_do_simple() {
17
1
    compile_and_validate(&wrap_with_runtime_n("(do ((I 0 (+ I 1))) ((>= I N) I))"));
18
1
}
19

            
20
#[test]
21
1
fn runtime_do_with_body() {
22
1
    compile_and_validate(&wrap_with_runtime_n(
23
1
        "(let* ((SUM 0)) \
24
1
           (do ((I 0 (+ I 1))) ((>= I N) SUM) \
25
1
             (setf SUM (+ SUM I))))",
26
1
    ));
27
1
}
28

            
29
#[test]
30
1
fn runtime_do_star() {
31
1
    compile_and_validate(&wrap_with_runtime_n("(do* ((I 0 (+ I 1))) ((>= I N) I))"));
32
1
}
33

            
34
#[test]
35
1
fn runtime_do_multiple_vars() {
36
1
    compile_and_validate(&wrap_with_runtime_n(
37
1
        "(do ((I 0 (+ I 1)) (J N (- J 1))) ((>= I J) (+ I J)))",
38
1
    ));
39
1
}
40

            
41
#[test]
42
1
fn runtime_do_no_result_form() {
43
1
    compile_and_validate(&wrap_with_runtime_n("(do ((I 0 (+ I 1))) ((>= I N)))"));
44
1
}
45

            
46
/// A runtime DO whose accumulator step is `(cons <host-fn> acc)` drives the
47
/// pair-element inference walker (`infer_result_pair_element` →
48
/// `step_pair_element` → `resolve_pair_element_from_expr`), which eval-probes
49
/// the pure-native cons car (gated by `is_pure_native_expr` / `head_is_native`)
50
/// to size the accumulator local. The car here is a native host-fn call, so
51
/// the probe fires and the accumulator is sized to its element type.
52
#[test]
53
1
fn runtime_do_cons_accumulator_pure_native_car() {
54
1
    compile_and_validate(&wrap_with_runtime_n(
55
1
        "(do ((I 0 (+ I 1)) (ACC nil (cons (transaction-post-date 0) ACC))) ((>= I N) ACC))",
56
1
    ));
57
1
}
58

            
59
/// Same accumulator shape but the cons car wraps a USER function. The
60
/// pair-element walker (`is_pure_native_expr` / `names_user_callable`) must
61
/// REFUSE to eval-probe a user-callable subtree — so inference can't recurse
62
/// into (possibly non-terminating) user code. With the car un-probeable the
63
/// nil-init accumulator can't be sized from it and falls to the nil default,
64
/// so this `(cons <user-fn> nil-acc)` shape is a clean STRUCTURED compile
65
/// error, never a compiler overflow. (Probing it instead would be the DoS bug
66
/// the guard exists to prevent.)
67
#[test]
68
1
fn runtime_do_cons_accumulator_user_fn_car_not_probed() {
69
1
    let err = compile_expect_error(&wrap_with_runtime_n(
70
1
        "(defun g (x) (* x 2)) \
71
1
         (do ((I 0 (+ I 1)) (ACC nil (cons (g (transaction-post-date 0)) ACC))) ((>= I N) ACC))",
72
1
    ));
73
1
    assert!(!err.is_empty(), "expected a structured compile error");
74
1
}