158 lines
6.3 KiB
Rust
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,
|
|
},
|
|
}
|
|
}
|
|
}
|