Path: blob/main/crates/core/src/error/context.rs
3073 views
use crate::error::{1Error, ErrorExt, OutOfMemory, Result,2boxed::try_new_uninit_box,3error::{OomOrDynError, OomOrDynErrorMut, OomOrDynErrorRef},4};5use core::any::TypeId;6use core::fmt;7use core::ptr::NonNull;8use std_alloc::boxed::Box;910mod sealed {11use super::*;12pub trait Sealed {}13impl<T, E> Sealed for Result<T, E> {}14impl<T> Sealed for Option<T> {}15}1617/// Extension trait to add error context to results.18///19/// This extension trait, and its methods, are the primary way to create error20/// chains. An error's debug output will include the full chain of21/// errors. Errors in these chains are accessible via the22/// [`Error::chain`] and [`Error::root_cause`] methods.23///24/// After applying error context of type `C`, calling25/// [`error.is::<C>()`](Error::is) will return `true` for the new error26/// (unless there was a memory allocation failure) in addition to any other27/// types `T` for which it was already the case that `error.is::<T>()`.28///29/// This boxes the inner `C` type, but if that box allocation fails, then this30/// trait's functions return an `Error` where31/// [`error.is::<OutOfMemory>()`](OutOfMemory) is true.32///33/// # Example34///35/// ```36/// # use wasmtime_internal_core::error as wasmtime;37/// use wasmtime::{Context as _, Result};38/// # #[cfg(feature = "backtrace")]39/// # wasmtime_internal_core::error::disable_backtrace();40///41/// fn u32_to_u8(x: u32) -> Result<u8> {42/// let y = u8::try_from(x).with_context(|| {43/// format!("failed to convert `{x}` into a `u8` (max = `{}`)", u8::MAX)44/// })?;45/// Ok(y)46/// }47///48/// let x = u32_to_u8(42).unwrap();49/// assert_eq!(x, 42);50///51/// let error = u32_to_u8(999).unwrap_err();52///53/// // The error is a `String` because of our added context.54/// assert!(error.is::<String>());55/// assert_eq!(56/// error.to_string(),57/// "failed to convert `999` into a `u8` (max = `255`)",58/// );59///60/// // But it is also a `TryFromIntError` because of the inner error.61/// assert!(error.is::<std::num::TryFromIntError>());62/// assert_eq!(63/// error.root_cause().to_string(),64/// "out of range integral type conversion attempted",65/// );66///67/// // The debug output of the error contains the full error chain.68/// assert_eq!(69/// format!("{error:?}").trim(),70/// r#"71/// failed to convert `999` into a `u8` (max = `255`)72///73/// Caused by:74/// out of range integral type conversion attempted75/// "#.trim(),76/// );77/// ```78///79/// # Example with `Option<T>`80///81/// You can also use this trait to create the initial, root-cause `Error` when a82/// fallible function returns an `Option`:83///84/// ```85/// # use wasmtime_internal_core as wasmtime;86/// use wasmtime::error::{Context as _, Result};87///88/// fn try_get<T>(slice: &[T], i: usize) -> Result<&T> {89/// let elem: Option<&T> = slice.get(i);90/// elem.with_context(|| {91/// format!("out of bounds access: index is {i} but length is {}", slice.len())92/// })93/// }94///95/// let arr = [921, 36, 123, 42, 785];96///97/// let x = try_get(&arr, 2).unwrap();98/// assert_eq!(*x, 123);99///100/// let error = try_get(&arr, 9999).unwrap_err();101/// assert_eq!(102/// error.to_string(),103/// "out of bounds access: index is 9999 but length is 5",104/// );105/// ```106pub trait Context<T, E>: sealed::Sealed {107/// Add additional, already-computed error context to this result.108///109/// Because this method requires that the error context is already computed,110/// it should only be used when the `context` is already available or is111/// effectively a constant. Otherwise, it effectively forces computation of112/// the context, even when we aren't on an error path. The113/// [`Context::with_context`](Context::with_context) method is114/// preferred in these scenarios, as it lazily computes the error context,115/// only doing so when we are actually on an error path.116fn context<C>(self, context: C) -> Result<T, Error>117where118C: fmt::Display + Send + Sync + 'static;119120/// Add additional, lazily-computed error context to this result.121///122/// Only invokes `f` to compute the error context when we are actually on an123/// error path. Does not invoke `f` if we are not on an error path.124fn with_context<C, F>(self, f: F) -> Result<T, Error>125where126C: fmt::Display + Send + Sync + 'static,127F: FnOnce() -> C;128}129130impl<T, E> Context<T, E> for Result<T, E>131where132E: core::error::Error + Send + Sync + 'static,133{134#[inline]135fn context<C>(self, context: C) -> Result<T>136where137C: fmt::Display + Send + Sync + 'static,138{139match self {140Ok(x) => Ok(x),141Err(e) => Err(Error::new(e).context(context)),142}143}144145#[inline]146fn with_context<C, F>(self, f: F) -> Result<T>147where148C: fmt::Display + Send + Sync + 'static,149F: FnOnce() -> C,150{151match self {152Ok(x) => Ok(x),153Err(e) => Err(Error::new(e).context(f())),154}155}156}157158impl<T> Context<T, Error> for Result<T> {159fn context<C>(self, context: C) -> Result<T, Error>160where161C: fmt::Display + Send + Sync + 'static,162{163match self {164Ok(x) => Ok(x),165Err(e) => Err(e.context(context)),166}167}168169fn with_context<C, F>(self, f: F) -> Result<T, Error>170where171C: fmt::Display + Send + Sync + 'static,172F: FnOnce() -> C,173{174match self {175Ok(x) => Ok(x),176Err(e) => Err(e.context(f())),177}178}179}180181impl<T> Context<T, core::convert::Infallible> for Option<T> {182fn context<C>(self, context: C) -> Result<T>183where184C: fmt::Display + Send + Sync + 'static,185{186match self {187Some(x) => Ok(x),188None => Err(Error::from_error_ext(ContextError {189context,190error: None,191})),192}193}194195fn with_context<C, F>(self, f: F) -> Result<T>196where197C: fmt::Display + Send + Sync + 'static,198F: FnOnce() -> C,199{200match self {201Some(x) => Ok(x),202None => Err(Error::from_error_ext(ContextError {203context: f(),204error: None,205})),206}207}208}209210// NB: The `repr(C)` is required for safety of the `ErrorExt::ext_is`211// implementation and the casts that are performed using that method's212// return value.213#[repr(C)]214pub(crate) struct ContextError<C> {215// NB: This must be the first field for safety of the `ErrorExt::ext_is`216// implementation and the casts that are performed using that method's217// return value.218pub(crate) context: C,219220pub(crate) error: Option<Error>,221}222223impl<C> fmt::Debug for ContextError<C>224where225C: fmt::Display,226{227fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {228fmt::Display::fmt(self, f)229}230}231232impl<C> fmt::Display for ContextError<C>233where234C: fmt::Display,235{236fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {237self.context.fmt(f)238}239}240241impl<C> core::error::Error for ContextError<C>242where243C: fmt::Display + Send + Sync + 'static,244{245fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {246let source = self.ext_source()?;247Some(source.as_dyn_core_error())248}249}250251unsafe impl<C> ErrorExt for ContextError<C>252where253C: fmt::Display + Send + Sync + 'static,254{255fn ext_as_dyn_core_error(&self) -> &(dyn core::error::Error + Send + Sync + 'static) {256self257}258259fn ext_into_boxed_dyn_core_error(260self,261) -> Result<Box<dyn core::error::Error + Send + Sync + 'static>, OutOfMemory> {262let boxed = try_new_uninit_box()?;263Ok(Box::write(boxed, self) as _)264}265266fn ext_source(&self) -> Option<OomOrDynErrorRef<'_>> {267let error = self.error.as_ref()?;268Some(error.inner.unpack())269}270271fn ext_source_mut(&mut self) -> Option<OomOrDynErrorMut<'_>> {272let error = self.error.as_mut()?;273Some(error.inner.unpack_mut())274}275276fn ext_take_source(&mut self) -> Option<OomOrDynError> {277let error = self.error.take()?;278Some(error.inner)279}280281fn ext_is(&self, type_id: TypeId) -> bool {282// NB: need to check type id of `C`, not `Self` aka283// `ContextError<C>`.284type_id == TypeId::of::<C>()285}286287unsafe fn ext_move(self, to: NonNull<u8>) {288// Safety: implied by this trait method's contract.289unsafe {290to.cast::<C>().write(self.context);291}292}293294#[cfg(feature = "backtrace")]295fn take_backtrace(&mut self) -> Option<std::backtrace::Backtrace> {296let error = self.error.as_mut()?;297match error.inner.unpack_mut() {298OomOrDynErrorMut::Oom(_) => None,299OomOrDynErrorMut::DynError(mut e) => {300let r = unsafe { e.as_mut() };301r.backtrace.take()302}303}304}305}306307308