diff --git a/crates/error/src/context.rs b/crates/error/src/context.rs index b1890b5ee368..5e2157334edd 100644 --- a/crates/error/src/context.rs +++ b/crates/error/src/context.rs @@ -1,7 +1,9 @@ use crate::{ - Error, ErrorExt, Result, + Error, ErrorExt, OutOfMemory, Result, + boxed::try_new_uninit_box, error::{OomOrDynError, OomOrDynErrorMut, OomOrDynErrorRef}, }; +use alloc::boxed::Box; use core::any::TypeId; use core::fmt; use core::ptr::NonNull; @@ -252,6 +254,17 @@ unsafe impl ErrorExt for ContextError where C: fmt::Display + Send + Sync + 'static, { + fn ext_as_dyn_core_error(&self) -> &(dyn core::error::Error + Send + Sync + 'static) { + self + } + + fn ext_into_boxed_dyn_core_error( + self, + ) -> Result, OutOfMemory> { + let boxed = try_new_uninit_box()?; + Ok(Box::write(boxed, self) as _) + } + fn ext_source(&self) -> Option> { let error = self.error.as_ref()?; Some(error.inner.unpack()) diff --git a/crates/error/src/error.rs b/crates/error/src/error.rs index 61a544900fb7..f2d2ea3e2c50 100644 --- a/crates/error/src/error.rs +++ b/crates/error/src/error.rs @@ -22,7 +22,16 @@ use std::backtrace::{Backtrace, BacktraceStatus}; /// not lie about whether they are or are not an instance of the given type id's /// associated type (or a newtype wrapper around that type). Violations will /// lead to unsafety. -pub(crate) unsafe trait ErrorExt: core::error::Error + Send + Sync + 'static { +pub(crate) unsafe trait ErrorExt: Send + Sync + 'static { + /// Get this error as a shared reference to a `dyn core::error::Error` trait + /// object. + fn ext_as_dyn_core_error(&self) -> &(dyn core::error::Error + Send + Sync + 'static); + + /// Get this error as a boxed `dyn core::error::Error` trait object. + fn ext_into_boxed_dyn_core_error( + self, + ) -> Result, OutOfMemory>; + /// Get a shared borrow of the next error in the chain. fn ext_source(&self) -> Option>; @@ -488,6 +497,20 @@ impl From for Box { } } +impl From for Box { + #[inline] + fn from(error: Error) -> Self { + error.into_boxed_dyn_error() + } +} + +impl From for Box { + #[inline] + fn from(error: Error) -> Self { + error.into_boxed_dyn_error() + } +} + /// Convert a [`Error`] into an [`anyhow::Error`]. /// /// # Example @@ -517,6 +540,28 @@ impl From for anyhow::Error { } } +impl core::ops::Deref for Error { + type Target = dyn core::error::Error + Send + Sync + 'static; + + fn deref(&self) -> &Self::Target { + self.as_ref() + } +} + +impl AsRef for Error { + #[inline] + fn as_ref(&self) -> &(dyn core::error::Error + 'static) { + self.inner.unpack().as_dyn_core_error() + } +} + +impl AsRef for Error { + #[inline] + fn as_ref(&self) -> &(dyn core::error::Error + Send + Sync + 'static) { + self.inner.unpack().as_dyn_core_error() + } +} + impl Error { /// Construct a new `Error` from a type that implements /// `core::error::Error`. @@ -1107,38 +1152,22 @@ impl Error { #[repr(transparent)] struct ForeignError(E); -impl fmt::Debug for ForeignError -where - E: core::error::Error + Send + Sync + 'static, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.0, f) - } -} - -impl fmt::Display for ForeignError +// Safety: `ext_is` is correct, `ext_move` always writes to `dest`. +unsafe impl ErrorExt for ForeignError where E: core::error::Error + Send + Sync + 'static, { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0, f) + fn ext_as_dyn_core_error(&self) -> &(dyn core::error::Error + Send + Sync + 'static) { + &self.0 } -} -impl core::error::Error for ForeignError -where - E: core::error::Error + Send + Sync + 'static, -{ - fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { - self.0.source() + fn ext_into_boxed_dyn_core_error( + self, + ) -> Result, OutOfMemory> { + let boxed = try_new_uninit_box()?; + Ok(Box::write(boxed, self.0) as _) } -} -// Safety: `ext_is` is correct, `ext_move` always writes to `dest`. -unsafe impl ErrorExt for ForeignError -where - E: core::error::Error + Send + Sync + 'static, -{ fn ext_source(&self) -> Option> { None } @@ -1206,6 +1235,17 @@ unsafe impl ErrorExt for MessageError where M: fmt::Debug + fmt::Display + Send + Sync + 'static, { + fn ext_as_dyn_core_error(&self) -> &(dyn core::error::Error + Send + Sync + 'static) { + self + } + + fn ext_into_boxed_dyn_core_error( + self, + ) -> Result, OutOfMemory> { + let boxed = try_new_uninit_box()?; + Ok(Box::write(boxed, self) as _) + } + fn ext_source(&self) -> Option> { None } @@ -1247,27 +1287,19 @@ where #[repr(transparent)] struct BoxedError(Box); -impl fmt::Debug for BoxedError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.0, f) +// Safety: `ext_is` is implemented correctly and `ext_move` always +// writes to its pointer. +unsafe impl ErrorExt for BoxedError { + fn ext_as_dyn_core_error(&self) -> &(dyn core::error::Error + Send + Sync + 'static) { + &*self.0 } -} -impl fmt::Display for BoxedError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0, f) + fn ext_into_boxed_dyn_core_error( + self, + ) -> Result, OutOfMemory> { + Ok(self.0) } -} -impl core::error::Error for BoxedError { - fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { - self.0.source() - } -} - -// Safety: `ext_is` is implemented correctly and `ext_move` always -// writes to its pointer. -unsafe impl ErrorExt for BoxedError { fn ext_source(&self) -> Option> { None } @@ -1311,31 +1343,20 @@ unsafe impl ErrorExt for BoxedError { #[cfg(feature = "anyhow")] struct AnyhowError(anyhow::Error); +// Safety: `ext_is` is implemented correctly and `ext_move` always +// writes to its pointer. #[cfg(feature = "anyhow")] -impl fmt::Debug for AnyhowError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.0, f) - } -} - -#[cfg(feature = "anyhow")] -impl fmt::Display for AnyhowError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0, f) +unsafe impl ErrorExt for AnyhowError { + fn ext_as_dyn_core_error(&self) -> &(dyn core::error::Error + Send + Sync + 'static) { + self.0.as_ref() } -} -#[cfg(feature = "anyhow")] -impl core::error::Error for AnyhowError { - fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { - self.0.source() + fn ext_into_boxed_dyn_core_error( + self, + ) -> Result, OutOfMemory> { + Ok(self.0.into_boxed_dyn_error()) } -} -// Safety: `ext_is` is implemented correctly and `ext_move` always -// writes to its pointer. -#[cfg(feature = "anyhow")] -unsafe impl ErrorExt for AnyhowError { fn ext_source(&self) -> Option> { None } diff --git a/crates/error/src/vtable.rs b/crates/error/src/vtable.rs index 88fc3b39da7f..a46d3d415dd1 100644 --- a/crates/error/src/vtable.rs +++ b/crates/error/src/vtable.rs @@ -1,5 +1,4 @@ use super::{ConcreteError, DynError, ErrorExt, OomOrDynErrorMut, OomOrDynErrorRef, OutOfMemory}; -use crate::boxed::try_new_uninit_box; use crate::ptr::{MutPtr, OwnedPtr, SharedPtr}; use alloc::boxed::Box; use core::{any::TypeId, fmt, ptr::NonNull}; @@ -73,7 +72,7 @@ where let error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let error = unsafe { error.as_ref() }; - fmt::Display::fmt(&error.error, f) + fmt::Display::fmt(error.error.ext_as_dyn_core_error(), f) } unsafe fn debug(error: SharedPtr<'_, DynError>, f: &mut fmt::Formatter<'_>) -> fmt::Result @@ -83,7 +82,7 @@ where let error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let error = unsafe { error.as_ref() }; - fmt::Debug::fmt(&error.error, f) + fmt::Debug::fmt(error.error.ext_as_dyn_core_error(), f) } unsafe fn source(error: SharedPtr<'_, DynError>) -> Option> @@ -125,7 +124,7 @@ where let error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let error = unsafe { error.as_ref() }; - &error.error as _ + error.error.ext_as_dyn_core_error() } unsafe fn into_boxed_dyn_core_error( @@ -137,11 +136,7 @@ where let error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let error = unsafe { error.into_box() }; - - let boxed = try_new_uninit_box()?; - let error = Box::write(boxed, error.error); - - Ok(error as _) + error.error.ext_into_boxed_dyn_core_error() } unsafe fn drop_and_deallocate(error: OwnedPtr) diff --git a/crates/error/tests/tests.rs b/crates/error/tests/tests.rs index aeabde5d5b1d..5d817e5d2239 100644 --- a/crates/error/tests/tests.rs +++ b/crates/error/tests/tests.rs @@ -200,6 +200,14 @@ fn is() { assert!(!e.is::<&str>()); } +#[test] +fn is_for_root_cause_with_initial_error_source() { + let error = std::fmt::Error; + let error = ChainError::new("whoops", Some(Box::new(error))); + let error = Error::new(error); + assert!(error.root_cause().is::()); +} + #[test] #[cfg(feature = "backtrace")] fn backtrace() {