1
use super::*;
2

            
3
#[tokio::test]
4
1
async fn integer_list_if_branches_unify_with_runtime_index_list() {
5
    // An all-integer-literal LIST is an Index chain (ADR-0028), so it
6
    // unifies with a runtime index list instead of widening to AnyRef.
7
1
    assert_eq!(
8
1
        eval_value_of(
9
1
            "(if (> (rpc-protocol-version) 0) \
10
1
                 (list 1 (* 0 (rpc-protocol-version))) (list 1 2))"
11
1
        )
12
1
        .await,
13
        "(:id 4 :value \"(1 0)\")"
14
    );
15
1
    assert_eq!(
16
1
        eval_value_of(
17
1
            "(let ((xs (if (> (rpc-protocol-version) 0) \
18
1
                 (list 1 2) (list (rpc-protocol-version))))) (car xs))"
19
1
        )
20
1
        .await,
21
1
        "(:id 4 :value 1)"
22
1
    );
23
1
}
24

            
25
#[tokio::test]
26
1
async fn integer_list_local_accepts_runtime_index_stores() {
27
1
    assert_eq!(
28
1
        eval_value_of("(let ((xs (list 1 2))) (setf xs (list (* 0 (rpc-protocol-version)))) xs)")
29
1
            .await,
30
        "(:id 4 :value \"(0)\")"
31
    );
32
1
    assert_eq!(
33
1
        eval_value_of(
34
1
            "(let ((xs (list 1 2))) (when (> (rpc-protocol-version) 0) \
35
1
                 (setf xs (cons (* 0 (rpc-protocol-version)) xs))) xs)"
36
1
        )
37
1
        .await,
38
1
        "(:id 4 :value \"(0 1 2)\")"
39
1
    );
40
1
}
41

            
42
#[tokio::test]
43
1
async fn cons_runtime_index_onto_integer_lists() {
44
1
    assert_eq!(
45
1
        eval_value_of("(cons (* 0 (rpc-protocol-version)) (list 1 2))").await,
46
        "(:id 4 :value \"(0 1 2)\")"
47
    );
48
1
    assert_eq!(
49
1
        eval_value_of("(cons (* 0 (rpc-protocol-version)) '(1 2))").await,
50
1
        "(:id 4 :value \"(0 1 2)\")"
51
1
    );
52
1
}
53

            
54
#[tokio::test]
55
1
async fn handler_case_integer_list_arms_agree() {
56
1
    assert_eq!(
57
1
        eval_value_of("(handler-case (list 1 2) (t (e) (list (rpc-protocol-version))))").await,
58
1
        "(:id 4 :value \"(1 2)\")"
59
1
    );
60
1
}
61

            
62
#[tokio::test]
63
1
async fn mapcar_over_runtime_index_list() {
64
1
    assert_eq!(
65
1
        eval_value_of("(mapcar (lambda (x) (+ x 1)) (list 1 (* 0 (rpc-protocol-version))))").await,
66
1
        "(:id 4 :value \"(2 1)\")"
67
1
    );
68
1
}
69

            
70
#[tokio::test]
71
1
async fn integer_literal_beyond_i31_never_takes_an_index_cell() {
72
1
    assert_eq!(
73
1
        eval_value_of("(list 1073741824 2)").await,
74
        "(:id 4 :value \"(1073741824 2)\")"
75
    );
76
1
    assert_eq!(
77
1
        eval_value_of("(list 2147483647 2)").await,
78
        "(:id 4 :value \"(2147483647 2)\")"
79
    );
80
1
    assert_eq!(
81
1
        eval_value_of("(list 1073741823 (* 0 (rpc-protocol-version)))").await,
82
        "(:id 4 :value \"(1073741823 0)\")"
83
    );
84
2
    for big in ["1073741824", "2147483647"] {
85
2
        assert_eq!(
86
2
            eval_value_of(&format!("(list {big} (* 0 (rpc-protocol-version)))")).await,
87
1
            "(:id 4 :value \"(<anyref> <anyref>)\")",
88
1
            "a {big} literal must widen, not truncate into an index cell"
89
1
        );
90
1
    }
91
1
}
92

            
93
#[tokio::test]
94
1
async fn runtime_index_beyond_i31_throws_index_overflow() {
95
3
    for form in [
96
1
        "(list (* 1073741824 (rpc-protocol-version)))",
97
1
        "(list (* -1073741825 (rpc-protocol-version)))",
98
1
        "(cons (* 2147483647 (rpc-protocol-version)) (list 1))",
99
1
    ] {
100
3
        let response = eval_value_of(form).await;
101
3
        assert!(
102
3
            response.contains(":code INDEX-OVERFLOW"),
103
1
            "{form} must raise index-overflow, got: {response}"
104
1
        );
105
1
    }
106
1
}
107

            
108
#[tokio::test]
109
1
async fn index_overflow_is_catchable_by_handler_case() {
110
1
    assert_eq!(
111
1
        eval_value_of(
112
1
            "(handler-case (car (list (* 1073741824 (rpc-protocol-version)))) \
113
1
                 (index-overflow (e) -1))"
114
1
        )
115
1
        .await,
116
1
        "(:id 4 :value -1)"
117
1
    );
