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

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

            
33
3486
pub(super) fn compile_error_for_stack(
34
3486
    ctx: &mut CompileContext,
35
3486
    emit: &mut FunctionEmitter,
36
3486
    symbols: &mut SymbolTable,
37
3486
    args: &[Expr],
38
3486
) -> Result<WasmType> {
39
3486
    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
3154
    emit.unreachable();
46
3154
    Ok(WasmType::I32)
47
3486
}
48

            
49
3652
fn emit_raise_call(
50
3652
    ctx: &mut CompileContext,
51
3652
    emit: &mut FunctionEmitter,
52
3652
    symbols: &mut SymbolTable,
53
3652
    args: &[Expr],
54
3652
) -> Result<()> {
55
3652
    expect_arity(args)?;
56
3403
    let code = expect_code_symbol(&args[0])?;
57
3320
    push_string_literal(ctx, emit, &code)?;
58
3320
    let msg_ty = compile_for_stack(ctx, emit, symbols, &args[1])?;
59
3320
    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
3320
    }
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
3320
    emit.struct_new(ctx.condition_type_idx());
70
3320
    emit.throw(ctx.nomi_error_tag());
71
3320
    Ok(())
72
3652
}
73

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

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

            
97
3735
fn expect_code_symbol(arg: &Expr) -> Result<String> {
98
3735
    match arg {
99
3652
        Expr::Quote(inner) => match inner.as_ref() {
100
3652
            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
83
        other => Err(Error::Type {
107
83
            expected: format!("quoted symbol for argument 0 of '{NAME}'"),
108
83
            actual: format!("{other:?}"),
109
83
        }),
110
    }
111
3735
}