From 49eeec1cdcb9807bcddc94d0ac5bdd818abb09ee Mon Sep 17 00:00:00 2001 From: Lukas Kalbertodt Date: Wed, 18 Jan 2023 12:24:34 +0100 Subject: [PATCH] Enable backtrace support for >= 1.65 stable compilers 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. --- README.md | 11 ++++------- build.rs | 15 ++++++++++++++- src/backtrace.rs | 4 ++-- src/context.rs | 6 +++--- src/error.rs | 36 +++++++++++++++++------------------- src/lib.rs | 14 +++++--------- src/wrapper.rs | 4 ++-- 7 files changed, 47 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 6380c1c..a863cab 100644 --- a/README.md +++ b/README.md @@ -75,10 +75,10 @@ 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`; @@ -86,10 +86,7 @@ anyhow = "1.0" - 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 diff --git a/build.rs b/build.rs index 3800683..8ac9b79 100644 --- a/build.rs +++ b/build.rs @@ -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; + } _ => {} } } @@ -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 { diff --git a/src/backtrace.rs b/src/backtrace.rs index 23c0c85..bd9dc4e 100644 --- a/src/backtrace.rs +++ b/src/backtrace.rs @@ -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::() { @@ -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!() diff --git a/src/context.rs b/src/context.rs index 9df8693..a03a2f1 100644 --- a/src/context.rs +++ b/src/context.rs @@ -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 { @@ -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); } @@ -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); } diff --git a/src/error.rs b/src/error.rs index 9f6ce8c..d8dfbea 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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}; @@ -99,7 +99,7 @@ impl Error { #[cfg(anyhow_no_ptr_addr_of)] object_downcast_mut: object_downcast_mut::, object_drop_rest: object_drop_front::, - #[cfg(all(not(backtrace), feature = "backtrace"))] + #[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))] object_backtrace: no_backtrace, }; @@ -124,7 +124,7 @@ impl Error { #[cfg(anyhow_no_ptr_addr_of)] object_downcast_mut: object_downcast_mut::, object_drop_rest: object_drop_front::, - #[cfg(all(not(backtrace), feature = "backtrace"))] + #[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))] object_backtrace: no_backtrace, }; @@ -150,7 +150,7 @@ impl Error { #[cfg(anyhow_no_ptr_addr_of)] object_downcast_mut: object_downcast_mut::, object_drop_rest: object_drop_front::, - #[cfg(all(not(backtrace), feature = "backtrace"))] + #[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))] object_backtrace: no_backtrace, }; @@ -178,7 +178,7 @@ impl Error { #[cfg(anyhow_no_ptr_addr_of)] object_downcast_mut: context_downcast_mut::, object_drop_rest: context_drop_rest::, - #[cfg(all(not(backtrace), feature = "backtrace"))] + #[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))] object_backtrace: no_backtrace, }; @@ -204,7 +204,7 @@ impl Error { #[cfg(anyhow_no_ptr_addr_of)] object_downcast_mut: object_downcast_mut::>, object_drop_rest: object_drop_front::>, - #[cfg(all(not(backtrace), feature = "backtrace"))] + #[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))] object_backtrace: no_backtrace, }; @@ -317,7 +317,7 @@ impl Error { #[cfg(anyhow_no_ptr_addr_of)] object_downcast_mut: context_chain_downcast_mut::, object_drop_rest: context_chain_drop_rest::, - #[cfg(all(not(backtrace), feature = "backtrace"))] + #[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))] object_backtrace: context_backtrace::, }; @@ -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. /// @@ -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 @@ -598,7 +596,7 @@ struct ErrorVTable { #[cfg(anyhow_no_ptr_addr_of)] object_downcast_mut: unsafe fn(Mut, TypeId) -> Option>, object_drop_rest: unsafe fn(Own, TypeId), - #[cfg(all(not(backtrace), feature = "backtrace"))] + #[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))] object_backtrace: unsafe fn(Ref) -> Option<&Backtrace>, } @@ -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) -> Option<&Backtrace> { let _ = e; None @@ -822,7 +820,7 @@ where } // Safety: requires layout of *e to match ErrorImpl>. -#[cfg(all(not(backtrace), feature = "backtrace"))] +#[cfg(all(not(provide_api), any(backtrace, feature = "backtrace")))] #[allow(clippy::unnecessary_wraps)] unsafe fn context_backtrace(e: Ref) -> Option<&Backtrace> where @@ -899,15 +897,15 @@ impl ErrorImpl { .backtrace .as_ref() .or_else(|| { - #[cfg(backtrace)] + #[cfg(provide_api)] return Self::error(this).request_ref::(); - #[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); @@ -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) } } diff --git a/src/lib.rs b/src/lib.rs index 3510d19..635aa8a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -128,11 +128,10 @@ //! # ; //! ``` //! -//! - 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`; @@ -140,10 +139,7 @@ //! - 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)` @@ -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)] diff --git a/src/wrapper.rs b/src/wrapper.rs index 5f18a50..e9cdf7a 100644 --- a/src/wrapper.rs +++ b/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)] @@ -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); }