118
1
}
119

            
120
#[tokio::test]
121
1
async fn runtime_index_at_i31_bounds_round_trips() {
122
1
    assert_eq!(
123
1
        eval_value_of(
124
1
            "(list (* 1073741823 (rpc-protocol-version)) \
125
1
                 (* -1073741824 (rpc-protocol-version)))"
126
1
        )
127
1
        .await,
128
1
        "(:id 4 :value \"(1073741823 -1073741824)\")"
129
1
    );
130
1
}
131

            
132
#[tokio::test]
133
1
async fn constant_nil_never_becomes_a_zero_cell() {
134
1
    assert_eq!(
135
1
        eval_value_of(
136
1
            "(fold (lambda (acc x) (cons x acc)) '(nil) (list (* 0 (rpc-protocol-version))))"
137
1
        )
138
1
        .await,
139
        "(:id 4 :value \"(<anyref> nil)\")"
140
    );
141
1
    assert_eq!(
142
1
        eval_value_of("(cons nil (list (* 0 (rpc-protocol-version))))").await,
143
1
        "(:id 4 :value \"(nil <anyref>)\")"
144
1
    );
145
1
}
146

            
147
#[tokio::test]
148
1
async fn null_p_and_not_see_a_nil_in_an_anyref_cell() {
149
1
    assert_values(&[
150
1
        (
151
1
            "(null? (car (cons nil (list (rpc-protocol-version)))))",
152
1
            "t",
153
1
        ),
154
1
        (
155
1
            "(let ((c (car (cons nil (list (rpc-protocol-version)))))) (null? c))",
156
1
            "t",
157
1
        ),
158
1
        ("(null? (car (list nil (rpc-protocol-version))))", "t"),
159
1
        (
160
1
            "(null? (car (cdr (cons (rpc-protocol-version) (list nil)))))",
161
1
            "t",
162
1
        ),
163
1
        ("(not (car (list nil (rpc-protocol-version))))", "t"),
164
1
        ("(null? (car (list \"a\" (rpc-protocol-version))))", "nil"),
165
1
        ("(not (index->scalar (rpc-protocol-version)))", "nil"),
166
1
    ])
167
1
    .await;
168
1
}
169

            
170
#[tokio::test]
171
1
async fn catch_each_binds_an_index_iteration_variable() {
172
1
    assert_values(&[
173
1
        ("(length (catch-each (list 1 2) x (+ x 1)))", "2"),
174
1
        (
175
1
            "(let ((xs (list 1 2))) (length (catch-each xs x (+ x 1))))",
176
1
            "2",
177
1
        ),
178
1
        (
179
1
            "(catch-each (list 1 2) x (+ x 1))",
180
1
            "\"(<anyref> <anyref>)\"",
181
1
        ),
182
1
    ])
183
1
    .await;
184
1
}
185

            
186
#[tokio::test]
187
1
async fn filter_of_an_empty_list_agrees_with_its_closure_predicate() {
188
1
    assert_values(&[(
189
1
        "(let ((p (lambda (x) (string= x \"a\")))) (cons \"b\" (filter p '())))",
190
1
        "\"(\\\"b\\\")\"",
191
1
    )])
192
1
    .await;
193
1
}
194

            
195
#[tokio::test]
196
1
async fn mapcar_and_mapc_accept_let_bound_closures() {
197
1
    assert_values(&[
198
1
        (
199
1
            "(let ((f (lambda (x) (* x 2/3)))) (mapcar f '(1 2)))",
200
1
            "\"(2/3 4/3)\"",
201
1
        ),
202
1
        (
203
1
            "(let ((f (lambda (x) (* x 2/3)))) (mapc f '(1 2)))",
204
1
            "\"(2/3 4/3)\"",
205
1
        ),
206
1
        (
207
1
            "(let ((f (lambda (x) (+ x 1)))) \
208
1
                 (map f (list 1/2 (index->scalar (rpc-protocol-version)))))",
209
1
            "\"(3/2 2)\"",
210
1
        ),
211
1
        (
212
1
            "(let ((f (lambda (x) (+ x 1)))) \
213
1
                 (mapcar f (list 1/2 (index->scalar (rpc-protocol-version)))))",
214
1
            "\"(3/2 2)\"",
215
1
        ),
216
1
        (
217
1
            "(let ((func 5)) (mapcar (lambda (x) (+ x func)) '(1 1)))",
218
1
            "\"(6 6)\"",
219
1
        ),
220
1
    ])
