Skip to content

Commit

Permalink
Split core's PanicInfo and std's PanicInfo.
Browse files Browse the repository at this point in the history
  • Loading branch information
m-ou-se committed Jun 11, 2024
1 parent c8170e6 commit 702405e
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 97 deletions.
80 changes: 7 additions & 73 deletions core/src/panic/panic_info.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,14 @@
use crate::any::Any;
use crate::fmt;
use crate::panic::Location;

/// A struct providing information about a panic.
///
/// `PanicInfo` structure is passed to a panic hook set by the [`set_hook`]
/// function.
///
/// [`set_hook`]: ../../std/panic/fn.set_hook.html
///
/// # Examples
///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|panic_info| {
/// println!("panic occurred: {panic_info}");
/// }));
///
/// panic!("critical system failure");
/// ```
/// A `PanicInfo` structure is passed to the panic handler defined by `#[panic_handler]`.
#[lang = "panic_info"]
#[stable(feature = "panic_hooks", since = "1.10.0")]
#[derive(Debug)]
pub struct PanicInfo<'a> {
payload: &'a (dyn Any + Send),
message: Option<&'a fmt::Arguments<'a>>,
message: fmt::Arguments<'a>,
location: &'a Location<'a>,
can_unwind: bool,
force_no_backtrace: bool,
Expand All @@ -40,59 +23,20 @@ impl<'a> PanicInfo<'a> {
#[doc(hidden)]
#[inline]
pub fn internal_constructor(
message: Option<&'a fmt::Arguments<'a>>,
message: fmt::Arguments<'a>,
location: &'a Location<'a>,
can_unwind: bool,
force_no_backtrace: bool,
) -> Self {
struct NoPayload;
PanicInfo { location, message, payload: &NoPayload, can_unwind, force_no_backtrace }
}

#[unstable(
feature = "panic_internals",
reason = "internal details of the implementation of the `panic!` and related macros",
issue = "none"
)]
#[doc(hidden)]
#[inline]
pub fn set_payload(&mut self, info: &'a (dyn Any + Send)) {
self.payload = info;
}

/// Returns the payload associated with the panic.
///
/// This will commonly, but not always, be 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 {
/// println!("panic occurred");
/// }
/// }));
///
/// panic!("Normal panic");
/// ```
#[must_use]
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub fn payload(&self) -> &(dyn Any + Send) {
self.payload
PanicInfo { location, message, can_unwind, force_no_backtrace }
}

/// If the `panic!` macro from the `core` crate (not from `std`)
/// was used with a formatting string and some additional arguments,
/// returns that message ready to be used for example with [`fmt::write`]
#[must_use]
#[unstable(feature = "panic_info_message", issue = "66745")]
pub fn message(&self) -> Option<&fmt::Arguments<'_>> {
pub fn message(&self) -> fmt::Arguments<'_> {
self.message
}

Expand Down Expand Up @@ -161,18 +105,8 @@ impl fmt::Display for PanicInfo<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("panicked at ")?;
self.location.fmt(formatter)?;
formatter.write_str(":")?;
if let Some(message) = self.message {
formatter.write_str("\n")?;
formatter.write_fmt(*message)?;
} else if let Some(payload) = self.payload.downcast_ref::<&'static str>() {
formatter.write_str("\n")?;
formatter.write_str(payload)?;
}
// NOTE: we cannot use downcast_ref::<String>() here
// since String is not available in core!
// The payload is a String when `std::panic!` is called with multiple arguments,
// but in that case the message is also available.
formatter.write_str(":\n")?;
formatter.write_fmt(self.message)?;
Ok(())
}
}
4 changes: 2 additions & 2 deletions core/src/panicking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
}

