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