221
1
    .await;
222
    // A let-bound closure keeps its Scalar parameter, so a runtime index
223
    // list is refused identically through MAP and its MAPCAR alias.
224
2
    for head in ["map", "mapcar"] {
225
2
        assert_error_code(
226
2
            &format!("(let ((f (lambda (x) (+ x 1)))) ({head} f (list 1 (rpc-protocol-version))))"),
227
2
            "compile",
228
2
        )
229
2
        .await;
230
1
    }
231
1
}
232

            
233
#[tokio::test]
234
1
async fn runtime_index_beyond_i31_throws_in_an_anyref_arm() {
235
1
    assert_error_code(
236
1
        "(if (> (rpc-protocol-version) 0) (* 1073741824 (rpc-protocol-version)) \"a\")",
237
1
        "INDEX-OVERFLOW",
238
1
    )
239
1
    .await;
240
1
    assert_values(&[
241
1
        ("(+ 1073741823 (rpc-protocol-version))", "1073741824"),
242
1
        (
243
1
            "(let ((x (* 1073741824 (rpc-protocol-version)))) x)",
244
1
            "1073741824",
245
1
        ),
246
1
        ("(+ 1073741822 (rpc-protocol-version))", "1073741823"),
247
1
        ("(- -1073741823 (rpc-protocol-version))", "-1073741824"),
248
1
        (
249
1
            "(null? (if (> (rpc-protocol-version) 0) (* 1073741823 (rpc-protocol-version)) \"a\"))",
250
1
            "nil",
251
1
        ),
252
1
    ])
253
1
    .await;
254
1
}
255

            
256
#[tokio::test]
257
1
async fn index_division_by_zero_and_overflow_are_catchable() {
258
1
    assert_values(&[
259
1
        (
260
1
            "(handler-case (/ 7 (* 0 (rpc-protocol-version))) (t (e) 0))",
261
1
            "0",
262
1
        ),
263
1
        (
264
1
            "(handler-case (mod 7 (* 0 (rpc-protocol-version))) (division-by-zero (e) -1))",
265
1
            "-1",
266
1
        ),
267
1
        ("(/ 7 (* 2 (rpc-protocol-version)))", "3"),
268
1
        ("(mod 7 (* 2 (rpc-protocol-version)))", "1"),
269
1
    ])
270
1
    .await;
271
1
    assert_error_code("(/ (* 0 (rpc-protocol-version)))", "DIVISION-BY-ZERO").await;
272
1
    assert_error_code(
273
1
        "(/ (- -2147483647 (rpc-protocol-version)) (- 0 (rpc-protocol-version)))",
274
1
        "INDEX-OVERFLOW",
275
1
    )
276
1
    .await;
277
1
}
278

            
279
#[tokio::test]
280
1
async fn if_over_differently_typed_lists_stays_a_list() {
281
1
    assert_values(&[
282
1
        (
283
1
            "(if (> (rpc-protocol-version) 0) (list 1 2) (list \"a\" \"b\"))",
284
1
            "\"(<anyref> <anyref>)\"",
285
1
        ),
286
1
        (
287
1
            "(length (if (> (rpc-protocol-version) 0) (list 1 2) (list \"a\" \"b\")))",
288
1
            "2",
289
1
        ),
290
1
    ])
291
1
    .await;
292
1
}
293

            
294
#[tokio::test]
295
1
async fn filter_and_mapcar_literal_paths_share_the_chain_rule() {
296
1
    assert_values(&[
297
1
        (
298
1
            "(filter (lambda (x) (> (rpc-protocol-version) 0)) '(1 1/2))",
299
1
            "\"(1 1/2)\"",
300
1
        ),
301
1
        (
302
1
            "(mapcar (lambda (x) (+ x 1/2)) (list 1 1/2))",
303
1
            "\"(3/2 1)\"",
304
1
        ),
305
1
    ])
306
1
    .await;
307
1
}
308

            
309
#[tokio::test]
310
1
async fn handler_case_reconciles_literal_and_nil_arms() {
311
1
    assert_values(&[
312
1
        (
313
1
            "(handler-case (index->scalar (rpc-protocol-version)) (t (e) 7))",
314
1
            "1",
315
1
        ),
316
1
        (
317
1
            "(handler-case (mapcar (lambda (x) (* x 1073741824)) \
318
1
                 (list 1 (rpc-protocol-version))) (index-overflow (e) nil))",
319
1
            "nil",
320
1
        ),
321
1
        (
322
1
            "(handler-case (mapcar (lambda (x) (* x 2)) \
323
1
                 (list 1 (rpc-protocol-version))) (index-overflow (e) nil))",
