1 use crate::error::{Error, ErrorExt, OutOfMemory, Result, boxed::try_new_uninit_box}; 2 use core::any::TypeId; 3 use core::fmt; 4 use core::ptr::NonNull; 5 use std_alloc::boxed::Box; 6 7 mod sealed { 8 use super::*; 9 pub trait Sealed {} 10 impl<T, E> Sealed for Result<T, E> {} 11 impl<T> Sealed for Option<T> {} 12 } 13 14 /// Extension trait to add error context to results. 15 /// 16 /// This extension trait, and its methods, are the primary way to create error 17 /// chains. An error's debug output will include the full chain of 18 /// errors. Errors in these chains are accessible via the 19 /// [`Error::chain`] and [`Error::root_cause`] methods. 20 /// 21 /// After applying error context of type `C`, calling 22 /// [`error.is::<C>()`](Error::is) will return `true` for the new error 23 /// (unless there was a memory allocation failure) in addition to any other 24 /// types `T` for which it was already the case that `error.is::<T>()`. 25 /// 26 /// This boxes the inner `C` type, but if that box allocation fails, then this 27 /// trait's functions return an `Error` where 28 /// [`error.is::<OutOfMemory>()`](OutOfMemory) is true. 29 /// 30 /// # Example 31 /// 32 /// ``` 33 /// # use wasmtime_internal_core::error as wasmtime; 34 /// use wasmtime::{Context as _, Result}; 35 /// # #[cfg(feature = "backtrace")] 36 /// # wasmtime_internal_core::error::disable_backtrace(); 37 /// 38 /// fn u32_to_u8(x: u32) -> Result<u8> { 39 /// let y = u8::try_from(x).with_context(|| { 40 /// format!("failed to convert `{x}` into a `u8` (max = `{}`)", u8::MAX) 41 /// })?; 42 /// Ok(y) 43 /// } 44 /// 45 /// let x = u32_to_u8(42).unwrap(); 46 /// assert_eq!(x, 42); 47 /// 48 /// let error = u32_to_u8(999).unwrap_err(); 49 /// 50 /// // The error is a `String` because of our added context. 51 /// assert!(error.is::<String>()); 52 /// assert_eq!( 53 /// error.to_string(), 54 /// "failed to convert `999` into a `u8` (max = `255`)", 55 /// ); 56 /// 57 /// // But it is also a `TryFromIntError` because of the inner error. 58 /// assert!(error.is::<std::num::TryFromIntError>()); 59 /// assert_eq!( 60 /// error.root_cause().to_string(), 61 /// "out of range integral type conversion attempted", 62 /// ); 63 /// 64 /// // The debug output of the error contains the full error chain. 65 /// assert_eq!( 66 /// format!("{error:?}").trim(), 67 /// r#" 68 /// failed to convert `999` into a `u8` (max = `255`) 69 /// 70 /// Caused by: 71 /// out of range integral type conversion attempted 72 /// "#.trim(), 73 /// ); 74 /// ``` 75 /// 76 /// # Example with `Option<T>` 77 /// 78 /// You can also use this trait to create the initial, root-cause `Error` when a 79 /// fallible function returns an `Option`: 80 /// 81 /// ``` 82 /// # use wasmtime_internal_core as wasmtime; 83 /// use wasmtime::error::{Context as _, Result}; 84 /// 85 /// fn try_get<T>(slice: &[T], i: usize) -> Result<&T> { 86 /// let elem: Option<&T> = slice.get(i); 87 /// elem.with_context(|| { 88 /// format!("out of bounds access: index is {i} but length is {}", slice.len()) 89 /// }) 90 /// } 91 /// 92 /// let arr = [921, 36, 123, 42, 785]; 93 /// 94 /// let x = try_get(&arr, 2).unwrap(); 95 /// assert_eq!(*x, 123); 96 /// 97 /// let error = try_get(&arr, 9999).unwrap_err(); 98 /// assert_eq!( 99 /// error.to_string(), 100 /// "out of bounds access: index is 9999 but length is 5", 101 /// ); 102 /// ``` 103 pub trait Context<T, E>: sealed::Sealed { 104 /// Add additional, already-computed error context to this result. 105 /// 106 /// Because this method requires that the error context is already computed, 107 /// it should only be used when the `context` is already available or is 108 /// effectively a constant. Otherwise, it effectively forces computation of 109 /// the context, even when we aren't on an error path. The 110 /// [`Context::with_context`](Context::with_context) method is 111 /// preferred in these scenarios, as it lazily computes the error context, 112 /// only doing so when we are actually on an error path. context<C>(self, context: C) -> Result<T, Error> where C: fmt::Display + Send + Sync + 'static113 fn context<C>(self, context: C) -> Result<T, Error> 114 where 115 C: fmt::Display + Send + Sync + 'static; 116 117 /// Add additional, lazily-computed error context to this result. 118 /// 119 /// Only invokes `f` to compute the error context when we are actually on an 120 /// error path. Does not invoke `f` if we are not on an error path. with_context<C, F>(self, f: F) -> Result<T, Error> where C: fmt::Display + Send + Sync + 'static, F: FnOnce() -> C121 fn with_context<C, F>(self, f: F) -> Result<T, Error> 122 where 123 C: fmt::Display + Send + Sync + 'static, 124 F: FnOnce() -> C; 125 } 126 127 impl<T, E> Context<T, E> for Result<T, E> 128 where 129 E: core::error::Error + Send + Sync + 'static, 130 { 131 #[inline] context<C>(self, context: C) -> Result<T> where C: fmt::Display + Send + Sync + 'static,132 fn context<C>(self, context: C) -> Result<T> 133 where 134 C: fmt::Display + Send + Sync + 'static, 135 { 136 match self { 137 Ok(x) => Ok(x), 138 Err(e) => Err(Error::new(e).context(context)), 139 } 140 } 141 142 #[inline] with_context<C, F>(self, f: F) -> Result<T> where C: fmt::Display + Send + Sync + 'static, F: FnOnce() -> C,143 fn with_context<C, F>(self, f: F) -> Result<T> 144 where 145 C: fmt::Display + Send + Sync + 'static, 146 F: FnOnce() -> C, 147 { 148 match self { 149 Ok(x) => Ok(x), 150 Err(e) => Err(Error::new(e).context(f())), 151 } 152 } 153 } 154 155 impl<T> Context<T, Error> for Result<T> { context<C>(self, context: C) -> Result<T, Error> where C: fmt::Display + Send + Sync + 'static,156 fn context<C>(self, context: C) -> Result<T, Error> 157 where 158 C: fmt::Display + Send + Sync + 'static, 159 { 160 match self { 161 Ok(x) => Ok(x), 162 Err(e) => Err(e.context(context)), 163 } 164 } 165 with_context<C, F>(self, f: F) -> Result<T, Error> where C: fmt::Display + Send + Sync + 'static, F: FnOnce() -> C,166 fn with_context<C, F>(self, f: F) -> Result<T, Error> 167 where 168 C: fmt::Display + Send + Sync + 'static, 169 F: FnOnce() -> C, 170 { 171 match self { 172 Ok(x) => Ok(x), 173 Err(e) => Err(e.context(f())), 174 } 175 } 176 } 177 178 impl<T> Context<T, core::convert::Infallible> for Option<T> { context<C>(self, context: C) -> Result<T> where C: fmt::Display + Send + Sync + 'static,179 fn context<C>(self, context: C) -> Result<T> 180 where 181 C: fmt::Display + Send + Sync + 'static, 182 { 183 match self { 184 Some(x) => Ok(x), 185 None => Err(Error::from_error_ext(ContextError { 186 context, 187 error: None, 188 })), 189 } 190 } 191 with_context<C, F>(self, f: F) -> Result<T> where C: fmt::Display + Send + Sync + 'static, F: FnOnce() -> C,192 fn with_context<C, F>(self, f: F) -> Result<T> 193 where 194 C: fmt::Display + Send + Sync + 'static, 195 F: FnOnce() -> C, 196 { 197 match self { 198 Some(x) => Ok(x), 199 None => Err(Error::from_error_ext(ContextError { 200 context: f(), 201 error: None, 202 })), 203 } 204 } 205 } 206 207 // NB: The `repr(C)` is required for safety of the `ErrorExt::ext_is` 208 // implementation and the casts that are performed using that method's 209 // return value. 210 #[repr(C)] 211 pub(crate) struct ContextError<C> { 212 // NB: This must be the first field for safety of the `ErrorExt::ext_is` 213 // implementation and the casts that are performed using that method's 214 // return value. 215 pub(crate) context: C, 216 217 pub(crate) error: Option<Error>, 218 } 219 220 impl<C> fmt::Debug for ContextError<C> 221 where 222 C: fmt::Display, 223 { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result224 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 225 fmt::Display::fmt(self, f) 226 } 227 } 228 229 impl<C> fmt::Display for ContextError<C> 230 where 231 C: fmt::Display, 232 { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result233 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 234 self.context.fmt(f) 235 } 236 } 237 238 impl<C> core::error::Error for ContextError<C> 239 where 240 C: fmt::Display + Send + Sync + 'static, 241 { source(&self) -> Option<&(dyn core::error::Error + 'static)>242 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { 243 let source = self.ext_source()?; 244 Some(source.inner.as_dyn_core_error()) 245 } 246 } 247 248 unsafe impl<C> ErrorExt for ContextError<C> 249 where 250 C: fmt::Display + Send + Sync + 'static, 251 { ext_as_dyn_core_error(&self) -> &(dyn core::error::Error + Send + Sync + 'static)252 fn ext_as_dyn_core_error(&self) -> &(dyn core::error::Error + Send + Sync + 'static) { 253 self 254 } 255 ext_into_boxed_dyn_core_error( self, ) -> Result<Box<dyn core::error::Error + Send + Sync + 'static>, OutOfMemory>256 fn ext_into_boxed_dyn_core_error( 257 self, 258 ) -> Result<Box<dyn core::error::Error + Send + Sync + 'static>, OutOfMemory> { 259 let boxed = try_new_uninit_box()?; 260 Ok(Box::write(boxed, self) as _) 261 } 262 ext_source(&self) -> Option<&Error>263 fn ext_source(&self) -> Option<&Error> { 264 self.error.as_ref() 265 } 266 ext_source_mut(&mut self) -> Option<&mut Error>267 fn ext_source_mut(&mut self) -> Option<&mut Error> { 268 self.error.as_mut() 269 } 270 ext_is(&self, type_id: TypeId) -> bool271 fn ext_is(&self, type_id: TypeId) -> bool { 272 // NB: need to check type id of `C`, not `Self` aka 273 // `ContextError<C>`. 274 type_id == TypeId::of::<C>() 275 } 276 ext_move(self, to: NonNull<u8>)277 unsafe fn ext_move(self, to: NonNull<u8>) { 278 // Safety: implied by this trait method's contract. 279 unsafe { 280 to.cast::<C>().write(self.context); 281 } 282 } 283 284 #[cfg(feature = "backtrace")] take_backtrace(&mut self) -> Option<std::backtrace::Backtrace>285 fn take_backtrace(&mut self) -> Option<std::backtrace::Backtrace> { 286 self.error.as_mut()?.take_backtrace() 287 } 288 289 #[cfg(feature = "anyhow")] ext_into_anyhow(mut self) -> anyhow::Error290 fn ext_into_anyhow(mut self) -> anyhow::Error { 291 match self.error.take() { 292 Some(error) => anyhow::Error::from(error).context(self.context), 293 None => anyhow::Error::msg(self), 294 } 295 } 296 } 297