From 5f3429b0626034328a0c2f1317b8a0e712c63775 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Thu, 25 Aug 2022 08:30:18 -0700 Subject: [PATCH] fix(miri): Resolve Miri's concerns around unsafe code (#197) --- .github/workflows/ci.yml | 18 +++ src/eyreish/context.rs | 14 +- src/eyreish/error.rs | 280 ++++++++++++++++++++------------------- src/eyreish/fmt.rs | 20 +-- src/eyreish/mod.rs | 9 +- src/eyreish/ptr.rs | 188 ++++++++++++++++++++++++++ src/handler.rs | 9 ++ src/source_impls.rs | 2 +- tests/compiletest.rs | 1 + 9 files changed, 388 insertions(+), 153 deletions(-) create mode 100644 src/eyreish/ptr.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 70c48474..dc3e10ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,6 +45,24 @@ jobs: - name: Run tests run: cargo test --all --verbose --features fancy + miri: + name: Miri + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + components: miri,rust-src + override: true + - name: Run tests with miri + env: + MIRIFLAGS: -Zmiri-disable-isolation -Zmiri-strict-provenance + run: cargo miri test --all --verbose --features fancy + minimal_versions: name: Minimal versions check runs-on: ${{ matrix.os }} diff --git a/src/eyreish/context.rs b/src/eyreish/context.rs index 7673d8eb..3d9238b2 100644 --- a/src/eyreish/context.rs +++ b/src/eyreish/context.rs @@ -1,4 +1,4 @@ -use super::error::ContextError; +use super::error::{ContextError, ErrorImpl}; use super::{Report, WrapErr}; use core::fmt::{self, Debug, Display, Write}; @@ -116,7 +116,7 @@ where D: Display, { fn source(&self) -> Option<&(dyn StdError + 'static)> { - Some(self.error.inner.error()) + unsafe { Some(ErrorImpl::error(self.error.inner.by_ref())) } } } @@ -159,23 +159,23 @@ where D: Display, { fn code<'a>(&'a self) -> Option> { - self.error.inner.diagnostic().code() + unsafe { ErrorImpl::diagnostic(self.error.inner.by_ref()).code() } } fn severity(&self) -> Option { - self.error.inner.diagnostic().severity() + unsafe { ErrorImpl::diagnostic(self.error.inner.by_ref()).severity() } } fn help<'a>(&'a self) -> Option> { - self.error.inner.diagnostic().help() + unsafe { ErrorImpl::diagnostic(self.error.inner.by_ref()).help() } } fn url<'a>(&'a self) -> Option> { - self.error.inner.diagnostic().url() + unsafe { ErrorImpl::diagnostic(self.error.inner.by_ref()).url() } } fn labels<'a>(&'a self) -> Option + 'a>> { - self.error.inner.diagnostic().labels() + unsafe { ErrorImpl::diagnostic(self.error.inner.by_ref()).labels() } } fn source_code(&self) -> Option<&dyn crate::SourceCode> { diff --git a/src/eyreish/error.rs b/src/eyreish/error.rs index 3ae84b6f..a7ed3bd9 100644 --- a/src/eyreish/error.rs +++ b/src/eyreish/error.rs @@ -1,9 +1,10 @@ use core::any::TypeId; use core::fmt::{self, Debug, Display}; -use core::mem::{self, ManuallyDrop}; +use core::mem::ManuallyDrop; use core::ptr::{self, NonNull}; use std::error::Error as StdError; +use super::ptr::{Mut, Own, Ref}; use super::Report; use super::ReportHandler; use crate::chain::Chain; @@ -80,7 +81,6 @@ impl Report { let vtable = &ErrorVTable { object_drop: object_drop::, object_ref: object_ref::, - object_mut: object_mut::, object_ref_stderr: object_ref_stderr::, object_boxed: object_boxed::, object_boxed_stderr: object_boxed_stderr::, @@ -104,7 +104,6 @@ impl Report { let vtable = &ErrorVTable { object_drop: object_drop::>, object_ref: object_ref::>, - object_mut: object_mut::>, object_ref_stderr: object_ref_stderr::>, object_boxed: object_boxed::>, object_boxed_stderr: object_boxed_stderr::>, @@ -130,7 +129,6 @@ impl Report { let vtable = &ErrorVTable { object_drop: object_drop::>, object_ref: object_ref::>, - object_mut: object_mut::>, object_ref_stderr: object_ref_stderr::>, object_boxed: object_boxed::>, object_boxed_stderr: object_boxed_stderr::>, @@ -153,7 +151,6 @@ impl Report { let vtable = &ErrorVTable { object_drop: object_drop::, object_ref: object_ref::, - object_mut: object_mut::, object_ref_stderr: object_ref_stderr::, object_boxed: object_boxed::, object_boxed_stderr: object_boxed_stderr::, @@ -190,8 +187,7 @@ impl Report { // result is a thin pointer. The necessary behavior for manipulating the // underlying ErrorImpl is preserved in the vtable provided by the // caller rather than a builtin fat pointer vtable. - let erased = mem::transmute::>, Box>>(inner); - let inner = ManuallyDrop::new(erased); + let inner = Own::new(inner).cast::(); Report { inner } } @@ -204,17 +200,16 @@ impl Report { /// The primary reason to use `error.wrap_err(...)` instead of /// `result.wrap_err(...)` via the `WrapErr` trait would be if the /// message needs to depend on some data held by the underlying error: - pub fn wrap_err(mut self, msg: D) -> Self + pub fn wrap_err(self, msg: D) -> Self where D: Display + Send + Sync + 'static, { - let handler = self.inner.handler.take(); + let handler = unsafe { self.inner.by_mut().deref_mut().handler.take() }; let error: ContextError = ContextError { msg, error: self }; let vtable = &ErrorVTable { object_drop: object_drop::>, object_ref: object_ref::>, - object_mut: object_mut::>, object_ref_stderr: object_ref_stderr::>, object_boxed: object_boxed::>, object_boxed_stderr: object_boxed_stderr::>, @@ -256,7 +251,7 @@ impl Report { /// } /// ``` pub fn chain(&self) -> Chain<'_> { - self.inner.chain() + unsafe { ErrorImpl::chain(self.inner.by_ref()) } } /// The lowest level cause of this error — this error's cause's @@ -265,12 +260,7 @@ impl Report { /// The root cause is the last error in the iterator produced by /// [`chain()`](Report::chain). pub fn root_cause(&self) -> &(dyn StdError + 'static) { - let mut chain = self.chain(); - let mut root_cause = chain.next().unwrap(); - for cause in chain { - root_cause = cause; - } - root_cause + self.chain().last().unwrap() } /// Returns true if `E` is the type held by this error object. @@ -294,11 +284,12 @@ impl Report { E: Display + Debug + Send + Sync + 'static, { let target = TypeId::of::(); + 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 (self.inner.vtable.object_downcast)(&self.inner, target) { - Some(addr) => addr, + let addr = match (vtable(inner.ptr).object_downcast)(inner.by_ref(), target) { + Some(addr) => addr.by_mut().extend(), None => return Err(self), }; @@ -307,15 +298,10 @@ impl Report { let outer = ManuallyDrop::new(self); // Read E from where the vtable found it. - let error = ptr::read(addr.cast::().as_ptr()); - - // Read Box> from self. Can't move it out because - // Report has a Drop impl which we want to not run. - let inner = ptr::read(&outer.inner); - let erased = ManuallyDrop::into_inner(inner); + let error = addr.cast::().read(); // Drop rest of the data structure outside of E. - (erased.vtable.object_drop_rest)(erased, target); + (vtable(outer.inner.ptr).object_drop_rest)(outer.inner, target); Ok(error) } @@ -365,8 +351,8 @@ impl Report { unsafe { // Use vtable to find NonNull<()> which points to a value of type E // somewhere inside the data structure. - let addr = (self.inner.vtable.object_downcast)(&self.inner, target)?; - Some(&*addr.cast::().as_ptr()) + let addr = (vtable(self.inner.ptr).object_downcast)(self.inner.by_ref(), target)?; + Some(addr.cast::().deref()) } } @@ -379,19 +365,36 @@ impl Report { unsafe { // Use vtable to find NonNull<()> which points to a value of type E // somewhere inside the data structure. - let addr = (self.inner.vtable.object_downcast)(&self.inner, target)?; - Some(&mut *addr.cast::().as_ptr()) + let addr = + (vtable(self.inner.ptr).object_downcast)(self.inner.by_ref(), target)?.by_mut(); + Some(addr.cast::().deref_mut()) } } /// Get a reference to the Handler for this Report. pub fn handler(&self) -> &dyn ReportHandler { - self.inner.handler.as_ref().unwrap().as_ref() + unsafe { + self.inner + .by_ref() + .deref() + .handler + .as_ref() + .unwrap() + .as_ref() + } } /// Get a mutable reference to the Handler for this Report. pub fn handler_mut(&mut self) -> &mut dyn ReportHandler { - self.inner.handler.as_mut().unwrap().as_mut() + unsafe { + self.inner + .by_mut() + .deref_mut() + .handler + .as_mut() + .unwrap() + .as_mut() + } } /// Provide source code for this error @@ -418,154 +421,160 @@ impl Deref for Report { type Target = dyn Diagnostic + Send + Sync + 'static; fn deref(&self) -> &Self::Target { - self.inner.diagnostic() + unsafe { ErrorImpl::diagnostic(self.inner.by_ref()) } } } impl DerefMut for Report { fn deref_mut(&mut self) -> &mut Self::Target { - self.inner.diagnostic_mut() + unsafe { ErrorImpl::diagnostic_mut(self.inner.by_mut()) } } } impl Display for Report { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - self.inner.display(formatter) + unsafe { ErrorImpl::display(self.inner.by_ref(), formatter) } } } impl Debug for Report { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - self.inner.debug(formatter) + unsafe { ErrorImpl::debug(self.inner.by_ref(), formatter) } } } impl Drop for Report { fn drop(&mut self) { unsafe { - // Read Box> from self. - let inner = ptr::read(&self.inner); - let erased = ManuallyDrop::into_inner(inner); - // Invoke the vtable's drop behavior. - (erased.vtable.object_drop)(erased); + (vtable(self.inner.ptr).object_drop)(self.inner); } } } struct ErrorVTable { - object_drop: unsafe fn(Box>), - object_ref: unsafe fn(&ErrorImpl<()>) -> &(dyn Diagnostic + Send + Sync + 'static), - object_mut: unsafe fn(&mut ErrorImpl<()>) -> &mut (dyn Diagnostic + Send + Sync + 'static), - object_ref_stderr: unsafe fn(&ErrorImpl<()>) -> &(dyn StdError + Send + Sync + 'static), + object_drop: unsafe fn(Own), + object_ref: + unsafe fn(Ref<'_, ErasedErrorImpl>) -> Ref<'_, dyn Diagnostic + Send + Sync + 'static>, + object_ref_stderr: + unsafe fn(Ref<'_, ErasedErrorImpl>) -> Ref<'_, dyn StdError + Send + Sync + 'static>, #[allow(clippy::type_complexity)] - object_boxed: unsafe fn(Box>) -> Box, + object_boxed: unsafe fn(Own) -> Box, #[allow(clippy::type_complexity)] - object_boxed_stderr: unsafe fn(Box>) -> Box, - object_downcast: unsafe fn(&ErrorImpl<()>, TypeId) -> Option>, - object_drop_rest: unsafe fn(Box>, TypeId), + object_boxed_stderr: + unsafe fn(Own) -> Box, + object_downcast: unsafe fn(Ref<'_, ErasedErrorImpl>, TypeId) -> Option>, + object_drop_rest: unsafe fn(Own, TypeId), } // Safety: requires layout of *e to match ErrorImpl. -unsafe fn object_drop(e: Box>) { +unsafe fn object_drop(e: Own) { // Cast back to ErrorImpl so that the allocator receives the correct // Layout to deallocate the Box's memory. - let unerased = mem::transmute::>, Box>>(e); + let unerased = e.cast::>().boxed(); drop(unerased); } // Safety: requires layout of *e to match ErrorImpl. -unsafe fn object_drop_front(e: Box>, target: TypeId) { +unsafe fn object_drop_front(e: Own, target: TypeId) { // 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 _ = target; - let unerased = mem::transmute::>, Box>>>(e); + let unerased = e.cast::>>().boxed(); drop(unerased); } // Safety: requires layout of *e to match ErrorImpl. -unsafe fn object_ref(e: &ErrorImpl<()>) -> &(dyn Diagnostic + Send + Sync + 'static) +unsafe fn object_ref( + e: Ref<'_, ErasedErrorImpl>, +) -> Ref<'_, dyn Diagnostic + Send + Sync + 'static> where E: Diagnostic + Send + Sync + 'static, { // Attach E's native StdError vtable onto a pointer to self._object. - &(*(e as *const ErrorImpl<()> as *const ErrorImpl))._object -} + let unerased = e.cast::>(); -// Safety: requires layout of *e to match ErrorImpl. -unsafe fn object_mut(e: &mut ErrorImpl<()>) -> &mut (dyn Diagnostic + Send + Sync + 'static) -where - E: Diagnostic + Send + Sync + 'static, -{ - // Attach E's native StdError vtable onto a pointer to self._object. - &mut (*(e as *mut ErrorImpl<()> as *mut ErrorImpl))._object + Ref::from_raw(NonNull::new_unchecked( + ptr::addr_of!((*unerased.as_ptr())._object) as *mut E, + )) } // Safety: requires layout of *e to match ErrorImpl. -unsafe fn object_ref_stderr(e: &ErrorImpl<()>) -> &(dyn StdError + Send + Sync + 'static) +unsafe fn object_ref_stderr( + e: Ref<'_, ErasedErrorImpl>, +) -> 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 as *const ErrorImpl<()> as *const ErrorImpl))._object + let unerased = e.cast::>(); + + Ref::from_raw(NonNull::new_unchecked( + ptr::addr_of!((*unerased.as_ptr())._object) as *mut E, + )) } // Safety: requires layout of *e to match ErrorImpl. -unsafe fn object_boxed(e: Box>) -> Box +unsafe fn object_boxed(e: Own) -> Box where E: Diagnostic + Send + Sync + 'static, { // Attach ErrorImpl's native StdError vtable. The StdError impl is below. - mem::transmute::>, Box>>(e) + e.cast::>().boxed() } // Safety: requires layout of *e to match ErrorImpl. -unsafe fn object_boxed_stderr(e: Box>) -> Box +unsafe fn object_boxed_stderr( + e: Own, +) -> Box where E: StdError + Send + Sync + 'static, { // Attach ErrorImpl's native StdError vtable. The StdError impl is below. - mem::transmute::>, Box>>(e) + e.cast::>().boxed() } // Safety: requires layout of *e to match ErrorImpl. -unsafe fn object_downcast(e: &ErrorImpl<()>, target: TypeId) -> Option> +unsafe fn object_downcast(e: Ref<'_, ErasedErrorImpl>, target: TypeId) -> Option> where E: 'static, { if TypeId::of::() == target { // Caller is looking for an E pointer and e is ErrorImpl, take a // pointer to its E field. - let unerased = e as *const ErrorImpl<()> as *const ErrorImpl; - let addr = &(*unerased)._object as *const E as *mut (); - Some(NonNull::new_unchecked(addr)) + let unerased = e.cast::>(); + + Some( + Ref::from_raw(NonNull::new_unchecked( + ptr::addr_of!((*unerased.as_ptr())._object) as *mut E, + )) + .cast::<()>(), + ) } else { None } } // Safety: requires layout of *e to match ErrorImpl>. -unsafe fn context_downcast(e: &ErrorImpl<()>, target: TypeId) -> Option> +unsafe fn context_downcast(e: Ref<'_, ErasedErrorImpl>, target: TypeId) -> Option> where D: 'static, E: 'static, { if TypeId::of::() == target { - let unerased = e as *const ErrorImpl<()> as *const ErrorImpl>; - let addr = &(*unerased)._object.msg as *const D as *mut (); - Some(NonNull::new_unchecked(addr)) + let unerased = e.cast::>>().deref(); + Some(Ref::new(&unerased._object.msg).cast::<()>()) } else if TypeId::of::() == target { - let unerased = e as *const ErrorImpl<()> as *const ErrorImpl>; - let addr = &(*unerased)._object.error as *const E as *mut (); - Some(NonNull::new_unchecked(addr)) + let unerased = e.cast::>>().deref(); + Some(Ref::new(&unerased._object.error).cast::<()>()) } else { None } } // Safety: requires layout of *e to match ErrorImpl>. -unsafe fn context_drop_rest(e: Box>, target: TypeId) +unsafe fn context_drop_rest(e: Own, target: TypeId) where D: 'static, E: 'static, @@ -573,61 +582,59 @@ where // Called after downcasting by value to either the D or the E and doing a // ptr::read to take ownership of that value. if TypeId::of::() == target { - let unerased = mem::transmute::< - Box>, - Box, E>>>, - >(e); + let unerased = e + .cast::, E>>>() + .boxed(); drop(unerased); } else { - let unerased = mem::transmute::< - Box>, - Box>>>, - >(e); + let unerased = e + .cast::>>>() + .boxed(); drop(unerased); } } // Safety: requires layout of *e to match ErrorImpl>. -unsafe fn context_chain_downcast(e: &ErrorImpl<()>, target: TypeId) -> Option> +unsafe fn context_chain_downcast( + e: Ref<'_, ErasedErrorImpl>, + target: TypeId, +) -> Option> where D: 'static, { - let unerased = e as *const ErrorImpl<()> as *const ErrorImpl>; + let unerased = e.cast::>>().deref(); if TypeId::of::() == target { - let addr = &(*unerased)._object.msg as *const D as *mut (); - Some(NonNull::new_unchecked(addr)) + Some(Ref::new(&unerased._object.msg).cast::<()>()) } else { // Recurse down the context chain per the inner error's vtable. - let source = &(*unerased)._object.error; - (source.inner.vtable.object_downcast)(&source.inner, target) + let source = &unerased._object.error; + (vtable(source.inner.ptr).object_downcast)(source.inner.by_ref(), target) } } // Safety: requires layout of *e to match ErrorImpl>. -unsafe fn context_chain_drop_rest(e: Box>, target: TypeId) +unsafe fn context_chain_drop_rest(e: Own, target: TypeId) where D: 'static, { // Called after downcasting by value to either the D or one of the causes // and doing a ptr::read to take ownership of that value. if TypeId::of::() == target { - let unerased = mem::transmute::< - Box>, - Box, Report>>>, - >(e); + let unerased = e + .cast::, Report>>>() + .boxed(); // Drop the entire rest of the data structure rooted in the next Report. drop(unerased); } else { - let unerased = mem::transmute::< - Box>, - Box>>>, - >(e); - // Read out a ManuallyDrop>> from the next error. - let inner = ptr::read(&unerased._object.error.inner); + let unerased = e + .cast::>>>() + .boxed(); + // Read out a ManuallyDrop> from the next error. + let inner = unerased._object.error.inner; drop(unerased); - let erased = ManuallyDrop::into_inner(inner); + let vtable = vtable(inner.ptr); // Recursively drop the next error using the same target typeid. - (erased.vtable.object_drop_rest)(erased, target); + (vtable.object_drop_rest)(inner, target); } } @@ -649,36 +656,51 @@ pub(crate) struct ContextError { pub(crate) error: E, } +type ErasedErrorImpl = ErrorImpl<()>; + +// Safety: `ErrorVTable` must be the first field of `ErrorImpl` +unsafe fn vtable(p: NonNull) -> &'static ErrorVTable { + (p.as_ptr() as *const &'static ErrorVTable).read() +} + impl ErrorImpl { - fn erase(&self) -> &ErrorImpl<()> { + fn erase(&self) -> Ref<'_, ErasedErrorImpl> { // Erase the concrete type of E but preserve the vtable in self.vtable // for manipulating the resulting thin pointer. This is analogous to an // unsize coercion. - unsafe { &*(self as *const ErrorImpl as *const ErrorImpl<()>) } + Ref::new(self).cast::() } } -impl ErrorImpl<()> { - pub(crate) fn error(&self) -> &(dyn StdError + Send + Sync + 'static) { +impl ErasedErrorImpl { + pub(crate) unsafe fn error<'a>( + this: Ref<'a, Self>, + ) -> &'a (dyn StdError + Send + Sync + 'static) { // Use vtable to attach E's native StdError vtable for the right // original type E. - unsafe { &*(self.vtable.object_ref_stderr)(self) } + (vtable(this.ptr).object_ref_stderr)(this).deref() } - pub(crate) fn diagnostic(&self) -> &(dyn Diagnostic + Send + Sync + 'static) { + pub(crate) unsafe fn diagnostic<'a>( + this: Ref<'a, Self>, + ) -> &'a (dyn Diagnostic + Send + Sync + 'static) { // Use vtable to attach E's native StdError vtable for the right // original type E. - unsafe { &*(self.vtable.object_ref)(self) } + (vtable(this.ptr).object_ref)(this).deref() } - pub(crate) fn diagnostic_mut(&mut self) -> &mut (dyn Diagnostic + Send + Sync + 'static) { + pub(crate) unsafe fn diagnostic_mut<'a>( + this: Mut<'a, Self>, + ) -> &'a mut (dyn Diagnostic + Send + Sync + 'static) { // Use vtable to attach E's native StdError vtable for the right // original type E. - unsafe { &mut *(self.vtable.object_mut)(self) } + (vtable(this.ptr).object_ref)(this.by_ref()) + .by_mut() + .deref_mut() } - pub(crate) fn chain(&self) -> Chain<'_> { - Chain::new(self.error()) + pub(crate) unsafe fn chain(this: Ref<'_, Self>) -> Chain<'_> { + Chain::new(Self::error(this)) } } @@ -687,7 +709,7 @@ where E: StdError, { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - self.erase().diagnostic().source() + unsafe { ErrorImpl::diagnostic(self.erase()).source() } } } @@ -698,7 +720,7 @@ where E: Debug, { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - self.erase().debug(formatter) + unsafe { ErrorImpl::debug(self.erase(), formatter) } } } @@ -707,7 +729,7 @@ where E: Display, { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(&self.erase().diagnostic(), formatter) + unsafe { Display::fmt(ErrorImpl::diagnostic(self.erase()), formatter) } } } @@ -715,14 +737,9 @@ impl From for Box { fn from(error: Report) -> Self { let outer = ManuallyDrop::new(error); unsafe { - // Read Box> from error. Can't move it out because - // Report has a Drop impl which we want to not run. - let inner = ptr::read(&outer.inner); - let erased = ManuallyDrop::into_inner(inner); - // Use vtable to attach ErrorImpl's native StdError vtable for // the right original type E. - (erased.vtable.object_boxed)(erased) + (vtable(outer.inner.ptr).object_boxed)(outer.inner) } } } @@ -731,14 +748,9 @@ impl From for Box { fn from(error: Report) -> Self { let outer = ManuallyDrop::new(error); unsafe { - // Read Box> from error. Can't move it out because - // Report has a Drop impl which we want to not run. - let inner = ptr::read(&outer.inner); - let erased = ManuallyDrop::into_inner(inner); - // Use vtable to attach ErrorImpl's native StdError vtable for // the right original type E. - (erased.vtable.object_boxed_stderr)(erased) + (vtable(outer.inner.ptr).object_boxed_stderr)(outer.inner) } } } diff --git a/src/eyreish/fmt.rs b/src/eyreish/fmt.rs index bb094cad..9e385d1b 100644 --- a/src/eyreish/fmt.rs +++ b/src/eyreish/fmt.rs @@ -1,18 +1,20 @@ -use super::error::ErrorImpl; +use super::{error::ErrorImpl, ptr::Ref}; use core::fmt; impl ErrorImpl<()> { - pub(crate) fn display(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.handler + pub(crate) unsafe fn display(this: Ref<'_, Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result { + this.deref() + .handler .as_ref() - .map(|handler| handler.display(self.error(), f)) - .unwrap_or_else(|| core::fmt::Display::fmt(self.diagnostic(), f)) + .map(|handler| handler.display(Self::error(this), f)) + .unwrap_or_else(|| core::fmt::Display::fmt(Self::diagnostic(this), f)) } - pub(crate) fn debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.handler + pub(crate) unsafe fn debug(this: Ref<'_, Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result { + this.deref() + .handler .as_ref() - .map(|handler| handler.debug(self.diagnostic(), f)) - .unwrap_or_else(|| core::fmt::Debug::fmt(self.diagnostic(), f)) + .map(|handler| handler.debug(Self::diagnostic(this), f)) + .unwrap_or_else(|| core::fmt::Debug::fmt(Self::diagnostic(this), f)) } } diff --git a/src/eyreish/mod.rs b/src/eyreish/mod.rs index e7ada1d1..0efceedf 100644 --- a/src/eyreish/mod.rs +++ b/src/eyreish/mod.rs @@ -5,7 +5,6 @@ clippy::wrong_self_convention )] use core::fmt::Display; -use core::mem::ManuallyDrop; use std::error::Error as StdError; @@ -34,12 +33,15 @@ use crate::MietteHandler; use error::ErrorImpl; +use self::ptr::Own; + mod context; mod error; mod fmt; mod into_diagnostic; mod kind; mod macros; +mod ptr; mod wrapper; /** @@ -50,9 +52,12 @@ Core Diagnostic wrapper type. You can just replace `use`s of `eyre::Report` with `miette::Report`. */ pub struct Report { - inner: ManuallyDrop>>, + inner: Own>, } +unsafe impl Sync for Report {} +unsafe impl Send for Report {} + /// pub type ErrorHook = Box Box + Sync + Send + 'static>; diff --git a/src/eyreish/ptr.rs b/src/eyreish/ptr.rs new file mode 100644 index 00000000..c8d63d2d --- /dev/null +++ b/src/eyreish/ptr.rs @@ -0,0 +1,188 @@ +use std::{marker::PhantomData, ptr::NonNull}; + +#[repr(transparent)] +/// A raw pointer that owns its pointee +pub(crate) struct Own +where + T: ?Sized, +{ + pub(crate) ptr: NonNull, +} + +unsafe impl Send for Own where T: ?Sized {} +unsafe impl Sync for Own where T: ?Sized {} + +impl Copy for Own where T: ?Sized {} + +impl Clone for Own +where + T: ?Sized, +{ + fn clone(&self) -> Self { + *self + } +} + +impl Own +where + T: ?Sized, +{ + pub(crate) fn new(ptr: Box) -> Self { + Own { + ptr: unsafe { NonNull::new_unchecked(Box::into_raw(ptr)) }, + } + } + + pub(crate) fn cast(self) -> Own { + Own { + ptr: self.ptr.cast(), + } + } + + pub(crate) unsafe fn boxed(self) -> Box { + Box::from_raw(self.ptr.as_ptr()) + } + + pub(crate) fn by_ref<'a>(&self) -> Ref<'a, T> { + Ref { + ptr: self.ptr, + lifetime: PhantomData, + } + } + + pub(crate) fn by_mut<'a>(self) -> Mut<'a, T> { + Mut { + ptr: self.ptr, + lifetime: PhantomData, + } + } +} + +#[allow(explicit_outlives_requirements)] +#[repr(transparent)] +/// A raw pointer that represents a shared borrow of its pointee +pub(crate) struct Ref<'a, T> +where + T: ?Sized, +{ + pub(crate) ptr: NonNull, + lifetime: PhantomData<&'a T>, +} + +impl<'a, T> Copy for Ref<'a, T> where T: ?Sized {} + +impl<'a, T> Clone for Ref<'a, T> +where + T: ?Sized, +{ + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T> Ref<'a, T> +where + T: ?Sized, +{ + pub(crate) fn new(ptr: &'a T) -> Self { + Ref { + ptr: NonNull::from(ptr), + lifetime: PhantomData, + } + } + + pub(crate) fn from_raw(ptr: NonNull) -> Self { + Ref { + ptr, + lifetime: PhantomData, + } + } + + pub(crate) fn cast(self) -> Ref<'a, U::Target> { + Ref { + ptr: self.ptr.cast(), + lifetime: PhantomData, + } + } + + pub(crate) fn by_mut(self) -> Mut<'a, T> { + Mut { + ptr: self.ptr, + lifetime: PhantomData, + } + } + + pub(crate) fn as_ptr(self) -> *const T { + self.ptr.as_ptr() as *const T + } + + pub(crate) unsafe fn deref(self) -> &'a T { + &*self.ptr.as_ptr() + } +} + +#[allow(explicit_outlives_requirements)] +#[repr(transparent)] +/// A raw pointer that represents a unique borrow of its pointee +pub(crate) struct Mut<'a, T> +where + T: ?Sized, +{ + pub(crate) ptr: NonNull, + lifetime: PhantomData<&'a mut T>, +} + +impl<'a, T> Copy for Mut<'a, T> where T: ?Sized {} + +impl<'a, T> Clone for Mut<'a, T> +where + T: ?Sized, +{ + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T> Mut<'a, T> +where + T: ?Sized, +{ + pub(crate) fn cast(self) -> Mut<'a, U::Target> { + Mut { + ptr: self.ptr.cast(), + lifetime: PhantomData, + } + } + + pub(crate) fn by_ref(self) -> Ref<'a, T> { + Ref { + ptr: self.ptr, + lifetime: PhantomData, + } + } + + pub(crate) fn extend<'b>(self) -> Mut<'b, T> { + Mut { + ptr: self.ptr, + lifetime: PhantomData, + } + } + + pub(crate) unsafe fn deref_mut(self) -> &'a mut T { + &mut *self.ptr.as_ptr() + } +} + +impl<'a, T> Mut<'a, T> { + pub(crate) unsafe fn read(self) -> T { + self.ptr.as_ptr().read() + } +} + +pub(crate) trait CastTo { + type Target; +} + +impl CastTo for T { + type Target = T; +} diff --git a/src/handler.rs b/src/handler.rs index b5f9993c..3aca5727 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -261,6 +261,7 @@ impl MietteHandlerOpts { } } + #[cfg(not(miri))] pub(crate) fn get_width(&self) -> usize { self.width.unwrap_or_else(|| { terminal_size::terminal_size() @@ -269,6 +270,14 @@ impl MietteHandlerOpts { .0 as usize }) } + + #[cfg(miri)] + // miri doesn't support a syscall (specifically ioctl) + // performed by terminal_size, which causes test execution to fail + // so when miri is running we'll just fallback to a constant + pub(crate) fn get_width(&self) -> usize { + self.width.unwrap_or(80) + } } /** diff --git a/src/source_impls.rs b/src/source_impls.rs index 44145953..14fe7824 100644 --- a/src/source_impls.rs +++ b/src/source_impls.rs @@ -73,7 +73,7 @@ fn context_info<'a>( } if offset >= (span.offset() + span.len()).saturating_sub(1) { - let starting_offset = before_lines_starts.get(0).copied().unwrap_or_else(|| { + let starting_offset = before_lines_starts.first().copied().unwrap_or_else(|| { if context_lines_before == 0 { span.offset() } else { diff --git a/tests/compiletest.rs b/tests/compiletest.rs index f9aea23b..7974a624 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -1,4 +1,5 @@ #[rustversion::attr(not(nightly), ignore)] +#[cfg_attr(miri, ignore)] #[test] fn ui() { let t = trybuild::TestCases::new();