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