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