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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
strategy:
fail-fast: false
matrix:
rust: [nightly, beta, stable]
rust: [nightly, beta, stable, 1.51.0, 1.50.0]
steps:
- uses: actions/checkout@v2
- uses: dtolnay/rust-toolchain@master
Expand Down
4 changes: 4 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ fn main() {
if rustc < 38 {
println!("cargo:rustc-cfg=anyhow_no_macro_reexport");
}

if rustc < 51 {
println!("cargo:rustc-cfg=anyhow_no_ptr_addr_of");
}
}

fn compile_probe() -> Option<ExitStatus> {
Expand Down
94 changes: 75 additions & 19 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use crate::alloc::Box;
use crate::backtrace::Backtrace;
use crate::chain::Chain;
use crate::ptr::{Mut, Own, Ref};
#[cfg(any(feature = "std", anyhow_no_ptr_addr_of))]
use crate::ptr::Mut;
use crate::ptr::{Own, Ref};
use crate::{Error, StdError};
use core::any::TypeId;
use core::fmt::{self, Debug, Display};
use core::mem::ManuallyDrop;
#[cfg(not(anyhow_no_ptr_addr_of))]
use core::ptr;
use core::ptr::NonNull;

#[cfg(feature = "std")]
Expand Down Expand Up @@ -81,9 +85,11 @@ impl Error {
let vtable = &ErrorVTable {
object_drop: object_drop::<E>,
object_ref: object_ref::<E>,
#[cfg(anyhow_no_ptr_addr_of)]
object_mut: object_mut::<E>,
object_boxed: object_boxed::<E>,
object_downcast: object_downcast::<E>,
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: object_downcast_mut::<E>,
object_drop_rest: object_drop_front::<E>,
#[cfg(all(not(backtrace), feature = "backtrace"))]
Expand All @@ -103,10 +109,11 @@ impl Error {
let vtable = &ErrorVTable {
object_drop: object_drop::<MessageError<M>>,
object_ref: object_ref::<MessageError<M>>,
#[cfg(feature = "std")]
#[cfg(all(feature = "std", anyhow_no_ptr_addr_of))]
object_mut: object_mut::<MessageError<M>>,
object_boxed: object_boxed::<MessageError<M>>,
object_downcast: object_downcast::<M>,
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: object_downcast_mut::<M>,
object_drop_rest: object_drop_front::<M>,
#[cfg(all(not(backtrace), feature = "backtrace"))]
Expand All @@ -127,10 +134,11 @@ impl Error {
let vtable = &ErrorVTable {
object_drop: object_drop::<DisplayError<M>>,
object_ref: object_ref::<DisplayError<M>>,
#[cfg(feature = "std")]
#[cfg(all(feature = "std", anyhow_no_ptr_addr_of))]
object_mut: object_mut::<DisplayError<M>>,
object_boxed: object_boxed::<DisplayError<M>>,
object_downcast: object_downcast::<M>,
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: object_downcast_mut::<M>,
object_drop_rest: object_drop_front::<M>,
#[cfg(all(not(backtrace), feature = "backtrace"))]
Expand All @@ -153,9 +161,11 @@ impl Error {
let vtable = &ErrorVTable {
object_drop: object_drop::<ContextError<C, E>>,
object_ref: object_ref::<ContextError<C, E>>,
#[cfg(anyhow_no_ptr_addr_of)]
object_mut: object_mut::<ContextError<C, E>>,
object_boxed: object_boxed::<ContextError<C, E>>,
object_downcast: context_downcast::<C, E>,
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: context_downcast_mut::<C, E>,
object_drop_rest: context_drop_rest::<C, E>,
#[cfg(all(not(backtrace), feature = "backtrace"))]
Expand All @@ -176,9 +186,11 @@ impl Error {
let vtable = &ErrorVTable {
object_drop: object_drop::<BoxedError>,
object_ref: object_ref::<BoxedError>,
#[cfg(anyhow_no_ptr_addr_of)]
object_mut: object_mut::<BoxedError>,
object_boxed: object_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>>,
object_drop_rest: object_drop_front::<Box<dyn StdError + Send + Sync>>,
#[cfg(all(not(backtrace), feature = "backtrace"))]
Expand Down Expand Up @@ -284,10 +296,11 @@ impl Error {
let vtable = &ErrorVTable {
object_drop: object_drop::<ContextError<C, Error>>,
object_ref: object_ref::<ContextError<C, Error>>,
#[cfg(feature = "std")]
#[cfg(all(feature = "std", anyhow_no_ptr_addr_of))]
object_mut: object_mut::<ContextError<C, Error>>,
object_boxed: object_boxed::<ContextError<C, Error>>,
object_downcast: context_chain_downcast::<C>,
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: context_chain_downcast_mut::<C>,
object_drop_rest: context_chain_drop_rest::<C>,
#[cfg(all(not(backtrace), feature = "backtrace"))]
Expand Down Expand Up @@ -396,14 +409,20 @@ impl Error {
E: Display + Debug + Send + Sync + 'static,
{
let target = TypeId::of::<E>();
let inner = self.inner.by_mut();
unsafe {
// Use vtable to find NonNull<()> which points to a value of type E
// somewhere inside the data structure.
let addr =
match (vtable(self.inner.ptr).object_downcast_mut)(self.inner.by_mut(), target) {
Some(addr) => addr.extend(),
None => return Err(self),
};
#[cfg(not(anyhow_no_ptr_addr_of))]
let addr = match (vtable(inner.ptr).object_downcast)(inner.by_ref(), target) {
Some(addr) => addr.by_mut().extend(),
None => return Err(self),
};
#[cfg(anyhow_no_ptr_addr_of)]
let addr = match (vtable(inner.ptr).object_downcast_mut)(inner, target) {
Some(addr) => addr.extend(),
None => return Err(self),
};

// Prepare to read E out of the data structure. We'll drop the rest
// of the data structure separately so that E is not dropped.
Expand Down Expand Up @@ -477,7 +496,14 @@ impl Error {
unsafe {
// Use vtable to find NonNull<()> which points to a value of type E
// somewhere inside the data structure.

#[cfg(not(anyhow_no_ptr_addr_of))]
let addr =
(vtable(self.inner.ptr).object_downcast)(self.inner.by_ref(), target)?.by_mut();

#[cfg(anyhow_no_ptr_addr_of)]
let addr = (vtable(self.inner.ptr).object_downcast_mut)(self.inner.by_mut(), target)?;

Some(addr.cast::<E>().deref_mut())
}
}
Expand Down Expand Up @@ -536,11 +562,12 @@ impl Drop for Error {

struct ErrorVTable {
object_drop: unsafe fn(Own<ErrorImpl>),
object_ref: unsafe fn(Ref<ErrorImpl>) -> &(dyn StdError + Send + Sync + 'static),
#[cfg(feature = "std")]
object_ref: unsafe fn(Ref<ErrorImpl>) -> Ref<dyn StdError + Send + Sync + 'static>,
#[cfg(all(feature = "std", anyhow_no_ptr_addr_of))]
object_mut: unsafe fn(Mut<ErrorImpl>) -> &mut (dyn StdError + Send + Sync + 'static),
object_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<()>>,
object_drop_rest: unsafe fn(Own<ErrorImpl>, TypeId),
#[cfg(all(not(backtrace), feature = "backtrace"))]
Expand All @@ -566,17 +593,26 @@ unsafe fn object_drop_front<E>(e: Own<ErrorImpl>, target: TypeId) {
}

// Safety: requires layout of *e to match ErrorImpl<E>.
unsafe fn object_ref<E>(e: Ref<ErrorImpl>) -> &(dyn StdError + Send + Sync + 'static)
unsafe fn object_ref<E>(e: Ref<ErrorImpl>) -> Ref<dyn StdError + Send + Sync + 'static>
where
E: StdError + Send + Sync + 'static,
{
// Attach E's native StdError vtable onto a pointer to self._object.
&e.cast::<ErrorImpl<E>>().deref()._object

let unerased = e.cast::<ErrorImpl<E>>();

#[cfg(not(anyhow_no_ptr_addr_of))]
return Ref::from_raw(NonNull::new_unchecked(
ptr::addr_of!((*unerased.as_ptr())._object) as *mut E,
));

#[cfg(anyhow_no_ptr_addr_of)]
return Ref::new(&unerased.deref()._object);
}

// Safety: requires layout of *e to match ErrorImpl<E>, and for `e` to be derived
// from a `&mut`
#[cfg(feature = "std")]
#[cfg(all(feature = "std", anyhow_no_ptr_addr_of))]
unsafe fn object_mut<E>(e: Mut<ErrorImpl>) -> &mut (dyn StdError + Send + Sync + 'static)
where
E: StdError + Send + Sync + 'static,
Expand All @@ -602,14 +638,26 @@ where
if TypeId::of::<E>() == target {
// Caller is looking for an E pointer and e is ErrorImpl<E>, take a
// pointer to its E field.
let unerased = e.cast::<ErrorImpl<E>>().deref();
Some(Ref::new(&unerased._object).cast::<()>())

let unerased = e.cast::<ErrorImpl<E>>();

#[cfg(not(anyhow_no_ptr_addr_of))]
return Some(
Ref::from_raw(NonNull::new_unchecked(
ptr::addr_of!((*unerased.as_ptr())._object) as *mut E,
))
.cast::<()>(),
);

#[cfg(anyhow_no_ptr_addr_of)]
return Some(Ref::new(&unerased.deref()._object).cast::<()>());
} else {
None
}
}

// Safety: requires layout of *e to match ErrorImpl<E>.
#[cfg(anyhow_no_ptr_addr_of)]
unsafe fn object_downcast_mut<E>(e: Mut<ErrorImpl>, target: TypeId) -> Option<Mut<()>>
where
E: 'static,
Expand Down Expand Up @@ -649,7 +697,7 @@ where
}

// Safety: requires layout of *e to match ErrorImpl<ContextError<C, E>>.
#[cfg(feature = "std")]
#[cfg(all(feature = "std", anyhow_no_ptr_addr_of))]
unsafe fn context_downcast_mut<C, E>(e: Mut<ErrorImpl>, target: TypeId) -> Option<Mut<()>>
where
C: 'static,
Expand Down Expand Up @@ -705,6 +753,7 @@ where
}

// Safety: requires layout of *e to match ErrorImpl<ContextError<C, Error>>.
#[cfg(anyhow_no_ptr_addr_of)]
unsafe fn context_chain_downcast_mut<C>(e: Mut<ErrorImpl>, target: TypeId) -> Option<Mut<()>>
where
C: 'static,
Expand Down Expand Up @@ -798,14 +847,21 @@ impl ErrorImpl {
pub(crate) unsafe fn error(this: Ref<Self>) -> &(dyn StdError + Send + Sync + 'static) {
// Use vtable to attach E's native StdError vtable for the right
// original type E.
(vtable(this.ptr).object_ref)(this)
(vtable(this.ptr).object_ref)(this).deref()
}

#[cfg(feature = "std")]
pub(crate) unsafe fn error_mut(this: Mut<Self>) -> &mut (dyn StdError + Send + Sync + 'static) {
// Use vtable to attach E's native StdError vtable for the right
// original type E.
(vtable(this.ptr).object_mut)(this)

#[cfg(not(anyhow_no_ptr_addr_of))]
return (vtable(this.ptr).object_ref)(this.by_ref())
.by_mut()
.deref_mut();

#[cfg(anyhow_no_ptr_addr_of)]
return (vtable(this.ptr).object_mut)(this);
}

#[cfg(any(backtrace, feature = "backtrace"))]
Expand Down
30 changes: 30 additions & 0 deletions src/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,34 @@ where
}
}

#[cfg(not(anyhow_no_ptr_addr_of))]
pub fn from_raw(ptr: NonNull<T>) -> Self {
Ref {
ptr,
lifetime: PhantomData,
}
}

pub fn cast<U: CastTo>(self) -> Ref<'a, U::Target> {
Ref {
ptr: self.ptr.cast(),
lifetime: PhantomData,
}
}

#[cfg(not(anyhow_no_ptr_addr_of))]
pub fn by_mut(self) -> Mut<'a, T> {
Mut {
ptr: self.ptr,
lifetime: PhantomData,
}
}

#[cfg(not(anyhow_no_ptr_addr_of))]
pub fn as_ptr(self) -> *const T {
self.ptr.as_ptr() as *const T
}

pub unsafe fn deref(self) -> &'a T {
&*self.ptr.as_ptr()
}
Expand Down Expand Up @@ -127,6 +148,7 @@ impl<'a, T> Mut<'a, T>
where
T: ?Sized,
{
#[cfg(anyhow_no_ptr_addr_of)]
pub fn new(ptr: &'a mut T) -> Self {
Mut {
ptr: NonNull::from(ptr),
Expand All @@ -141,6 +163,14 @@ where
}
}

#[cfg(not(anyhow_no_ptr_addr_of))]
pub fn by_ref(self) -> Ref<'a, T> {
Ref {
ptr: self.ptr,
lifetime: PhantomData,
}
}

pub fn extend<'b>(self) -> Mut<'b, T> {
Mut {
ptr: self.ptr,
Expand Down