Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/core/src/error/macros.rs
3071 views
1
//! Macro definitions and the private runtime functions used in their generated
2
//! code.
3
4
// Items used by macro-generated code.
5
pub use core::format_args;
6
pub use core::result::Result::Err;
7
8
use super::{Error, OutOfMemory};
9
use core::fmt::{self, write};
10
use std_alloc::string::String;
11
12
/// Construct an [`Error`] via string formatting or another error.
13
///
14
/// Like `anyhow::format_err!` or `anyhow::anyhow!` but for
15
/// [`wasmtime::Error`](Error).
16
///
17
/// # String Formatting
18
///
19
/// When a string literal is the first argument, it is interpreted as a format
20
/// string template and the rest of the arguments are format arguments:
21
///
22
/// ```
23
/// # use wasmtime_internal_core::error as wasmtime;
24
/// use wasmtime::{format_err, Error};
25
///
26
/// let x = 42;
27
/// let error: Error = format_err!("x is {x}");
28
/// assert_eq!(error.to_string(), "x is 42");
29
///
30
/// let error: Error = format_err!("x / 2 is {}", x / 2);
31
/// assert_eq!(error.to_string(), "x / 2 is 21");
32
///
33
/// let error: Error = format_err!("x + 1 is {y}", y = x + 1);
34
/// assert_eq!(error.to_string(), "x + 1 is 43");
35
/// ```
36
///
37
/// # From Another Error
38
///
39
/// When a string literal is not the first argument, then it is treated as a
40
/// foreign error and is converted into an [`Error`]. The argument
41
/// must be of a type that can be passed to either [`Error::new`] or
42
/// [`Error::msg`].
43
///
44
/// ```
45
/// # fn _foo() {
46
/// #![cfg(feature = "std")]
47
/// # use wasmtime_internal_core::error as wasmtime;
48
/// use std::fmt;
49
/// use wasmtime::{format_err, Error};
50
///
51
/// #[derive(Debug)]
52
/// struct SomeOtherError(u32);
53
///
54
/// impl fmt::Display for SomeOtherError {
55
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56
/// write!(f, "some other error (code {})", self.0)
57
/// }
58
/// }
59
///
60
/// impl std::error::Error for SomeOtherError {}
61
///
62
/// let error: Error = format_err!(SomeOtherError(36));
63
/// assert!(error.is::<SomeOtherError>());
64
/// assert_eq!(error.to_string(), "some other error (code 36)");
65
/// # }
66
/// ```
67
///
68
/// # From an `anyhow::Error`
69
///
70
/// The `format_err!` macro can always convert an `anyhow::Error` into a
71
/// `wasmtime::Error`, but when the `"anyhow"` cargo feature is enabled the
72
/// resulting error will also return true for
73
/// [`error.is::<anyhow::Error>()`](Error::is) invocations.
74
///
75
/// ```
76
/// # fn _foo() {
77
/// #![cfg(feature = "anyhow")]
78
/// # use wasmtime_internal_core::error as wasmtime;
79
/// use wasmtime::format_err;
80
///
81
/// let anyhow_error: anyhow::Error = anyhow::anyhow!("aw crap");
82
/// let wasmtime_error: wasmtime::Error = format_err!(anyhow_error);
83
/// assert!(wasmtime_error.is::<anyhow::Error>());
84
/// # }
85
/// ```
86
#[macro_export]
87
macro_rules! format_err {
88
// Format-style invocation without explicit format arguments.
89
( $message:literal $(,)? ) => {
90
$crate::error::Error::from_format_args($crate::error::macros::format_args!($message))
91
};
92
93
// Format-style invocation with explicit format arguments.
94
( $message:literal , $( $args:tt )* ) => {
95
$crate::error::Error::from_format_args($crate::error::macros::format_args!($message , $( $args )* ))
96
};
97
98
// Do either `Error::new($error)` or `Error::msg($error)` depending on
99
// whether `$error` implements `core::error::Error` or not.
100
( $error:expr $(,)? ) => {{
101
use $crate::error::macros::ctor_specialization::*;
102
let error = $error;
103
(&&&error).wasmtime_error_choose_ctor().construct(error)
104
}};
105
}
106
107
/// Identical to the [`format_err!`] macro.
108
///
109
/// This is provided for API compatibility with the `anyhow` crate, but you
110
/// should prefer using `format_err!` instead.
111
#[macro_export]
112
#[deprecated = "Use `format_err!(...)` instead"]
113
macro_rules! anyhow {
114
( $( $args:tt )* ) => {
115
$crate::error::format_err!( $( $args )* )
116
};
117
}
118
119
/// Early exit from the current function with an error.
120
///
121
/// This helper is equivalent to `return Err(format_err!(...))`.
122
///
123
/// See the docs for the [`format_err!`] macro for details on
124
/// the kinds of errors that can be constructed.
125
///
126
/// Like `anyhow::bail!` but for [`wasmtime::Error`](Error).
127
///
128
/// # Example
129
///
130
/// ```
131
/// # use wasmtime_internal_core::error as wasmtime;
132
/// use wasmtime::{bail, Result};
133
///
134
/// fn error_on_none(option: Option<u32>) -> Result<u32> {
135
/// match option {
136
/// None => bail!("`error_on_none` got `None`!"),
137
/// Some(x) => Ok(x),
138
/// }
139
/// }
140
///
141
/// let x = error_on_none(Some(42)).unwrap();
142
/// assert_eq!(x, 42);
143
///
144
/// let error = error_on_none(None).unwrap_err();
145
/// assert_eq!(
146
/// error.to_string(),
147
/// "`error_on_none` got `None`!",
148
/// );
149
/// ```
150
#[macro_export]
151
macro_rules! bail {
152
( $($args:tt)* ) => {{
153
return $crate::error::macros::Err($crate::error::format_err!( $( $args )* ));
154
}};
155
}
156
157
/// Ensure that a condition holds true, or else early exit from the current
158
/// function with an error.
159
///
160
/// `ensure!(condition, ...)` is equivalent to the following:
161
///
162
/// ```ignore
163
/// if !condition {
164
/// return Err(format_err!(...));
165
/// }
166
/// ```
167
///
168
/// Like `anyhow::ensure!` but for [`wasmtime::Error`](Error).
169
///
170
/// # Example
171
///
172
/// ```rust
173
/// # use wasmtime_internal_core::error as wasmtime;
174
/// use wasmtime::{ensure, Result};
175
///
176
/// fn checked_div(a: u32, b: u32) -> Result<u32> {
177
/// ensure!(b != 0, "cannot divide by zero: {a} / {b}");
178
/// Ok(a / b)
179
/// }
180
///
181
/// let x = checked_div(6, 2).unwrap();
182
/// assert_eq!(x, 3);
183
///
184
/// let error = checked_div(9, 0).unwrap_err();
185
/// assert_eq!(
186
/// error.to_string(),
187
/// "cannot divide by zero: 9 / 0",
188
/// );
189
/// ```
190
#[macro_export]
191
macro_rules! ensure {
192
( $condition:expr ) => {{
193
$crate::error::ensure!($condition, concat!("Condition failed: `", stringify!($condition), "`"))
194
}};
195
196
( $condition:expr , $( $args:tt )* ) => {{
197
if $crate::error::macros::ensure::not($condition) {
198
$crate::error::bail!( $( $args )* );
199
}
200
}};
201
}
202
203
/// We don't have specialization in stable Rust, so do a poor-person's
204
/// equivalent by hacking Rust's method name resolution and auto-deref. Given
205
/// that we have `n` versions of the "same" method, we do the following:
206
///
207
/// * We define `n` different traits, which each define the same trait method
208
/// name. The method need not have the same type across traits, but each must
209
/// type-check when chosen by method resolution at a particular call site.
210
///
211
/// * We implement each trait for an `i`-deep borrow of the type(s) we want to
212
/// specialize the `i`th implementation on, for example:
213
///
214
/// ```ignore
215
/// impl Specialization1 for &MyType { ... }
216
/// impl Specialization2 for &&OtherType { ... }
217
/// impl Specialization3 for &&&AnotherType { ... }
218
/// ```
219
///
220
/// * Call sites must have all specialization traits in scope and must borrow
221
/// the receiver `n` times before calling the method. Rust's method name
222
/// resolution will choose the method with the least number of references that
223
/// is well-typed. Therefore, specialization implementations for lower numbers
224
/// of borrows are preferred over those with higher numbers of borrows when
225
/// specializations overlap. For example, if both `<&&&T as
226
/// Specialization3>::method` and `<&T as Specialization1>::method` are
227
/// well-typed at the trait method call site `(&&&&&t).method()`, then
228
/// `Specialization1` will be prioritized over `Specialization3`.
229
///
230
/// In our specific case here of choosing an `Error` constructor, we have
231
/// three specializations:
232
///
233
/// 1. For `anyhow::Error`, we want to use the `Error::from_anyhow` constructor.
234
///
235
/// 2. When the type implements `core::error::Error`, we want to use the
236
/// `Error::new` constructor, which will preserve
237
/// `core::error::Error::source` chains.
238
///
239
/// 3. Otherwise, we want to use the `Error::msg` constructor.
240
///
241
/// The `*CtorTrait`s are our `n` specialization traits. Their
242
/// `wasmtime_error_choose_ctor` methods will return different types, each of
243
/// which is a dispatcher to their associated constructor. Those dispatchers
244
/// each have a constructor signature that is syntactically identical, but only
245
/// guaranteed to be well-typed based on the specialization that we did by
246
/// getting the dispatcher in the first place.
247
pub mod ctor_specialization {
248
use super::*;
249
250
#[cfg(feature = "anyhow")]
251
pub use anyhow::*;
252
#[cfg(feature = "anyhow")]
253
mod anyhow {
254
use super::*;
255
256
pub trait AnyhowCtorTrait {
257
#[inline]
258
fn wasmtime_error_choose_ctor(&self) -> AnyhowCtor {
259
AnyhowCtor
260
}
261
}
262
263
impl AnyhowCtorTrait for &anyhow::Error {}
264
265
pub struct AnyhowCtor;
266
267
impl AnyhowCtor {
268
#[inline]
269
pub fn construct(&self, anyhow_error: ::anyhow::Error) -> Error {
270
Error::from_anyhow(anyhow_error)
271
}
272
}
273
}
274
275
pub trait NewCtorTrait {
276
#[inline]
277
fn wasmtime_error_choose_ctor(&self) -> NewCtor {
278
NewCtor
279
}
280
}
281
282
impl<E: core::error::Error + Send + Sync + 'static> NewCtorTrait for &&E {}
283
284
pub struct NewCtor;
285
286
impl NewCtor {
287
#[inline]
288
pub fn construct<E>(&self, error: E) -> Error
289
where
290
E: core::error::Error + Send + Sync + 'static,
291
{
292
Error::new(error)
293
}
294
}
295
296
pub trait MsgCtorTrait {
297
#[inline]
298
fn wasmtime_error_choose_ctor(&self) -> MsgCtor {
299
MsgCtor
300
}
301
}
302
303
impl<M: fmt::Debug + fmt::Display + Send + Sync + 'static> MsgCtorTrait for &&&M {}
304
305
pub struct MsgCtor;
306
307
impl MsgCtor {
308
#[inline]
309
pub fn construct<M>(&self, message: M) -> Error
310
where
311
M: fmt::Debug + fmt::Display + Send + Sync + 'static,
312
{
313
Error::msg(message)
314
}
315
}
316
}
317
318
/// Runtime code for creating an `Error` from format arguments, handling OOM in
319
/// the process.
320
pub mod formatting {
321
use super::*;
322
323
#[derive(Default)]
324
struct Formatter {
325
message: String,
326
oom: Option<OutOfMemory>,
327
}
328
329
impl fmt::Write for Formatter {
330
fn write_str(&mut self, s: &str) -> fmt::Result {
331
match self.message.try_reserve(s.len()) {
332
Ok(()) => {
333
self.message.push_str(s);
334
Ok(())
335
}
336
Err(_) => {
337
self.oom = Some(OutOfMemory::new(self.message.len() + s.len()));
338
Err(fmt::Error)
339
}
340
}
341
}
342
}
343
344
impl Error {
345
/// Construct an `Error` from format arguments.
346
///
347
/// Only for use by the `format_err!` macro.
348
#[doc(hidden)]
349
pub fn from_format_args(args: fmt::Arguments<'_>) -> Self {
350
if let Some(s) = args.as_str() {
351
return Self::msg(s);
352
}
353
354
let mut f = Formatter::default();
355
match write(&mut f, args) {
356
Ok(()) => {
357
debug_assert!(f.oom.is_none());
358
Error::msg(f.message)
359
}
360
Err(fmt_error) => match f.oom {
361
Some(oom) => Error::new(oom),
362
None => Error::new(fmt_error),
363
},
364
}
365
}
366
}
367
}
368
369
pub mod ensure {
370
/// Convenience trait to enable `ensure!(cond, ...)` to work when `cond` is of
371
/// type `&bool`, not just `bool`. Saves useless rewrite-to-`*cond` busywork and
372
/// matches `anyhow`'s behavior.
373
pub trait ToBool {
374
fn to_bool(self) -> bool;
375
}
376
377
impl ToBool for bool {
378
#[inline]
379
fn to_bool(self) -> bool {
380
self
381
}
382
}
383
384
impl ToBool for &bool {
385
#[inline]
386
fn to_bool(self) -> bool {
387
*self
388
}
389
}
390
391
#[inline]
392
pub fn not(b: impl ToBool) -> bool {
393
!b.to_bool()
394
}
395
}
396
397