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
913
fn arity_one(name: &str, args: &[Expr]) -> Result<()> {
18
913
    if args.len() != 1 {
19
        return Err(Error::Arity {
20
            name: name.to_string(),
21
            expected: 1,
22
            actual: args.len(),
23
        });
24
913
    }
25
913
    Ok(())
26
913
}
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
498
fn condition_field_stack(
32
498
    ctx: &mut CompileContext,
33
498
    emit: &mut FunctionEmitter,
34
498
    symbols: &mut SymbolTable,
35
498
    args: &[Expr],
36
498
    name: &str,
37
498
    field: u32,
38
498
) -> Result<WasmType> {
39
498
    arity_one(name, args)?;
40
498
    let ty = crate::compiler::expr::compile_for_stack(ctx, emit, symbols, &args[0])?;
41
498
    let expected = WasmType::EntityRef(EntityKind::Condition);
42
498
    if ty != expected {
43
83
        return Err(Error::Type {
44
83
            expected: format!("{expected} for {name}"),
45
83
            actual: ty.to_string(),
46
83
        });
47
415
    }
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
415
    let condition_idx = ctx.ids.ty_nomi_condition;
53
415
    emit.ref_cast(condition_idx);
54
415
    emit.struct_get(condition_idx, field);
55
415
    Ok(WasmType::StringRef)
56
498
}
57

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

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

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

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