diff options
Diffstat (limited to 'library/std/src/panic.rs')
| -rw-r--r-- | library/std/src/panic.rs | 208 |
1 files changed, 206 insertions, 2 deletions
diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index e63b46ab705..5282c00fcca 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -4,11 +4,215 @@ use crate::any::Any; use crate::collections; +use crate::fmt; use crate::panicking; use crate::sync::atomic::{AtomicU8, Ordering}; use crate::sync::{Condvar, Mutex, RwLock}; use crate::thread::Result; +#[stable(feature = "panic_hooks", since = "1.10.0")] +#[deprecated( + since = "1.82.0", + note = "use `PanicHookInfo` instead", + suggestion = "std::panic::PanicHookInfo" +)] +/// A struct providing information about a panic. +/// +/// `PanicInfo` has been renamed to [`PanicHookInfo`] to avoid confusion with +/// [`core::panic::PanicInfo`]. +pub type PanicInfo<'a> = PanicHookInfo<'a>; + +/// A struct providing information about a panic. +/// +/// `PanicHookInfo` structure is passed to a panic hook set by the [`set_hook`] function. +/// +/// # Examples +/// +/// ```should_panic +/// use std::panic; +/// +/// panic::set_hook(Box::new(|panic_info| { +/// println!("panic occurred: {panic_info}"); +/// })); +/// +/// panic!("critical system failure"); +/// ``` +/// +/// [`set_hook`]: ../../std/panic/fn.set_hook.html +#[stable(feature = "panic_hook_info", since = "CURRENT_RUSTC_VERSION")] +#[derive(Debug)] +pub struct PanicHookInfo<'a> { + payload: &'a (dyn Any + Send), + location: &'a Location<'a>, + can_unwind: bool, + force_no_backtrace: bool, +} + +impl<'a> PanicHookInfo<'a> { + #[inline] + pub(crate) fn new( + location: &'a Location<'a>, + payload: &'a (dyn Any + Send), + can_unwind: bool, + force_no_backtrace: bool, + ) -> Self { + PanicHookInfo { payload, location, can_unwind, force_no_backtrace } + } + + /// Returns the payload associated with the panic. + /// + /// This will commonly, but not always, be a `&'static str` or [`String`]. + /// + /// A invocation of the `panic!()` macro in Rust 2021 or later will always result in a + /// panic payload of type `&'static str` or `String`. + /// + /// Only an invocation of [`panic_any`] + /// (or, in Rust 2018 and earlier, `panic!(x)` where `x` is something other than a string) + /// can result in a panic payload other than a `&'static str` or `String`. + /// + /// [`String`]: ../../std/string/struct.String.html + /// + /// # Examples + /// + /// ```should_panic + /// use std::panic; + /// + /// panic::set_hook(Box::new(|panic_info| { + /// if let Some(s) = panic_info.payload().downcast_ref::<&str>() { + /// println!("panic occurred: {s:?}"); + /// } else if let Some(s) = panic_info.payload().downcast_ref::<String>() { + /// println!("panic occurred: {s:?}"); + /// } else { + /// println!("panic occurred"); + /// } + /// })); + /// + /// panic!("Normal panic"); + /// ``` + #[must_use] + #[inline] + #[stable(feature = "panic_hooks", since = "1.10.0")] + pub fn payload(&self) -> &(dyn Any + Send) { + self.payload + } + + /// Returns the payload associated with the panic, if it is a string. + /// + /// This returns the payload if it is of type `&'static str` or `String`. + /// + /// A invocation of the `panic!()` macro in Rust 2021 or later will always result in a + /// panic payload where `payload_as_str` returns `Some`. + /// + /// Only an invocation of [`panic_any`] + /// (or, in Rust 2018 and earlier, `panic!(x)` where `x` is something other than a string) + /// can result in a panic payload where `payload_as_str` returns `None`. + /// + /// # Example + /// + /// ```should_panic + /// #![feature(panic_payload_as_str)] + /// + /// std::panic::set_hook(Box::new(|panic_info| { + /// if let Some(s) = panic_info.payload_as_str() { + /// println!("panic occurred: {s:?}"); + /// } else { + /// println!("panic occurred"); + /// } + /// })); + /// + /// panic!("Normal panic"); + /// ``` + #[must_use] + #[inline] + #[unstable(feature = "panic_payload_as_str", issue = "125175")] + pub fn payload_as_str(&self) -> Option<&str> { + if let Some(s) = self.payload.downcast_ref::<&str>() { + Some(s) + } else if let Some(s) = self.payload.downcast_ref::<String>() { + Some(s) + } else { + None + } + } + + /// Returns information about the location from which the panic originated, + /// if available. + /// + /// This method will currently always return [`Some`], but this may change + /// in future versions. + /// + /// # Examples + /// + /// ```should_panic + /// use std::panic; + /// + /// panic::set_hook(Box::new(|panic_info| { + /// if let Some(location) = panic_info.location() { + /// println!("panic occurred in file '{}' at line {}", + /// location.file(), + /// location.line(), + /// ); + /// } else { + /// println!("panic occurred but can't get location information..."); + /// } + /// })); + /// + /// panic!("Normal panic"); + /// ``` + #[must_use] + #[inline] + #[stable(feature = "panic_hooks", since = "1.10.0")] + pub fn location(&self) -> Option<&Location<'_>> { + // NOTE: If this is changed to sometimes return None, + // deal with that case in std::panicking::default_hook and core::panicking::panic_fmt. + Some(&self.location) + } + + /// Returns whether the panic handler is allowed to unwind the stack from + /// the point where the panic occurred. + /// + /// This is true for most kinds of panics with the exception of panics + /// caused by trying to unwind out of a `Drop` implementation or a function + /// whose ABI does not support unwinding. + /// + /// It is safe for a panic handler to unwind even when this function returns + /// false, however this will simply cause the panic handler to be called + /// again. + #[must_use] + #[inline] + #[unstable(feature = "panic_can_unwind", issue = "92988")] + pub fn can_unwind(&self) -> bool { + self.can_unwind + } + + #[unstable( + feature = "panic_internals", + reason = "internal details of the implementation of the `panic!` and related macros", + issue = "none" + )] + #[doc(hidden)] + #[inline] + pub fn force_no_backtrace(&self) -> bool { + self.force_no_backtrace + } +} + +#[stable(feature = "panic_hook_display", since = "1.26.0")] +impl fmt::Display for PanicHookInfo<'_> { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("panicked at ")?; + self.location.fmt(formatter)?; + if let Some(payload) = self.payload.downcast_ref::<&'static str>() { + formatter.write_str(":\n")?; + formatter.write_str(payload)?; + } else if let Some(payload) = self.payload.downcast_ref::<String>() { + formatter.write_str(":\n")?; + formatter.write_str(payload)?; + } + Ok(()) + } +} + #[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] #[allow_internal_unstable(libstd_sys_internals, const_format_args, panic_internals, rt)] @@ -43,7 +247,7 @@ pub use crate::panicking::{set_hook, take_hook}; pub use crate::panicking::update_hook; #[stable(feature = "panic_hooks", since = "1.10.0")] -pub use core::panic::{Location, PanicInfo}; +pub use core::panic::Location; #[stable(feature = "catch_unwind", since = "1.9.0")] pub use core::panic::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe}; @@ -53,7 +257,7 @@ pub use core::panic::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe}; /// The message can be of any (`Any + Send`) type, not just strings. /// /// The message is wrapped in a `Box<'static + Any + Send>`, which can be -/// accessed later using [`PanicInfo::payload`]. +/// accessed later using [`PanicHookInfo::payload`]. /// /// See the [`panic!`] macro for more information about panicking. #[stable(feature = "panic_any", since = "1.51.0")] |
