Lines
89.61 %
Functions
20 %
Branches
100 %
//! `(error 'code "message")` special form. Lowers to a native wasm
//! `struct.new $nomi_condition` + `throw $nomi_error` (Tier 3,
//! ADR-0026), so an enclosing `(handler-case)` / `(unwind-protect)`
//! `try_table` can catch it in-module. An uncaught raise is caught by
//! the compiler-emitted boundary wrapper around each host-invoked body,
//! which bridges it to `__nomi_raise` for the classifier. The form
//! never returns normally — execution does not continue past the throw.
use crate::ast::{Expr, WasmType};
use crate::compiler::context::CompileContext;
use crate::compiler::emit::FunctionEmitter;
use crate::compiler::expr::compile_for_stack;
use crate::error::{Error, Result};
use crate::runtime::SymbolTable;
const NAME: &str = "error";
pub(super) fn eval_error(_symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
expect_arity(args)?;
expect_code_symbol(&args[0])?;
Ok(Expr::WasmRuntime(WasmType::I32))
}
pub(super) fn compile_error(
ctx: &mut CompileContext,
emit: &mut FunctionEmitter,
symbols: &mut SymbolTable,
args: &[Expr],
) -> Result<()> {
emit_raise_call(ctx, emit, symbols, args)
pub(super) fn compile_error_for_stack(
) -> Result<WasmType> {
emit_raise_call(ctx, emit, symbols, args)?;
// The host fn never returns normally, but the wasm validator still
// demands a stack-shaped value of the caller's expected type.
// `unreachable` engages wasm's stack-polymorphism so any branch
// type the IF / COND BlockType picked matches without forcing the
// caller to plumb an expected type down here.
emit.unreachable();
Ok(WasmType::I32)
fn emit_raise_call(
let code = expect_code_symbol(&args[0])?;
push_string_literal(ctx, emit, &code)?;
let msg_ty = compile_for_stack(ctx, emit, symbols, &args[1])?;
if msg_ty != WasmType::StringRef {
return Err(Error::Type {
expected: format!("StringRef for argument 1 of '{NAME}'"),
actual: msg_ty.to_string(),
});
// code + message arrays are on the stack in field order; pack them
// into a `$nomi_condition` and throw `$nomi_error`. Catchable by an
// in-module `try_table`; the boundary wrapper bridges an uncaught
// throw to `__nomi_raise`.
emit.struct_new(ctx.condition_type_idx());
emit.throw(ctx.nomi_error_tag());
Ok(())
fn push_string_literal(
value: &str,
let data_idx = ctx.add_data(value.as_bytes())?;
emit.i32_const(0);
emit.i32_const(value.len() as i32);
emit.array_new_data(ctx.ids.ty_i8_array, data_idx);
fn expect_arity(args: &[Expr]) -> Result<()> {
if args.len() != 2 {
return Err(Error::Arity {
name: NAME.to_string(),
expected: 2,
actual: args.len(),
fn expect_code_symbol(arg: &Expr) -> Result<String> {
match arg {
Expr::Quote(inner) => match inner.as_ref() {
Expr::Symbol(name) => Ok(name.clone()),
other => Err(Error::Type {
expected: format!("quoted symbol for argument 0 of '{NAME}'"),
actual: format!("{other:?}"),
}),
},