Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eliminate functionally duplicate vtable methods on rustc 1.51+ #145

Merged
merged 4 commits into from Mar 25, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
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
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
@@ -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
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