let pi = PanicInfo::internal_constructor(
Some(&fmt),
fmt,
Location::caller(),
/* can_unwind */ true,
/* force_no_backtrace */ false,
Expand Down Expand Up @@ -100,7 +100,7 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo

// PanicInfo with the `can_unwind` flag set to false forces an abort.
let pi = PanicInfo::internal_constructor(
Some(&fmt),
&fmt,
Location::caller(),
/* can_unwind */ false,
force_no_backtrace,
Expand Down
153 changes: 152 additions & 1 deletion std/src/panic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,162 @@

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;

/// A struct providing information about a panic.
///
/// `PanicInfo` structure is passed to a panic hook set by the [`set_hook`]
/// function.
///
/// [`set_hook`]: ../../std/panic/fn.set_hook.html
///
/// # Examples
///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|panic_info| {
/// println!("panic occurred: {panic_info}");
/// }));
///
/// panic!("critical system failure");
/// ```
#[stable(feature = "panic_hooks", since = "1.10.0")]
#[derive(Debug)]
pub struct PanicInfo<'a> {
payload: &'a (dyn Any + Send),
location: &'a Location<'a>,
can_unwind: bool,
force_no_backtrace: bool,
}

impl<'a> PanicInfo<'a> {
#[unstable(feature = "panic_internals", issue = "none")]
#[doc(hidden)]
#[inline]
pub fn internal_constructor(
location: &'a Location<'a>,
can_unwind: bool,
force_no_backtrace: bool,
) -> Self {
struct NoPayload;
PanicInfo { payload: &NoPayload, location, can_unwind, force_no_backtrace }
}

#[unstable(feature = "panic_internals", issue = "none")]
#[doc(hidden)]
#[inline]
pub fn set_payload(&mut self, info: &'a (dyn Any + Send)) {
self.payload = info;
}

/// Returns the payload associated with the panic.
///
/// This will commonly, but not always, be 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 {
/// println!("panic occurred");
/// }
/// }));
///
/// panic!("Normal panic");
/// ```
#[must_use]
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub fn payload(&self) -> &(dyn Any + Send) {
self.payload
}

/// 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]
#[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]
#[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 PanicInfo<'_> {
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)]
Expand Down Expand Up @@ -43,7 +194,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};
Expand Down
37 changes: 16 additions & 21 deletions std/src/panicking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@

#![deny(unsafe_op_in_unsafe_fn)]

use crate::panic::BacktraceStyle;
use core::panic::{Location, PanicInfo, PanicPayload};
use crate::panic::{BacktraceStyle, PanicInfo};
use core::panic::{Location, PanicPayload};

use crate::any::Any;
use crate::fmt;
Expand Down Expand Up @@ -597,7 +597,7 @@ pub fn panicking() -> bool {
/// Entry point of panics from the core crate (`panic_impl` lang item).
#[cfg(not(any(test, doctest)))]
#[panic_handler]
pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
pub fn begin_panic_handler(info: &core::panic::PanicInfo<'_>) -> ! {
struct FormatStringPayload<'a> {
inner: &'a fmt::Arguments<'a>,
string: Option<String>,
Expand Down Expand Up @@ -648,22 +648,20 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
}

let loc = info.location().unwrap(); // The current implementation always returns Some
let msg = info.message().unwrap(); // The current implementation always returns Some
let msg = info.message();
crate::sys_common::backtrace::__rust_end_short_backtrace(move || {
// FIXME: can we just pass `info` along rather than taking it apart here, only to have
// `rust_panic_with_hook` construct a new `PanicInfo`?
if let Some(msg) = msg.as_str() {
if let Some(s) = msg.as_str() {
rust_panic_with_hook(
&mut StaticStrPayload(msg),
info.message(),
&mut StaticStrPayload(s),
Some(msg),
loc,
info.can_unwind(),
info.force_no_backtrace(),
);
} else {
rust_panic_with_hook(
&mut FormatStringPayload::new(msg),
info.message(),
&mut FormatStringPayload::new(&msg),
Some(msg),
loc,
info.can_unwind(),
info.force_no_backtrace(),
Expand Down Expand Up @@ -740,7 +738,7 @@ pub const fn begin_panic<M: Any + Send>(msg: M) -> ! {
/// abort or unwind.
fn rust_panic_with_hook(
payload: &mut dyn PanicPayload,
message: Option<&fmt::Arguments<'_>>,
message: Option<fmt::Arguments<'_>>,
location: &Location<'_>,
can_unwind: bool,
force_no_backtrace: bool,
Expand All @@ -767,20 +765,17 @@ fn rust_panic_with_hook(
panic_count::MustAbort::AlwaysAbort => {
// Unfortunately, this does not print a backtrace, because creating
// a `Backtrace` will allocate, which we must avoid here.
let panicinfo = PanicInfo::internal_constructor(
message,
location,
can_unwind,
force_no_backtrace,
);
rtprintpanic!("{panicinfo}\npanicked after panic::always_abort(), aborting.\n");
if let Some(message) = message {
rtprintpanic!("aborting due to panic at {location}:\n{message}\n");
} else {
rtprintpanic!("aborting due to panic at {location}\n");
}
}
}
crate::sys::abort_internal();
}

let mut info =
PanicInfo::internal_constructor(message, location, can_unwind, force_no_backtrace);
let mut info = PanicInfo::internal_constructor(location, can_unwind, force_no_backtrace);
let hook = HOOK.read().unwrap_or_else(PoisonError::into_inner);
match *hook {
// Some platforms (like wasm) know that printing to stderr won't ever actually
Expand Down

0 comments on commit 702405e

Please sign in to comment.