Skip to content
Merged
Show file tree
Hide file tree
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
15 changes: 14 additions & 1 deletion crates/error/src/context.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -252,6 +254,17 @@ unsafe impl<C> ErrorExt for ContextError<C>
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<Box<dyn core::error::Error + Send + Sync + 'static>, OutOfMemory> {
let boxed = try_new_uninit_box()?;
Ok(Box::write(boxed, self) as _)
}

fn ext_source(&self) -> Option<OomOrDynErrorRef<'_>> {
let error = self.error.as_ref()?;
Some(error.inner.unpack())
Expand Down
147 changes: 84 additions & 63 deletions crates/error/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Box<dyn core::error::Error + Send + Sync + 'static>, OutOfMemory>;

/// Get a shared borrow of the next error in the chain.
fn ext_source(&self) -> Option<OomOrDynErrorRef<'_>>;

Expand Down Expand Up @@ -488,6 +497,20 @@ impl From<Error> for Box<dyn core::error::Error + Send + Sync + 'static> {
}
}

impl From<Error> for Box<dyn core::error::Error + Send + 'static> {
#[inline]
fn from(error: Error) -> Self {
error.into_boxed_dyn_error()
}
}

impl From<Error> for Box<dyn core::error::Error + 'static> {
#[inline]
fn from(error: Error) -> Self {
error.into_boxed_dyn_error()
}
}

/// Convert a [`Error`] into an [`anyhow::Error`].
///
/// # Example
Expand Down Expand Up @@ -517,6 +540,28 @@ impl From<Error> 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<dyn core::error::Error> for Error {
#[inline]
fn as_ref(&self) -> &(dyn core::error::Error + 'static) {
self.inner.unpack().as_dyn_core_error()
}
}

impl AsRef<dyn core::error::Error + Send + Sync> 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`.
Expand Down Expand Up @@ -1107,38 +1152,22 @@ impl Error {
#[repr(transparent)]
struct ForeignError<E>(E);

impl<E> fmt::Debug for ForeignError<E>
where
E: core::error::Error + Send + Sync + 'static,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}

impl<E> fmt::Display for ForeignError<E>
// Safety: `ext_is` is correct, `ext_move` always writes to `dest`.
unsafe impl<E> ErrorExt for ForeignError<E>
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<E> core::error::Error for ForeignError<E>
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<Box<dyn core::error::Error + Send + Sync + 'static>, 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<E> ErrorExt for ForeignError<E>
where
E: core::error::Error + Send + Sync + 'static,
{
fn ext_source(&self) -> Option<OomOrDynErrorRef<'_>> {
None
}
Expand Down Expand Up @@ -1206,6 +1235,17 @@ unsafe impl<M> ErrorExt for MessageError<M>
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<Box<dyn core::error::Error + Send + Sync + 'static>, OutOfMemory> {
let boxed = try_new_uninit_box()?;
Ok(Box::write(boxed, self) as _)
}

fn ext_source(&self) -> Option<OomOrDynErrorRef<'_>> {
None
}
Expand Down Expand Up @@ -1247,27 +1287,19 @@ where
#[repr(transparent)]
struct BoxedError(Box<dyn core::error::Error + Send + Sync + 'static>);

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<Box<dyn core::error::Error + Send + Sync + 'static>, 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<OomOrDynErrorRef<'_>> {
None
}
Expand Down Expand Up @@ -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<Box<dyn core::error::Error + Send + Sync + 'static>, 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<OomOrDynErrorRef<'_>> {
None
}
Expand Down
13 changes: 4 additions & 9 deletions crates/error/src/vtable.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -73,7 +72,7 @@ where
let error = error.cast::<ConcreteError<E>>();
// 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<E>(error: SharedPtr<'_, DynError>, f: &mut fmt::Formatter<'_>) -> fmt::Result
Expand All @@ -83,7 +82,7 @@ where
let error = error.cast::<ConcreteError<E>>();
// 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<E>(error: SharedPtr<'_, DynError>) -> Option<OomOrDynErrorRef<'_>>
Expand Down Expand Up @@ -125,7 +124,7 @@ where
let error = error.cast::<ConcreteError<E>>();
// 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<E>(
Expand All @@ -137,11 +136,7 @@ where
let error = error.cast::<ConcreteError<E>>();
// 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<E>(error: OwnedPtr<DynError>)
Expand Down
8 changes: 8 additions & 0 deletions crates/error/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<std::fmt::Error>());
}

#[test]
#[cfg(feature = "backtrace")]
fn backtrace() {
Expand Down