Skip to main content

codegen_lang/
error.rs

1//! The error a backend reports when it cannot lower a function.
2
3use core::fmt;
4
5use ir_lang::ValidationError;
6
7/// The reason a [`Backend`](crate::Backend) could not lower a function.
8///
9/// Lowering needs well-formed SSA: every value defined before it is used, every branch
10/// targeting a real block with arguments that match its parameters, every operation
11/// applied to operands of the right type. A backend checks that up front with
12/// [`Function::validate`](ir_lang::Function::validate) and refuses to emit code for
13/// input that fails, rather than producing a program that is wrong in a way the IR
14/// already forbids.
15///
16/// The set is `#[non_exhaustive]`: a backend that performs target-specific checks may
17/// report failures the bytecode backend never does, so a `match` on this type must
18/// include a wildcard arm.
19///
20/// # Examples
21///
22/// ```
23/// use codegen_lang::{compile, CodegenError};
24/// use ir_lang::{Builder, Type};
25///
26/// // A function whose entry block never receives a terminator is not well-formed.
27/// let func = Builder::new("f", &[], Type::Unit).finish();
28/// match compile(&func) {
29///     Err(CodegenError::InvalidIr(reason)) => {
30///         assert!(reason.to_string().contains("terminator"));
31///     }
32///     other => panic!("expected an InvalidIr error, got {other:?}"),
33/// }
34/// ```
35#[derive(Clone, PartialEq, Debug)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37#[non_exhaustive]
38pub enum CodegenError {
39    /// The input function did not pass
40    /// [`Function::validate`](ir_lang::Function::validate). The wrapped
41    /// [`ValidationError`] names the offending block or value and explains the
42    /// violation; fix the lowering that produced the IR and try again.
43    InvalidIr(ValidationError),
44}
45
46impl fmt::Display for CodegenError {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        match self {
49            CodegenError::InvalidIr(reason) => {
50                write!(
51                    f,
52                    "cannot lower a function that is not well-formed: {reason}"
53                )
54            }
55        }
56    }
57}
58
59impl core::error::Error for CodegenError {
60    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
61        match self {
62            CodegenError::InvalidIr(reason) => Some(reason),
63        }
64    }
65}
66
67impl From<ValidationError> for CodegenError {
68    fn from(reason: ValidationError) -> Self {
69        CodegenError::InvalidIr(reason)
70    }
71}
72
73#[cfg(test)]
74#[allow(
75    clippy::unwrap_used,
76    clippy::expect_used,
77    clippy::panic,
78    reason = "tests assert on specific outcomes; a wrong outcome should fail the test loudly"
79)]
80mod tests {
81    use super::CodegenError;
82    use crate::compile;
83    use ir_lang::{Builder, Type, ValidationError};
84
85    #[test]
86    fn test_invalid_ir_error_wraps_the_validation_reason() {
87        let func = Builder::new("f", &[], Type::Unit).finish();
88        let err = match compile(&func) {
89            Err(e) => e,
90            Ok(_) => panic!("an unterminated function must not compile"),
91        };
92        assert!(matches!(
93            err,
94            CodegenError::InvalidIr(ValidationError::MissingTerminator { .. })
95        ));
96    }
97
98    #[test]
99    fn test_display_carries_the_underlying_reason() {
100        let func = Builder::new("f", &[], Type::Int).finish();
101        let err = compile(&func).expect_err("non-unit function with no return is invalid");
102        let text = err.to_string();
103        assert!(text.starts_with("cannot lower a function that is not well-formed"));
104    }
105
106    #[test]
107    fn test_error_source_is_the_validation_error() {
108        use core::error::Error;
109        let func = Builder::new("f", &[], Type::Unit).finish();
110        let err = compile(&func).expect_err("unterminated function is invalid");
111        assert!(err.source().is_some());
112    }
113
114    #[test]
115    fn test_from_validation_error_constructs_invalid_ir() {
116        let func = Builder::new("f", &[], Type::Unit).finish();
117        let reason = func
118            .validate()
119            .expect_err("unterminated function is invalid");
120        let err: CodegenError = reason.into();
121        assert!(matches!(err, CodegenError::InvalidIr(_)));
122    }
123}