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), #[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), #[error("Compiler doesn't currently support function arguments")] NoFunctionArguments { function_name: String, argument_name: String, }, } impl From for Diagnostic { 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, }, } } }