Skip to main content

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}