1
//! Condition accessors for `(handler-case)` clause variables (Tier 3.3,
2
//! ADR-0026). `(error-code e)` and `(error-message e)` read the two
3
//! `$nomi_condition` fields (both `(ref $i8_array)` strings) of a caught
4
//! condition bound at `EntityRef(Condition)`. Mirror the typed-entity
5
//! accessor shape: type-check the arg, then `struct.get` the field.
6

            
7
use super::super::context::CompileContext;
8
use super::super::emit::FunctionEmitter;
9
use super::NativeSpec;
10
use crate::ast::{EntityKind, Expr, WasmType};
11
use crate::error::{Error, Result};
12
use crate::runtime::SymbolTable;
13

            
14
const CODE_FIELD: u32 = 0;
15
const MESSAGE_FIELD: u32 = 1;
16

            
17
408
fn arity_one(name: &str, args: &[Expr]) -> Result<()> {
18
408
    if args.len() != 1 {
19
        return Err(Error::Arity {
20
            name: name.to_string(),
21
            expected: 1,
22
            actual: args.len(),
23
        });
24
408
    }
25
408
    Ok(())
26
408
}
27

            
28
/// Shared stack emit for a condition field accessor: compile the arg
29
/// (must be `EntityRef(Condition)`), then `struct.get` the field. Returns
30
/// `StringRef`.
31
408
fn condition_field_stack(
32
408
    ctx: &mut CompileContext,
33
408
    emit: &mut FunctionEmitter,
34
408
    symbols: &mut SymbolTable,
35
408
    args: &[Expr],
36
408
    name: &str,
37
408
    field: u32,
38
408
) -> Result<WasmType> {
39
408
    arity_one(name, args)?;
40
408
    let ty = crate::compiler::expr::compile_for_stack(ctx, emit, symbols, &args[0])?;
41
408
    let expected = WasmType::EntityRef(EntityKind::Condition);
42
408
    if ty != expected {
43
68
        return Err(Error::Type {
44
68
            expected: format!("{expected} for {name}"),
45
68
            actual: ty.to_string(),
46
68
        });
47
340
    }
48
    // The handler-case clause var is stashed in an `anyref` local (the
49
    // caught condition has no narrower WasmType slot), so the value on the
50
    // stack is `anyref` even though its compile-time type is
51
    // `EntityRef(Condition)`. Downcast to the concrete struct before reading.
52
340
    let condition_idx = ctx.ids.ty_nomi_condition;
53
340
    emit.ref_cast(condition_idx);
54
340
    emit.struct_get(condition_idx, field);
55
340
    Ok(WasmType::StringRef)
56
408
}
57

            
58
fn error_code_eval(_symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
59
    arity_one("ERROR-CODE", args)?;
60
    Ok(Expr::WasmRuntime(WasmType::StringRef))
61
}
62

            
63
204
fn error_code_stack(
64
204
    ctx: &mut CompileContext,
65
204
    emit: &mut FunctionEmitter,
66
204
    symbols: &mut SymbolTable,
67
204
    args: &[Expr],
68
204
) -> Result<WasmType> {
69
204
    condition_field_stack(ctx, emit, symbols, args, "ERROR-CODE", CODE_FIELD)
70
204
}
71

            
72
fn error_message_eval(_symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
73
    arity_one("ERROR-MESSAGE", args)?;
74
    Ok(Expr::WasmRuntime(WasmType::StringRef))
75
}
76

            
77
204
fn error_message_stack(
78
204
    ctx: &mut CompileContext,
79
204
    emit: &mut FunctionEmitter,
80
204
    symbols: &mut SymbolTable,
81
204
    args: &[Expr],
82
204
) -> Result<WasmType> {
83
204
    condition_field_stack(ctx, emit, symbols, args, "ERROR-MESSAGE", MESSAGE_FIELD)
84
204
}
85

            
86
pub(in crate::compiler::native) const NATIVES: &[NativeSpec] = &[
87
    NativeSpec {
88
        name: "ERROR-CODE",
89
        eval: error_code_eval,
90
        stack: Some(error_code_stack),
91
        effect: None,
92
    },
93
    NativeSpec {
94
        name: "ERROR-MESSAGE",
95
        eval: error_message_eval,
96
        stack: Some(error_message_stack),
97
        effect: None,
98
    },
99
];