use crate::Error;
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ErrorCode {
DeviceOutOfMemory,
UnsupportedFeature,
PoisonedLock,
KernelCompileFailed,
DispatchFailed,
InvalidProgram,
Unknown,
}
impl ErrorCode {
#[must_use]
pub const fn stable_id(self) -> u32 {
match self {
Self::DeviceOutOfMemory => 1001,
Self::UnsupportedFeature => 1002,
Self::PoisonedLock => 1003,
Self::KernelCompileFailed => 1004,
Self::DispatchFailed => 1005,
Self::InvalidProgram => 1006,
Self::Unknown => 1999,
}
}
}
impl std::fmt::Display for ErrorCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::DeviceOutOfMemory => f.write_str("device-out-of-memory"),
Self::UnsupportedFeature => f.write_str("unsupported-feature"),
Self::PoisonedLock => f.write_str("poisoned-lock"),
Self::KernelCompileFailed => f.write_str("shader-compile-failed"),
Self::DispatchFailed => f.write_str("dispatch-failed"),
Self::InvalidProgram => f.write_str("invalid-program"),
Self::Unknown => f.write_str("unknown"),
}
}
}
#[non_exhaustive]
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
pub enum BackendError {
#[error(
"device out of memory: requested {requested} bytes, {available} available. Fix: reduce buffer sizes or split the dispatch into smaller chunks."
)]
DeviceOutOfMemory {
requested: u64,
available: u64,
},
#[error(
"unsupported feature `{name}` on backend `{backend}`. Fix: check backend capability before using this feature, or select a backend that supports it."
)]
UnsupportedFeature {
name: String,
backend: String,
},
#[error(
"backend lock poisoned: {lock_error}. Fix: report the panic origin, prevent panics on lock guards, and retry the backend operation."
)]
PoisonedLock {
lock_error: String,
},
#[error(
"kernel-source compile failed on backend `{backend}`: {compiler_message}. Fix: validate the vyre IR before lowering and check the lowered kernel source for type errors."
)]
KernelCompileFailed {
backend: String,
compiler_message: String,
},
#[error(
"dispatch failed (code {code:?}): {message}. Fix: verify adapter limits, buffer sizes, and GPU queue health before retrying."
)]
DispatchFailed {
code: Option<i32>,
message: String,
},
#[error("{fix}")]
InvalidProgram {
fix: String,
},
#[error("{0}")]
Raw(String),
}
impl From<crate::Error> for BackendError {
fn from(error: crate::Error) -> Self {
Self::new(error.to_string())
}
}
impl BackendError {
pub fn new(message: impl Into<String>) -> Self {
let message = message.into();
if message.contains("Fix: ") {
return Self::Raw(message);
}
Self::Raw(format!(
"{message}. Fix: include backend-specific recovery guidance."
))
}
#[must_use]
pub fn unsupported_extension(
backend: impl Into<String>,
extension_kind: &str,
debug_identity: &str,
) -> Self {
Self::UnsupportedFeature {
name: format!("opaque IR extension `{extension_kind}`/`{debug_identity}`"),
backend: backend.into(),
}
}
pub fn poisoned_lock<T>(error: std::sync::PoisonError<T>) -> Self {
Self::PoisonedLock {
lock_error: error.to_string(),
}
}
#[must_use]
pub fn message(&self) -> String {
self.to_string()
}
#[must_use]
pub fn into_message(self) -> String {
self.to_string()
}
#[must_use]
pub fn code(&self) -> ErrorCode {
match self {
Self::DeviceOutOfMemory { .. } => ErrorCode::DeviceOutOfMemory,
Self::UnsupportedFeature { .. } => ErrorCode::UnsupportedFeature,
Self::PoisonedLock { .. } => ErrorCode::PoisonedLock,
Self::KernelCompileFailed { .. } => ErrorCode::KernelCompileFailed,
Self::DispatchFailed { .. } => ErrorCode::DispatchFailed,
Self::InvalidProgram { .. } => ErrorCode::InvalidProgram,
Self::Raw(_) => ErrorCode::Unknown,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn error_code_display_stable_names() {
let cases = [
(ErrorCode::DeviceOutOfMemory, "device-out-of-memory"),
(ErrorCode::UnsupportedFeature, "unsupported-feature"),
(ErrorCode::PoisonedLock, "poisoned-lock"),
(ErrorCode::KernelCompileFailed, "shader-compile-failed"),
(ErrorCode::DispatchFailed, "dispatch-failed"),
(ErrorCode::InvalidProgram, "invalid-program"),
(ErrorCode::Unknown, "unknown"),
];
for (code, expected) in cases {
assert_eq!(format!("{}", code), expected);
}
}
}