1 use crate::error::ptr::{MutPtr, OwnedPtr, SharedPtr};
2 use crate::error::{ConcreteError, DynError, Error, ErrorExt, OutOfMemory};
3 use core::{any::TypeId, fmt, ptr::NonNull};
4 use std_alloc::boxed::Box;
5 
6 /// A vtable containing the `ErrorExt` methods for some type `T`.
7 ///
8 /// This is used to create thin-pointer equivalents of `Box<dyn ErrorExt>`,
9 /// `&dyn ErrorExt`, and `&mut ErrorExt`, which would all otherwise be two words
10 /// in size.
11 ///
12 /// # Safety
13 ///
14 /// The safety contract for all vtable functions is the same:
15 ///
16 /// * `SharedPtr<'_, DynError>`s must be valid for reading a `ConcreteError<T>`,
17 ///   `MutPtr<'_, DynError>`s must additionally be valid for writing a
18 ///   `ConcreteError<T>`, and `OwnedPtr<DynError>`s must additionally be valid
19 ///   to deallocate with `ConcreteError<T>`'s layout.
20 ///
21 /// * If a `OomOrDynError{Ref,Mut}` return value contains a `{Shared,Mut}Ptr<'_,
22 ///   DynError>`, it must be valid for reading (and, in the case of `MutPtr`,
23 ///   writing) `DynError`s.
24 #[repr(C)]
25 pub(crate) struct Vtable {
26     pub(crate) display: unsafe fn(SharedPtr<'_, DynError>, &mut fmt::Formatter<'_>) -> fmt::Result,
27     pub(crate) debug: unsafe fn(SharedPtr<'_, DynError>, &mut fmt::Formatter<'_>) -> fmt::Result,
28     pub(crate) source: unsafe fn(SharedPtr<'_, DynError>) -> Option<&Error>,
29     pub(crate) source_mut: unsafe fn(MutPtr<'_, DynError>) -> Option<&mut Error>,
30     pub(crate) is: unsafe fn(SharedPtr<'_, DynError>, TypeId) -> bool,
31     pub(crate) as_dyn_core_error:
32         unsafe fn(SharedPtr<'_, DynError>) -> &(dyn core::error::Error + Send + Sync + 'static),
33     pub(crate) into_boxed_dyn_core_error:
34         unsafe fn(
35             OwnedPtr<DynError>,
36         )
37             -> Result<Box<dyn core::error::Error + Send + Sync + 'static>, OutOfMemory>,
38     pub(crate) drop_and_deallocate: unsafe fn(OwnedPtr<DynError>),
39 
40     /// Additional safety requirement: the `NonNull<u8>` pointer must be valid
41     /// for writing a `T`.
42     ///
43     /// Upon successful return, a `T` will have been written to that memory
44     /// block.
45     pub(crate) move_into: unsafe fn(OwnedPtr<DynError>, NonNull<u8>),
46 
47     /// Conversion into `anyhow::Error` from `Box<Self>`.
48     #[cfg(feature = "anyhow")]
49     pub(crate) into_anyhow: unsafe fn(OwnedPtr<DynError>) -> anyhow::Error,
50 }
51 
52 impl Vtable {
53     /// Get the `Vtable` of the `E: ErrorExt` type parameter.
of<E>() -> &'static Self where E: ErrorExt,54     pub(crate) fn of<E>() -> &'static Self
55     where
56         E: ErrorExt,
57     {
58         &Vtable {
59             display: display::<E>,
60             debug: debug::<E>,
61             source: source::<E>,
62             source_mut: source_mut::<E>,
63             is: is::<E>,
64             as_dyn_core_error: as_dyn_core_error::<E>,
65             into_boxed_dyn_core_error: into_boxed_dyn_core_error::<E>,
66             drop_and_deallocate: drop_and_deallocate::<E>,
67             move_into: move_into::<E>,
68             #[cfg(feature = "anyhow")]
69             into_anyhow: into_anyhow::<E>,
70         }
71     }
72 }
73 
display<E>(error: SharedPtr<'_, DynError>, f: &mut fmt::Formatter<'_>) -> fmt::Result where E: ErrorExt,74 unsafe fn display<E>(error: SharedPtr<'_, DynError>, f: &mut fmt::Formatter<'_>) -> fmt::Result
75 where
76     E: ErrorExt,
77 {
78     let error = error.cast::<ConcreteError<E>>();
79     // Safety: implied by all vtable functions' safety contract.
80     let error = unsafe { error.as_ref() };
81     fmt::Display::fmt(error.error.ext_as_dyn_core_error(), f)
82 }
83 
debug<E>(error: SharedPtr<'_, DynError>, f: &mut fmt::Formatter<'_>) -> fmt::Result where E: ErrorExt,84 unsafe fn debug<E>(error: SharedPtr<'_, DynError>, f: &mut fmt::Formatter<'_>) -> fmt::Result
85 where
86     E: ErrorExt,
87 {
88     let error = error.cast::<ConcreteError<E>>();
89     // Safety: implied by all vtable functions' safety contract.
90     let error = unsafe { error.as_ref() };
91     fmt::Debug::fmt(error.error.ext_as_dyn_core_error(), f)
92 }
93 
source<E>(error: SharedPtr<'_, DynError>) -> Option<&Error> where E: ErrorExt,94 unsafe fn source<E>(error: SharedPtr<'_, DynError>) -> Option<&Error>
95 where
96     E: ErrorExt,
97 {
98     let error = error.cast::<ConcreteError<E>>();
99     // Safety: implied by all vtable functions' safety contract.
100     let error = unsafe { error.as_ref() };
101     error.error.ext_source()
102 }
103 
source_mut<E>(error: MutPtr<'_, DynError>) -> Option<&mut Error> where E: ErrorExt,104 unsafe fn source_mut<E>(error: MutPtr<'_, DynError>) -> Option<&mut Error>
105 where
106     E: ErrorExt,
107 {
108     let mut error = error.cast::<ConcreteError<E>>();
109     // Safety: implied by all vtable functions' safety contract.
110     let error = unsafe { error.as_mut() };
111     error.error.ext_source_mut()
112 }
113 
is<E>(error: SharedPtr<'_, DynError>, type_id: TypeId) -> bool where E: ErrorExt,114 unsafe fn is<E>(error: SharedPtr<'_, DynError>, type_id: TypeId) -> bool
115 where
116     E: ErrorExt,
117 {
118     let error = error.cast::<ConcreteError<E>>();
119     // Safety: implied by all vtable functions' safety contract.
120     let error = unsafe { error.as_ref() };
121     error.error.ext_is(type_id)
122 }
123 
as_dyn_core_error<E>( error: SharedPtr<'_, DynError>, ) -> &(dyn core::error::Error + Send + Sync + 'static) where E: ErrorExt,124 unsafe fn as_dyn_core_error<E>(
125     error: SharedPtr<'_, DynError>,
126 ) -> &(dyn core::error::Error + Send + Sync + 'static)
127 where
128     E: ErrorExt,
129 {
130     let error = error.cast::<ConcreteError<E>>();
131     // Safety: implied by all vtable functions' safety contract.
132     let error = unsafe { error.as_ref() };
133     error.error.ext_as_dyn_core_error()
134 }
135 
into_boxed_dyn_core_error<E>( error: OwnedPtr<DynError>, ) -> Result<Box<dyn core::error::Error + Send + Sync + 'static>, OutOfMemory> where E: ErrorExt,136 unsafe fn into_boxed_dyn_core_error<E>(
137     error: OwnedPtr<DynError>,
138 ) -> Result<Box<dyn core::error::Error + Send + Sync + 'static>, OutOfMemory>
139 where
140     E: ErrorExt,
141 {
142     let error = error.cast::<ConcreteError<E>>();
143     // Safety: implied by all vtable functions' safety contract.
144     let error = unsafe { error.into_box() };
145     error.error.ext_into_boxed_dyn_core_error()
146 }
147 
drop_and_deallocate<E>(error: OwnedPtr<DynError>) where E: ErrorExt,148 unsafe fn drop_and_deallocate<E>(error: OwnedPtr<DynError>)
149 where
150     E: ErrorExt,
151 {
152     let error = error.cast::<ConcreteError<E>>();
153     // Safety: implied by all vtable functions' safety contract.
154     let _ = unsafe { error.into_box() };
155 }
156 
move_into<E>(error: OwnedPtr<DynError>, ret_ptr: NonNull<u8>) where E: ErrorExt,157 unsafe fn move_into<E>(error: OwnedPtr<DynError>, ret_ptr: NonNull<u8>)
158 where
159     E: ErrorExt,
160 {
161     let error = error.cast::<ConcreteError<E>>();
162     // Safety: implied by all vtable functions' safety contract.
163     let error = unsafe { error.into_box() };
164     // Safety: Implied by `move`'s additional safety safety requirement.
165     unsafe {
166         error.error.ext_move(ret_ptr);
167     }
168 }
169 
170 #[cfg(feature = "anyhow")]
into_anyhow<E>(error: OwnedPtr<DynError>) -> anyhow::Error where E: ErrorExt,171 unsafe fn into_anyhow<E>(error: OwnedPtr<DynError>) -> anyhow::Error
172 where
173     E: ErrorExt,
174 {
175     let error = error.cast::<ConcreteError<E>>();
176     // Safety: implied by all vtable functions' safety contract.
177     let error = unsafe { error.into_box() };
178     error.error.ext_into_anyhow()
179 }
180