cljrs_runtime/env/error.rs
1//! Evaluation-time error types.
2
3use cljrs_value::Value;
4use cljrs_value::ValueError;
5
6#[derive(Debug, thiserror::Error)]
7pub enum EvalError {
8 #[error("runtime error: {0}")]
9 Runtime(String),
10
11 /// The cooperative execution-credit budget was exhausted.
12 #[error("gas exhausted")]
13 GasExhausted,
14
15 /// The process memory governor refused a governed allocation (strict
16 /// mode), and a collection did not bring committed memory back within
17 /// the hard limit. Fixed-size; building it never allocates. Like
18 /// `GasExhausted`, `try`/`catch` does not intercept it.
19 #[error("{0}")]
20 MemoryLimitExceeded(cljrs_gc::MemoryLimitExceeded),
21
22 /// An operation attempted to use a capability unavailable to an isolated
23 /// transaction function.
24 #[error("effect forbidden in transaction function: {0}")]
25 ForbiddenEffect(String),
26
27 #[error("unbound symbol: {0}")]
28 UnboundSymbol(String),
29
30 #[error("arity error calling {name}: expected {expected}, got {got}")]
31 Arity {
32 name: String,
33 expected: String,
34 got: usize,
35 },
36
37 #[error("not callable: {0}")]
38 NotCallable(String),
39
40 /// A value thrown via `throw` or `ex-info`.
41 #[error("{0}")]
42 Thrown(Value),
43
44 #[error("read error: {0}")]
45 Read(#[from] cljrs_types::error::CljxError),
46
47 /// Internal signal for `recur` — caught by the loop/fn trampoline.
48 /// Never propagated to user code.
49 #[doc(hidden)]
50 #[error("internal: recur outside loop or fn")]
51 Recur(Vec<Value>),
52
53 #[error(
54 "commit {commit:?} failed signature verification — \
55 refusing to execute versioned symbol (enable GPG/SSH trust or disable \
56 :verify-commit-signatures): {reason}"
57 )]
58 CommitSignatureVerificationFailed { commit: String, reason: String },
59}
60
61impl EvalError {
62 /// Convert this error into a Clojure error *value* (`Value::Error`).
63 ///
64 /// A `Thrown` value is returned unchanged (preserving its `ex-data` /
65 /// `ex-cause`); any other error is wrapped in a fresh `ExceptionInfo` with
66 /// the error's display string as the message. Used where an error must be
67 /// stored as a value and later re-thrown — e.g. a failed `Future`'s state.
68 pub fn to_error_value(self) -> Value {
69 match self {
70 EvalError::Thrown(v) => v,
71 other => {
72 let msg = other.to_string();
73 let error = match other {
74 EvalError::MemoryLimitExceeded(fault) => {
75 cljrs_value::ValueError::MemoryLimitExceeded(fault)
76 }
77 _ => cljrs_value::ValueError::Other(msg.clone()),
78 };
79 // Error reporting may use the critical memory reserve.
80 Value::Error(cljrs_gc::GcPtr::new_critical(
81 cljrs_value::ExceptionInfo::new(error, msg, None, None),
82 ))
83 }
84 }
85 }
86}
87
88/// Surface a builtin's `ValueError` to the evaluator as a *catchable* condition.
89///
90/// Internal runtime errors (`IndexOutOfBounds`, `WrongType`, `ArityError`, …)
91/// are normalized into an `EvalError::Thrown(Value::Error(..))` carrying the
92/// original `ValueError` variant and its plain display message — no
93/// `runtime error:` prefix — so that `(catch :default e ..)` /
94/// `(catch Throwable e ..)` bind a value on which `ex-message` / `ex-data`
95/// behave the same as for a user `throw` / `ex-info`. A `ValueError::Thrown`
96/// (a builtin re-throwing a Clojure value) is surfaced as that exact value.
97pub fn value_error_to_eval_error(err: ValueError) -> EvalError {
98 match err {
99 ValueError::Thrown(v) => EvalError::Thrown(v),
100 ValueError::GasExhausted => EvalError::GasExhausted,
101 ValueError::MemoryLimitExceeded(fault) => {
102 // The error is back on a path that carries it; a copy left in
103 // the runtime-fault slot (see `callback::invoke`) would report
104 // it a second time.
105 let _ = cljrs_gc::governor::take_runtime_fault();
106 EvalError::MemoryLimitExceeded(fault)
107 }
108 other => {
109 let msg = other.to_string();
110 // Error reporting may use the critical memory reserve.
111 EvalError::Thrown(Value::Error(cljrs_gc::GcPtr::new_critical(
112 cljrs_value::ExceptionInfo::new(other, msg, None, None),
113 )))
114 }
115 }
116}
117
118pub type EvalResult<T = Value> = Result<T, EvalError>;