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}