Skip to main content

hara_native/vm/
error.rs

1//! Error types for the experimental bytecode VM.
2//!
3//! Compile and runtime errors carry source positions and render like the
4//! parser's errors: `message [line L, column C]`.
5
6use crate::kernel::{ParseError, Position};
7
8/// What went wrong while compiling a form.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum CompileErrorKind {
11    /// The source could not be read at all.
12    Parse,
13    /// A form outside the supported synchronous subset.
14    UnsupportedForm,
15    /// A symbol that is not a lexical local.
16    UnboundSymbol,
17    /// Wrong argument or binding counts (`if`, `let`, `loop`, primitive
18    /// arity above the `u8` limit).
19    Arity,
20    /// `recur` outside a loop, in a non-tail position, or with mismatched
21    /// arity.
22    Recur,
23    /// A suspension form appears outside an async function or direct
24    /// coroutine construction body.
25    InvalidEffect,
26    /// A program limit (constants, code size, locals, stack, arguments).
27    Limit,
28    /// The compiler produced a program the validator rejected; indicates a
29    /// compiler bug rather than bad source.
30    Internal,
31}
32
33/// A compile-time failure with source context.
34#[derive(Debug, Clone)]
35pub struct CompileError {
36    kind: CompileErrorKind,
37    message: String,
38    position: Option<Position>,
39}
40
41impl CompileError {
42    pub(crate) fn new(
43        kind: CompileErrorKind,
44        message: impl Into<String>,
45        position: Option<Position>,
46    ) -> CompileError {
47        CompileError {
48            kind,
49            message: message.into(),
50            position,
51        }
52    }
53
54    pub fn kind(&self) -> CompileErrorKind {
55        self.kind
56    }
57
58    pub fn message(&self) -> &str {
59        &self.message
60    }
61
62    pub fn position(&self) -> Option<Position> {
63        self.position
64    }
65}
66
67impl std::fmt::Display for CompileError {
68    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69        match &self.position {
70            Some(position) => write!(
71                formatter,
72                "{} [line {}, column {}]",
73                self.message, position.line, position.column
74            ),
75            None => formatter.write_str(&self.message),
76        }
77    }
78}
79
80impl std::error::Error for CompileError {}
81
82impl From<ParseError> for CompileError {
83    fn from(error: ParseError) -> CompileError {
84        CompileError::new(CompileErrorKind::Parse, error.message, Some(error.position))
85    }
86}
87
88/// A program rejected by the validator before execution.
89#[derive(Debug, Clone)]
90pub struct ValidationError {
91    pub message: String,
92    /// The instruction index the failure concerns, when applicable.
93    pub instruction: Option<u32>,
94}
95
96impl ValidationError {
97    pub(crate) fn new(message: impl Into<String>, instruction: Option<u32>) -> ValidationError {
98        ValidationError {
99            message: message.into(),
100            instruction,
101        }
102    }
103}
104
105impl std::fmt::Display for ValidationError {
106    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        match self.instruction {
108            Some(instruction) => {
109                write!(
110                    formatter,
111                    "validation failed at {instruction:04}: {}",
112                    self.message
113                )
114            }
115            None => write!(formatter, "validation failed: {}", self.message),
116        }
117    }
118}
119
120impl std::error::Error for ValidationError {}
121
122/// A runtime failure with the failing instruction and its source position.
123#[derive(Debug, Clone)]
124pub struct VmError {
125    pub message: String,
126    pub instruction: u32,
127    pub position: Option<Position>,
128}
129
130impl VmError {
131    pub(crate) fn new(
132        message: impl Into<String>,
133        instruction: u32,
134        position: Option<Position>,
135    ) -> VmError {
136        VmError {
137            message: message.into(),
138            instruction,
139            position,
140        }
141    }
142}
143
144impl std::fmt::Display for VmError {
145    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146        match &self.position {
147            Some(position) => write!(
148                formatter,
149                "{} [line {}, column {}] (instruction {:04})",
150                self.message, position.line, position.column, self.instruction
151            ),
152            None => write!(
153                formatter,
154                "{} (instruction {:04})",
155                self.message, self.instruction
156            ),
157        }
158    }
159}
160
161impl std::error::Error for VmError {}