Files
ngr/src/backend/error.rs
2024-02-02 10:31:54 -08:00

158 lines
6.3 KiB
Rust

use crate::{backend::runtime::RuntimeFunctionError, eval::PrimitiveType, ir::Type};
use codespan_reporting::diagnostic::Diagnostic;
use cranelift_codegen::{isa::LookupError, settings::SetError, CodegenError};
use cranelift_module::ModuleError;
use internment::ArcIntern;
use thiserror::Error;
/// An error in the translation to a backend (either the JIT or the static compiler).
///
/// In general, this is just a nice summary error type for a bunch of downstream
/// errors; the exception are internal errors from builtin functions or variable
/// lookups.
///
/// Unlike some other errors in the system, the translation to a `Diagnostic` does
/// not necessarily provide a whole lot of value, because we have lost most of the
/// source information by the time we're generating these errors. That being said,
/// people who want to provide nicer error messages might consider using the
/// translation through `Diagnostic` anyways, just in case we add more information
/// in the future.
///
/// Finally, the `PartialEq` for this function is a bit fuzzy. In some cases, it
/// ensures that the errors match exactly. In other cases, though, it just checks to
/// see if the two errors are of the same class; e.g., it will return true if both
/// errors are `BackendError::CodegenError`, regardless of what the specific
/// `CodegenError` is.
#[derive(Debug, Error)]
pub enum BackendError {
#[error("Cranelift module error: {0}")]
Cranelift(#[from] ModuleError),
#[error("Builtin function error: {0}")]
BuiltinError(#[from] RuntimeFunctionError),
#[error("Internal variable lookup error")]
VariableLookupFailure(ArcIntern<String>),
#[error(transparent)]
CodegenError(#[from] CodegenError),
#[error(transparent)]
SetError(#[from] SetError),
#[error(transparent)]
LookupError(#[from] LookupError),
#[error(transparent)]
Write(#[from] cranelift_object::object::write::Error),
#[error("Invalid type cast from {from} to {to}")]
InvalidTypeCast { from: PrimitiveType, to: Type },
#[error("Unknown string constant '{0}")]
UnknownString(ArcIntern<String>),
#[error("Compiler doesn't currently support function arguments")]
NoFunctionArguments {
function_name: String,
argument_name: String,
},
}
impl From<BackendError> for Diagnostic<usize> {
fn from(value: BackendError) -> Self {
match value {
BackendError::Cranelift(me) => {
Diagnostic::error().with_message(format!("Internal cranelift error: {}", me))
.with_notes(vec![format!("{:?}", me)])
}
BackendError::BuiltinError(me) => {
Diagnostic::error().with_message(format!("Internal runtime function error: {}", me))
}
BackendError::VariableLookupFailure(x) => Diagnostic::error()
.with_message(format!("Internal variable lookup error for {}", x)),
BackendError::CodegenError(me) => {
Diagnostic::error().with_message(format!("Internal codegen error: {}", me))
}
BackendError::SetError(me) => {
Diagnostic::error().with_message(format!("Internal backend setup error: {}", me))
}
BackendError::LookupError(me) => {
Diagnostic::error().with_message(format!("Internal error: {}", me))
}
BackendError::Write(me) => {
Diagnostic::error().with_message(format!("Cranelift object write error: {}", me))
}
BackendError::InvalidTypeCast { from, to } => Diagnostic::error().with_message(
format!("Internal error trying to cast from {} to {}", from, to),
),
BackendError::UnknownString(str) => Diagnostic::error()
.with_message(format!("Unknown string found trying to compile: '{}'", str)),
BackendError::NoFunctionArguments {
function_name,
argument_name,
} => Diagnostic::error().with_message(format!(
"Function {} takes a function argument ({}), which is not supported",
function_name, argument_name
)),
}
}
}
impl PartialEq for BackendError {
fn eq(&self, other: &Self) -> bool {
match self {
BackendError::BuiltinError(a) => match other {
BackendError::BuiltinError(b) => a == b,
_ => false,
},
// because the underlying `CodegenError` doesn't implement `PartialEq',
// we just check that they're both `CodegenError`s.
BackendError::CodegenError(_) => matches!(other, BackendError::CodegenError(_)),
// because the underlying `ModuleError` doesn't implement `PartialEq',
// we just check that they're both `Cranelift`s.
BackendError::Cranelift(_) => matches!(other, BackendError::Cranelift(_)),
BackendError::LookupError(a) => match other {
BackendError::LookupError(b) => a == b,
_ => false,
},
BackendError::SetError(a) => match other {
BackendError::SetError(b) => a == b,
_ => false,
},
BackendError::VariableLookupFailure(a) => match other {
BackendError::VariableLookupFailure(b) => a == b,
_ => false,
},
BackendError::Write(a) => match other {
BackendError::Write(b) => a == b,
_ => false,
},
BackendError::InvalidTypeCast {
from: from1,
to: to1,
} => match other {
BackendError::InvalidTypeCast {
from: from2,
to: to2,
} => from1 == from2 && to1 == to2,
_ => false,
},
BackendError::UnknownString(a) => match other {
BackendError::UnknownString(b) => a == b,
_ => false,
},
BackendError::NoFunctionArguments {
function_name: f1,
argument_name: a1,
} => match other {
BackendError::NoFunctionArguments {
function_name: f2,
argument_name: a2,
} => f1 == f2 && a1 == a2,
_ => false,
},
}
}
}