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