Skip to main content

opt_lang/
error.rs

1//! The error type of the optimizer.
2
3use core::fmt;
4
5use ir_lang::{BuildError, FuncId, ModuleError, ValidationError};
6
7use crate::pipeline::PassKind;
8
9/// Why optimization stopped.
10///
11/// Optimizing a valid function never fails: [`OptError::InvalidInput`] means
12/// the IR handed in was not valid (nothing was changed), and the other variants
13/// mean a pass produced IR the validator rejects, which is a defect in this crate
14/// (please report it with the input). A function that runs out of
15/// [budget](crate::Budget) is not an error: it is left valid, partly optimized,
16/// and counted in [`Stats::budget_exhausted`](crate::Stats::budget_exhausted).
17///
18/// # Examples
19///
20/// ```
21/// use ir_lang::{FuncId, Module};
22/// use opt_lang::OptError;
23///
24/// let mut m = Module::new("m");
25/// let err = opt_lang::Optimizer::new()
26///     .run_function(&mut m, FuncId::from_u32(3))
27///     .unwrap_err();
28/// assert!(matches!(err, OptError::Module(_)));
29/// ```
30#[derive(Clone, PartialEq, Eq, Debug)]
31#[non_exhaustive]
32pub enum OptError {
33    /// The function is unknown or has no body.
34    Module(ModuleError),
35    /// The function did not validate before optimization; it was left untouched.
36    InvalidInput {
37        /// The function.
38        func: FuncId,
39        /// What the validator reported.
40        error: ValidationError,
41    },
42    /// A pass left the function invalid (a defect in this crate). Reported only
43    /// when validation between passes is on, which it is by default in debug
44    /// builds and tests.
45    InvalidOutput {
46        /// The function.
47        func: FuncId,
48        /// The pass that ran last.
49        pass: PassKind,
50        /// What the validator reported.
51        error: ValidationError,
52    },
53    /// The IR builder refused a rewrite a pass attempted (a defect in this
54    /// crate). The function may be partly rewritten.
55    Build {
56        /// The function.
57        func: FuncId,
58        /// The pass.
59        pass: PassKind,
60        /// What the builder reported.
61        error: BuildError,
62    },
63}
64
65impl fmt::Display for OptError {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        match self {
68            OptError::Module(e) => write!(f, "{e}"),
69            OptError::InvalidInput { func, error } => {
70                write!(f, "function {func} is not valid IR: {error}")
71            }
72            OptError::InvalidOutput { func, pass, error } => write!(
73                f,
74                "pass `{}` left function {func} invalid: {error}",
75                pass.name()
76            ),
77            OptError::Build { func, pass, error } => write!(
78                f,
79                "pass `{}` could not rewrite function {func}: {error}",
80                pass.name()
81            ),
82        }
83    }
84}
85
86impl core::error::Error for OptError {
87    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
88        match self {
89            OptError::Module(e) => Some(e),
90            OptError::InvalidInput { error, .. } | OptError::InvalidOutput { error, .. } => {
91                Some(error)
92            }
93            OptError::Build { error, .. } => Some(error),
94        }
95    }
96}
97
98impl From<ModuleError> for OptError {
99    fn from(e: ModuleError) -> Self {
100        OptError::Module(e)
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107    use alloc::string::ToString;
108    use core::error::Error;
109
110    #[test]
111    fn test_display_names_the_pass_and_function() {
112        let e = OptError::Build {
113            func: FuncId::from_u32(2),
114            pass: PassKind::Gvn,
115            error: BuildError::CapacityExceeded,
116        };
117        let text = e.to_string();
118        assert!(text.contains("gvn") && text.contains("f2"), "{text}");
119        assert!(e.source().is_some());
120        let m = OptError::from(ModuleError::NotDefined {
121            func: FuncId::from_u32(1),
122        });
123        assert!(m.source().is_some());
124        assert!(!m.to_string().is_empty());
125    }
126}