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