diff --git a/build.rs b/build.rs index c470ba1..78ea52c 100644 --- a/build.rs +++ b/build.rs @@ -15,15 +15,17 @@ compile_error! { // type. If the current toolchain is able to compile it, we go ahead and use // backtrace in anyhow. const PROBE: &str = r#" - #![feature(backtrace)] - #![allow(dead_code)] + #![feature(error_generic_member_access, provide_any)] + use std::any::Demand; use std::backtrace::{Backtrace, BacktraceStatus}; use std::error::Error; use std::fmt::{self, Display}; #[derive(Debug)] - struct E; + struct E { + backtrace: Backtrace, + } impl Display for E { fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result { @@ -32,14 +34,20 @@ const PROBE: &str = r#" } impl Error for E { - fn backtrace(&self) -> Option<&Backtrace> { - let backtrace = Backtrace::capture(); - match backtrace.status() { - BacktraceStatus::Captured | BacktraceStatus::Disabled | _ => {} - } - unimplemented!() + fn provide<'a>(&'a self, req: &mut Demand<'a>) { + req.provide_ref(&self.backtrace); } } + + const _: fn() = || { + let backtrace: Backtrace = Backtrace::capture(); + let status: BacktraceStatus = backtrace.status(); + match status { + BacktraceStatus::Captured | BacktraceStatus::Disabled | _ => {} + } + }; + + const _: fn(&dyn Error) -> Option<&Backtrace> = |err| err.request_ref::(); "#; fn main() { diff --git a/src/backtrace.rs b/src/backtrace.rs index e4c9076..23c0c85 100644 --- a/src/backtrace.rs +++ b/src/backtrace.rs @@ -38,7 +38,7 @@ macro_rules! backtrace { #[cfg(backtrace)] macro_rules! backtrace_if_absent { ($err:expr) => { - match $err.backtrace() { + match ($err as &dyn std::error::Error).request_ref::() { Some(_) => None, None => backtrace!(), } diff --git a/src/context.rs b/src/context.rs index c228413..c5c45c4 100644 --- a/src/context.rs +++ b/src/context.rs @@ -4,7 +4,7 @@ use core::convert::Infallible; use core::fmt::{self, Debug, Display, Write}; #[cfg(backtrace)] -use std::backtrace::Backtrace; +use std::any::Demand; mod ext { use super::*; @@ -24,7 +24,7 @@ mod ext { where C: Display + Send + Sync + 'static, { - let backtrace = backtrace_if_absent!(self); + let backtrace = backtrace_if_absent!(&self); Error::from_context(context, self, backtrace) } } @@ -123,28 +123,29 @@ where C: Display, E: StdError + 'static, { - #[cfg(backtrace)] - fn backtrace(&self) -> Option<&Backtrace> { - self.error.backtrace() - } - fn source(&self) -> Option<&(dyn StdError + 'static)> { Some(&self.error) } + + #[cfg(backtrace)] + fn provide<'a>(&'a self, req: &mut Demand<'a>) { + self.error.provide(req); + } } impl StdError for ContextError where C: Display, { - #[cfg(backtrace)] - fn backtrace(&self) -> Option<&Backtrace> { - Some(self.error.backtrace()) - } - fn source(&self) -> Option<&(dyn StdError + 'static)> { Some(unsafe { crate::ErrorImpl::error(self.error.inner.by_ref()) }) } + + #[cfg(backtrace)] + fn provide<'a>(&'a self, req: &mut Demand<'a>) { + req.provide_ref(self.error.backtrace()); + self.error.provide(req); + } } struct Quoted(C); diff --git a/src/error.rs b/src/error.rs index 0971146..1c5e7c7 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,6 +5,8 @@ use crate::ptr::Mut; use crate::ptr::{Own, Ref}; use crate::{Error, StdError}; use alloc::boxed::Box; +#[cfg(backtrace)] +use core::any::Demand; use core::any::TypeId; use core::fmt::{self, Debug, Display}; use core::mem::ManuallyDrop; @@ -31,7 +33,7 @@ impl Error { where E: StdError + Send + Sync + 'static, { - let backtrace = backtrace_if_absent!(error); + let backtrace = backtrace_if_absent!(&error); Error::from_std(error, backtrace) } @@ -530,7 +532,7 @@ where { #[cold] fn from(error: E) -> Self { - let backtrace = backtrace_if_absent!(error); + let backtrace = backtrace_if_absent!(&error); Error::from_std(error, backtrace) } } @@ -886,13 +888,21 @@ impl ErrorImpl { .as_ref() .or_else(|| { #[cfg(backtrace)] - return Self::error(this).backtrace(); - #[cfg(all(not(backtrace), feature = "backtrace"))] + return Self::error(this).request_ref::(); + #[cfg(not(backtrace))] return (vtable(this.ptr).object_backtrace)(this); }) .expect("backtrace capture failed") } + #[cfg(backtrace)] + unsafe fn provide<'a>(this: Ref<'a, Self>, req: &mut Demand<'a>) { + if let Some(backtrace) = &this.deref().backtrace { + req.provide_ref(backtrace); + } + Self::error(this).provide(req); + } + #[cold] pub(crate) unsafe fn chain(this: Ref) -> Chain { Chain::new(Self::error(this)) @@ -903,14 +913,14 @@ impl StdError for ErrorImpl where E: StdError, { - #[cfg(backtrace)] - fn backtrace(&self) -> Option<&Backtrace> { - Some(unsafe { ErrorImpl::backtrace(self.erase()) }) - } - fn source(&self) -> Option<&(dyn StdError + 'static)> { unsafe { ErrorImpl::error(self.erase()).source() } } + + #[cfg(backtrace)] + fn provide<'a>(&'a self, req: &mut Demand<'a>) { + unsafe { ErrorImpl::provide(self.erase(), req) } + } } impl Debug for ErrorImpl diff --git a/src/kind.rs b/src/kind.rs index c1b961c..f47fe44 100644 --- a/src/kind.rs +++ b/src/kind.rs @@ -110,7 +110,7 @@ impl BoxedKind for Box {} impl Boxed { #[cold] pub fn new(self, error: Box) -> Error { - let backtrace = backtrace_if_absent!(error); + let backtrace = backtrace_if_absent!(&*error); Error::from_boxed(error, backtrace) } } diff --git a/src/lib.rs b/src/lib.rs index af7f97b..ec48b4a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -211,7 +211,7 @@ //! non-Anyhow error type inside a function that returns Anyhow's error type. #![doc(html_root_url = "https://docs.rs/anyhow/1.0.62")] -#![cfg_attr(backtrace, feature(backtrace))] +#![cfg_attr(backtrace, 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 3ebe51a..3a71a55 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -1,6 +1,9 @@ use crate::StdError; use core::fmt::{self, Debug, Display}; +#[cfg(backtrace)] +use std::any::Demand; + #[repr(transparent)] pub struct MessageError(pub M); @@ -67,12 +70,12 @@ impl Display for BoxedError { #[cfg(feature = "std")] impl StdError for BoxedError { - #[cfg(backtrace)] - fn backtrace(&self) -> Option<&crate::backtrace::Backtrace> { - self.0.backtrace() - } - fn source(&self) -> Option<&(dyn StdError + 'static)> { self.0.source() } + + #[cfg(backtrace)] + fn provide<'a>(&'a self, req: &mut Demand<'a>) { + self.0.provide(req); + } }