From 5d1e685152b9733a92a312f3137a50a0ae3023ad Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 27 Oct 2019 16:58:51 -0700 Subject: [PATCH] Move downcasting into vtable --- src/error.rs | 70 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/src/error.rs b/src/error.rs index 1cb089a..51fb7ff 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,7 +5,7 @@ use std::error::Error as StdError; use std::fmt::{self, Debug, Display}; use std::mem::{self, ManuallyDrop}; use std::ops::{Deref, DerefMut}; -use std::ptr; +use std::ptr::{self, NonNull}; impl Error { /// Create a new error object from any error type. @@ -34,6 +34,7 @@ impl Error { object_mut: object_mut::, object_boxed: object_boxed::, object_is: object_is::, + object_downcast: object_downcast::, }; // Safety: passing vtable that operates on the right type E. @@ -52,6 +53,7 @@ impl Error { object_mut: object_mut::>, object_boxed: object_boxed::>, object_is: object_is::, + object_downcast: object_downcast::, }; // Safety: MessageError is repr(transparent) so it is okay for the @@ -71,6 +73,7 @@ impl Error { object_mut: object_mut::>, object_boxed: object_boxed::>, object_is: object_is::, + object_downcast: object_downcast::, }; // Safety: DisplayError is repr(transparent) so it is okay for the @@ -92,6 +95,7 @@ impl Error { object_mut: object_mut::>, object_boxed: object_boxed::>, object_is: object_is::>, + object_downcast: object_downcast::>, }; // Safety: passing vtable that operates on the right type. @@ -191,6 +195,7 @@ impl Error { object_mut: object_mut::>, object_boxed: object_boxed::>, object_is: object_is::>, + object_downcast: object_downcast::>, }; // As the cause is anyhow::Error, we already have a backtrace for it. @@ -269,19 +274,18 @@ impl Error { where E: Display + Debug + Send + Sync + 'static, { - if self.is::() { + let target = TypeId::of::(); + unsafe { + let addr = match (self.inner.vtable.object_downcast)(&self.inner, target) { + Some(addr) => addr, + None => return Err(self), + }; let outer = ManuallyDrop::new(self); - unsafe { - let error = ptr::read( - outer.inner.error() as *const (dyn StdError + Send + Sync) as *const E - ); - let inner = ptr::read(&outer.inner); - let erased = ManuallyDrop::into_inner(inner); - (erased.vtable.object_drop_front)(erased); - Ok(error) - } - } else { - Err(self) + let error = ptr::read(addr.cast::().as_ptr()); + let inner = ptr::read(&outer.inner); + let erased = ManuallyDrop::into_inner(inner); + (erased.vtable.object_drop_front)(erased); + Ok(error) } } @@ -325,12 +329,10 @@ impl Error { where E: Display + Debug + Send + Sync + 'static, { - if self.is::() { - Some(unsafe { - &*(self.inner.error() as *const (dyn StdError + Send + Sync) as *const E) - }) - } else { - None + let target = TypeId::of::(); + unsafe { + let addr = (self.inner.vtable.object_downcast)(&self.inner, target)?; + Some(&*addr.cast::().as_ptr()) } } @@ -339,12 +341,10 @@ impl Error { where E: Display + Debug + Send + Sync + 'static, { - if self.is::() { - Some(unsafe { - &mut *(self.inner.error_mut() as *mut (dyn StdError + Send + Sync) as *mut E) - }) - } else { - None + let target = TypeId::of::(); + unsafe { + let addr = (self.inner.vtable.object_downcast)(&self.inner, target)?; + Some(&mut *addr.cast::().as_ptr()) } } } @@ -405,6 +405,7 @@ struct ErrorVTable { object_mut: unsafe fn(&mut ErrorImpl<()>) -> &mut (dyn StdError + Send + Sync + 'static), object_boxed: unsafe fn(Box>) -> Box, object_is: unsafe fn(TypeId) -> bool, + object_downcast: unsafe fn(&ErrorImpl<()>, TypeId) -> Option>, } unsafe fn object_drop(e: Box>) { @@ -443,11 +444,24 @@ where mem::transmute::>, Box>>(e) } -unsafe fn object_is(target: TypeId) -> bool +unsafe fn object_is(target: TypeId) -> bool where - T: 'static, + E: 'static, { - TypeId::of::() == target + TypeId::of::() == target +} + +unsafe fn object_downcast(e: &ErrorImpl<()>, target: TypeId) -> Option> +where + E: 'static, +{ + if TypeId::of::() == target { + let unerased = e as *const ErrorImpl<()> as *const ErrorImpl; + let addr = &(*unerased)._error as *const E as *mut (); + Some(NonNull::new_unchecked(addr)) + } else { + None + } } // repr C to ensure that `E` remains in the final position