1
//! Closure registry behavioural tests.
2
//!
3
//! The registry is the foundation of Tier 1.5 lambdas-as-real-wasm-fns:
4
//! `intern_closure_signature` dedupes per `(arg-types, ret-type)` so two
5
//! distinct lambdas with the same shape share `$fn_<sig>` and
6
//! `$closure_<sig>`. These tests lock the dedup invariant in.
7

            
8
use super::super::CompileContext;
9
use crate::ast::{Expr, LambdaParams, WasmType};
10
use wasm_encoder::{HeapType, ValType};
11

            
12
/// A recorded closure body must NOT survive `reset_locals` — local indices are
13
/// reused across top-level function emits, so a stale body left under a reused
14
/// idx would be wrongly inlined into a later, unrelated closure at that idx.
15
#[test]
16
1
fn reset_locals_clears_recorded_closure_bodies() {
17
1
    let mut ctx = CompileContext::new().unwrap();
18
1
    let idx = ctx.alloc_local(WasmType::I32).unwrap();
19
1
    ctx.record_closure_body(idx, LambdaParams::simple(vec!["x".to_string()]), Expr::Nil);
20
1
    assert!(ctx.closure_body(idx).is_some());
21

            
22
1
    ctx.reset_locals();
23
1
    assert!(
24
1
        ctx.closure_body(idx).is_none(),
25
        "reset_locals must drop recorded closure bodies with the local pool"
26
    );
27
1
}
28

            
29
/// A body recorded while a helper carved out its own local pool must be rolled
30
/// back when the caller's pool is restored, mirroring `local_types`.
31
#[test]
32
1
fn restore_local_pool_restores_closure_bodies() {
33
1
    let mut ctx = CompileContext::new().unwrap();
34
1
    let snapshot = ctx.take_local_pool(0);
35
1
    let idx = ctx.alloc_local(WasmType::I32).unwrap();
36
1
    ctx.record_closure_body(idx, LambdaParams::simple(vec!["x".to_string()]), Expr::Nil);
37
1
    assert!(ctx.closure_body(idx).is_some());
38

            
39
1
    ctx.restore_local_pool(snapshot);
40
1
    assert!(
41
1
        ctx.closure_body(idx).is_none(),
42
        "restore_local_pool must discard bodies recorded into the helper pool"
43
    );
44
1
}
45

            
46
#[test]
47
1
fn intern_signature_deduplicates_by_shape() {
48
1
    let mut ctx = CompileContext::new().unwrap();
49
1
    let sig_a = ctx
50
1
        .intern_closure_signature(&[WasmType::Ratio, WasmType::Ratio], WasmType::Ratio)
51
1
        .unwrap();
52
1
    let sig_b = ctx
53
1
        .intern_closure_signature(&[WasmType::Ratio, WasmType::Ratio], WasmType::Ratio)
54
1
        .unwrap();
55
1
    assert_eq!(sig_a, sig_b, "same signature must collapse to one id");
56
1
}
57

            
58
#[test]
59
1
fn intern_signature_distinguishes_arg_types() {
60
1
    let mut ctx = CompileContext::new().unwrap();
61
1
    let r = ctx
62
1
        .intern_closure_signature(&[WasmType::Ratio], WasmType::Ratio)
63
1
        .unwrap();
64
1
    let i = ctx
65
1
        .intern_closure_signature(&[WasmType::I32], WasmType::Ratio)
66
1
        .unwrap();
67
1
    assert_ne!(r, i, "different param types must produce distinct ids");
68
1
}
69

            
70
#[test]
71
1
fn intern_signature_distinguishes_return_types() {
72
1
    let mut ctx = CompileContext::new().unwrap();
73
1
    let to_ratio = ctx
74
1
        .intern_closure_signature(&[WasmType::Ratio], WasmType::Ratio)
75
1
        .unwrap();
76
1
    let to_i32 = ctx
77
1
        .intern_closure_signature(&[WasmType::Ratio], WasmType::I32)
78
1
        .unwrap();
79
1
    assert_ne!(to_ratio, to_i32, "different return types are distinct sigs");
80
1
}
81

            
82
#[test]
83
1
fn closure_ref_resolves_to_concrete_struct() {
84
1
    let mut ctx = CompileContext::new().unwrap();
85
1
    let sig = ctx
86
1
        .intern_closure_signature(&[WasmType::Ratio], WasmType::Ratio)
87
1
        .unwrap();
88
1
    let val = ctx.closure_ref(sig);
89
1
    match val {
90
1
        ValType::Ref(r) => {
91
1
            assert!(r.nullable, "closure_ref must be nullable");
92
1
            assert!(matches!(r.heap_type, HeapType::Concrete(_)));
93
        }
94
        _ => panic!("closure_ref must produce a ref ValType"),
95
    }
96
1
}
97

            
98
#[test]
99
1
fn intern_then_closure_ref_via_wasm_val_type() {
100
1
    let mut ctx = CompileContext::new().unwrap();
101
1
    let sig = ctx
102
1
        .intern_closure_signature(&[WasmType::Ratio], WasmType::I32)
103
1
        .unwrap();
104
1
    let direct = ctx.closure_ref(sig);
105
1
    let via_dispatch = ctx.wasm_val_type(WasmType::Closure(sig));
106
1
    assert_eq!(direct, via_dispatch);
107
1
}