Skip to main content

wasmtime_internal_core/error/
context.rs

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