mirror of
https://github.com/eyre-rs/eyre.git
synced 2025-09-28 05:21:34 +00:00
Fix miri validation errors through now stricter provenance (#103)
* fix(miri): box transmute and invalid references The general causes for the miri invalidation is the prevelant use of `Box` and its references to `ErrorImpl<()>`. `mem::transmute` does not preserve the tag stack for transmuting the boxes. Additionally, having references to `ErrorImpl<()>` which has a different layout than the allocation or `ErrorImpl<E>` for some unknown `E`. This causes the new "untyped" reference to now have a provenance that includes the size of E and thus is outside the provenance. * fix(miri): downcast_mut using `&mut _ => *const _ => *mut` * fix(miri): stub file reading * fix(miri): don't construct temp references of shunk provenance * ci: miri * fix: `unsafe_op_in_unsafe_fn` * chore!: bump MSRV * chore: address PR comments * fix: ci workflow names * chore: raise msrv to 1.65 (addr2line) * chore: revert distinctive CI names due to branch protection rules The new names, such as `Test Platform Matrix` which do make it easier to see which jobs failed, rather than msrv, test, and miri all being called `Test Suite`, the in-place branch protection rules wait forever until the now non-existent `Test Suite` passes
This commit is contained in:
parent
2d984da845
commit
9f4ecc497e
19
.github/workflows/ci.yml
vendored
19
.github/workflows/ci.yml
vendored
@ -6,6 +6,9 @@ on:
|
||||
|
||||
name: Continuous integration
|
||||
|
||||
env:
|
||||
MIRIFLAGS: -Zmiri-strict-provenance
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Check
|
||||
@ -66,7 +69,7 @@ jobs:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.42
|
||||
toolchain: 1.65
|
||||
override: true
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
@ -126,3 +129,17 @@ jobs:
|
||||
with:
|
||||
command: clippy
|
||||
args: -- -D warnings
|
||||
miri:
|
||||
name: Miri
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
components: miri
|
||||
override: true
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: miri
|
||||
args: test
|
||||
|
@ -27,7 +27,7 @@ pyo3 = { version = "0.13", default-features = false, features = ["auto-initializ
|
||||
|
||||
[dependencies]
|
||||
indenter = "0.3.0"
|
||||
once_cell = "1.4.0"
|
||||
once_cell = "1.18.0"
|
||||
pyo3 = { version = "0.13", optional = true, default-features = false }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::error::ContextError;
|
||||
use crate::error::{ContextError, ErrorImpl};
|
||||
use crate::{ContextCompat, Report, StdError, WrapErr};
|
||||
use core::fmt::{self, Debug, Display, Write};
|
||||
|
||||
@ -158,7 +158,7 @@ where
|
||||
D: Display,
|
||||
{
|
||||
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||
Some(self.error.inner.error())
|
||||
Some(ErrorImpl::error(self.error.inner.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
|
385
src/error.rs
385
src/error.rs
@ -1,4 +1,5 @@
|
||||
use crate::chain::Chain;
|
||||
use crate::ptr::{MutPtr, OwnedPtr, RefPtr};
|
||||
use crate::EyreHandler;
|
||||
use crate::{Report, StdError};
|
||||
use core::any::TypeId;
|
||||
@ -70,6 +71,7 @@ impl Report {
|
||||
}
|
||||
|
||||
#[cfg_attr(track_caller, track_caller)]
|
||||
/// Creates a new error from an implementor of [`std::error::Error`]
|
||||
pub(crate) fn from_std<E>(error: E) -> Self
|
||||
where
|
||||
E: StdError + Send + Sync + 'static,
|
||||
@ -80,6 +82,7 @@ impl Report {
|
||||
object_mut: object_mut::<E>,
|
||||
object_boxed: object_boxed::<E>,
|
||||
object_downcast: object_downcast::<E>,
|
||||
object_downcast_mut: object_downcast_mut::<E>,
|
||||
object_drop_rest: object_drop_front::<E>,
|
||||
};
|
||||
|
||||
@ -102,6 +105,7 @@ impl Report {
|
||||
object_mut: object_mut::<MessageError<M>>,
|
||||
object_boxed: object_boxed::<MessageError<M>>,
|
||||
object_downcast: object_downcast::<M>,
|
||||
object_downcast_mut: object_downcast_mut::<M>,
|
||||
object_drop_rest: object_drop_front::<M>,
|
||||
};
|
||||
|
||||
@ -125,6 +129,7 @@ impl Report {
|
||||
object_mut: object_mut::<DisplayError<M>>,
|
||||
object_boxed: object_boxed::<DisplayError<M>>,
|
||||
object_downcast: object_downcast::<M>,
|
||||
object_downcast_mut: object_downcast_mut::<M>,
|
||||
object_drop_rest: object_drop_front::<M>,
|
||||
};
|
||||
|
||||
@ -149,6 +154,7 @@ impl Report {
|
||||
object_mut: object_mut::<ContextError<D, E>>,
|
||||
object_boxed: object_boxed::<ContextError<D, E>>,
|
||||
object_downcast: context_downcast::<D, E>,
|
||||
object_downcast_mut: context_downcast_mut::<D, E>,
|
||||
object_drop_rest: context_drop_rest::<D, E>,
|
||||
};
|
||||
|
||||
@ -170,6 +176,7 @@ impl Report {
|
||||
object_mut: object_mut::<BoxedError>,
|
||||
object_boxed: object_boxed::<BoxedError>,
|
||||
object_downcast: object_downcast::<Box<dyn StdError + Send + Sync>>,
|
||||
object_downcast_mut: object_downcast_mut::<Box<dyn StdError + Send + Sync>>,
|
||||
object_drop_rest: object_drop_front::<Box<dyn StdError + Send + Sync>>,
|
||||
};
|
||||
|
||||
@ -191,20 +198,20 @@ impl Report {
|
||||
where
|
||||
E: StdError + Send + Sync + 'static,
|
||||
{
|
||||
let inner = Box::new(ErrorImpl {
|
||||
vtable,
|
||||
handler,
|
||||
let inner = ErrorImpl {
|
||||
header: ErrorHeader { vtable, handler },
|
||||
_object: error,
|
||||
});
|
||||
// Erase the concrete type of E from the compile-time type system. This
|
||||
// is equivalent to the safe unsize coersion from Box<ErrorImpl<E>> to
|
||||
// Box<ErrorImpl<dyn StdError + Send + Sync + 'static>> except that the
|
||||
// result is a thin pointer. The necessary behavior for manipulating the
|
||||
// underlying ErrorImpl<E> is preserved in the vtable provided by the
|
||||
// caller rather than a builtin fat pointer vtable.
|
||||
let erased = mem::transmute::<Box<ErrorImpl<E>>, Box<ErrorImpl<()>>>(inner);
|
||||
let inner = ManuallyDrop::new(erased);
|
||||
Report { inner }
|
||||
};
|
||||
|
||||
// Construct a new owned allocation through a raw pointer
|
||||
//
|
||||
// This does not keep the allocation around as a `Box` which would invalidate an
|
||||
// references when moved
|
||||
let ptr = OwnedPtr::<ErrorImpl<E>>::new(inner);
|
||||
|
||||
// Safety: the type
|
||||
let ptr = ptr.cast::<ErrorImpl<()>>();
|
||||
Report { inner: ptr }
|
||||
}
|
||||
|
||||
/// Create a new error from an error message to wrap the existing error.
|
||||
@ -264,7 +271,11 @@ impl Report {
|
||||
where
|
||||
D: Display + Send + Sync + 'static,
|
||||
{
|
||||
let handler = self.inner.handler.take();
|
||||
// Safety: this access a `ErrorImpl<unknown>` as a valid reference to a `ErrorImpl<()>`
|
||||
//
|
||||
// As the generic is at the end of the struct and the struct is `repr(C)` this reference
|
||||
// will be within bounds of the original pointer, and the field will have the same offset
|
||||
let handler = header_mut(self.inner.as_mut()).handler.take();
|
||||
let error: ContextError<D, Report> = ContextError { msg, error: self };
|
||||
|
||||
let vtable = &ErrorVTable {
|
||||
@ -273,6 +284,7 @@ impl Report {
|
||||
object_mut: object_mut::<ContextError<D, Report>>,
|
||||
object_boxed: object_boxed::<ContextError<D, Report>>,
|
||||
object_downcast: context_chain_downcast::<D>,
|
||||
object_downcast_mut: context_chain_downcast_mut::<D>,
|
||||
object_drop_rest: context_chain_drop_rest::<D>,
|
||||
};
|
||||
|
||||
@ -280,6 +292,11 @@ impl Report {
|
||||
unsafe { Report::construct(error, vtable, handler) }
|
||||
}
|
||||
|
||||
/// Access the vtable for the current error object.
|
||||
fn vtable(&self) -> &'static ErrorVTable {
|
||||
header(self.inner.as_ref()).vtable
|
||||
}
|
||||
|
||||
/// An iterator of the chain of source errors contained by this Report.
|
||||
///
|
||||
/// This iterator will visit every error in the cause chain of this error
|
||||
@ -302,7 +319,7 @@ impl Report {
|
||||
/// }
|
||||
/// ```
|
||||
pub fn chain(&self) -> Chain<'_> {
|
||||
self.inner.chain()
|
||||
ErrorImpl::chain(self.inner.as_ref())
|
||||
}
|
||||
|
||||
/// The lowest level cause of this error — this error's cause's
|
||||
@ -342,7 +359,7 @@ impl Report {
|
||||
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) {
|
||||
let addr = match (self.vtable().object_downcast)(self.inner.as_ref(), target) {
|
||||
Some(addr) => addr,
|
||||
None => return Err(self),
|
||||
};
|
||||
@ -357,10 +374,9 @@ impl Report {
|
||||
// Read Box<ErrorImpl<()>> 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);
|
||||
|
||||
// Drop rest of the data structure outside of E.
|
||||
(erased.vtable.object_drop_rest)(erased, target);
|
||||
(outer.vtable().object_drop_rest)(inner, target);
|
||||
|
||||
Ok(error)
|
||||
}
|
||||
@ -413,8 +429,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::<E>().as_ptr())
|
||||
let addr = (self.vtable().object_downcast)(self.inner.as_ref(), target)?;
|
||||
Some(addr.cast::<E>().as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
@ -427,31 +443,47 @@ 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::<E>().as_ptr())
|
||||
let addr = (self.vtable().object_downcast_mut)(self.inner.as_mut(), target)?;
|
||||
Some(addr.cast::<E>().as_mut())
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a reference to the Handler for this Report.
|
||||
pub fn handler(&self) -> &dyn EyreHandler {
|
||||
self.inner.handler.as_ref().unwrap().as_ref()
|
||||
header(self.inner.as_ref())
|
||||
.handler
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the Handler for this Report.
|
||||
pub fn handler_mut(&mut self) -> &mut dyn EyreHandler {
|
||||
self.inner.handler.as_mut().unwrap().as_mut()
|
||||
header_mut(self.inner.as_mut())
|
||||
.handler
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.as_mut()
|
||||
}
|
||||
|
||||
/// Get a reference to the Handler for this Report.
|
||||
#[doc(hidden)]
|
||||
pub fn context(&self) -> &dyn EyreHandler {
|
||||
self.inner.handler.as_ref().unwrap().as_ref()
|
||||
header(self.inner.as_ref())
|
||||
.handler
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the Handler for this Report.
|
||||
#[doc(hidden)]
|
||||
pub fn context_mut(&mut self) -> &mut dyn EyreHandler {
|
||||
self.inner.handler.as_mut().unwrap().as_mut()
|
||||
header_mut(self.inner.as_mut())
|
||||
.handler
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
@ -469,25 +501,25 @@ impl Deref for Report {
|
||||
type Target = dyn StdError + Send + Sync + 'static;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.inner.error()
|
||||
ErrorImpl::error(self.inner.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Report {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.inner.error_mut()
|
||||
ErrorImpl::error_mut(self.inner.as_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Report {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.inner.display(formatter)
|
||||
ErrorImpl::display(self.inner.as_ref(), formatter)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Report {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.inner.debug(formatter)
|
||||
ErrorImpl::debug(self.inner.as_ref(), formatter)
|
||||
}
|
||||
}
|
||||
|
||||
@ -495,39 +527,35 @@ impl Drop for Report {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// Read Box<ErrorImpl<()>> 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);
|
||||
(self.vtable().object_drop)(self.inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ErrorVTable {
|
||||
object_drop: unsafe fn(Box<ErrorImpl<()>>),
|
||||
object_ref: unsafe fn(&ErrorImpl<()>) -> &(dyn StdError + Send + Sync + 'static),
|
||||
object_mut: unsafe fn(&mut ErrorImpl<()>) -> &mut (dyn StdError + Send + Sync + 'static),
|
||||
object_drop: unsafe fn(OwnedPtr<ErrorImpl<()>>),
|
||||
object_ref: unsafe fn(RefPtr<'_, ErrorImpl<()>>) -> &(dyn StdError + Send + Sync + 'static),
|
||||
object_mut: unsafe fn(MutPtr<'_, ErrorImpl<()>>) -> &mut (dyn StdError + Send + Sync + 'static),
|
||||
#[allow(clippy::type_complexity)]
|
||||
object_boxed: unsafe fn(Box<ErrorImpl<()>>) -> Box<dyn StdError + Send + Sync + 'static>,
|
||||
object_downcast: unsafe fn(&ErrorImpl<()>, TypeId) -> Option<NonNull<()>>,
|
||||
object_drop_rest: unsafe fn(Box<ErrorImpl<()>>, TypeId),
|
||||
object_boxed: unsafe fn(OwnedPtr<ErrorImpl<()>>) -> Box<dyn StdError + Send + Sync + 'static>,
|
||||
object_downcast: unsafe fn(RefPtr<'_, ErrorImpl<()>>, TypeId) -> Option<NonNull<()>>,
|
||||
object_downcast_mut: unsafe fn(MutPtr<'_, ErrorImpl<()>>, TypeId) -> Option<NonNull<()>>,
|
||||
object_drop_rest: unsafe fn(OwnedPtr<ErrorImpl<()>>, TypeId),
|
||||
}
|
||||
|
||||
// Safety: requires layout of *e to match ErrorImpl<E>.
|
||||
unsafe fn object_drop<E>(e: Box<ErrorImpl<()>>) {
|
||||
// Cast back to ErrorImpl<E> so that the allocator receives the correct
|
||||
// Layout to deallocate the Box's memory.
|
||||
// Note: This must not use `mem::transmute` because it tries to reborrow the `Unique`
|
||||
// contained in `Box`, which must not be done. In practice this probably won't make any
|
||||
// difference by now, but technically it's unsound.
|
||||
// see: https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md
|
||||
let unerased: Box<ErrorImpl<E>> = Box::from_raw(Box::into_raw(e).cast());
|
||||
/// # Safety
|
||||
///
|
||||
/// Requires layout of *e to match ErrorImpl<E>.
|
||||
unsafe fn object_drop<E>(e: OwnedPtr<ErrorImpl<()>>) {
|
||||
// Cast to a context type and drop the Box allocation.
|
||||
let unerased = unsafe { e.cast::<ErrorImpl<E>>().into_box() };
|
||||
drop(unerased);
|
||||
}
|
||||
|
||||
// Safety: requires layout of *e to match ErrorImpl<E>.
|
||||
unsafe fn object_drop_front<E>(e: Box<ErrorImpl<()>>, target: TypeId) {
|
||||
/// # Safety
|
||||
///
|
||||
/// Requires layout of *e to match ErrorImpl<E>.
|
||||
unsafe fn object_drop_front<E>(e: OwnedPtr<ErrorImpl<()>>, 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.
|
||||
@ -536,74 +564,132 @@ unsafe fn object_drop_front<E>(e: Box<ErrorImpl<()>>, target: TypeId) {
|
||||
// contained in `Box`, which must not be done. In practice this probably won't make any
|
||||
// difference by now, but technically it's unsound.
|
||||
// see: https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.m
|
||||
let unerased: Box<ErrorImpl<ManuallyDrop<E>>> = Box::from_raw(Box::into_raw(e).cast());
|
||||
drop(unerased);
|
||||
let unerased = unsafe { e.cast::<ErrorImpl<E>>().into_box() };
|
||||
|
||||
mem::forget(unerased._object)
|
||||
}
|
||||
|
||||
// Safety: requires layout of *e to match ErrorImpl<E>.
|
||||
unsafe fn object_ref<E>(e: &ErrorImpl<()>) -> &(dyn StdError + Send + Sync + 'static)
|
||||
/// # Safety
|
||||
///
|
||||
/// Requires layout of *e to match ErrorImpl<E>.
|
||||
unsafe fn object_ref<E>(e: RefPtr<'_, ErrorImpl<()>>) -> &(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<E>))._object
|
||||
&unsafe { e.cast::<ErrorImpl<E>>().as_ref() }._object
|
||||
}
|
||||
|
||||
// Safety: requires layout of *e to match ErrorImpl<E>.
|
||||
unsafe fn object_mut<E>(e: &mut ErrorImpl<()>) -> &mut (dyn StdError + Send + Sync + 'static)
|
||||
/// # Safety
|
||||
///
|
||||
/// Requires layout of *e to match ErrorImpl<E>.
|
||||
unsafe fn object_mut<E>(e: MutPtr<'_, ErrorImpl<()>>) -> &mut (dyn StdError + Send + Sync + 'static)
|
||||
where
|
||||
E: StdError + Send + Sync + 'static,
|
||||
{
|
||||
// Attach E's native StdError vtable onto a pointer to self._object.
|
||||
&mut (*(e as *mut ErrorImpl<()> as *mut ErrorImpl<E>))._object
|
||||
&mut unsafe { e.cast::<ErrorImpl<E>>().into_mut() }._object
|
||||
}
|
||||
|
||||
// Safety: requires layout of *e to match ErrorImpl<E>.
|
||||
unsafe fn object_boxed<E>(e: Box<ErrorImpl<()>>) -> Box<dyn StdError + Send + Sync + 'static>
|
||||
/// # Safety
|
||||
///
|
||||
/// Requires layout of *e to match ErrorImpl<E>.
|
||||
unsafe fn object_boxed<E>(e: OwnedPtr<ErrorImpl<()>>) -> Box<dyn StdError + Send + Sync + 'static>
|
||||
where
|
||||
E: StdError + Send + Sync + 'static,
|
||||
{
|
||||
// Attach ErrorImpl<E>'s native StdError vtable. The StdError impl is below.
|
||||
mem::transmute::<Box<ErrorImpl<()>>, Box<ErrorImpl<E>>>(e)
|
||||
unsafe { e.cast::<ErrorImpl<E>>().into_box() }
|
||||
}
|
||||
|
||||
// Safety: requires layout of *e to match ErrorImpl<E>.
|
||||
unsafe fn object_downcast<E>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>>
|
||||
/// # Safety
|
||||
///
|
||||
/// Requires layout of *e to match ErrorImpl<E>.
|
||||
unsafe fn object_downcast<E>(e: RefPtr<'_, ErrorImpl<()>>, target: TypeId) -> Option<NonNull<()>>
|
||||
where
|
||||
E: 'static,
|
||||
{
|
||||
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 as *const ErrorImpl<()> as *const ErrorImpl<E>;
|
||||
let addr = &(*unerased)._object as *const E as *mut ();
|
||||
Some(NonNull::new_unchecked(addr))
|
||||
let unerased = unsafe { e.cast::<ErrorImpl<E>>().as_ref() };
|
||||
Some(NonNull::from(&(unerased._object)).cast::<()>())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Safety: requires layout of *e to match ErrorImpl<ContextError<D, E>>.
|
||||
unsafe fn context_downcast<D, E>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>>
|
||||
/// # Safety
|
||||
///
|
||||
/// Requires layout of *e to match ErrorImpl<E>.
|
||||
unsafe fn object_downcast_mut<E>(
|
||||
e: MutPtr<'_, ErrorImpl<()>>,
|
||||
target: TypeId,
|
||||
) -> Option<NonNull<()>>
|
||||
where
|
||||
E: 'static,
|
||||
{
|
||||
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 = unsafe { e.cast::<ErrorImpl<E>>().into_mut() };
|
||||
Some(NonNull::from(&mut (unerased._object)).cast::<()>())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Requires layout of *e to match ErrorImpl<ContextError<D, E>>.
|
||||
unsafe fn context_downcast<D, E>(
|
||||
e: RefPtr<'_, ErrorImpl<()>>,
|
||||
target: TypeId,
|
||||
) -> Option<NonNull<()>>
|
||||
where
|
||||
D: 'static,
|
||||
E: 'static,
|
||||
{
|
||||
if TypeId::of::<D>() == target {
|
||||
let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<D, E>>;
|
||||
let addr = &(*unerased)._object.msg as *const D as *mut ();
|
||||
Some(NonNull::new_unchecked(addr))
|
||||
let unerased = unsafe { e.cast::<ErrorImpl<ContextError<D, E>>>().as_ref() };
|
||||
let addr = NonNull::from(&unerased._object.msg).cast::<()>();
|
||||
Some(addr)
|
||||
} else if TypeId::of::<E>() == target {
|
||||
let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<D, E>>;
|
||||
let addr = &(*unerased)._object.error as *const E as *mut ();
|
||||
Some(NonNull::new_unchecked(addr))
|
||||
let unerased = unsafe { e.cast::<ErrorImpl<ContextError<D, E>>>().as_ref() };
|
||||
let addr = NonNull::from(&unerased._object.error).cast::<()>();
|
||||
Some(addr)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Safety: requires layout of *e to match ErrorImpl<ContextError<D, E>>.
|
||||
unsafe fn context_drop_rest<D, E>(e: Box<ErrorImpl<()>>, target: TypeId)
|
||||
/// # Safety
|
||||
///
|
||||
/// Requires layout of *e to match ErrorImpl<ContextError<D, E>>.
|
||||
unsafe fn context_downcast_mut<D, E>(
|
||||
e: MutPtr<'_, ErrorImpl<()>>,
|
||||
target: TypeId,
|
||||
) -> Option<NonNull<()>>
|
||||
where
|
||||
D: 'static,
|
||||
E: 'static,
|
||||
{
|
||||
if TypeId::of::<D>() == target {
|
||||
let unerased = unsafe { e.cast::<ErrorImpl<ContextError<D, E>>>().into_mut() };
|
||||
let addr = NonNull::from(&unerased._object.msg).cast::<()>();
|
||||
Some(addr)
|
||||
} else if TypeId::of::<E>() == target {
|
||||
let unerased = unsafe { e.cast::<ErrorImpl<ContextError<D, E>>>().into_mut() };
|
||||
let addr = NonNull::from(&mut unerased._object.error).cast::<()>();
|
||||
Some(addr)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// # Safety
|
||||
///
|
||||
/// Requires layout of *e to match ErrorImpl<ContextError<D, E>>.
|
||||
unsafe fn context_drop_rest<D, E>(e: OwnedPtr<ErrorImpl<()>>, target: TypeId)
|
||||
where
|
||||
D: 'static,
|
||||
E: 'static,
|
||||
@ -611,69 +697,101 @@ 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::<D>() == target {
|
||||
let unerased = mem::transmute::<
|
||||
Box<ErrorImpl<()>>,
|
||||
Box<ErrorImpl<ContextError<ManuallyDrop<D>, E>>>,
|
||||
>(e);
|
||||
drop(unerased);
|
||||
unsafe {
|
||||
e.cast::<ErrorImpl<ContextError<ManuallyDrop<E>, E>>>()
|
||||
.into_box()
|
||||
};
|
||||
} else {
|
||||
let unerased = mem::transmute::<
|
||||
Box<ErrorImpl<()>>,
|
||||
Box<ErrorImpl<ContextError<D, ManuallyDrop<E>>>>,
|
||||
>(e);
|
||||
drop(unerased);
|
||||
debug_assert_eq!(TypeId::of::<E>(), target);
|
||||
unsafe {
|
||||
e.cast::<ErrorImpl<ContextError<E, ManuallyDrop<E>>>>()
|
||||
.into_box()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Safety: requires layout of *e to match ErrorImpl<ContextError<D, Report>>.
|
||||
unsafe fn context_chain_downcast<D>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>>
|
||||
/// # Safety
|
||||
///
|
||||
/// Requires layout of *e to match ErrorImpl<ContextError<D, Report>>.
|
||||
unsafe fn context_chain_downcast<D>(
|
||||
e: RefPtr<'_, ErrorImpl<()>>,
|
||||
target: TypeId,
|
||||
) -> Option<NonNull<()>>
|
||||
where
|
||||
D: 'static,
|
||||
{
|
||||
let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<D, Report>>;
|
||||
let unerased = unsafe { e.cast::<ErrorImpl<ContextError<D, Report>>>().as_ref() };
|
||||
if TypeId::of::<D>() == target {
|
||||
let addr = &(*unerased)._object.msg as *const D as *mut ();
|
||||
Some(NonNull::new_unchecked(addr))
|
||||
let addr = NonNull::from(&unerased._object.msg).cast::<()>();
|
||||
Some(addr)
|
||||
} 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;
|
||||
unsafe { (source.vtable().object_downcast)(source.inner.as_ref(), target) }
|
||||
}
|
||||
}
|
||||
|
||||
// Safety: requires layout of *e to match ErrorImpl<ContextError<D, Report>>.
|
||||
unsafe fn context_chain_drop_rest<D>(e: Box<ErrorImpl<()>>, target: TypeId)
|
||||
/// # Safety
|
||||
///
|
||||
/// Requires layout of *e to match ErrorImpl<ContextError<D, Report>>.
|
||||
unsafe fn context_chain_downcast_mut<D>(
|
||||
e: MutPtr<'_, ErrorImpl<()>>,
|
||||
target: TypeId,
|
||||
) -> Option<NonNull<()>>
|
||||
where
|
||||
D: 'static,
|
||||
{
|
||||
let unerased = unsafe { e.cast::<ErrorImpl<ContextError<D, Report>>>().into_mut() };
|
||||
if TypeId::of::<D>() == target {
|
||||
let addr = NonNull::from(&unerased._object.msg).cast::<()>();
|
||||
Some(addr)
|
||||
} else {
|
||||
// Recurse down the context chain per the inner error's vtable.
|
||||
let source = &mut unerased._object.error;
|
||||
unsafe { (source.vtable().object_downcast_mut)(source.inner.as_mut(), target) }
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Requires layout of *e to match ErrorImpl<ContextError<D, Report>>.
|
||||
unsafe fn context_chain_drop_rest<D>(e: OwnedPtr<ErrorImpl<()>>, 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::<D>() == target {
|
||||
let unerased = mem::transmute::<
|
||||
Box<ErrorImpl<()>>,
|
||||
Box<ErrorImpl<ContextError<ManuallyDrop<D>, Report>>>,
|
||||
>(e);
|
||||
let unerased = unsafe {
|
||||
e.cast::<ErrorImpl<ContextError<ManuallyDrop<D>, Report>>>()
|
||||
.into_box()
|
||||
};
|
||||
// Drop the entire rest of the data structure rooted in the next Report.
|
||||
drop(unerased);
|
||||
} else {
|
||||
let unerased = mem::transmute::<
|
||||
Box<ErrorImpl<()>>,
|
||||
Box<ErrorImpl<ContextError<D, ManuallyDrop<Report>>>>,
|
||||
>(e);
|
||||
unsafe {
|
||||
let unerased = e
|
||||
.cast::<ErrorImpl<ContextError<D, ManuallyDrop<Report>>>>()
|
||||
.into_box();
|
||||
// Read out a ManuallyDrop<Box<ErrorImpl<()>>> from the next error.
|
||||
let inner = ptr::read(&unerased._object.error.inner);
|
||||
let inner = ptr::read(&unerased.as_ref()._object.error.inner);
|
||||
drop(unerased);
|
||||
let erased = ManuallyDrop::into_inner(inner);
|
||||
// Recursively drop the next error using the same target typeid.
|
||||
(erased.vtable.object_drop_rest)(erased, target);
|
||||
(header(inner.as_ref()).vtable.object_drop_rest)(inner, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub(crate) struct ErrorHeader {
|
||||
vtable: &'static ErrorVTable,
|
||||
pub(crate) handler: Option<Box<dyn EyreHandler>>,
|
||||
}
|
||||
|
||||
// repr C to ensure that E remains in the final position.
|
||||
#[repr(C)]
|
||||
pub(crate) struct ErrorImpl<E> {
|
||||
vtable: &'static ErrorVTable,
|
||||
pub(crate) handler: Option<Box<dyn EyreHandler>>,
|
||||
pub(crate) struct ErrorImpl<E = ()> {
|
||||
header: ErrorHeader,
|
||||
// NOTE: Don't use directly. Use only through vtable. Erased type may have
|
||||
// different alignment.
|
||||
_object: E,
|
||||
@ -688,29 +806,47 @@ pub(crate) struct ContextError<D, E> {
|
||||
}
|
||||
|
||||
impl<E> ErrorImpl<E> {
|
||||
fn erase(&self) -> &ErrorImpl<()> {
|
||||
/// Returns a type erased Error
|
||||
fn erase(&self) -> RefPtr<'_, ErrorImpl<()>> {
|
||||
// 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 coersion.
|
||||
unsafe { &*(self as *const ErrorImpl<E> as *const ErrorImpl<()>) }
|
||||
RefPtr::new(self).cast()
|
||||
}
|
||||
}
|
||||
|
||||
// Reads the header out of `p`. This is the same as `p.as_ref().header`, but
|
||||
// avoids converting `p` into a reference of a shrunk provenance with a type different than the
|
||||
// allocation.
|
||||
fn header(p: RefPtr<'_, ErrorImpl<()>>) -> &'_ ErrorHeader {
|
||||
// Safety: `ErrorHeader` is the first field of repr(C) `ErrorImpl`
|
||||
unsafe { p.cast().as_ref() }
|
||||
}
|
||||
|
||||
fn header_mut(p: MutPtr<'_, ErrorImpl<()>>) -> &mut ErrorHeader {
|
||||
// Safety: `ErrorHeader` is the first field of repr(C) `ErrorImpl`
|
||||
unsafe { p.cast().into_mut() }
|
||||
}
|
||||
|
||||
impl ErrorImpl<()> {
|
||||
pub(crate) fn error(&self) -> &(dyn StdError + Send + Sync + 'static) {
|
||||
pub(crate) fn error(this: RefPtr<'_, Self>) -> &(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)(self) }
|
||||
unsafe { (header(this).vtable.object_ref)(this) }
|
||||
}
|
||||
|
||||
pub(crate) fn error_mut(&mut self) -> &mut (dyn StdError + Send + Sync + 'static) {
|
||||
pub(crate) fn error_mut(this: MutPtr<'_, Self>) -> &mut (dyn StdError + 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) }
|
||||
unsafe { (header_mut(this).vtable.object_mut)(this) }
|
||||
}
|
||||
|
||||
pub(crate) fn chain(&self) -> Chain<'_> {
|
||||
Chain::new(self.error())
|
||||
pub(crate) fn chain(this: RefPtr<'_, Self>) -> Chain<'_> {
|
||||
Chain::new(Self::error(this))
|
||||
}
|
||||
|
||||
pub(crate) fn header(this: RefPtr<'_, ErrorImpl>) -> &ErrorHeader {
|
||||
header(this)
|
||||
}
|
||||
}
|
||||
|
||||
@ -719,7 +855,7 @@ where
|
||||
E: StdError,
|
||||
{
|
||||
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||
self.erase().error().source()
|
||||
ErrorImpl::<()>::error(self.erase()).source()
|
||||
}
|
||||
}
|
||||
|
||||
@ -728,7 +864,7 @@ where
|
||||
E: Debug,
|
||||
{
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.erase().debug(formatter)
|
||||
ErrorImpl::debug(self.erase(), formatter)
|
||||
}
|
||||
}
|
||||
|
||||
@ -737,7 +873,7 @@ where
|
||||
E: Display,
|
||||
{
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
Display::fmt(&self.erase().error(), formatter)
|
||||
Display::fmt(ErrorImpl::error(self.erase()), formatter)
|
||||
}
|
||||
}
|
||||
|
||||
@ -747,12 +883,9 @@ impl From<Report> for Box<dyn StdError + Send + Sync + 'static> {
|
||||
unsafe {
|
||||
// Read Box<ErrorImpl<()>> 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<E>'s native StdError vtable for
|
||||
// the right original type E.
|
||||
(erased.vtable.object_boxed)(erased)
|
||||
(header(outer.inner.as_ref()).vtable.object_boxed)(outer.inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
21
src/fmt.rs
21
src/fmt.rs
@ -1,18 +1,21 @@
|
||||
use crate::error::ErrorImpl;
|
||||
use crate::{error::ErrorImpl, ptr::RefPtr};
|
||||
use core::fmt;
|
||||
|
||||
impl ErrorImpl<()> {
|
||||
pub(crate) fn display(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.handler
|
||||
pub(crate) fn display(this: RefPtr<'_, Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
ErrorImpl::header(this)
|
||||
.handler
|
||||
.as_ref()
|
||||
.map(|handler| handler.display(self.error(), f))
|
||||
.unwrap_or_else(|| core::fmt::Display::fmt(self.error(), f))
|
||||
.map(|handler| handler.display(Self::error(this), f))
|
||||
.unwrap_or_else(|| core::fmt::Display::fmt(Self::error(this), f))
|
||||
}
|
||||
|
||||
pub(crate) fn debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.handler
|
||||
/// Debug formats the error using the captured handler
|
||||
pub(crate) fn debug(this: RefPtr<'_, Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
ErrorImpl::header(this)
|
||||
.handler
|
||||
.as_ref()
|
||||
.map(|handler| handler.debug(self.error(), f))
|
||||
.unwrap_or_else(|| core::fmt::Debug::fmt(self.error(), f))
|
||||
.map(|handler| handler.debug(Self::error(this), f))
|
||||
.unwrap_or_else(|| core::fmt::Debug::fmt(Self::error(this), f))
|
||||
}
|
||||
}
|
||||
|
@ -334,6 +334,7 @@
|
||||
missing_docs,
|
||||
// FIXME: this lint is currently nightly only
|
||||
rustdoc::missing_doc_code_examples,
|
||||
unsafe_op_in_unsafe_fn,
|
||||
rust_2018_idioms,
|
||||
unreachable_pub,
|
||||
bad_style,
|
||||
@ -370,12 +371,12 @@ mod error;
|
||||
mod fmt;
|
||||
mod kind;
|
||||
mod macros;
|
||||
mod ptr;
|
||||
mod wrapper;
|
||||
|
||||
use crate::backtrace::Backtrace;
|
||||
use crate::error::ErrorImpl;
|
||||
use core::fmt::Display;
|
||||
use core::mem::ManuallyDrop;
|
||||
|
||||
use std::error::Error as StdError;
|
||||
|
||||
@ -383,6 +384,7 @@ pub use eyre as format_err;
|
||||
/// Compatibility re-export of `eyre` for interop with `anyhow`
|
||||
pub use eyre as anyhow;
|
||||
use once_cell::sync::OnceCell;
|
||||
use ptr::OwnedPtr;
|
||||
#[doc(hidden)]
|
||||
pub use DefaultHandler as DefaultContext;
|
||||
#[doc(hidden)]
|
||||
@ -469,7 +471,7 @@ pub use WrapErr as Context;
|
||||
/// [`hook`]: fn.set_hook.html
|
||||
#[must_use]
|
||||
pub struct Report {
|
||||
inner: ManuallyDrop<Box<ErrorImpl<()>>>,
|
||||
inner: OwnedPtr<ErrorImpl<()>>,
|
||||
}
|
||||
|
||||
type ErrorHook =
|
||||
|
149
src/ptr.rs
Normal file
149
src/ptr.rs
Normal file
@ -0,0 +1,149 @@
|
||||
use std::{marker::PhantomData, ptr::NonNull};
|
||||
|
||||
/// An owned pointer
|
||||
///
|
||||
/// **NOTE**: Does not deallocate when dropped
|
||||
pub(crate) struct OwnedPtr<T: ?Sized> {
|
||||
ptr: NonNull<T>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Copy for OwnedPtr<T> {}
|
||||
|
||||
impl<T: ?Sized> Clone for OwnedPtr<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> Send for OwnedPtr<T> where T: Send {}
|
||||
unsafe impl<T> Sync for OwnedPtr<T> where T: Send {}
|
||||
|
||||
impl<T> OwnedPtr<T> {
|
||||
pub(crate) fn new(value: T) -> Self {
|
||||
Self::from_boxed(Box::new(value))
|
||||
}
|
||||
|
||||
pub(crate) fn from_boxed(boxed: Box<T>) -> Self {
|
||||
// Safety: `Box::into_raw` is guaranteed to be non-null
|
||||
Self {
|
||||
ptr: unsafe { NonNull::new_unchecked(Box::into_raw(boxed)) },
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the pointer to another type
|
||||
pub(crate) fn cast<U>(self) -> OwnedPtr<U> {
|
||||
OwnedPtr {
|
||||
ptr: self.ptr.cast(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Context the pointer into a Box
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Dropping the Box will deallocate a layout of `T` and run the destructor of `T`.
|
||||
///
|
||||
/// A cast pointer must therefore be cast back to the original type before calling this method.
|
||||
pub(crate) unsafe fn into_box(self) -> Box<T> {
|
||||
unsafe { Box::from_raw(self.ptr.as_ptr()) }
|
||||
}
|
||||
|
||||
pub(crate) const fn as_ref(&self) -> RefPtr<'_, T> {
|
||||
RefPtr {
|
||||
ptr: self.ptr,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_mut(&mut self) -> MutPtr<'_, T> {
|
||||
MutPtr {
|
||||
ptr: self.ptr,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience lifetime annotated mutable pointer which facilitates returning an inferred lifetime
|
||||
/// in a `fn` pointer.
|
||||
pub(crate) struct RefPtr<'a, T: ?Sized> {
|
||||
pub(crate) ptr: NonNull<T>,
|
||||
_marker: PhantomData<&'a T>,
|
||||
}
|
||||
|
||||
/// Safety: RefPtr indicates a shared reference to a value and as such exhibits the same Send +
|
||||
/// Sync behavior of &'a T
|
||||
unsafe impl<'a, T: ?Sized> Send for RefPtr<'a, T> where &'a T: Send {}
|
||||
unsafe impl<'a, T: ?Sized> Sync for RefPtr<'a, T> where &'a T: Sync {}
|
||||
|
||||
impl<'a, T: ?Sized> Copy for RefPtr<'a, T> {}
|
||||
impl<'a, T: ?Sized> Clone for RefPtr<'a, T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> RefPtr<'a, T> {
|
||||
pub(crate) fn new(ptr: &'a T) -> Self {
|
||||
Self {
|
||||
ptr: NonNull::from(ptr),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the pointer to another type
|
||||
pub(crate) fn cast<U>(self) -> RefPtr<'a, U> {
|
||||
RefPtr {
|
||||
ptr: self.ptr.cast(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a shared reference to the owned value
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// See: [`NonNull::as_ref`]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn as_ref(&self) -> &'a T {
|
||||
unsafe { self.ptr.as_ref() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience lifetime annotated mutable pointer which facilitates returning an inferred lifetime
|
||||
/// in a `fn` pointer.
|
||||
pub(crate) struct MutPtr<'a, T: ?Sized> {
|
||||
pub(crate) ptr: NonNull<T>,
|
||||
_marker: PhantomData<&'a mut T>,
|
||||
}
|
||||
|
||||
/// Safety: RefPtr indicates an exclusive reference to a value and as such exhibits the same Send +
|
||||
/// Sync behavior of &'a mut T
|
||||
unsafe impl<'a, T: ?Sized> Send for MutPtr<'a, T> where &'a mut T: Send {}
|
||||
unsafe impl<'a, T: ?Sized> Sync for MutPtr<'a, T> where &'a mut T: Sync {}
|
||||
|
||||
impl<'a, T: ?Sized> Copy for MutPtr<'a, T> {}
|
||||
impl<'a, T: ?Sized> Clone for MutPtr<'a, T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> MutPtr<'a, T> {
|
||||
/// Convert the pointer to another type
|
||||
pub(crate) fn cast<U>(self) -> MutPtr<'a, U> {
|
||||
MutPtr {
|
||||
ptr: self.ptr.cast(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the owned value with the lifetime decoupled from self
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// See: [`NonNull::as_mut`]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn into_mut(mut self) -> &'a mut T {
|
||||
unsafe { self.ptr.as_mut() }
|
||||
}
|
||||
}
|
@ -5,6 +5,9 @@ use core::fmt::{self, Debug, Display};
|
||||
pub(crate) struct DisplayError<M>(pub(crate) M);
|
||||
|
||||
#[repr(transparent)]
|
||||
/// Wraps a Debug + Display type as an error.
|
||||
///
|
||||
/// Its Debug and Display impls are the same as the wrapped type.
|
||||
pub(crate) struct MessageError<M>(pub(crate) M);
|
||||
|
||||
pub(crate) struct NoneError;
|
||||
|
@ -1,4 +1,5 @@
|
||||
#[rustversion::attr(not(nightly), ignore)]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
#[test]
|
||||
fn ui() {
|
||||
let t = trybuild::TestCases::new();
|
||||
|
@ -46,7 +46,7 @@ fn test_wrap_err() {
|
||||
Box::new(LocationHandler::new(expected_location))
|
||||
}));
|
||||
|
||||
let err = std::fs::read_to_string("totally_fake_path")
|
||||
let err = read_path("totally_fake_path")
|
||||
.wrap_err("oopsie")
|
||||
.unwrap_err();
|
||||
|
||||
@ -54,6 +54,20 @@ fn test_wrap_err() {
|
||||
println!("{:?}", err);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
fn read_path(path: &str) -> Result<String, std::io::Error> {
|
||||
std::fs::read_to_string(path)
|
||||
}
|
||||
|
||||
#[cfg(miri)]
|
||||
fn read_path(_path: &str) -> Result<String, std::io::Error> {
|
||||
// Miri doesn't support reading files, so we just return an error
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"Miri doesn't support reading files",
|
||||
))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wrap_err_with() {
|
||||
let _ = eyre::set_hook(Box::new(|_e| {
|
||||
@ -61,7 +75,7 @@ fn test_wrap_err_with() {
|
||||
Box::new(LocationHandler::new(expected_location))
|
||||
}));
|
||||
|
||||
let err = std::fs::read_to_string("totally_fake_path")
|
||||
let err = read_path("totally_fake_path")
|
||||
.wrap_err_with(|| "oopsie")
|
||||
.unwrap_err();
|
||||
|
||||
@ -76,7 +90,7 @@ fn test_context() {
|
||||
Box::new(LocationHandler::new(expected_location))
|
||||
}));
|
||||
|
||||
let err = std::fs::read_to_string("totally_fake_path")
|
||||
let err = read_path("totally_fake_path")
|
||||
.context("oopsie")
|
||||
.unwrap_err();
|
||||
|
||||
@ -91,7 +105,7 @@ fn test_with_context() {
|
||||
Box::new(LocationHandler::new(expected_location))
|
||||
}));
|
||||
|
||||
let err = std::fs::read_to_string("totally_fake_path")
|
||||
let err = read_path("totally_fake_path")
|
||||
.with_context(|| "oopsie")
|
||||
.unwrap_err();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user