jit_lang/error.rs
1//! The error type a compile can fail with, and the conversions that build it.
2
3use std::error::Error;
4use std::fmt;
5
6use ir_lang::ValidationError;
7use pager_lang::PagerError;
8
9/// The reason a function could not be compiled and made executable.
10///
11/// A compile runs three stages, and each contributes a variant: the input is
12/// validated as well-formed SSA, translated and lowered to machine code, then placed
13/// in executable memory. [`InvalidIr`](JitError::InvalidIr) reports the first stage
14/// rejecting malformed input; [`Unsupported`](JitError::Unsupported) reports a
15/// function that is valid but uses something this back-end does not lower, or a host
16/// the code generator cannot target; [`Codegen`](JitError::Codegen) reports the code
17/// generator itself failing; and [`Memory`](JitError::Memory) reports executable
18/// memory being unavailable.
19///
20/// The enum is `#[non_exhaustive]`: a future release may add a variant without it
21/// being a breaking change, so a `match` on it must keep a wildcard arm. It implements
22/// [`Display`](fmt::Display) and [`std::error::Error`], and converts from the two
23/// wrapped error types with [`From`], so `?` propagates them.
24///
25/// # Examples
26///
27/// ```
28/// use jit_lang::{compile, JitError};
29/// use ir_lang::{Builder, Type};
30///
31/// // A function whose entry block never gets a terminator is not well-formed.
32/// let func = Builder::new("f", &[], Type::Unit).finish();
33/// match compile(&func) {
34/// Err(JitError::InvalidIr(reason)) => {
35/// assert!(reason.to_string().contains("terminator"));
36/// }
37/// other => panic!("expected InvalidIr, got {other:?}"),
38/// }
39/// ```
40#[derive(Debug)]
41#[non_exhaustive]
42pub enum JitError {
43 /// The function did not pass [`Function::validate`](ir_lang::Function::validate);
44 /// the wrapped [`ValidationError`] names the offending block or value. The code
45 /// generator is never handed input the IR already forbids.
46 InvalidIr(ValidationError),
47
48 /// The function is well-formed but uses something this back-end cannot lower, or
49 /// the host architecture has no code generator. The message says which — for
50 /// example a parameter typed [`Unit`](ir_lang::Type::Unit), which has no value at
51 /// the machine level, or an unsupported target CPU.
52 Unsupported(&'static str),
53
54 /// The native code generator rejected the translated function. This does not
55 /// happen for a validated function the translator accepts; it is the surfaced
56 /// form of an internal code-generation failure, carried as the generator's own
57 /// message rather than swallowed.
58 Codegen(String),
59
60 /// Executable memory for the compiled code could not be obtained or protected;
61 /// the wrapped [`PagerError`] says why (the mapping failed, or the
62 /// write-then-execute protection change was refused).
63 Memory(PagerError),
64}
65
66impl fmt::Display for JitError {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 match self {
69 JitError::InvalidIr(err) => write!(f, "the function is not well-formed: {err}"),
70 JitError::Unsupported(what) => write!(f, "unsupported: {what}"),
71 JitError::Codegen(msg) => write!(f, "code generation failed: {msg}"),
72 JitError::Memory(err) => write!(f, "executable memory unavailable: {err}"),
73 }
74 }
75}
76
77impl Error for JitError {
78 fn source(&self) -> Option<&(dyn Error + 'static)> {
79 match self {
80 JitError::InvalidIr(err) => Some(err),
81 JitError::Memory(err) => Some(err),
82 JitError::Unsupported(_) | JitError::Codegen(_) => None,
83 }
84 }
85}
86
87impl From<ValidationError> for JitError {
88 fn from(err: ValidationError) -> Self {
89 JitError::InvalidIr(err)
90 }
91}
92
93impl From<PagerError> for JitError {
94 fn from(err: PagerError) -> Self {
95 JitError::Memory(err)
96 }
97}
98
99#[cfg(test)]
100#[allow(
101 clippy::unwrap_used,
102 clippy::panic,
103 reason = "tests assert on specific variants; a wrong one should fail loudly"
104)]
105mod tests {
106 use super::JitError;
107 use ir_lang::{Builder, Type};
108 use std::error::Error;
109
110 #[test]
111 fn test_invalid_ir_reports_the_validation_reason_as_source() {
112 let func = Builder::new("f", &[], Type::Unit).finish(); // no terminator
113 let err: JitError = func.validate().unwrap_err().into();
114 assert!(matches!(err, JitError::InvalidIr(_)));
115 assert!(err.source().is_some());
116 assert!(err.to_string().contains("not well-formed"));
117 }
118
119 #[test]
120 fn test_unsupported_carries_its_message_and_has_no_source() {
121 let err = JitError::Unsupported("a parameter has type unit");
122 assert!(err.to_string().contains("unit"));
123 assert!(err.source().is_none());
124 }
125
126 #[test]
127 fn test_codegen_message_is_preserved() {
128 let err = JitError::Codegen("verifier error".to_string());
129 assert!(err.to_string().contains("verifier error"));
130 assert!(err.source().is_none());
131 }
132}