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. 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 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 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 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 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 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 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 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 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 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")] 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