324
1
            "\"(2 2)\"",
325
1
        ),
326
1
    ])
327
1
    .await;
328
1
}
329

            
330
#[tokio::test]
331
1
async fn fold_seed_members_widen_the_accumulator() {
332
1
    assert_eq!(
333
1
        eval_value_of(
334
1
            "(fold (lambda (acc x) (cons x acc)) '(1) (list (* 0 (rpc-protocol-version))))"
335
1
        )
336
1
        .await,
337
        "(:id 4 :value \"(0 1)\")"
338
    );
339
2
    for seed in ["'(t)", "'(\"a\")"] {
340
2
        assert_eq!(
341
2
            eval_value_of(&format!(
342
2
                "(fold (lambda (acc x) (cons x acc)) {seed} (list (rpc-protocol-version)))"
343
2
            ))
344
2
            .await,
345
1
            "(:id 4 :value \"(<anyref> <anyref>)\")",
346
1
            "seed {seed}"
347
1
        );
348
1
    }
349
1
}
350

            
351
#[tokio::test]
352
1
async fn list_of_literals_renders_elements_on_eval_path() {
353
    // Regression: an all-literal LIST at stack position defaulted its cells
354
    // to AnyRef, so the host rendered `(<anyref> <anyref>)`.
355
1
    assert_eq!(
356
1
        handle_form_smoke("(:id 4 :form (list 1 2))").await,
357
        "(:id 4 :value \"(1 2)\")"
358
    );
359
1
    assert_eq!(
360
1
        handle_form_smoke("(:id 4 :form (list (> 2 1) (= 1 2)))").await,
361
        "(:id 4 :value \"(t nil)\")"
362
    );
363
1
    assert_eq!(
364
1
        handle_form_smoke("(:id 4 :form (list 1/2 2))").await,
365
1
        "(:id 4 :value \"(1/2 2)\")"
366
1
    );
367
1
}
368

            
369
#[tokio::test]
370
1
async fn list_mixing_runtime_and_literal_elements_unifies_cells() {
371
    // An integer literal rides its runtime index neighbor's slot on either
372
    // side; a disagreeing literal tail widens instead of failing to compile.
373
1
    assert_eq!(
374
1
        handle_form_smoke("(:id 4 :form (list (* 0 (rpc-protocol-version)) 2))").await,
375
        "(:id 4 :value \"(0 2)\")"
376
    );
377
1
    assert_eq!(
378
1
        handle_form_smoke("(:id 4 :form (list 2 (* 0 (rpc-protocol-version))))").await,
379
        "(:id 4 :value \"(2 0)\")"
380
    );
381
1
    assert_eq!(
382
1
        handle_form_smoke(
383
1
            "(:id 4 :form (list (* 0 (index->scalar (rpc-protocol-version))) \"a\"))"
384
1
        )
385
1
        .await,
386
        "(:id 4 :value \"(<anyref> <anyref>)\")"
387
    );
388
1
    assert_eq!(
389
1
        handle_form_smoke("(:id 4 :form (list (* 0 (rpc-protocol-version)) nil))").await,
390
1
        "(:id 4 :value \"(<anyref> nil)\")"
391
1
    );
392
1
}
393

            
394
#[tokio::test]
395
1
async fn append_runtime_list_onto_constant_list_renders_elements() {
396
    // Regression: the constant suffix was materialized as AnyRef cells under
397
    // a concrete result label, so a ratio label panicked the host decoder.
398
1
    assert_eq!(
399
1
        handle_form_smoke(
400
1
            "(:id 4 :form (append (list (* 0 (index->scalar (rpc-protocol-version)))) '(1 2)))"
401
1
        )
402
1
        .await,
403
        "(:id 4 :value \"(0 1 2)\")"
404
    );
405
1
    assert_eq!(
406
1
        handle_form_smoke("(:id 4 :form (append (list (* 0 (rpc-protocol-version))) '(1 2)))")
407
1
            .await,
408
        "(:id 4 :value \"(0 1 2)\")"
409
    );
410
1
    assert_eq!(
411
1
        handle_form_smoke("(:id 4 :form (append (list (> (rpc-protocol-version) 0)) '(t nil)))")
412
1
            .await,
413
1
        "(:id 4 :value \"(t t nil)\")"
414
1
    );
415
1
}
416

            
417
#[tokio::test]
418
1
async fn fold_with_constant_list_seed_renders_elements() {
419
    // Regression: the constant seed chain was built as AnyRef cells under the
420
    // accumulator's ratio label, panicking the host decoder.
421
1
    assert_eq!(
422
1
        handle_form_smoke(
423
1
            "(:id 4 :form (fold (lambda (acc x) (cons x acc)) '(1 2) \
424
1
                 (list (* 0 (index->scalar (rpc-protocol-version))))))"
425
1
        )
426
1
        .await,
427
        "(:id 4 :value \"(0 1 2)\")"
428
    );
429
1
    assert_eq!(
430
1
        handle_form_smoke(
431
1
            "(:id 4 :form (fold (lambda (acc x) (cons x acc)) '(t) \
432
1
                 (list (> (rpc-protocol-version) 0))))"
433
1
        )
434
1
        .await,
435
1
        "(:id 4 :value \"(t t)\")"
436
1
    );
437
1
}