Files
rust/compiler/stable_mir/src/error.rs
Nicholas Nethercote e72e7e9ae3 Merge CompilerError::CompilationFailed and CompilerError::ICE.
`CompilerError` has `CompilationFailed` and `ICE` variants, which seems
reasonable at first. But the way it identifies them is flawed:
- If compilation errors out, i.e. `RunCompiler::run` returns an `Err`,
  it uses `CompilationFailed`, which is reasonable.
- If compilation panics with `FatalError`, it catches the panic and uses
  `ICE`. This is sometimes right, because ICEs do cause `FatalError`
  panics, but sometimes wrong, because certain compiler errors also
  cause `FatalError` panics. (The compiler/rustdoc/clippy/whatever just
  catches the `FatalError` with `catch_with_exit_code` in `main`.)

In other words, certain non-ICE compilation failures get miscategorized
as ICEs. It's not possible to reliably distinguish the two cases, so
this commit merges them. It also renames the combined variant as just
`Failed`, to better match the existing `Interrupted` and `Skipped`
variants.

Here is an example of a non-ICE failure that causes a `FatalError`
panic, from `tests/ui/recursion_limit/issue-105700.rs`:
```
 #![recursion_limit="4"]
 #![invalid_attribute]
 #![invalid_attribute]
 #![invalid_attribute]
 #![invalid_attribute]
 #![invalid_attribute]
 //~^ERROR recursion limit reached while expanding

 fn main() {{}}
```
2024-02-17 09:40:44 +11:00

83 lines
2.4 KiB
Rust

//! When things go wrong, we need some error handling.
//! There are a few different types of errors in StableMIR:
//!
//! - [CompilerError]: This represents errors that can be raised when invoking the compiler.
//! - [Error]: Generic error that represents the reason why a request that could not be fulfilled.
use std::fmt::{Debug, Display, Formatter};
use std::{error, fmt, io};
macro_rules! error {
($fmt: literal $(,)?) => { Error(format!($fmt)) };
($fmt: literal, $($arg:tt)*) => { Error(format!($fmt, $($arg)*)) };
}
/// An error type used to represent an error that has already been reported by the compiler.
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum CompilerError<T> {
/// Compilation failed, either due to normal errors or ICE.
Failed,
/// Compilation was interrupted.
Interrupted(T),
/// Compilation skipped. This happens when users invoke rustc to retrieve information such as
/// --version.
Skipped,
}
/// A generic error to represent an API request that cannot be fulfilled.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Error(pub(crate) String);
impl Error {
pub fn new(msg: String) -> Self {
Self(msg)
}
}
impl From<&str> for Error {
fn from(value: &str) -> Self {
Self(value.into())
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl<T> Display for CompilerError<T>
where
T: Display,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
CompilerError::Failed => write!(f, "Compilation Failed"),
CompilerError::Interrupted(reason) => write!(f, "Compilation Interrupted: {reason}"),
CompilerError::Skipped => write!(f, "Compilation Skipped"),
}
}
}
impl<T> Debug for CompilerError<T>
where
T: Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
CompilerError::Failed => write!(f, "Compilation Failed"),
CompilerError::Interrupted(reason) => write!(f, "Compilation Interrupted: {reason:?}"),
CompilerError::Skipped => write!(f, "Compilation Skipped"),
}
}
}
impl error::Error for Error {}
impl<T> error::Error for CompilerError<T> where T: Display + Debug {}
impl From<io::Error> for Error {
fn from(value: io::Error) -> Self {
Error(value.to_string())
}
}