Skip to content

Commit

Permalink
Enable backtrace support for >= 1.65 stable compilers
Browse files Browse the repository at this point in the history
The `std::backtrace` APIs relevant for this crate were stabilized in
1.65. This commit makes anyhow use those API if possible.
`std::backtrace` is preferred over the `backtrace` crate, so for 1.65+
compilers, enabling the `backtrace` crate feature does nothing
essentially.

Previously, when a nightly compiler was used, use of `std::backtrace`
as well as the Provider API was enabled. This commit separates those
two APIs such that `std::backtrace` is used for nightly OR 1.65+ stable
compilers; and the Provider API is used for nightly compilers only.
  • Loading branch information
LukasKalbertodt committed Jan 18, 2023
1 parent 0a45d76 commit 49eeec1
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 43 deletions.
11 changes: 4 additions & 7 deletions README.md
Expand Up @@ -75,21 +75,18 @@ anyhow = "1.0"
}
```

- If using the nightly channel, or stable with `features = ["backtrace"]`, a
backtrace is captured and printed with the error if the underlying error type
does not already provide its own. In order to see backtraces, they must be
enabled through the environment variables described in [`std::backtrace`]:
- If using Rust ≥ 1.65 or `features = ["backtrace"]`, a backtrace is captured
and printed with the error if the underlying error type does not already
provide its own. In order to see backtraces, they must be enabled through the
environment variables described in [`std::backtrace`]:

- If you want panics and errors to both have backtraces, set
`RUST_BACKTRACE=1`;
- If you want only errors to have backtraces, set `RUST_LIB_BACKTRACE=1`;
- If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and
`RUST_LIB_BACKTRACE=0`.

The tracking issue for this feature is [rust-lang/rust#53487].

[`std::backtrace`]: https://doc.rust-lang.org/std/backtrace/index.html#environment-variables
[rust-lang/rust#53487]: https://github.com/rust-lang/rust/issues/53487

- Anyhow works with any error type that has an impl of `std::error::Error`,
including ones defined in your crate. We do not bundle a `derive(Error)` macro
Expand Down
15 changes: 14 additions & 1 deletion build.rs
Expand Up @@ -57,9 +57,18 @@ const PROBE: &str = r#"
"#;

fn main() {
// If the compile probe succeeds, i.e. if a nightly compiler is used and the
// provide API exists, then `std::backtrace` is also usable. So `cfg
// (provide_api)` implies `cfg(backtrace)`. We do set `cfg(backtrace)` in
// this script explicitly anyway to make the `cfg()` attributes in the main
// code less complex.
let mut nightly_backtrace_support = false;
if cfg!(feature = "std") {
match compile_probe() {
Some(status) if status.success() => println!("cargo:rustc-cfg=backtrace"),
Some(status) if status.success() => {
println!("cargo:rustc-cfg=provide_api");
nightly_backtrace_support = true;
}
_ => {}
}
}
Expand All @@ -76,6 +85,10 @@ fn main() {
if rustc < 52 {
println!("cargo:rustc-cfg=anyhow_no_fmt_arguments_as_str");
}

if nightly_backtrace_support || (cfg!(feature = "std") && rustc >= 65) {
println!("cargo:rustc-cfg=backtrace");
}
}

fn compile_probe() -> Option<ExitStatus> {
Expand Down
4 changes: 2 additions & 2 deletions src/backtrace.rs
Expand Up @@ -35,7 +35,7 @@ macro_rules! backtrace {
};
}

