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