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