#[cfg(backtrace)]
#[cfg(provide_api)]
macro_rules! backtrace_if_absent {
($err:expr) => {
match ($err as &dyn std::error::Error).request_ref::<std::backtrace::Backtrace>() {
Expand All @@ -45,7 +45,7 @@ macro_rules! backtrace_if_absent {
};
}

#[cfg(all(feature = "std", not(backtrace), feature = "backtrace"))]
#[cfg(all(feature = "std", not(provide_api), any(backtrace, feature = "backtrace")))]
macro_rules! backtrace_if_absent {
($err:expr) => {
backtrace!()
Expand Down
6 changes: 3 additions & 3 deletions src/context.rs
Expand Up @@ -3,7 +3,7 @@ use crate::{Context, Error, StdError};
use core::convert::Infallible;
use core::fmt::{self, Debug, Display, Write};

#[cfg(backtrace)]
#[cfg(provide_api)]
use std::any::{Demand, Provider};

mod ext {
Expand Down Expand Up @@ -143,7 +143,7 @@ where
Some(&self.error)
}

#[cfg(backtrace)]
#[cfg(provide_api)]
fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
StdError::provide(&self.error, demand);
}
Expand All @@ -157,7 +157,7 @@ where
Some(unsafe { crate::ErrorImpl::error(self.error.inner.by_ref()) })
}

#[cfg(backtrace)]
#[cfg(provide_api)]
fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
Provider::provide(&self.error, demand);
}
Expand Down
36 changes: 17 additions & 19 deletions src/error.rs
Expand Up @@ -5,7 +5,7 @@ use crate::ptr::Mut;
use crate::ptr::{Own, Ref};
use crate::{Error, StdError};
use alloc::boxed::Box;
#[cfg(backtrace)]
#[cfg(provide_api)]
use core::any::Demand;
use core::any::TypeId;
use core::fmt::{self, Debug, Display};
Expand Down Expand Up @@ -99,7 +99,7 @@ impl Error {
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: object_downcast_mut::<E>,
object_drop_rest: object_drop_front::<E>,
#[cfg(all(not(backtrace), feature = "backtrace"))]
#[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))]
object_backtrace: no_backtrace,
};

Expand All @@ -124,7 +124,7 @@ impl Error {
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: object_downcast_mut::<M>,
object_drop_rest: object_drop_front::<M>,
#[cfg(all(not(backtrace), feature = "backtrace"))]
#[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))]
object_backtrace: no_backtrace,
};

Expand All @@ -150,7 +150,7 @@ impl Error {
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: object_downcast_mut::<M>,
object_drop_rest: object_drop_front::<M>,
#[cfg(all(not(backtrace), feature = "backtrace"))]
#[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))]
object_backtrace: no_backtrace,
};

Expand Down Expand Up @@ -178,7 +178,7 @@ impl Error {
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: context_downcast_mut::<C, E>,
object_drop_rest: context_drop_rest::<C, E>,
#[cfg(all(not(backtrace), feature = "backtrace"))]
#[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))]
object_backtrace: no_backtrace,
};

Expand All @@ -204,7 +204,7 @@ impl Error {
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: object_downcast_mut::<Box<dyn StdError + Send + Sync>>,
object_drop_rest: object_drop_front::<Box<dyn StdError + Send + Sync>>,
#[cfg(all(not(backtrace), feature = "backtrace"))]
#[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))]
object_backtrace: no_backtrace,
};

Expand Down Expand Up @@ -317,7 +317,7 @@ impl Error {
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: context_chain_downcast_mut::<C>,
object_drop_rest: context_chain_drop_rest::<C>,
#[cfg(all(not(backtrace), feature = "backtrace"))]
#[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))]
object_backtrace: context_backtrace::<C>,
};

Expand Down Expand Up @@ -345,10 +345,8 @@ impl Error {
///
/// # Stability
///
/// Standard library backtraces are only available on the nightly channel.
/// Tracking issue: [rust-lang/rust#53487][tracking].
///
/// On stable compilers, this function is only available if the crate's
/// Standard library backtraces are only available when using Rust ≥ 1.65.
/// On older compilers, this function is only available if the crate's
/// "backtrace" feature is enabled, and will use the `backtrace` crate as
/// the underlying backtrace implementation.
///
Expand Down Expand Up @@ -524,7 +522,7 @@ impl Error {
}
}

#[cfg(backtrace)]
#[cfg(provide_api)]
impl std::any::Provider for Error {
// Called by thiserror when you have `#[source] anyhow::Error`. This provide
// implementation includes the anyhow::Error's Backtrace if any, unlike
Expand Down Expand Up @@ -598,7 +596,7 @@ struct ErrorVTable {
#[cfg(anyhow_no_ptr_addr_of)]
object_downcast_mut: unsafe fn(Mut<ErrorImpl>, TypeId) -> Option<Mut<()>>,
object_drop_rest: unsafe fn(Own<ErrorImpl>, TypeId),
#[cfg(all(not(backtrace), feature = "backtrace"))]
#[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))]
object_backtrace: unsafe fn(Ref<ErrorImpl>) -> Option<&Backtrace>,
}

Expand Down Expand Up @@ -700,7 +698,7 @@ where
}
}

