Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 122 additions & 8 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ impl Error {
object_mut: object_mut::<E>,
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
object_boxed: object_boxed::<E>,
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
object_reallocate_boxed: object_reallocate_boxed::<E>,
object_downcast: object_downcast::<E>,
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: object_downcast_mut::<E>,
Expand Down Expand Up @@ -188,6 +190,8 @@ impl Error {
object_mut: object_mut::<MessageError<M>>,
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
object_boxed: object_boxed::<MessageError<M>>,
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
object_reallocate_boxed: object_reallocate_boxed::<MessageError<M>>,
object_downcast: object_downcast::<M>,
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: object_downcast_mut::<M>,
Expand Down Expand Up @@ -218,6 +222,8 @@ impl Error {
object_mut: object_mut::<DisplayError<M>>,
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
object_boxed: object_boxed::<DisplayError<M>>,
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
object_reallocate_boxed: object_reallocate_boxed::<DisplayError<M>>,
object_downcast: object_downcast::<M>,
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: object_downcast_mut::<M>,
Expand Down Expand Up @@ -254,6 +260,8 @@ impl Error {
object_mut: object_mut::<ContextError<C, E>>,
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
object_boxed: object_boxed::<ContextError<C, E>>,
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
object_reallocate_boxed: object_reallocate_boxed::<ContextError<C, E>>,
object_downcast: context_downcast::<C, E>,
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: context_downcast_mut::<C, E>,
Expand Down Expand Up @@ -284,6 +292,8 @@ impl Error {
object_mut: object_mut::<BoxedError>,
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
object_boxed: object_boxed::<BoxedError>,
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
object_reallocate_boxed: object_reallocate_boxed::<BoxedError>,
object_downcast: object_downcast::<Box<dyn StdError + Send + Sync>>,
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: object_downcast_mut::<Box<dyn StdError + Send + Sync>>,
Expand Down Expand Up @@ -401,6 +411,8 @@ impl Error {
object_mut: object_mut::<ContextError<C, Error>>,
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
object_boxed: object_boxed::<ContextError<C, Error>>,
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
object_reallocate_boxed: object_reallocate_boxed::<ContextError<C, Error>>,
object_downcast: context_chain_downcast::<C>,
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: context_chain_downcast_mut::<C>,
Expand Down Expand Up @@ -609,6 +621,98 @@ impl Error {
}
}

/// Convert to a standard library error trait object.
///
/// This is implemented as a cheap pointer cast that does not allocate or
/// deallocate memory. Like [`anyhow::Error::from_boxed`], it's useful for
/// interop with other error libraries.
///
/// The same conversion is also available as
/// <code style="display:inline;white-space:normal;">impl From&lt;anyhow::Error&gt;
/// for Box&lt;dyn Error + Send + Sync + &apos;static&gt;</code>.
///
/// If a backtrace was collected during construction of the `anyhow::Error`,
/// that backtrace remains accessible using the standard library `Error`
/// trait's provider API, but as a consequence, the resulting boxed error
/// can no longer be downcast to its original underlying type.
///
/// ```
#[cfg_attr(not(error_generic_member_access), doc = "# _ = stringify! {")]
/// #![feature(error_generic_member_access)]
///
/// use anyhow::anyhow;
/// use std::backtrace::Backtrace;
/// use thiserror::Error;
///
/// #[derive(Error, Debug)]
/// #[error("...")]
/// struct MyError;
///
/// let anyhow_error = anyhow!(MyError);
/// println!("{}", anyhow_error.backtrace()); // has Backtrace
/// assert!(anyhow_error.downcast_ref::<MyError>().is_some()); // can downcast
///
/// let boxed_dyn_error = anyhow_error.into_boxed_dyn_error();
/// assert!(std::error::request_ref::<Backtrace>(&*boxed_dyn_error).is_some()); // has Backtrace
/// assert!(boxed_dyn_error.downcast_ref::<MyError>().is_none()); // can no longer downcast
#[cfg_attr(not(error_generic_member_access), doc = "# };")]
/// ```
///
/// [`anyhow::Error::from_boxed`]: Self::from_boxed
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
#[must_use]
pub fn into_boxed_dyn_error(self) -> Box<dyn StdError + Send + Sync + 'static> {
let outer = ManuallyDrop::new(self);
unsafe {
// Use vtable to attach ErrorImpl<E>'s native StdError vtable for
// the right original type E.
(vtable(outer.inner.ptr).object_boxed)(outer.inner)
}
}

/// Convert to a standard library error trait object.
///
/// Unlike `self.into_boxed_dyn_error()`, this method relocates the
/// underlying error into a new allocation in order to make it downcastable
/// to `&E` or `Box<E>` for its original underlying error type. Any
/// backtrace collected during construction of the `anyhow::Error` is
/// discarded.
///
/// ```
#[cfg_attr(not(error_generic_member_access), doc = "# _ = stringify!{")]
/// #![feature(error_generic_member_access)]
///
/// use anyhow::anyhow;
/// use std::backtrace::Backtrace;
/// use thiserror::Error;
///
/// #[derive(Error, Debug)]
/// #[error("...")]
/// struct MyError;
///
/// let anyhow_error = anyhow!(MyError);
/// println!("{}", anyhow_error.backtrace()); // has Backtrace
/// assert!(anyhow_error.downcast_ref::<MyError>().is_some()); // can downcast
///
/// let boxed_dyn_error = anyhow_error.reallocate_into_boxed_dyn_error_without_backtrace();
/// assert!(std::error::request_ref::<Backtrace>(&*boxed_dyn_error).is_none()); // Backtrace lost
/// assert!(boxed_dyn_error.downcast_ref::<MyError>().is_some()); // can downcast to &MyError
/// assert!(boxed_dyn_error.downcast::<MyError>().is_ok()); // can downcast to Box<MyError>
#[cfg_attr(not(error_generic_member_access), doc = "# };")]
/// ```
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
#[must_use]
pub fn reallocate_into_boxed_dyn_error_without_backtrace(
self,
) -> Box<dyn StdError + Send + Sync + 'static> {
let outer = ManuallyDrop::new(self);
unsafe {
// Use vtable to attach E's native StdError vtable for the right
// original type E.
(vtable(outer.inner.ptr).object_reallocate_boxed)(outer.inner)
}
}

#[cfg(error_generic_member_access)]
pub(crate) fn provide<'a>(&'a self, request: &mut Request<'a>) {
unsafe { ErrorImpl::provide(self.inner.by_ref(), request) }
Expand Down Expand Up @@ -682,6 +786,8 @@ struct ErrorVTable {
object_mut: unsafe fn(Mut<ErrorImpl>) -> &mut (dyn StdError + Send + Sync + 'static),
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
object_boxed: unsafe fn(Own<ErrorImpl>) -> Box<dyn StdError + Send + Sync + 'static>,
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
object_reallocate_boxed: unsafe fn(Own<ErrorImpl>) -> Box<dyn StdError + Send + Sync + 'static>,
object_downcast: unsafe fn(Ref<ErrorImpl>, TypeId) -> Option<Ref<()>>,
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: unsafe fn(Mut<ErrorImpl>, TypeId) -> Option<Mut<()>>,
Expand Down Expand Up @@ -752,6 +858,17 @@ where
unsafe { unerased_own.boxed() }
}

// Safety: requires layout of *e to match ErrorImpl<E>.
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
unsafe fn object_reallocate_boxed<E>(e: Own<ErrorImpl>) -> Box<dyn StdError + Send + Sync + 'static>
where
E: StdError + Send + Sync + 'static,
{
// Attach E's native StdError vtable.
let unerased_own = e.cast::<ErrorImpl<E>>();
Box::new(unsafe { unerased_own.boxed() }._object)
}

// Safety: requires layout of *e to match ErrorImpl<E>.
unsafe fn object_downcast<E>(e: Ref<ErrorImpl>, target: TypeId) -> Option<Ref<()>>
where
Expand Down Expand Up @@ -1060,26 +1177,23 @@ where
impl From<Error> for Box<dyn StdError + Send + Sync + 'static> {
#[cold]
fn from(error: Error) -> Self {
let outer = ManuallyDrop::new(error);
unsafe {
// Use vtable to attach ErrorImpl<E>'s native StdError vtable for
// the right original type E.
(vtable(outer.inner.ptr).object_boxed)(outer.inner)
}
error.into_boxed_dyn_error()
}
}

#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
impl From<Error> for Box<dyn StdError + Send + 'static> {
#[cold]
fn from(error: Error) -> Self {
Box::<dyn StdError + Send + Sync>::from(error)
error.into_boxed_dyn_error()
}
}

#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
impl From<Error> for Box<dyn StdError + 'static> {
#[cold]
fn from(error: Error) -> Self {
Box::<dyn StdError + Send + Sync>::from(error)
error.into_boxed_dyn_error()
}
}

Expand Down