Rename core types

This commit is contained in:
Jane Lusby 2020-02-27 13:04:19 -08:00
parent bac602c301
commit 86da223cde
22 changed files with 227 additions and 227 deletions

View File

@ -1,17 +1,17 @@
[package]
name = "anyhow"
name = "eyre"
version = "1.0.26" # remember to update html_root_url
authors = ["David Tolnay <dtolnay@gmail.com>"]
edition = "2018"
license = "MIT OR Apache-2.0"
description = "Flexible concrete Error type built on std::error::Error"
repository = "https://github.com/dtolnay/anyhow"
documentation = "https://docs.rs/anyhow"
repository = "https://github.com/dtolnay/eyre"
documentation = "https://docs.rs/eyre"
readme = "README.md"
categories = ["rust-patterns"]
[badges]
travis-ci = { repository = "dtolnay/anyhow" }
travis-ci = { repository = "dtolnay/eyre" }
[features]
default = ["std"]

View File

@ -1,18 +1,18 @@
Anyhow&ensp;¯\\\_(ツ)\_/¯
=========================
[![Build Status](https://api.travis-ci.com/dtolnay/anyhow.svg?branch=master)](https://travis-ci.com/dtolnay/anyhow)
[![Latest Version](https://img.shields.io/crates/v/anyhow.svg)](https://crates.io/crates/anyhow)
[![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/anyhow)
[![Build Status](https://api.travis-ci.com/dtolnay/eyre.svg?branch=master)](https://travis-ci.com/dtolnay/eyre)
[![Latest Version](https://img.shields.io/crates/v/eyre.svg)](https://crates.io/crates/eyre)
[![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/eyre)
This library provides [`anyhow::Error`][Error], a trait object based error type
This library provides [`eyre::Error`][Error], a trait object based error type
for easy idiomatic error handling in Rust applications.
[Error]: https://docs.rs/anyhow/1.0/anyhow/struct.Error.html
[Error]: https://docs.rs/eyre/1.0/eyre/struct.Error.html
```toml
[dependencies]
anyhow = "1.0"
eyre = "1.0"
```
*Compiler support: requires rustc 1.34+*
@ -21,14 +21,14 @@ anyhow = "1.0"
## Details
- Use `Result<T, anyhow::Error>`, or equivalently `anyhow::Result<T>`, as the
- Use `Result<T, eyre::Error>`, or equivalently `eyre::Result<T>`, as the
return type of any fallible function.
Within the function, use `?` to easily propagate any error that implements the
`std::error::Error` trait.
```rust
use anyhow::Result;
use eyre::Result;
fn get_cluster_info() -> Result<ClusterMap> {
let config = std::fs::read_to_string("cluster.json")?;
@ -43,7 +43,7 @@ anyhow = "1.0"
application was in the middle of.
```rust
use anyhow::{Context, Result};
use eyre::{Context, Result};
fn main() -> Result<()> {
...
@ -98,11 +98,11 @@ anyhow = "1.0"
}
```
- One-off error messages can be constructed using the `anyhow!` macro, which
supports string interpolation and produces an `anyhow::Error`.
- One-off error messages can be constructed using the `eyre!` macro, which
supports string interpolation and produces an `eyre::Error`.
```rust
return Err(anyhow!("Missing attribute: {}", missing));
return Err(eyre!("Missing attribute: {}", missing));
```
<br>
@ -115,7 +115,7 @@ Cargo.toml. A global allocator is required.
```toml
[dependencies]
anyhow = { version = "1.0", default-features = false }
eyre = { version = "1.0", default-features = false }
```
Since the `?`-based error conversions would normally rely on the
@ -127,7 +127,7 @@ type inside a function that returns Anyhow's error type.
## Comparison to failure
The `anyhow::Error` type works something like `failure::Error`, but unlike
The `eyre::Error` type works something like `failure::Error`, but unlike
failure ours is built around the standard library's `std::error::Error` trait
rather than a separate trait `failure::Fail`. The standard library has adopted
the necessary improvements for this to be possible as part of [RFC 2504].

View File

@ -5,7 +5,7 @@ use std::process::{Command, ExitStatus};
// This code exercises the surface area that we expect of the std Backtrace
// type. If the current toolchain is able to compile it, we go ahead and use
// backtrace in anyhow.
// backtrace in eyre.
const PROBE: &str = r#"
#![feature(backtrace)]
#![allow(dead_code)]
@ -51,7 +51,7 @@ fn compile_probe() -> Option<ExitStatus> {
fs::write(&probefile, PROBE).ok()?;
Command::new(rustc)
.arg("--edition=2018")
.arg("--crate-name=anyhow_build")
.arg("--crate-name=eyre_build")
.arg("--crate-type=lib")
.arg("--emit=metadata")
.arg("--out-dir")

View File

@ -1,5 +1,5 @@
use crate::error::ContextError;
use crate::{Context, Error, StdError};
use crate::{Context, ErrReport, StdError};
use core::convert::Infallible;
use core::fmt::{self, Debug, Display, Write};
@ -10,7 +10,7 @@ mod ext {
use super::*;
pub trait StdError {
fn ext_context<C>(self, context: C) -> Error
fn ext_context<C>(self, context: C) -> ErrReport
where
C: Display + Send + Sync + 'static;
}
@ -20,17 +20,17 @@ mod ext {
where
E: std::error::Error + Send + Sync + 'static,
{
fn ext_context<C>(self, context: C) -> Error
fn ext_context<C>(self, context: C) -> ErrReport
where
C: Display + Send + Sync + 'static,
{
let backtrace = backtrace_if_absent!(self);
Error::from_context(context, self, backtrace)
ErrReport::from_context(context, self, backtrace)
}
}
impl StdError for Error {
fn ext_context<C>(self, context: C) -> Error
impl StdError for ErrReport {
fn ext_context<C>(self, context: C) -> ErrReport
where
C: Display + Send + Sync + 'static,
{
@ -43,14 +43,14 @@ impl<T, E> Context<T, E> for Result<T, E>
where
E: ext::StdError + Send + Sync + 'static,
{
fn context<C>(self, context: C) -> Result<T, Error>
fn context<C>(self, context: C) -> Result<T, ErrReport>
where
C: Display + Send + Sync + 'static,
{
self.map_err(|error| error.ext_context(context))
}
fn with_context<C, F>(self, context: F) -> Result<T, Error>
fn with_context<C, F>(self, context: F) -> Result<T, ErrReport>
where
C: Display + Send + Sync + 'static,
F: FnOnce() -> C,
@ -62,7 +62,7 @@ where
/// ```
/// # type T = ();
/// #
/// use anyhow::{Context, Result};
/// use eyre::{Context, Result};
///
/// fn maybe_get() -> Option<T> {
/// # const IGNORE: &str = stringify! {
@ -80,19 +80,19 @@ where
/// }
/// ```
impl<T> Context<T, Infallible> for Option<T> {
fn context<C>(self, context: C) -> Result<T, Error>
fn context<C>(self, context: C) -> Result<T, ErrReport>
where
C: Display + Send + Sync + 'static,
{
self.ok_or_else(|| Error::from_display(context, backtrace!()))
self.ok_or_else(|| ErrReport::from_display(context, backtrace!()))
}
fn with_context<C, F>(self, context: F) -> Result<T, Error>
fn with_context<C, F>(self, context: F) -> Result<T, ErrReport>
where
C: Display + Send + Sync + 'static,
F: FnOnce() -> C,
{
self.ok_or_else(|| Error::from_display(context(), backtrace!()))
self.ok_or_else(|| ErrReport::from_display(context(), backtrace!()))
}
}
@ -133,7 +133,7 @@ where
}
}
impl<C> StdError for ContextError<C, Error>
impl<C> StdError for ContextError<C, ErrReport>
where
C: Display,
{

View File

@ -1,7 +1,7 @@
use crate::alloc::Box;
use crate::backtrace::Backtrace;
use crate::chain::Chain;
use crate::{Error, StdError};
use crate::{ErrReport, StdError};
use core::any::TypeId;
use core::fmt::{self, Debug, Display};
use core::mem::{self, ManuallyDrop};
@ -10,10 +10,10 @@ use core::ptr::{self, NonNull};
#[cfg(feature = "std")]
use core::ops::{Deref, DerefMut};
impl Error {
impl ErrReport {
/// Create a new error object from any error type.
///
/// The error type must be threadsafe and `'static`, so that the `Error`
/// The error type must be threadsafe and `'static`, so that the `ErrReport`
/// will be as well.
///
/// If the error type does not provide a backtrace, a backtrace will be
@ -24,18 +24,18 @@ impl Error {
E: StdError + Send + Sync + 'static,
{
let backtrace = backtrace_if_absent!(error);
Error::from_std(error, backtrace)
ErrReport::from_std(error, backtrace)
}
/// Create a new error object from a printable error message.
///
/// If the argument implements std::error::Error, prefer `Error::new`
/// If the argument implements std::error::Error, prefer `ErrReport::new`
/// instead which preserves the underlying error's cause chain and
/// backtrace. If the argument may or may not implement std::error::Error
/// now or in the future, use `anyhow!(err)` which handles either way
/// now or in the future, use `eyre!(err)` which handles either way
/// correctly.
///
/// `Error::msg("...")` is equivalent to `anyhow!("...")` but occasionally
/// `ErrReport::msg("...")` is equivalent to `eyre!("...")` but occasionally
/// convenient in places where a function is preferable over a macro, such
/// as iterator or stream combinators:
///
@ -50,7 +50,7 @@ impl Error {
/// #
/// # use ffi::{Input, Output};
/// #
/// use anyhow::{Error, Result};
/// use eyre::{ErrReport, Result};
/// use futures::stream::{Stream, StreamExt, TryStreamExt};
///
/// async fn demo<S>(stream: S) -> Result<Vec<Output>>
@ -59,7 +59,7 @@ impl Error {
/// {
/// stream
/// .then(ffi::do_some_work) // returns Result<Output, &str>
/// .map_err(Error::msg)
/// .map_err(ErrReport::msg)
/// .try_collect()
/// .await
/// }
@ -68,7 +68,7 @@ impl Error {
where
M: Display + Debug + Send + Sync + 'static,
{
Error::from_adhoc(message, backtrace!())
ErrReport::from_adhoc(message, backtrace!())
}
#[cfg(feature = "std")]
@ -87,7 +87,7 @@ impl Error {
};
// Safety: passing vtable that operates on the right type E.
unsafe { Error::construct(error, vtable, backtrace) }
unsafe { ErrReport::construct(error, vtable, backtrace) }
}
pub(crate) fn from_adhoc<M>(message: M, backtrace: Option<Backtrace>) -> Self
@ -108,7 +108,7 @@ impl Error {
// Safety: MessageError is repr(transparent) so it is okay for the
// vtable to allow casting the MessageError<M> to M.
unsafe { Error::construct(error, vtable, backtrace) }
unsafe { ErrReport::construct(error, vtable, backtrace) }
}
pub(crate) fn from_display<M>(message: M, backtrace: Option<Backtrace>) -> Self
@ -129,7 +129,7 @@ impl Error {
// Safety: DisplayError is repr(transparent) so it is okay for the
// vtable to allow casting the DisplayError<M> to M.
unsafe { Error::construct(error, vtable, backtrace) }
unsafe { ErrReport::construct(error, vtable, backtrace) }
}
#[cfg(feature = "std")]
@ -151,7 +151,7 @@ impl Error {
};
// Safety: passing vtable that operates on the right type.
unsafe { Error::construct(error, vtable, backtrace) }
unsafe { ErrReport::construct(error, vtable, backtrace) }
}
#[cfg(feature = "std")]
@ -173,7 +173,7 @@ impl Error {
// Safety: BoxedError is repr(transparent) so it is okay for the vtable
// to allow casting to Box<dyn StdError + Send + Sync>.
unsafe { Error::construct(error, vtable, backtrace) }
unsafe { ErrReport::construct(error, vtable, backtrace) }
}
// Takes backtrace as argument rather than capturing it here so that the
@ -202,7 +202,7 @@ impl Error {
// caller rather than a builtin fat pointer vtable.
let erased = mem::transmute::<Box<ErrorImpl<E>>, Box<ErrorImpl<()>>>(inner);
let inner = ManuallyDrop::new(erased);
Error { inner }
ErrReport { inner }
}
/// Wrap the error value with additional context.
@ -232,7 +232,7 @@ impl Error {
/// # }
/// # }
/// #
/// use anyhow::Result;
/// use eyre::Result;
/// use std::fs::File;
/// use std::path::Path;
///
@ -255,7 +255,7 @@ impl Error {
/// "only the first {} lines of {} are valid",
/// error.line, path.as_ref().display(),
/// );
/// anyhow::Error::new(error).context(context)
/// eyre::ErrReport::new(error).context(context)
/// })
/// }
/// ```
@ -263,29 +263,29 @@ impl Error {
where
C: Display + Send + Sync + 'static,
{
let error: ContextError<C, Error> = ContextError {
let error: ContextError<C, ErrReport> = ContextError {
context,
error: self,
};
let vtable = &ErrorVTable {
object_drop: object_drop::<ContextError<C, Error>>,
object_ref: object_ref::<ContextError<C, Error>>,
object_drop: object_drop::<ContextError<C, ErrReport>>,
object_ref: object_ref::<ContextError<C, ErrReport>>,
#[cfg(feature = "std")]
object_mut: object_mut::<ContextError<C, Error>>,
object_boxed: object_boxed::<ContextError<C, Error>>,
object_mut: object_mut::<ContextError<C, ErrReport>>,
object_boxed: object_boxed::<ContextError<C, ErrReport>>,
object_downcast: context_chain_downcast::<C>,
object_drop_rest: context_chain_drop_rest::<C>,
};
// As the cause is anyhow::Error, we already have a backtrace for it.
// As the cause is eyre::ErrReport, we already have a backtrace for it.
let backtrace = None;
// Safety: passing vtable that operates on the right type.
unsafe { Error::construct(error, vtable, backtrace) }
unsafe { ErrReport::construct(error, vtable, backtrace) }
}
/// Get the backtrace for this Error.
/// Get the backtrace for this ErrReport.
///
/// Backtraces are only available on the nightly channel. Tracking issue:
/// [rust-lang/rust#53487][tracking].
@ -301,7 +301,7 @@ impl Error {
self.inner.backtrace()
}
/// An iterator of the chain of source errors contained by this Error.
/// An iterator of the chain of source errors contained by this ErrReport.
///
/// This iterator will visit every error in the cause chain of this error
/// object, beginning with the error that this error object was created
@ -310,10 +310,10 @@ impl Error {
/// # Example
///
/// ```
/// use anyhow::Error;
/// use eyre::ErrReport;
/// use std::io;
///
/// pub fn underlying_io_error_kind(error: &Error) -> Option<io::ErrorKind> {
/// pub fn underlying_io_error_kind(error: &ErrReport) -> Option<io::ErrorKind> {
/// for cause in error.chain() {
/// if let Some(io_error) = cause.downcast_ref::<io::Error>() {
/// return Some(io_error.kind());
@ -331,7 +331,7 @@ impl Error {
/// cause's cause etc.
///
/// The root cause is the last error in the iterator produced by
/// [`chain()`][Error::chain].
/// [`chain()`][ErrReport::chain].
#[cfg(feature = "std")]
pub fn root_cause(&self) -> &(dyn StdError + 'static) {
let mut chain = self.chain();
@ -379,7 +379,7 @@ impl Error {
let error = ptr::read(addr.cast::<E>().as_ptr());
// Read Box<ErrorImpl<()>> from self. Can't move it out because
// Error has a Drop impl which we want to not run.
// ErrReport has a Drop impl which we want to not run.
let inner = ptr::read(&outer.inner);
let erased = ManuallyDrop::into_inner(inner);
@ -395,7 +395,7 @@ impl Error {
/// # Example
///
/// ```
/// # use anyhow::anyhow;
/// # use eyre::eyre;
/// # use std::fmt::{self, Display};
/// # use std::task::Poll;
/// #
@ -414,7 +414,7 @@ impl Error {
/// #
/// # const REDACTED_CONTENT: () = ();
/// #
/// # let error = anyhow!("...");
/// # let error = eyre!("...");
/// # let root_cause = &error;
/// #
/// # let ret =
@ -455,18 +455,18 @@ impl Error {
}
#[cfg(feature = "std")]
impl<E> From<E> for Error
impl<E> From<E> for ErrReport
where
E: StdError + Send + Sync + 'static,
{
fn from(error: E) -> Self {
let backtrace = backtrace_if_absent!(error);
Error::from_std(error, backtrace)
ErrReport::from_std(error, backtrace)
}
}
#[cfg(feature = "std")]
impl Deref for Error {
impl Deref for ErrReport {
type Target = dyn StdError + Send + Sync + 'static;
fn deref(&self) -> &Self::Target {
@ -475,25 +475,25 @@ impl Deref for Error {
}
#[cfg(feature = "std")]
impl DerefMut for Error {
impl DerefMut for ErrReport {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner.error_mut()
}
}
impl Display for Error {
impl Display for ErrReport {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
self.inner.display(formatter)
}
}
impl Debug for Error {
impl Debug for ErrReport {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
self.inner.debug(formatter)
}
}
impl Drop for Error {
impl Drop for ErrReport {
fn drop(&mut self) {
unsafe {
// Read Box<ErrorImpl<()>> from self.
@ -622,24 +622,24 @@ where
}
}
// Safety: requires layout of *e to match ErrorImpl<ContextError<C, Error>>.
// Safety: requires layout of *e to match ErrorImpl<ContextError<C, ErrReport>>.
unsafe fn context_chain_downcast<C>(e: &ErrorImpl<()>, target: TypeId) -> Option<NonNull<()>>
where
C: 'static,
{
if TypeId::of::<C>() == target {
let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, Error>>;
let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, ErrReport>>;
let addr = &(*unerased)._object.context as *const C as *mut ();
Some(NonNull::new_unchecked(addr))
} else {
// Recurse down the context chain per the inner error's vtable.
let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, Error>>;
let unerased = e as *const ErrorImpl<()> as *const ErrorImpl<ContextError<C, ErrReport>>;
let source = &(*unerased)._object.error;
(source.inner.vtable.object_downcast)(&source.inner, target)
}
}
// Safety: requires layout of *e to match ErrorImpl<ContextError<C, Error>>.
// Safety: requires layout of *e to match ErrorImpl<ContextError<C, ErrReport>>.
unsafe fn context_chain_drop_rest<C>(e: Box<ErrorImpl<()>>, target: TypeId)
where
C: 'static,
@ -649,14 +649,14 @@ where
if TypeId::of::<C>() == target {
let unerased = mem::transmute::<
Box<ErrorImpl<()>>,
Box<ErrorImpl<ContextError<ManuallyDrop<C>, Error>>>,
Box<ErrorImpl<ContextError<ManuallyDrop<C>, ErrReport>>>,
>(e);
// Drop the entire rest of the data structure rooted in the next Error.
// Drop the entire rest of the data structure rooted in the next ErrReport.
drop(unerased);
} else {
let unerased = mem::transmute::<
Box<ErrorImpl<()>>,
Box<ErrorImpl<ContextError<C, ManuallyDrop<Error>>>>,
Box<ErrorImpl<ContextError<C, ManuallyDrop<ErrReport>>>>,
>(e);
// Read out a ManuallyDrop<Box<ErrorImpl<()>>> from the next error.
let inner = ptr::read(&unerased._object.error.inner);
@ -756,12 +756,12 @@ where
}
}
impl From<Error> for Box<dyn StdError + Send + Sync + 'static> {
fn from(error: Error) -> Self {
impl From<ErrReport> for Box<dyn StdError + Send + Sync + 'static> {
fn from(error: ErrReport) -> Self {
let outer = ManuallyDrop::new(error);
unsafe {
// Read Box<ErrorImpl<()>> from error. Can't move it out because
// Error has a Drop impl which we want to not run.
// ErrReport has a Drop impl which we want to not run.
let inner = ptr::read(&outer.inner);
let erased = ManuallyDrop::into_inner(inner);
@ -772,21 +772,21 @@ impl From<Error> for Box<dyn StdError + Send + Sync + 'static> {
}
}
impl From<Error> for Box<dyn StdError + 'static> {
fn from(error: Error) -> Self {
impl From<ErrReport> for Box<dyn StdError + 'static> {
fn from(error: ErrReport) -> Self {
Box::<dyn StdError + Send + Sync>::from(error)
}
}
#[cfg(feature = "std")]
impl AsRef<dyn StdError + Send + Sync> for Error {
impl AsRef<dyn StdError + Send + Sync> for ErrReport {
fn as_ref(&self) -> &(dyn StdError + Send + Sync + 'static) {
&**self
}
}
#[cfg(feature = "std")]
impl AsRef<dyn StdError> for Error {
impl AsRef<dyn StdError> for ErrReport {
fn as_ref(&self) -> &(dyn StdError + 'static) {
&**self
}

View File

@ -1,50 +1,50 @@
// Tagged dispatch mechanism for resolving the behavior of `anyhow!($expr)`.
// Tagged dispatch mechanism for resolving the behavior of `eyre!($expr)`.
//
// When anyhow! is given a single expr argument to turn into anyhow::Error, we
// want the resulting Error to pick up the input's implementation of source()
// When eyre! is given a single expr argument to turn into eyre::ErrReport, we
// want the resulting ErrReport to pick up the input's implementation of source()
// and backtrace() if it has a std::error::Error impl, otherwise require nothing
// more than Display and Debug.
//
// Expressed in terms of specialization, we want something like:
//
// trait AnyhowNew {
// fn new(self) -> Error;
// trait EyreNew {
// fn new(self) -> ErrReport;
// }
//
// impl<T> AnyhowNew for T
// impl<T> EyreNew for T
// where
// T: Display + Debug + Send + Sync + 'static,
// {
// default fn new(self) -> Error {
// default fn new(self) -> ErrReport {
// /* no std error impl */
// }
// }
//
// impl<T> AnyhowNew for T
// impl<T> EyreNew for T
// where
// T: std::error::Error + Send + Sync + 'static,
// {
// fn new(self) -> Error {
// fn new(self) -> ErrReport {
// /* use std error's source() and backtrace() */
// }
// }
//
// Since specialization is not stable yet, instead we rely on autoref behavior
// of method resolution to perform tagged dispatch. Here we have two traits
// AdhocKind and TraitKind that both have an anyhow_kind() method. AdhocKind is
// AdhocKind and TraitKind that both have an eyre_kind() method. AdhocKind is
// implemented whether or not the caller's type has a std error impl, while
// TraitKind is implemented only when a std error impl does exist. The ambiguity
// is resolved by AdhocKind requiring an extra autoref so that it has lower
// precedence.
//
// The anyhow! macro will set up the call in this form:
// The eyre! macro will set up the call in this form:
//
// #[allow(unused_imports)]
// use $crate::private::{AdhocKind, TraitKind};
// let error = $msg;
// (&error).anyhow_kind().new(error)
// (&error).eyre_kind().new(error)
use crate::Error;
use crate::ErrReport;
use core::fmt::{Debug, Display};
#[cfg(feature = "std")]
@ -57,7 +57,7 @@ pub struct Adhoc;
pub trait AdhocKind: Sized {
#[inline]
fn anyhow_kind(&self) -> Adhoc {
fn eyre_kind(&self) -> Adhoc {
Adhoc
}
}
@ -65,11 +65,11 @@ pub trait AdhocKind: Sized {
impl<T> AdhocKind for &T where T: ?Sized + Display + Debug + Send + Sync + 'static {}
impl Adhoc {
pub fn new<M>(self, message: M) -> Error
pub fn new<M>(self, message: M) -> ErrReport
where
M: Display + Debug + Send + Sync + 'static,
{
Error::from_adhoc(message, backtrace!())
ErrReport::from_adhoc(message, backtrace!())
}
}
@ -77,17 +77,17 @@ pub struct Trait;
pub trait TraitKind: Sized {
#[inline]
fn anyhow_kind(&self) -> Trait {
fn eyre_kind(&self) -> Trait {
Trait
}
}
impl<E> TraitKind for E where E: Into<Error> {}
impl<E> TraitKind for E where E: Into<ErrReport> {}
impl Trait {
pub fn new<E>(self, error: E) -> Error
pub fn new<E>(self, error: E) -> ErrReport
where
E: Into<Error>,
E: Into<ErrReport>,
{
error.into()
}
@ -99,7 +99,7 @@ pub struct Boxed;
#[cfg(feature = "std")]
pub trait BoxedKind: Sized {
#[inline]
fn anyhow_kind(&self) -> Boxed {
fn eyre_kind(&self) -> Boxed {
Boxed
}
}
@ -109,8 +109,8 @@ impl BoxedKind for Box<dyn StdError + Send + Sync> {}
#[cfg(feature = "std")]
impl Boxed {
pub fn new(self, error: Box<dyn StdError + Send + Sync>) -> Error {
pub fn new(self, error: Box<dyn StdError + Send + Sync>) -> ErrReport {
let backtrace = backtrace_if_absent!(error);
Error::from_boxed(error, backtrace)
ErrReport::from_boxed(error, backtrace)
}
}

View File

@ -1,15 +1,15 @@
//! This library provides [`anyhow::Error`][Error], a trait object based error
//! This library provides [`eyre::ErrReport`][ErrReport], a trait object based error
//! type for easy idiomatic error handling in Rust applications.
//!
//! <br>
//!
//! # Details
//!
//! - Use `Result<T, anyhow::Error>`, or equivalently `anyhow::Result<T>`, as
//! - Use `Result<T, eyre::ErrReport>`, or equivalently `eyre::Result<T>`, as
//! the return type of any fallible function.
//!
//! Within the function, use `?` to easily propagate any error that implements
//! the `std::error::Error` trait.
//! the `std::error::ErrReport` trait.
//!
//! ```
//! # pub trait Deserialize {}
@ -27,7 +27,7 @@
//! #
//! # impl Deserialize for ClusterMap {}
//! #
//! use anyhow::Result;
//! use eyre::Result;
//!
//! fn get_cluster_info() -> Result<ClusterMap> {
//! let config = std::fs::read_to_string("cluster.json")?;
@ -52,7 +52,7 @@
//! # }
//! # }
//! #
//! use anyhow::{Context, Result};
//! use eyre::{Context, Result};
//!
//! fn main() -> Result<()> {
//! # return Ok(());
@ -88,7 +88,7 @@
//! mutable reference as needed.
//!
//! ```
//! # use anyhow::anyhow;
//! # use eyre::eyre;
//! # use std::fmt::{self, Display};
//! # use std::task::Poll;
//! #
@ -107,7 +107,7 @@
//! #
//! # const REDACTED_CONTENT: () = ();
//! #
//! # let error = anyhow!("...");
//! # let error = eyre!("...");
//! # let root_cause = &error;
//! #
//! # let ret =
@ -146,15 +146,15 @@
//! }
//! ```
//!
//! - One-off error messages can be constructed using the `anyhow!` macro, which
//! supports string interpolation and produces an `anyhow::Error`.
//! - One-off error messages can be constructed using the `eyre!` macro, which
//! supports string interpolation and produces an `eyre::ErrReport`.
//!
//! ```
//! # use anyhow::{anyhow, Result};
//! # use eyre::{eyre, Result};
//! #
//! # fn demo() -> Result<()> {
//! # let missing = "...";
//! return Err(anyhow!("Missing attribute: {}", missing));
//! return Err(eyre!("Missing attribute: {}", missing));
//! # Ok(())
//! # }
//! ```
@ -169,15 +169,15 @@
//!
//! ```toml
//! [dependencies]
//! anyhow = { version = "1.0", default-features = false }
//! eyre = { version = "1.0", default-features = false }
//! ```
//!
//! Since the `?`-based error conversions would normally rely on the
//! `std::error::Error` trait which is only available through std, no_std mode
//! will require an explicit `.map_err(Error::msg)` when working with a
//! `std::error::ErrReport` trait which is only available through std, no_std mode
//! will require an explicit `.map_err(ErrReport::msg)` when working with a
//! non-Anyhow error type inside a function that returns Anyhow's error type.
#![doc(html_root_url = "https://docs.rs/anyhow/1.0.26")]
#![doc(html_root_url = "https://docs.rs/eyre/1.0.26")]
#![cfg_attr(backtrace, feature(backtrace))]
#![cfg_attr(not(feature = "std"), no_std)]
#![allow(
@ -225,17 +225,17 @@ trait StdError: Debug + Display {
}
}
pub use anyhow as format_err;
pub use eyre as format_err;
/// The `Error` type, a wrapper around a dynamic error type.
/// The `ErrReport` type, a wrapper around a dynamic error type.
///
/// `Error` works a lot like `Box<dyn std::error::Error>`, but with these
/// `ErrReport` works a lot like `Box<dyn std::error::Error>`, but with these
/// differences:
///
/// - `Error` requires that the error is `Send`, `Sync`, and `'static`.
/// - `Error` guarantees that a backtrace is available, even if the underlying
/// - `ErrReport` requires that the error is `Send`, `Sync`, and `'static`.
/// - `ErrReport` guarantees that a backtrace is available, even if the underlying
/// error type does not provide one.
/// - `Error` is represented as a narrow pointer &mdash; exactly one word in
/// - `ErrReport` is represented as a narrow pointer &mdash; exactly one word in
/// size instead of two.
///
/// <br>
@ -245,13 +245,13 @@ pub use anyhow as format_err;
/// When you print an error object using "{}" or to_string(), only the outermost
/// underlying error or context is printed, not any of the lower level causes.
/// This is exactly as if you had called the Display impl of the error from
/// which you constructed your anyhow::Error.
/// which you constructed your eyre::ErrReport.
///
/// ```console
/// Failed to read instrs from ./path/to/instrs.json
/// ```
///
/// To print causes as well using anyhow's default formatting of causes, use the
/// To print causes as well using eyre's default formatting of causes, use the
/// alternate selector "{:#}".
///
/// ```console
@ -269,12 +269,12 @@ pub use anyhow as format_err;
/// No such file or directory (os error 2)
///
/// Stack backtrace:
/// 0: <E as anyhow::context::ext::StdError>::ext_context
/// at /git/anyhow/src/backtrace.rs:26
/// 0: <E as eyre::context::ext::StdError>::ext_context
/// at /git/eyre/src/backtrace.rs:26
/// 1: core::result::Result<T,E>::map_err
/// at /git/rustc/src/libcore/result.rs:596
/// 2: anyhow::context::<impl anyhow::Context<T,E> for core::result::Result<T,E>>::with_context
/// at /git/anyhow/src/context.rs:58
/// 2: eyre::context::<impl eyre::Context<T,E> for core::result::Result<T,E>>::with_context
/// at /git/eyre/src/context.rs:58
/// 3: testing::main
/// at src/main.rs:5
/// 4: std::rt::lang_start
@ -302,7 +302,7 @@ pub use anyhow as format_err;
/// like this:
///
/// ```
/// use anyhow::{Context, Result};
/// use eyre::{Context, Result};
///
/// fn main() {
/// if let Err(err) = try_main() {
@ -319,21 +319,21 @@ pub use anyhow as format_err;
/// # Ok(())
/// }
/// ```
pub struct Error {
pub struct ErrReport {
inner: ManuallyDrop<Box<ErrorImpl<()>>>,
}
/// Iterator of a chain of source errors.
///
/// This type is the iterator returned by [`Error::chain`].
/// This type is the iterator returned by [`ErrReport::chain`].
///
/// # Example
///
/// ```
/// use anyhow::Error;
/// use eyre::ErrReport;
/// use std::io;
///
/// pub fn underlying_io_error_kind(error: &Error) -> Option<io::ErrorKind> {
/// pub fn underlying_io_error_kind(error: &ErrReport) -> Option<io::ErrorKind> {
/// for cause in error.chain() {
/// if let Some(io_error) = cause.downcast_ref::<io::Error>() {
/// return Some(io_error.kind());
@ -354,14 +354,14 @@ pub struct Chain<'a> {
/// for `fn main`; if you do, failures will be printed along with any
/// [context][Context] and a backtrace if one was captured.
///
/// `anyhow::Result` may be used with one *or* two type parameters.
/// `eyre::Result` may be used with one *or* two type parameters.
///
/// ```rust
/// use anyhow::Result;
/// use eyre::Result;
///
/// # const IGNORE: &str = stringify! {
/// fn demo1() -> Result<T> {...}
/// // ^ equivalent to std::result::Result<T, anyhow::Error>
/// // ^ equivalent to std::result::Result<T, eyre::Error>
///
/// fn demo2() -> Result<T, OtherError> {...}
/// // ^ equivalent to std::result::Result<T, OtherError>
@ -387,7 +387,7 @@ pub struct Chain<'a> {
/// #
/// # impl Deserialize for ClusterMap {}
/// #
/// use anyhow::Result;
/// use eyre::Result;
///
/// fn main() -> Result<()> {
/// # return Ok(());
@ -397,19 +397,19 @@ pub struct Chain<'a> {
/// Ok(())
/// }
/// ```
pub type Result<T, E = Error> = core::result::Result<T, E>;
pub type Result<T, E = ErrReport> = core::result::Result<T, E>;
/// Provides the `context` method for `Result`.
///
/// This trait is sealed and cannot be implemented for types outside of
/// `anyhow`.
/// `eyre`.
///
/// <br>
///
/// # Example
///
/// ```
/// use anyhow::{Context, Result};
/// use eyre::{Context, Result};
/// use std::fs;
/// use std::path::PathBuf;
///
@ -452,7 +452,7 @@ pub type Result<T, E = Error> = core::result::Result<T, E>;
/// # Effect on downcasting
///
/// After attaching context of type `C` onto an error of type `E`, the resulting
/// `anyhow::Error` may be downcast to `C` **or** to `E`.
/// `eyre::Error` may be downcast to `C` **or** to `E`.
///
/// That is, in codebases that rely on downcasting, Anyhow's context supports
/// both of the following use cases:
@ -468,7 +468,7 @@ pub type Result<T, E = Error> = core::result::Result<T, E>;
/// be helpful.
///
/// ```
/// # use anyhow::bail;
/// # use eyre::bail;
/// # use thiserror::Error;
/// #
/// # #[derive(Error, Debug)]
@ -479,7 +479,7 @@ pub type Result<T, E = Error> = core::result::Result<T, E>;
/// # bail!(SuspiciousError);
/// # }
/// #
/// use anyhow::{Context, Result};
/// use eyre::{Context, Result};
///
/// fn do_it() -> Result<()> {
/// helper().context("Failed to complete the work")?;
@ -508,7 +508,7 @@ pub type Result<T, E = Error> = core::result::Result<T, E>;
/// the application.
///
/// ```
/// # use anyhow::bail;
/// # use eyre::bail;
/// # use thiserror::Error;
/// #
/// # #[derive(Error, Debug)]
@ -519,7 +519,7 @@ pub type Result<T, E = Error> = core::result::Result<T, E>;
/// # bail!("no such file or directory");
/// # }
/// #
/// use anyhow::{Context, Result};
/// use eyre::{Context, Result};
///
/// fn do_it() -> Result<()> {
/// helper().context(HelperFailed)?;
@ -542,13 +542,13 @@ pub type Result<T, E = Error> = core::result::Result<T, E>;
/// ```
pub trait Context<T, E>: context::private::Sealed {
/// Wrap the error value with additional context.
fn context<C>(self, context: C) -> Result<T, Error>
fn context<C>(self, context: C) -> Result<T, ErrReport>
where
C: Display + Send + Sync + 'static;
/// Wrap the error value with additional context that is evaluated lazily
/// only once an error does occur.
fn with_context<C, F>(self, f: F) -> Result<T, Error>
fn with_context<C, F>(self, f: F) -> Result<T, ErrReport>
where
C: Display + Send + Sync + 'static,
F: FnOnce() -> C;
@ -557,7 +557,7 @@ pub trait Context<T, E>: context::private::Sealed {
// Not public API. Referenced by macro-generated code.
#[doc(hidden)]
pub mod private {
use crate::Error;
use crate::ErrReport;
use core::fmt::{Debug, Display};
#[cfg(backtrace)]
@ -573,10 +573,10 @@ pub mod private {
pub use crate::kind::BoxedKind;
}
pub fn new_adhoc<M>(message: M) -> Error
pub fn new_adhoc<M>(message: M) -> ErrReport
where
M: Display + Debug + Send + Sync + 'static,
{
Error::from_adhoc(message, backtrace!())
ErrReport::from_adhoc(message, backtrace!())
}
}

View File

@ -5,7 +5,7 @@
/// # Example
///
/// ```
/// # use anyhow::{bail, Result};
/// # use eyre::{bail, Result};
/// #
/// # fn has_permission(user: usize, resource: usize) -> bool {
/// # true
@ -23,7 +23,7 @@
/// ```
///
/// ```
/// # use anyhow::{bail, Result};
/// # use eyre::{bail, Result};
/// # use thiserror::Error;
/// #
/// # const MAX_DEPTH: usize = 1;
@ -50,13 +50,13 @@
#[macro_export]
macro_rules! bail {
($msg:literal $(,)?) => {
return $crate::private::Err($crate::anyhow!($msg));
return $crate::private::Err($crate::eyre!($msg));
};
($err:expr $(,)?) => {
return $crate::private::Err($crate::anyhow!($err));
return $crate::private::Err($crate::eyre!($err));
};
($fmt:expr, $($arg:tt)*) => {
return $crate::private::Err($crate::anyhow!($fmt, $($arg)*));
return $crate::private::Err($crate::eyre!($fmt, $($arg)*));
};
}
@ -71,7 +71,7 @@ macro_rules! bail {
/// # Example
///
/// ```
/// # use anyhow::{ensure, Result};
/// # use eyre::{ensure, Result};
/// #
/// # fn main() -> Result<()> {
/// # let user = 0;
@ -82,7 +82,7 @@ macro_rules! bail {
/// ```
///
/// ```
/// # use anyhow::{ensure, Result};
/// # use eyre::{ensure, Result};
/// # use thiserror::Error;
/// #
/// # const MAX_DEPTH: usize = 1;
@ -108,17 +108,17 @@ macro_rules! bail {
macro_rules! ensure {
($cond:expr, $msg:literal $(,)?) => {
if !$cond {
return $crate::private::Err($crate::anyhow!($msg));
return $crate::private::Err($crate::eyre!($msg));
}
};
($cond:expr, $err:expr $(,)?) => {
if !$cond {
return $crate::private::Err($crate::anyhow!($err));
return $crate::private::Err($crate::eyre!($err));
}
};
($cond:expr, $fmt:expr, $($arg:tt)*) => {
if !$cond {
return $crate::private::Err($crate::anyhow!($fmt, $($arg)*));
return $crate::private::Err($crate::eyre!($fmt, $($arg)*));
}
};
}
@ -134,11 +134,11 @@ macro_rules! ensure {
/// ```
/// # type V = ();
/// #
/// use anyhow::{anyhow, Result};
/// use eyre::{eyre, Result};
///
/// fn lookup(key: &str) -> Result<V> {
/// if key.len() != 16 {
/// return Err(anyhow!("key length must be 16 characters, got {:?}", key));
/// return Err(eyre!("key length must be 16 characters, got {:?}", key));
/// }
///
/// // ...
@ -146,7 +146,7 @@ macro_rules! ensure {
/// }
/// ```
#[macro_export]
macro_rules! anyhow {
macro_rules! eyre {
($msg:literal $(,)?) => {
// Handle $:literal as a special case to make cargo-expanded code more
// concise in the common case.
@ -155,7 +155,7 @@ macro_rules! anyhow {
($err:expr $(,)?) => ({
use $crate::private::kind::*;
let error = $err;
(&error).anyhow_kind().new(error)
(&error).eyre_kind().new(error)
});
($fmt:expr, $($arg:tt)*) => {
$crate::private::new_adhoc(format!($fmt, $($arg)*))

View File

@ -1,4 +1,4 @@
use anyhow::{bail, Result};
use eyre::{bail, Result};
use std::io;
pub fn bail_literal() -> Result<()> {

View File

@ -1,13 +1,13 @@
use anyhow::Error;
use eyre::ErrReport;
#[test]
fn test_send() {
fn assert_send<T: Send>() {}
assert_send::<Error>();
assert_send::<ErrReport>();
}
#[test]
fn test_sync() {
fn assert_sync<T: Sync>() {}
assert_sync::<Error>();
assert_sync::<ErrReport>();
}

View File

@ -6,8 +6,8 @@ fn test_backtrace() {}
#[rustversion::nightly]
#[test]
fn test_backtrace() {
use anyhow::anyhow;
use eyre::eyre;
let error = anyhow!("oh no!");
let error = eyre!("oh no!");
let _ = error.backtrace();
}

View File

@ -1,4 +1,4 @@
use anyhow::anyhow;
use eyre::eyre;
use std::error::Error as StdError;
use std::io;
use thiserror::Error;
@ -12,7 +12,7 @@ struct MyError {
#[test]
fn test_boxed_str() {
let error = Box::<dyn StdError + Send + Sync>::from("oh no!");
let error = anyhow!(error);
let error = eyre!(error);
assert_eq!("oh no!", error.to_string());
assert_eq!(
"oh no!",
@ -28,13 +28,13 @@ fn test_boxed_thiserror() {
let error = MyError {
source: io::Error::new(io::ErrorKind::Other, "oh no!"),
};
let error = anyhow!(error);
let error = eyre!(error);
assert_eq!("oh no!", error.source().unwrap().to_string());
}
#[test]
fn test_boxed_anyhow() {
let error = anyhow!("oh no!").context("it failed");
let error = anyhow!(error);
fn test_boxed_eyre() {
let error = eyre!("oh no!").context("it failed");
let error = eyre!(error);
assert_eq!("oh no!", error.source().unwrap().to_string());
}

View File

@ -1,7 +1,7 @@
use anyhow::{anyhow, Error};
use eyre::{eyre, ErrReport};
fn error() -> Error {
anyhow!(0).context(1).context(2).context(3)
fn error() -> ErrReport {
eyre!(0).context(1).context(2).context(3)
}
#[test]

View File

@ -1,11 +1,11 @@
mod drop;
use crate::drop::{DetectDrop, Flag};
use anyhow::{Context, Error, Result};
use eyre::{Context, ErrReport, Result};
use std::fmt::{self, Display};
use thiserror::Error;
// https://github.com/dtolnay/anyhow/issues/18
// https://github.com/dtolnay/eyre/issues/18
#[test]
fn test_inference() -> Result<()> {
let x = "1";
@ -56,7 +56,7 @@ impl Dropped {
}
}
fn make_chain() -> (Error, Dropped) {
fn make_chain() -> (ErrReport, Dropped) {
let dropped = Dropped {
low: Flag::new(),
mid: Flag::new(),
@ -77,7 +77,7 @@ fn make_chain() -> (Error, Dropped) {
.unwrap_err();
// impl Context for Result<T, Error>
let high = Err::<(), Error>(mid)
let high = Err::<(), ErrReport>(mid)
.context(HighLevel {
message: "failed to start server",
drop: DetectDrop::new(&dropped.high),

View File

@ -1,13 +1,13 @@
mod drop;
use self::drop::{DetectDrop, Flag};
use anyhow::{Error, Result};
use eyre::{ErrReport, Result};
use std::error::Error as StdError;
#[test]
fn test_convert() {
let has_dropped = Flag::new();
let error = Error::new(DetectDrop::new(&has_dropped));
let error = ErrReport::new(DetectDrop::new(&has_dropped));
let box_dyn = Box::<dyn StdError + Send + Sync>::from(error);
assert_eq!("oh no!", box_dyn.to_string());
drop(box_dyn);

View File

@ -3,7 +3,7 @@ mod drop;
use self::common::*;
use self::drop::{DetectDrop, Flag};
use anyhow::Error;
use eyre::ErrReport;
use std::error::Error as StdError;
use std::fmt::{self, Display};
use std::io;
@ -71,7 +71,7 @@ fn test_downcast_mut() {
#[test]
fn test_drop() {
let has_dropped = Flag::new();
let error = Error::new(DetectDrop::new(&has_dropped));
let error = ErrReport::new(DetectDrop::new(&has_dropped));
drop(error.downcast::<DetectDrop>().unwrap());
assert!(has_dropped.get());
}
@ -90,7 +90,7 @@ fn test_large_alignment() {
impl StdError for LargeAlignedError {}
let error = Error::new(LargeAlignedError("oh no!"));
let error = ErrReport::new(LargeAlignedError("oh no!"));
assert_eq!(
"oh no!",
error.downcast_ref::<LargeAlignedError>().unwrap().0

View File

@ -1,4 +1,4 @@
use anyhow::{bail, Context, Result};
use eyre::{bail, Context, Result};
use std::io;
fn f() -> Result<()> {

View File

@ -1,7 +1,7 @@
mod common;
use self::common::*;
use anyhow::ensure;
use eyre::ensure;
#[test]
fn test_messages() {

View File

@ -1,29 +1,29 @@
mod drop;
use self::drop::{DetectDrop, Flag};
use anyhow::Error;
use eyre::ErrReport;
use std::marker::Unpin;
use std::mem;
#[test]
fn test_error_size() {
assert_eq!(mem::size_of::<Error>(), mem::size_of::<usize>());
assert_eq!(mem::size_of::<ErrReport>(), mem::size_of::<usize>());
}
#[test]
fn test_null_pointer_optimization() {
assert_eq!(mem::size_of::<Result<(), Error>>(), mem::size_of::<usize>());
assert_eq!(mem::size_of::<Result<(), ErrReport>>(), mem::size_of::<usize>());
}
#[test]
fn test_autotraits() {
fn assert<E: Unpin + Send + Sync + 'static>() {}
assert::<Error>();
assert::<ErrReport>();
}
#[test]
fn test_drop() {
let has_dropped = Flag::new();
drop(Error::new(DetectDrop::new(&has_dropped)));
drop(ErrReport::new(DetectDrop::new(&has_dropped)));
assert!(has_dropped.get());
}

View File

@ -1,4 +1,4 @@
use anyhow::anyhow;
use eyre::eyre;
use std::error::Error as StdError;
use std::fmt::{self, Display};
use std::io;
@ -26,37 +26,37 @@ impl StdError for TestError {
#[test]
fn test_literal_source() {
let error = anyhow!("oh no!");
let error = eyre!("oh no!");
assert!(error.source().is_none());
}
#[test]
fn test_variable_source() {
let msg = "oh no!";
let error = anyhow!(msg);
let error = eyre!(msg);
assert!(error.source().is_none());
let msg = msg.to_owned();
let error = anyhow!(msg);
let error = eyre!(msg);
assert!(error.source().is_none());
}
#[test]
fn test_fmt_source() {
let error = anyhow!("{} {}!", "oh", "no");
let error = eyre!("{} {}!", "oh", "no");
assert!(error.source().is_none());
}
#[test]
fn test_io_source() {
let io = io::Error::new(io::ErrorKind::Other, "oh no!");
let error = anyhow!(TestError::Io(io));
let error = eyre!(TestError::Io(io));
assert_eq!("oh no!", error.source().unwrap().to_string());
}
#[test]
fn test_anyhow_from_anyhow() {
let error = anyhow!("oh no!").context("context");
let error = anyhow!(error);
fn test_eyre_from_eyre() {
let error = eyre!("oh no!").context("context");
let error = eyre!(error);
assert_eq!("oh no!", error.source().unwrap().to_string());
}

View File

@ -1,8 +1,8 @@
use anyhow::anyhow;
use eyre::eyre;
#[derive(Debug)]
struct Error;
fn main() {
let _ = anyhow!(Error);
let _ = eyre!(Error);
}

View File

@ -1,16 +1,16 @@
error[E0599]: no method named `anyhow_kind` found for reference `&Error` in the current scope
error[E0599]: no method named `eyre_kind` found for reference `&Error` in the current scope
--> $DIR/no-impl.rs:7:13
|
7 | let _ = anyhow!(Error);
| ^^^^^^^^^^^^^^ method not found in `&Error`
7 | let _ = eyre!(Error);
| ^^^^^^^^^^^^ method not found in `&Error`
|
= note: the method `anyhow_kind` exists but the following trait bounds were not satisfied:
`&Error : anyhow::kind::AdhocKind`
`&Error : anyhow::kind::TraitKind`
`Error : anyhow::kind::TraitKind`
= note: the method `eyre_kind` exists but the following trait bounds were not satisfied:
`&Error : eyre::kind::AdhocKind`
`&Error : eyre::kind::TraitKind`
`Error : eyre::kind::TraitKind`
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following traits define an item `anyhow_kind`, perhaps you need to implement one of them:
candidate #1: `anyhow::kind::AdhocKind`
candidate #2: `anyhow::kind::TraitKind`
candidate #3: `anyhow::kind::BoxedKind`
= note: the following traits define an item `eyre_kind`, perhaps you need to implement one of them:
candidate #1: `eyre::kind::AdhocKind`
candidate #2: `eyre::kind::TraitKind`
candidate #3: `eyre::kind::BoxedKind`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)