#[cfg(all(not(backtrace), feature = "backtrace"))]
#[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))]
fn no_backtrace(e: Ref<ErrorImpl>) -> Option<&Backtrace> {
let _ = e;
None
Expand Down Expand Up @@ -822,7 +820,7 @@ where
}

// Safety: requires layout of *e to match ErrorImpl<ContextError<C, Error>>.
#[cfg(all(not(backtrace), feature = "backtrace"))]
#[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))]
#[allow(clippy::unnecessary_wraps)]
unsafe fn context_backtrace<C>(e: Ref<ErrorImpl>) -> Option<&Backtrace>
where
Expand Down Expand Up @@ -899,15 +897,15 @@ impl ErrorImpl {
.backtrace
.as_ref()
.or_else(|| {
#[cfg(backtrace)]
#[cfg(provide_api)]
return Self::error(this).request_ref::<Backtrace>();
#[cfg(not(backtrace))]
#[cfg(not(provide_api))]
return (vtable(this.ptr).object_backtrace)(this);
})
.expect("backtrace capture failed")
}

#[cfg(backtrace)]
#[cfg(provide_api)]
unsafe fn provide<'a>(this: Ref<'a, Self>, demand: &mut Demand<'a>) {
if let Some(backtrace) = &this.deref().backtrace {
demand.provide_ref(backtrace);
Expand All @@ -929,7 +927,7 @@ where
unsafe { ErrorImpl::error(self.erase()).source() }
}

#[cfg(backtrace)]
#[cfg(provide_api)]
fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
unsafe { ErrorImpl::provide(self.erase(), demand) }
}
Expand Down
14 changes: 5 additions & 9 deletions src/lib.rs
Expand Up @@ -128,22 +128,18 @@
//! # ;
//! ```
//!
//! - If using the nightly channel, or stable with `features = ["backtrace"]`, a
//! backtrace is captured and printed with the error if the underlying error
//! type does not already provide its own. In order to see backtraces, they
//! must be enabled through the environment variables described in
//! [`std::backtrace`]:
//! - If using Rust ≥ 1.65 or `features = ["backtrace"]`, a backtrace is captured
//! and printed with the error if the underlying error type does not already
//! provide its own. In order to see backtraces, they must be enabled through the
//! environment variables described in [`std::backtrace`]:
//!
//! - If you want panics and errors to both have backtraces, set
//! `RUST_BACKTRACE=1`;
//! - If you want only errors to have backtraces, set `RUST_LIB_BACKTRACE=1`;
//! - If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and
//! `RUST_LIB_BACKTRACE=0`.
//!
//! The tracking issue for this feature is [rust-lang/rust#53487].
//!
//! [`std::backtrace`]: https://doc.rust-lang.org/std/backtrace/index.html#environment-variables
//! [rust-lang/rust#53487]: https://github.com/rust-lang/rust/issues/53487
//!
//! - Anyhow works with any error type that has an impl of `std::error::Error`,
//! including ones defined in your crate. We do not bundle a `derive(Error)`
Expand Down Expand Up @@ -211,7 +207,7 @@
//! non-Anyhow error type inside a function that returns Anyhow's error type.

#![doc(html_root_url = "https://docs.rs/anyhow/1.0.68")]
#![cfg_attr(backtrace, feature(error_generic_member_access, provide_any))]
#![cfg_attr(provide_api, feature(error_generic_member_access, provide_any))]
#![cfg_attr(doc_cfg, feature(doc_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]
#![deny(dead_code, unused_imports, unused_mut)]
Expand Down
4 changes: 2 additions & 2 deletions src/wrapper.rs
@@ -1,7 +1,7 @@
use crate::StdError;
use core::fmt::{self, Debug, Display};

#[cfg(backtrace)]
#[cfg(provide_api)]
use std::any::Demand;

#[repr(transparent)]
Expand Down Expand Up @@ -74,7 +74,7 @@ impl StdError for BoxedError {
self.0.source()
}

#[cfg(backtrace)]
#[cfg(provide_api)]
fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
self.0.provide(demand);
}
Expand Down

0 comments on commit 49eeec1

Please sign in to comment.