use super::boxed::try_new_uninit_box;
use super::context::ContextError;
use super::ptr::{MutPtr, OwnedPtr, SharedPtr};
use super::vtable::Vtable;
use crate::error::{OutOfMemory, Result};
use core::{
any::TypeId,
fmt::{self, Debug},
iter::FusedIterator,
mem,
ptr::NonNull,
};
#[cfg(feature = "backtrace")]
use std::backtrace::{Backtrace, BacktraceStatus};
use std_alloc::boxed::Box;
pub(crate) unsafe trait ErrorExt: Send + Sync + 'static {
fn ext_as_dyn_core_error(&self) -> &(dyn core::error::Error + Send + Sync + 'static);
fn ext_into_boxed_dyn_core_error(
self,
) -> Result<Box<dyn core::error::Error + Send + Sync + 'static>, OutOfMemory>;
fn ext_source(&self) -> Option<OomOrDynErrorRef<'_>>;
fn ext_source_mut(&mut self) -> Option<OomOrDynErrorMut<'_>>;
fn ext_take_source(&mut self) -> Option<OomOrDynError>;
fn ext_is(&self, type_id: TypeId) -> bool;
unsafe fn ext_move(self, dest: NonNull<u8>);
#[cfg(feature = "backtrace")]
fn take_backtrace(&mut self) -> Option<Backtrace>;
}
#[repr(C)]
pub(crate) struct DynError {
pub(crate) vtable: &'static Vtable,
#[cfg(feature = "backtrace")]
pub(crate) backtrace: Option<Backtrace>,
}
#[repr(C)]
pub(crate) struct ConcreteError<E> {
pub(crate) vtable: &'static Vtable,
#[cfg(feature = "backtrace")]
pub(crate) backtrace: Option<Backtrace>,
pub(crate) error: E,
}
pub(crate) struct BoxedDynError {
inner: OwnedPtr<DynError>,
}
unsafe impl Send for BoxedDynError {}
unsafe impl Sync for BoxedDynError {}
impl Drop for BoxedDynError {
fn drop(&mut self) {
let ptr = self.inner.raw_copy();
let inner = unsafe { ptr.as_ref() };
let vtable = inner.vtable;
unsafe {
(vtable.drop_and_deallocate)(ptr);
}
}
}
impl BoxedDynError {
#[inline]
fn new<E>(mut error: E) -> Result<Self, OutOfMemory>
where
E: ErrorExt,
{
#[cfg(not(feature = "backtrace"))]
let _ = &mut error;
#[cfg(feature = "backtrace")]
let backtrace = match error.take_backtrace() {
Some(bt) => bt,
None => crate::error::backtrace::capture(),
};
let boxed = try_new_uninit_box()?;
let error = Box::write(
boxed,
ConcreteError {
vtable: Vtable::of::<E>(),
#[cfg(feature = "backtrace")]
backtrace: Some(backtrace),
error,
},
);
#[cfg(debug_assertions)]
{
let dyn_size = mem::size_of::<DynError>();
let concrete_size = mem::size_of::<ConcreteError<E>>();
assert!(
dyn_size <= concrete_size,
"assertion failed: {dyn_size} <= {concrete_size}"
);
let dyn_align = mem::align_of::<DynError>();
let concrete_align = mem::align_of::<ConcreteError<E>>();
assert!(
dyn_align <= concrete_align,
"assertion failed: {dyn_align} <= {concrete_align}"
);
let dyn_offset = mem::offset_of!(DynError, vtable);
let concrete_offset = mem::offset_of!(ConcreteError<E>, vtable);
assert_eq!(dyn_offset, concrete_offset);
#[cfg(feature = "backtrace")]
{
let dyn_offset = mem::offset_of!(DynError, backtrace);
let concrete_offset = mem::offset_of!(ConcreteError<E>, backtrace);
assert_eq!(dyn_offset, concrete_offset);
}
}
let ptr = Box::into_raw(error);
let ptr = ptr.cast::<DynError>();
let ptr = unsafe { NonNull::new_unchecked(ptr) };
let ptr = OwnedPtr::new(ptr);
Ok(unsafe { Self::from_owned_ptr(ptr) })
}
fn into_owned_ptr(self) -> OwnedPtr<DynError> {
let ptr = self.inner.raw_copy();
mem::forget(self);
ptr
}
unsafe fn from_owned_ptr(inner: OwnedPtr<DynError>) -> Self {
BoxedDynError { inner }
}
}
#[repr(transparent)]
pub struct Error {
pub(crate) inner: OomOrDynError,
}
const _ERROR_IS_ONE_WORD_LARGE: () = assert!(mem::size_of::<Error>() == mem::size_of::<usize>());
const _RESULT_OF_UNIT_IS_ONE_WORD_LARGE: () =
assert!(mem::size_of::<Result<()>>() == mem::size_of::<usize>());
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
return f
.debug_struct("Error")
.field("inner", &self.inner.unpack())
.finish();
}
let inner = self.inner.unpack();
inner.display(f)?;
if let Some(source) = inner.source() {
f.write_str("\n\nCaused by:\n")?;
let multiple_causes = source.source().is_some();
for (i, e) in Chain::new(source).enumerate() {
if multiple_causes {
write!(f, "{i: >5}: ")?;
} else {
write!(f, " ")?;
}
writeln!(f, "{e}")?;
}
}
#[cfg(feature = "backtrace")]
{
let backtrace = inner.backtrace();
if let BacktraceStatus::Captured = backtrace.status() {
f.write_str("\nStack backtrace:\n")?;
fmt::Display::fmt(backtrace, f)?;
}
}
Ok(())
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let inner = self.inner.unpack();
inner.display(f)?;
if f.alternate() {
if let Some(e) = inner.source() {
for e in Chain::new(e) {
write!(f, ": {e}")?;
}
}
}
Ok(())
}
}
impl<E> From<E> for Error
where
E: core::error::Error + Send + Sync + 'static,
{
fn from(error: E) -> Self {
Self::new(error)
}
}
impl From<Error> for Box<dyn core::error::Error + Send + Sync + 'static> {
#[inline]
fn from(error: Error) -> Self {
error.into_boxed_dyn_error()
}
}
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()
}
}
#[cfg(feature = "anyhow")]
impl From<Error> for anyhow::Error {
#[inline]
fn from(e: Error) -> Self {
anyhow::Error::from_boxed(e.into_boxed_dyn_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 {
pub fn new<E>(error: E) -> Self
where
E: core::error::Error + Send + Sync + 'static,
{
if TypeId::of::<E>() == TypeId::of::<OutOfMemory>() {
union ToOom<T> {
oom: OutOfMemory,
error: mem::ManuallyDrop<T>,
}
let error = mem::ManuallyDrop::new(error);
let oom = unsafe { (ToOom { error }).oom };
return Error { inner: oom.into() };
}
Self::from_error_ext(ForeignError(error))
}
pub fn msg<M>(message: M) -> Self
where
M: fmt::Debug + fmt::Display + Send + Sync + 'static,
{
Self::from_error_ext(MessageError(message))
}
pub fn from_boxed(error: Box<dyn core::error::Error + Send + Sync + 'static>) -> Self {
Self::from_error_ext(BoxedError(error))
}
#[cfg(feature = "anyhow")]
#[inline]
pub fn from_anyhow(error: anyhow::Error) -> Self {
Self::from_error_ext(AnyhowError(error))
}
pub fn context<C>(self, context: C) -> Self
where
C: fmt::Display + Send + Sync + 'static,
{
if self.inner.is_oom() {
self
} else {
Self::from_error_ext(ContextError {
context,
error: Some(self),
})
}
}
#[inline]
pub(crate) fn from_error_ext(error: impl ErrorExt) -> Self {
match BoxedDynError::new(error) {
Ok(boxed) => Error {
inner: boxed.into(),
},
Err(oom) => Error { inner: oom.into() },
}
}
#[inline]
#[cfg(feature = "backtrace")]
pub fn backtrace(&self) -> &Backtrace {
self.inner.unpack().backtrace()
}
#[inline]
pub fn chain(&self) -> Chain<'_> {
Chain::new(self.inner.unpack())
}
#[inline]
pub fn root_cause(&self) -> &(dyn core::error::Error + 'static) {
self.chain().last().expect("chain is always non-empty")
}
pub fn is<E>(&self) -> bool
where
E: fmt::Display + fmt::Debug + Send + Sync + 'static,
{
let mut error = Some(self.inner.unpack());
while let Some(e) = error {
if e.is::<E>() {
return true;
} else {
error = e.source();
}
}
false
}
pub fn downcast<E>(self) -> Result<E, Self>
where
E: fmt::Display + fmt::Debug + Send + Sync + 'static,
{
if !self.is::<E>() {
return Err(self);
}
let mut value = mem::MaybeUninit::<E>::uninit();
unsafe {
self.inner
.downcast(TypeId::of::<E>(), NonNull::from(&mut value).cast::<u8>());
}
Ok(unsafe { value.assume_init() })
}
pub fn downcast_ref<E>(&self) -> Option<&E>
where
E: fmt::Display + fmt::Debug + Send + Sync + 'static,
{
let mut error = Some(self.inner.unpack());
while let Some(e) = error {
if e.is::<E>() {
return Some(match e {
OomOrDynErrorRef::DynError(ptr) => {
let ptr = ptr.cast::<ConcreteError<E>>();
let r = unsafe { ptr.as_ref() };
&r.error
}
OomOrDynErrorRef::Oom(oom) => {
debug_assert_eq!(TypeId::of::<E>(), TypeId::of::<OutOfMemory>());
let ptr = NonNull::from(oom);
let ptr = ptr.cast::<E>();
unsafe { ptr.as_ref() }
}
});
} else {
error = e.source();
}
}
None
}
pub fn downcast_mut<E>(&mut self) -> Option<&mut E>
where
E: fmt::Display + fmt::Debug + Send + Sync + 'static,
{
let mut error = Some(self.inner.unpack_mut());
while let Some(mut e) = error.take() {
if e.as_ref().is::<E>() {
return Some(match e {
OomOrDynErrorMut::DynError(ptr) => {
let mut ptr = ptr.cast::<ConcreteError<E>>();
let r = unsafe { ptr.as_mut() };
&mut r.error
}
OomOrDynErrorMut::Oom(oom) => {
debug_assert_eq!(TypeId::of::<E>(), TypeId::of::<OutOfMemory>());
let ptr = NonNull::from(oom);
let mut ptr = ptr.cast::<E>();
unsafe { ptr.as_mut() }
}
});
} else {
error = e.source_mut();
}
}
None
}
#[inline]
pub fn into_boxed_dyn_error(self) -> Box<dyn core::error::Error + Send + Sync + 'static> {
#[derive(Debug)]
struct IntoBoxedDynCoreErrorFailure;
impl core::fmt::Display for IntoBoxedDynCoreErrorFailure {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"failed to box error into `Box<dyn core::error::Error>` \
(allocation of {} bytes failed)",
mem::size_of::<Error>()
)
}
}
impl core::error::Error for IntoBoxedDynCoreErrorFailure {}
match self.inner.into_boxed_dyn_core_error() {
Ok(boxed) => boxed,
Err(_oom) => {
Box::new(IntoBoxedDynCoreErrorFailure) as _
}
}
}
}
#[repr(transparent)]
struct ForeignError<E>(E);
unsafe impl<E> ErrorExt for ForeignError<E>
where
E: core::error::Error + Send + Sync + 'static,
{
fn ext_as_dyn_core_error(&self) -> &(dyn core::error::Error + Send + Sync + 'static) {
&self.0
}
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 _)
}
fn ext_source(&self) -> Option<OomOrDynErrorRef<'_>> {
None
}
fn ext_source_mut(&mut self) -> Option<OomOrDynErrorMut<'_>> {
None
}
fn ext_take_source(&mut self) -> Option<OomOrDynError> {
None
}
unsafe fn ext_move(self, dest: NonNull<u8>) {
unsafe {
dest.cast::<E>().write(self.0);
}
}
fn ext_is(&self, type_id: TypeId) -> bool {
type_id == TypeId::of::<E>()
}
#[cfg(feature = "backtrace")]
fn take_backtrace(&mut self) -> Option<Backtrace> {
None
}
}
#[repr(transparent)]
struct MessageError<M>(M);
impl<M> fmt::Debug for MessageError<M>
where
M: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl<M> fmt::Display for MessageError<M>
where
M: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl<M> core::error::Error for MessageError<M> where M: fmt::Debug + fmt::Display {}
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
}
fn ext_source_mut(&mut self) -> Option<OomOrDynErrorMut<'_>> {
None
}
fn ext_take_source(&mut self) -> Option<OomOrDynError> {
None
}
fn ext_is(&self, type_id: TypeId) -> bool {
type_id == TypeId::of::<M>()
}
unsafe fn ext_move(self, dest: NonNull<u8>) {
unsafe {
dest.cast::<M>().write(self.0);
}
}
#[cfg(feature = "backtrace")]
fn take_backtrace(&mut self) -> Option<Backtrace> {
None
}
}
#[repr(transparent)]
struct BoxedError(Box<dyn core::error::Error + Send + Sync + 'static>);
unsafe impl ErrorExt for BoxedError {
fn ext_as_dyn_core_error(&self) -> &(dyn core::error::Error + Send + Sync + 'static) {
&*self.0
}
fn ext_into_boxed_dyn_core_error(
self,
) -> Result<Box<dyn core::error::Error + Send + Sync + 'static>, OutOfMemory> {
Ok(self.0)
}
fn ext_source(&self) -> Option<OomOrDynErrorRef<'_>> {
None
}
fn ext_source_mut(&mut self) -> Option<OomOrDynErrorMut<'_>> {
None
}
fn ext_take_source(&mut self) -> Option<OomOrDynError> {
None
}
fn ext_is(&self, type_id: TypeId) -> bool {
type_id == TypeId::of::<Box<dyn core::error::Error + Send + Sync + 'static>>()
}
unsafe fn ext_move(self, dest: NonNull<u8>) {
unsafe {
dest.cast::<Box<dyn core::error::Error + Send + Sync + 'static>>()
.write(self.0);
}
}
#[cfg(feature = "backtrace")]
fn take_backtrace(&mut self) -> Option<Backtrace> {
None
}
}
#[repr(transparent)]
#[cfg(feature = "anyhow")]
struct AnyhowError(anyhow::Error);
#[cfg(feature = "anyhow")]
unsafe impl ErrorExt for AnyhowError {
fn ext_as_dyn_core_error(&self) -> &(dyn core::error::Error + Send + Sync + 'static) {
self.0.as_ref()
}
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())
}
fn ext_source(&self) -> Option<OomOrDynErrorRef<'_>> {
None
}
fn ext_source_mut(&mut self) -> Option<OomOrDynErrorMut<'_>> {
None
}
fn ext_take_source(&mut self) -> Option<OomOrDynError> {
None
}
fn ext_is(&self, type_id: TypeId) -> bool {
type_id == TypeId::of::<anyhow::Error>()
}
unsafe fn ext_move(self, dest: NonNull<u8>) {
unsafe {
dest.cast::<anyhow::Error>().write(self.0);
}
}
#[cfg(feature = "backtrace")]
fn take_backtrace(&mut self) -> Option<Backtrace> {
None
}
}
pub(crate) enum OomOrDynErrorRef<'a> {
DynError(SharedPtr<'a, DynError>),
Oom(&'a OutOfMemory),
}
impl<'a> Debug for OomOrDynErrorRef<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.debug(f)
}
}
impl<'a> OomOrDynErrorRef<'a> {
fn display(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
OomOrDynErrorRef::DynError(e) => {
let vtable = unsafe { e.as_ref().vtable };
unsafe { (vtable.display)(*e, f) }
}
OomOrDynErrorRef::Oom(oom) => fmt::Display::fmt(oom, f),
}
}
fn debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
OomOrDynErrorRef::Oom(oom) => f.debug_tuple("Oom").field(oom).finish(),
OomOrDynErrorRef::DynError(error) => {
struct DebugError<'a>(SharedPtr<'a, DynError>);
impl fmt::Debug for DebugError<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let vtable = unsafe { self.0.as_ref().vtable };
unsafe { (vtable.debug)(self.0, f) }
}
}
let mut f = f.debug_struct("DynError");
f.field("error", &DebugError(error));
if let Some(source) = self.source() {
f.field("source", &source);
}
f.finish()
}
}
}
fn source(&self) -> Option<OomOrDynErrorRef<'a>> {
match self {
OomOrDynErrorRef::DynError(e) => {
let vtable = unsafe { e.as_ref().vtable };
unsafe { (vtable.source)(*e) }
}
OomOrDynErrorRef::Oom(_) => None,
}
}
fn is<E>(&self) -> bool
where
E: fmt::Display + fmt::Debug + Send + Sync + 'static,
{
match self {
OomOrDynErrorRef::DynError(e) => {
let vtable = unsafe { e.as_ref().vtable };
unsafe { (vtable.is)(*e, TypeId::of::<E>()) }
}
OomOrDynErrorRef::Oom(_) => TypeId::of::<E>() == TypeId::of::<OutOfMemory>(),
}
}
pub(crate) fn as_dyn_core_error(&self) -> &'a (dyn core::error::Error + Send + Sync + 'static) {
match *self {
OomOrDynErrorRef::DynError(e) => {
let vtable = unsafe { e.as_ref().vtable };
unsafe { (vtable.as_dyn_core_error)(e) }
}
OomOrDynErrorRef::Oom(oom) => oom as _,
}
}
#[cfg(feature = "backtrace")]
fn backtrace(&self) -> &'a Backtrace {
match self {
OomOrDynErrorRef::DynError(e) => {
let r = unsafe { e.as_ref() };
r.backtrace
.as_ref()
.expect("the first error in the chain always has the backtrace")
}
OomOrDynErrorRef::Oom(_) => {
static DISABLED: Backtrace = Backtrace::disabled();
&DISABLED
}
}
}
}
pub(crate) enum OomOrDynErrorMut<'a> {
DynError(MutPtr<'a, DynError>),
Oom(&'a mut OutOfMemory),
}
impl<'a> OomOrDynErrorMut<'a> {
fn as_ref(&self) -> OomOrDynErrorRef<'_> {
match self {
OomOrDynErrorMut::DynError(e) => OomOrDynErrorRef::DynError(e.as_shared_ptr()),
OomOrDynErrorMut::Oom(oom) => OomOrDynErrorRef::Oom(oom),
}
}
fn source_mut(&mut self) -> Option<OomOrDynErrorMut<'a>> {
match self {
OomOrDynErrorMut::DynError(e) => {
let vtable = unsafe { e.as_ref().vtable };
unsafe { (vtable.source_mut)(e.raw_copy()) }
}
OomOrDynErrorMut::Oom(_) => None,
}
}
}
#[repr(transparent)]
pub(crate) struct OomOrDynError {
inner: NonNull<u8>,
}
unsafe impl Send for OomOrDynError {}
unsafe impl Sync for OomOrDynError {}
const _OOM_OR_DYN_ERROR_SEND_SYNC_SAFETY: () = {
const fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<OutOfMemory>();
assert_send_sync::<BoxedDynError>();
};
impl Drop for OomOrDynError {
fn drop(&mut self) {
if self.is_boxed_dyn_error() {
let inner = self.inner.cast::<DynError>();
let inner = OwnedPtr::new(inner);
let _ = unsafe { BoxedDynError::from_owned_ptr(inner) };
} else {
debug_assert!(self.is_oom());
}
}
}
impl From<BoxedDynError> for OomOrDynError {
fn from(boxed: BoxedDynError) -> Self {
let inner = boxed.into_owned_ptr().into_non_null().cast::<u8>();
debug_assert!(!Self::is_oom_ptr(inner));
OomOrDynError { inner }
}
}
impl OomOrDynError {
const _SIZE: () = assert!(mem::size_of::<OomOrDynError>() == mem::size_of::<usize>());
const _DYN_ERROR_HAS_GREATER_ALIGN_THAN_OOM: () = assert!(mem::align_of::<DynError>() > 1);
const OOM_BIT: usize = 0x1;
pub(crate) const fn new_oom_ptr(size: usize) -> NonNull<u8> {
let size = if size > (isize::MAX as usize) {
isize::MAX as usize
} else {
size
};
let repr = (size << 1) | Self::OOM_BIT;
let inner = core::ptr::without_provenance_mut(repr);
NonNull::new(inner).unwrap()
}
pub(crate) fn new_oom(bitpacked: NonNull<u8>) -> Self {
assert!(Self::is_oom_ptr(bitpacked));
OomOrDynError { inner: bitpacked }
}
fn is_oom_ptr(ptr: NonNull<u8>) -> bool {
(ptr.addr().get() & Self::OOM_BIT) == Self::OOM_BIT
}
fn is_oom(&self) -> bool {
Self::is_oom_ptr(self.inner)
}
fn is_boxed_dyn_error(&self) -> bool {
!self.is_oom()
}
pub(crate) fn oom_size(inner: NonNull<u8>) -> usize {
debug_assert!(Self::is_oom_ptr(inner));
inner.addr().get() >> 1
}
unsafe fn unchecked_oom(&self) -> &OutOfMemory {
debug_assert!(self.is_oom());
unsafe { mem::transmute(self) }
}
unsafe fn unchecked_oom_mut(&mut self) -> &mut OutOfMemory {
debug_assert!(self.is_oom());
unsafe { mem::transmute(self) }
}
unsafe fn unchecked_into_dyn_error(self) -> OwnedPtr<DynError> {
debug_assert!(self.is_boxed_dyn_error());
let inner = self.inner.cast::<DynError>();
mem::forget(self);
OwnedPtr::new(inner)
}
unsafe fn unchecked_dyn_error_ref(&self) -> SharedPtr<'_, DynError> {
debug_assert!(self.is_boxed_dyn_error());
SharedPtr::new(self.inner.cast::<DynError>())
}
unsafe fn unchecked_dyn_error_mut(&mut self) -> MutPtr<'_, DynError> {
debug_assert!(self.is_boxed_dyn_error());
MutPtr::new(self.inner.cast::<DynError>())
}
pub(crate) fn unpack(&self) -> OomOrDynErrorRef<'_> {
if self.is_oom() {
OomOrDynErrorRef::Oom(unsafe { self.unchecked_oom() })
} else {
debug_assert!(self.is_boxed_dyn_error());
OomOrDynErrorRef::DynError(unsafe { self.unchecked_dyn_error_ref() })
}
}
pub(crate) fn unpack_mut(&mut self) -> OomOrDynErrorMut<'_> {
if self.is_oom() {
OomOrDynErrorMut::Oom(unsafe { self.unchecked_oom_mut() })
} else {
debug_assert!(self.is_boxed_dyn_error());
OomOrDynErrorMut::DynError(unsafe { self.unchecked_dyn_error_mut() })
}
}
pub(crate) fn into_boxed_dyn_core_error(
self,
) -> Result<Box<dyn core::error::Error + Send + Sync + 'static>, OutOfMemory> {
if self.is_oom() {
let boxed = try_new_uninit_box::<OutOfMemory>()?;
let boxed = Box::write(boxed, unsafe { *self.unchecked_oom() });
Ok(boxed as _)
} else {
debug_assert!(self.is_boxed_dyn_error());
let ptr = unsafe { self.unchecked_into_dyn_error() };
let vtable = unsafe { ptr.as_ref().vtable };
unsafe { (vtable.into_boxed_dyn_core_error)(ptr) }
}
}
pub(crate) unsafe fn downcast(self, type_id: TypeId, ret_ptr: NonNull<u8>) {
if self.is_oom() {
debug_assert_eq!(type_id, TypeId::of::<OutOfMemory>());
let oom = unsafe { self.unchecked_oom() };
unsafe {
ret_ptr.cast::<OutOfMemory>().write(*oom);
}
} else {
debug_assert!(self.is_boxed_dyn_error());
let ptr = unsafe { self.unchecked_into_dyn_error() };
let vtable = unsafe { ptr.as_ref().vtable };
unsafe { (vtable.downcast)(ptr, type_id, ret_ptr) }
}
}
}
pub struct Chain<'a> {
state: ChainState<'a>,
}
enum ChainState<'a> {
Ours(OomOrDynErrorRef<'a>),
Core(Option<&'a (dyn core::error::Error + 'static)>),
}
impl<'a> Chain<'a> {
fn new(error: OomOrDynErrorRef<'a>) -> Self {
Self {
state: ChainState::Ours(error),
}
}
}
impl<'a> Iterator for Chain<'a> {
type Item = &'a (dyn core::error::Error + 'static);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
match &mut self.state {
ChainState::Ours(e) => {
let core = e.as_dyn_core_error();
self.state = if let Some(e) = e.source() {
ChainState::Ours(e)
} else {
ChainState::Core(core.source())
};
Some(core)
}
ChainState::Core(error) => {
let e = error.take()?;
self.state = ChainState::Core(e.source());
Some(e)
}
}
}
}
impl FusedIterator for Chain<'_> {}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug)]
struct TestError;
impl fmt::Display for TestError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl core::error::Error for TestError {}
#[test]
fn from_oom() {
let mut error = Error::from(OutOfMemory::new(5));
assert!(error.is::<OutOfMemory>());
assert!(error.downcast_ref::<OutOfMemory>().is_some());
assert!(error.downcast_mut::<OutOfMemory>().is_some());
assert!(error.inner.is_oom());
}
#[test]
fn dyn_error_and_concrete_error_layouts_are_compatible() {
type Concrete = ConcreteError<TestError>;
let dyn_size = mem::size_of::<DynError>();
let concrete_size = mem::size_of::<Concrete>();
assert!(
dyn_size <= concrete_size,
"assertion failed: {dyn_size} <= {concrete_size}"
);
let dyn_align = mem::align_of::<DynError>();
let concrete_align = mem::align_of::<Concrete>();
assert!(
dyn_align <= concrete_align,
"assertion failed: {dyn_align} <= {concrete_align}"
);
let dyn_offset = mem::offset_of!(DynError, vtable);
let concrete_offset = mem::offset_of!(Concrete, vtable);
assert_eq!(dyn_offset, concrete_offset);
#[cfg(feature = "backtrace")]
{
let dyn_offset = mem::offset_of!(DynError, backtrace);
let concrete_offset = mem::offset_of!(Concrete, backtrace);
assert_eq!(dyn_offset, concrete_offset);
}
}
}