Deallocate with the right Layout during downcast

This commit is contained in:
David Tolnay 2019-10-08 09:13:29 -07:00
parent 8895b006a7
commit 22c2608a84
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82

View File

@ -82,6 +82,7 @@ impl Error {
{ {
let vtable = &ErrorVTable { let vtable = &ErrorVTable {
object_drop: object_drop::<E>, object_drop: object_drop::<E>,
object_drop_front: object_drop_front::<E>,
object_raw: object_raw::<E>, object_raw: object_raw::<E>,
object_mut_raw: object_mut_raw::<E>, object_mut_raw: object_mut_raw::<E>,
}; };
@ -237,11 +238,13 @@ impl Error {
where where
E: Display + Debug + Send + Sync + 'static, E: Display + Debug + Send + Sync + 'static,
{ {
if let Some(error) = self.downcast_ref::<E>() { if self.is::<E>() {
let outer = ManuallyDrop::new(self);
unsafe { unsafe {
let error = ptr::read(error); let error = ptr::read(&outer.inner.error as *const () as *const E);
drop(ptr::read(&self.inner)); let inner = ptr::read(&outer.inner);
mem::forget(self); let erased = ManuallyDrop::into_inner(inner);
(erased.vtable.object_drop_front)(erased);
Ok(error) Ok(error)
} }
} else { } else {
@ -393,6 +396,7 @@ impl Drop for Error {
struct ErrorVTable { struct ErrorVTable {
object_drop: unsafe fn(Box<ErrorImpl<()>>), object_drop: unsafe fn(Box<ErrorImpl<()>>),
object_drop_front: unsafe fn(Box<ErrorImpl<()>>),
object_raw: fn(*const ()) -> *const (dyn StdError + Send + Sync + 'static), object_raw: fn(*const ()) -> *const (dyn StdError + Send + Sync + 'static),
object_mut_raw: fn(*mut ()) -> *mut (dyn StdError + Send + Sync + 'static), object_mut_raw: fn(*mut ()) -> *mut (dyn StdError + Send + Sync + 'static),
} }
@ -404,6 +408,14 @@ unsafe fn object_drop<E>(e: Box<ErrorImpl<()>>) {
drop(unerased); drop(unerased);
} }
unsafe fn object_drop_front<E>(e: Box<ErrorImpl<()>>) {
// Drop the fields of ErrorImpl other than E as well as the Box allocation,
// without dropping E itself. This is used by downcast after doing a
// ptr::read to take ownership of the E.
let unerased = mem::transmute::<Box<ErrorImpl<()>>, Box<ErrorImpl<ManuallyDrop<E>>>>(e);
drop(unerased);
}
fn object_raw<E>(e: *const ()) -> *const (dyn StdError + Send + Sync + 'static) fn object_raw<E>(e: *const ()) -> *const (dyn StdError + Send + Sync + 'static)
where where
E: StdError + Send + Sync + 'static, E: StdError + Send + Sync + 'static,