1
//! `(error 'code "message")` special form. Lowers to a native wasm
2
//! `struct.new $nomi_condition` + `throw $nomi_error` (Tier 3,
3
//! ADR-0026), so an enclosing `(handler-case)` / `(unwind-protect)`
4
//! `try_table` can catch it in-module. An uncaught raise is caught by
5
//! the compiler-emitted boundary wrapper around each host-invoked body,
6
//! which bridges it to `__nomi_raise` for the classifier. The form
7
//! never returns normally — execution does not continue past the throw.
8

            
9
use crate::ast::{Expr, WasmType};
10
use crate::compiler::context::CompileContext;
11
use crate::compiler::emit::FunctionEmitter;
12
use crate::compiler::expr::compile_for_stack;
13
use crate::error::{Error, Result};
14
use crate::runtime::SymbolTable;
15

            
16
const NAME: &str = "error";
17

            
18
204
pub(super) fn eval_error(_symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
19
204
    expect_arity(args)?;
20
204
    expect_code_symbol(&args[0])?;
21
204
    Ok(Expr::WasmRuntime(WasmType::I32))
22
204
}
23

            
24
136
pub(super) fn compile_error(
25
136
    ctx: &mut CompileContext,
26
136
    emit: &mut FunctionEmitter,
27
136
    symbols: &mut SymbolTable,
28
136
    args: &[Expr],
29
136
) -> Result<()> {
30
136
    emit_raise_call(ctx, emit, symbols, args)
31
136
}
32

            
33
2720
pub(super) fn compile_error_for_stack(
34
2720
    ctx: &mut CompileContext,
35
2720
    emit: &mut FunctionEmitter,
36
2720
    symbols: &mut SymbolTable,
37
2720
    args: &[Expr],
38
2720
) -> Result<WasmType> {
39
2720
    emit_raise_call(ctx, emit, symbols, args)?;
40
    // The host fn never returns normally, but the wasm validator still
41
    // demands a stack-shaped value of the caller's expected type.
42
    // `unreachable` engages wasm's stack-polymorphism so any branch
43
    // type the IF / COND BlockType picked matches without forcing the
44
    // caller to plumb an expected type down here.
45
2448
    emit.unreachable();
46
2448
    Ok(WasmType::I32)
47
2720
}
48

            
49
2856
fn emit_raise_call(
50
2856
    ctx: &mut CompileContext,
51
2856
    emit: &mut FunctionEmitter,
52
2856
    symbols: &mut SymbolTable,
53
2856
    args: &[Expr],
54
2856
) -> Result<()> {
55
2856
    expect_arity(args)?;
56
2652
    let code = expect_code_symbol(&args[0])?;
57
2584
    push_string_literal(ctx, emit, &code)?;
58
2584
    let msg_ty = compile_for_stack(ctx, emit, symbols, &args[1])?;
59
2584
    if msg_ty != WasmType::StringRef {
60
        return Err(Error::Type {
61
            expected: format!("StringRef for argument 1 of '{NAME}'"),
62
            actual: msg_ty.to_string(),
63
        });
64
2584
    }
65
    // code + message arrays are on the stack in field order; pack them
66
    // into a `$nomi_condition` and throw `$nomi_error`. Catchable by an
67
    // in-module `try_table`; the boundary wrapper bridges an uncaught
68
    // throw to `__nomi_raise`.
69
2584
    emit.struct_new(ctx.condition_type_idx());
70
2584
    emit.throw(ctx.nomi_error_tag());
71
2584
    Ok(())
72
2856
}
73

            
74
2584
fn push_string_literal(
75
2584
    ctx: &mut CompileContext,
76
2584
    emit: &mut FunctionEmitter,
77
2584
    value: &str,
78
2584
) -> Result<()> {
79
2584
    let data_idx = ctx.add_data(value.as_bytes())?;
80
2584
    emit.i32_const(0);
81
2584
    emit.i32_const(value.len() as i32);
82
2584
    emit.array_new_data(ctx.ids.ty_i8_array, data_idx);
83
2584
    Ok(())
84
2584
}
85

            
86
3060
fn expect_arity(args: &[Expr]) -> Result<()> {
87
3060
    if args.len() != 2 {
88
204
        return Err(Error::Arity {
89
204
            name: NAME.to_string(),
90
204
            expected: 2,
91
204
            actual: args.len(),
92
204
        });
93
2856
    }
94
2856
    Ok(())
95
3060
}
96

            
97
2856
fn expect_code_symbol(arg: &Expr) -> Result<String> {
98
2856
    match arg {
99
2788
        Expr::Quote(inner) => match inner.as_ref() {
100
2788
            Expr::Symbol(name) => Ok(name.clone()),
101
            other => Err(Error::Type {
102
                expected: format!("quoted symbol for argument 0 of '{NAME}'"),
103
                actual: format!("{other:?}"),
104
            }),
105
        },
106
68
        other => Err(Error::Type {
107
68
            expected: format!("quoted symbol for argument 0 of '{NAME}'"),
108
68
            actual: format!("{other:?}"),
109
68
        }),
110
    }
111
2856
}