Rollup merge of #115974 - m-ou-se:panicinfo-and-panicinfo, r=Amanieu

Split core's PanicInfo and std's PanicInfo

`PanicInfo` is used in two ways:

1. As argument to the `#[panic_handler]` in `no_std` context.
2. As argument to the [panic hook](https://doc.rust-lang.org/stable/std/panic/fn.set_hook.html) in `std` context.

In situation 1, the `PanicInfo` always has a *message* (of type `fmt::Arguments`), but never a *payload* (of type `&dyn Any`).

In situation 2, the `PanicInfo` always has a *payload* (which is often a `String`), but not always a *message*.

Having these as the same type is annoying. It means we can't add `.message()` to the first one without also finding a way to properly support it on the second one. (Which is what https://github.com/rust-lang/rust/issues/66745 is blocked on.)

It also means that, because the implementation is in `core`, the implementation cannot make use of the `String` type (which doesn't exist in `core`): 0692db1a90/library/core/src/panic/panic_info.rs (L171-L172)

This also means that we cannot easily add a useful method like `PanicInfo::payload_as_str() -> Option<&str>` that works for both `&'static str` and `String` payloads.

I don't see any good reasons for these to be the same type, other than historical reasons.

---

This PR is makes 1 and 2 separate types. To try to avoid breaking existing code and reduce churn, the first one is still named `core::panic::PanicInfo`, and `std::panic::PanicInfo` is a new (deprecated) alias to `PanicHookInfo`. The crater run showed this as a viable option, since people write `core::` when defining a `#[panic_handler]` (because they're in `no_std`) and `std::` when writing a panic hook (since then they're definitely using `std`). On top of that, many definitions of a panic hook don't specify a type at all: they are written as a closure with an inferred argument type.

(Based on some thoughts I was having here: https://github.com/rust-lang/rust/pull/115561#issuecomment-1725830032)

---

For the release notes:

> We have renamed `std::panic::PanicInfo` to `std::panic::PanicHookInfo`. The old name will continue to work as an alias, but will result in a deprecation warning starting in Rust 1.82.0.
>
> `core::panic::PanicInfo` will remain unchanged, however, as this is now a *different type*.
>
> The reason is that these types have different roles: `std::panic::PanicHookInfo` is the argument to the [panic hook](https://doc.rust-lang.org/stable/std/panic/fn.set_hook.html) in std context (where panics can have an arbitrary payload), while `core::panic::PanicInfo` is the argument to the [`#[panic_handler]`](https://doc.rust-lang.org/nomicon/panic-handler.html) in no_std context (where panics always carry a formatted *message*). Separating these types allows us to add more useful methods to these types, such as `std::panic::PanicHookInfo::payload_as_str()` and `core::panic::PanicInfo::message()`.
This commit is contained in:
许杰友 Jieyou Xu (Joe) 2024-06-11 21:27:45 +01:00 committed by GitHub
commit d9deb38ec0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 353 additions and 181 deletions

View File

@ -52,7 +52,7 @@ use std::ffi::OsString;
use std::fmt::Write as _;
use std::fs::{self, File};
use std::io::{self, IsTerminal, Read, Write};
use std::panic::{self, catch_unwind, PanicInfo};
use std::panic::{self, catch_unwind, PanicHookInfo};
use std::path::PathBuf;
use std::process::{self, Command, Stdio};
use std::str;
@ -1366,11 +1366,10 @@ pub fn install_ice_hook(
let using_internal_features = Arc::new(std::sync::atomic::AtomicBool::default());
let using_internal_features_hook = using_internal_features.clone();
panic::update_hook(Box::new(
move |default_hook: &(dyn Fn(&PanicInfo<'_>) + Send + Sync + 'static),
info: &PanicInfo<'_>| {
move |default_hook: &(dyn Fn(&PanicHookInfo<'_>) + Send + Sync + 'static),
info: &PanicHookInfo<'_>| {
// Lock stderr to prevent interleaving of concurrent panics.
let _guard = io::stderr().lock();
// If the error was caused by a broken pipe then this is not a bug.
// Write the error and return immediately. See #98700.
#[cfg(windows)]
@ -1431,7 +1430,7 @@ pub fn install_ice_hook(
/// When `install_ice_hook` is called, this function will be called as the panic
/// hook.
fn report_ice(
info: &panic::PanicInfo<'_>,
info: &panic::PanicHookInfo<'_>,
bug_report_url: &str,
extra_info: fn(&DiagCtxt),
using_internal_features: &AtomicBool,

View File

@ -17,8 +17,8 @@ The following are the primary interfaces of the panic system and the
responsibilities they cover:
* [`panic!`] and [`panic_any`] (Constructing, Propagated automatically)
* [`PanicInfo`] (Reporting)
* [`set_hook`], [`take_hook`], and [`#[panic_handler]`][panic-handler] (Reporting)
* [`set_hook`], [`take_hook`], and [`PanicHookInfo`] (Reporting)
* [`#[panic_handler]`][panic-handler] and [`PanicInfo`] (Reporting in no_std)
* [`catch_unwind`] and [`resume_unwind`] (Discarding, Propagating)
The following are the primary interfaces of the error system and the
@ -125,6 +125,7 @@ expect-as-precondition style error messages remember to focus on the word
should be available and executable by the current user".
[`panic_any`]: ../../std/panic/fn.panic_any.html
[`PanicHookInfo`]: ../../std/panic/struct.PanicHookInfo.html
[`PanicInfo`]: crate::panic::PanicInfo
[`catch_unwind`]: ../../std/panic/fn.catch_unwind.html
[`resume_unwind`]: ../../std/panic/fn.resume_unwind.html

View File

@ -144,7 +144,7 @@ pub macro unreachable_2021 {
/// use.
#[unstable(feature = "std_internals", issue = "none")]
#[doc(hidden)]
pub unsafe trait PanicPayload {
pub unsafe trait PanicPayload: crate::fmt::Display {
/// Take full ownership of the contents.
/// The return type is actually `Box<dyn Any + Send>`, but we cannot use `Box` in core.
///
@ -157,4 +157,9 @@ pub unsafe trait PanicPayload {
/// Just borrow the contents.
fn get(&mut self) -> &(dyn Any + Send);
/// Try to borrow the contents as `&str`, if possible without doing any allocations.
fn as_str(&mut self) -> Option<&str> {
None
}
}

View File

@ -2,9 +2,10 @@ use crate::fmt;
/// A struct containing information about the location of a panic.
///
/// This structure is created by [`PanicInfo::location()`].
/// This structure is created by [`PanicHookInfo::location()`] and [`PanicInfo::location()`].
///
/// [`PanicInfo::location()`]: crate::panic::PanicInfo::location
/// [`PanicHookInfo::location()`]: ../../std/panic/struct.PanicHookInfo.html#method.location
///
/// # Examples
///

View File

@ -1,90 +1,32 @@
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.
/// A `PanicInfo` structure is passed to the panic handler defined by `#[panic_handler]`.
///
/// [`set_hook`]: ../../std/panic/fn.set_hook.html
/// For the type used by the panic hook mechanism in `std`, see [`std::panic::PanicHookInfo`].
///
/// # Examples
///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|panic_info| {
/// println!("panic occurred: {panic_info}");
/// }));
///
/// panic!("critical system failure");
/// ```
/// [`std::panic::PanicHookInfo`]: ../../std/panic/struct.PanicHookInfo.html
#[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,
}
impl<'a> PanicInfo<'a> {
#[unstable(
feature = "panic_internals",
reason = "internal details of the implementation of the `panic!` and related macros",
issue = "none"
)]
#[doc(hidden)]
#[inline]
pub fn internal_constructor(
message: Option<&'a fmt::Arguments<'a>>,
pub(crate) fn new(
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`)
@ -92,7 +34,7 @@ impl<'a> PanicInfo<'a> {
/// 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
}
@ -128,6 +70,24 @@ impl<'a> PanicInfo<'a> {
Some(&self.location)
}
/// Returns the payload associated with the panic.
///
/// On `core::panic::PanicInfo`, this method never returns anything useful.
/// It only exists because of compatibility with [`std::panic::PanicHookInfo`],
/// which used to be the same type.
///
/// See [`std::panic::PanicHookInfo::payload`].
///
/// [`std::panic::PanicHookInfo`]: ../../std/panic/struct.PanicHookInfo.html
/// [`std::panic::PanicHookInfo::payload`]: ../../std/panic/struct.PanicHookInfo.html#method.payload
#[deprecated(since = "1.77.0", note = "this never returns anything useful")]
#[stable(feature = "panic_hooks", since = "1.10.0")]
#[allow(deprecated, deprecated_in_future)]
pub fn payload(&self) -> &(dyn crate::any::Any + Send) {
struct NoPayload;
&NoPayload
}
/// Returns whether the panic handler is allowed to unwind the stack from
/// the point where the panic occurred.
///
@ -161,18 +121,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(())
}
}

View File

@ -1,7 +1,14 @@
//! Panic support for core
//!
//! The core library cannot define panicking, but it does *declare* panicking. This
//! means that the functions inside of core are allowed to panic, but to be
//! In core, panicking is always done with a message, resulting in a `core::panic::PanicInfo`
//! containing a `fmt::Arguments`. In std, however, panicking can be done with panic_any, which
//! throws a `Box<dyn Any>` containing any type of value. Because of this,
//! `std::panic::PanicHookInfo` is a different type, which contains a `&dyn Any` instead of a
//! `fmt::Arguments`. std's panic handler will convert the `fmt::Arguments` to a `&dyn Any`
//! containing either a `&'static str` or `String` containing the formatted message.
//!
//! The core library cannot define any panic handler, but it can invoke it.
//! This means that the functions inside of core are allowed to panic, but to be
//! useful an upstream crate must define panicking for core to use. The current
//! interface for panicking is:
//!
@ -10,11 +17,6 @@
//! # { loop {} }
//! ```
//!
//! This definition allows for panicking with any general message, but it does not
//! allow for failing with a `Box<Any>` value. (`PanicInfo` just contains a `&(dyn Any + Send)`,
//! for which we fill in a dummy value in `PanicInfo::internal_constructor`.)
//! The reason for this is that core is not allowed to allocate.
//!
//! This module contains a few other panicking functions, but these are just the
//! necessary lang items for the compiler. All panics are funneled through this
//! one function. The actual symbol is declared through the `#[panic_handler]` attribute.
@ -61,8 +63,8 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
fn panic_impl(pi: &PanicInfo<'_>) -> !;
}
let pi = PanicInfo::internal_constructor(
Some(&fmt),
let pi = PanicInfo::new(
fmt,
Location::caller(),
/* can_unwind */ true,
/* force_no_backtrace */ false,
@ -99,8 +101,8 @@ 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),
let pi = PanicInfo::new(
fmt,
Location::caller(),
/* can_unwind */ false,
force_no_backtrace,

View File

@ -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")]

View File

@ -9,8 +9,8 @@
#![deny(unsafe_op_in_unsafe_fn)]
use crate::panic::BacktraceStyle;
use core::panic::{Location, PanicInfo, PanicPayload};
use crate::panic::{BacktraceStyle, PanicHookInfo};
use core::panic::{Location, PanicPayload};
use crate::any::Any;
use crate::fmt;
@ -70,12 +70,12 @@ extern "C" fn __rust_foreign_exception() -> ! {
enum Hook {
Default,
Custom(Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send>),
Custom(Box<dyn Fn(&PanicHookInfo<'_>) + 'static + Sync + Send>),
}
impl Hook {
#[inline]
fn into_box(self) -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
fn into_box(self) -> Box<dyn Fn(&PanicHookInfo<'_>) + 'static + Sync + Send> {
match self {
Hook::Default => Box::new(default_hook),
Hook::Custom(hook) => hook,
@ -105,7 +105,7 @@ static HOOK: RwLock<Hook> = RwLock::new(Hook::Default);
///
/// [`take_hook`]: ./fn.take_hook.html
///
/// The hook is provided with a `PanicInfo` struct which contains information
/// The hook is provided with a `PanicHookInfo` struct which contains information
/// about the origin of the panic, including the payload passed to `panic!` and
/// the source code location from which the panic originated.
///
@ -129,7 +129,7 @@ static HOOK: RwLock<Hook> = RwLock::new(Hook::Default);
/// panic!("Normal panic");
/// ```
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub fn set_hook(hook: Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send>) {
pub fn set_hook(hook: Box<dyn Fn(&PanicHookInfo<'_>) + 'static + Sync + Send>) {
if thread::panicking() {
panic!("cannot modify the panic hook from a panicking thread");
}
@ -173,7 +173,7 @@ pub fn set_hook(hook: Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send>) {
/// ```
#[must_use]
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
pub fn take_hook() -> Box<dyn Fn(&PanicHookInfo<'_>) + 'static + Sync + Send> {
if thread::panicking() {
panic!("cannot modify the panic hook from a panicking thread");
}
@ -219,7 +219,7 @@ pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
#[unstable(feature = "panic_update_hook", issue = "92649")]
pub fn update_hook<F>(hook_fn: F)
where
F: Fn(&(dyn Fn(&PanicInfo<'_>) + Send + Sync + 'static), &PanicInfo<'_>)
F: Fn(&(dyn Fn(&PanicHookInfo<'_>) + Send + Sync + 'static), &PanicHookInfo<'_>)
+ Sync
+ Send
+ 'static,
@ -234,7 +234,7 @@ where
}
/// The default panic handler.
fn default_hook(info: &PanicInfo<'_>) {
fn default_hook(info: &PanicHookInfo<'_>) {
// If this is a double panic, make sure that we print a backtrace
// for this panic. Otherwise only print it if logging is enabled.
let backtrace = if info.force_no_backtrace() {
@ -248,13 +248,7 @@ fn default_hook(info: &PanicInfo<'_>) {
// The current implementation always returns `Some`.
let location = info.location().unwrap();
let msg = match info.payload().downcast_ref::<&'static str>() {
Some(s) => *s,
None => match info.payload().downcast_ref::<String>() {
Some(s) => &s[..],
None => "Box<dyn Any>",
},
};
let msg = payload_as_str(info.payload());
let thread = thread::try_current();
let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>");
@ -597,17 +591,13 @@ 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>,
}
impl<'a> FormatStringPayload<'a> {
fn new(inner: &'a fmt::Arguments<'a>) -> Self {
Self { inner, string: None }
}
impl FormatStringPayload<'_> {
fn fill(&mut self) -> &mut String {
use crate::fmt::Write;
@ -621,7 +611,7 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
}
}
unsafe impl<'a> PanicPayload for FormatStringPayload<'a> {
unsafe impl PanicPayload for FormatStringPayload<'_> {
fn take_box(&mut self) -> *mut (dyn Any + Send) {
// We do two allocations here, unfortunately. But (a) they're required with the current
// scheme, and (b) we don't handle panic + OOM properly anyway (see comment in
@ -635,6 +625,12 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
}
}
impl fmt::Display for FormatStringPayload<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(s) = &self.string { f.write_str(s) } else { f.write_fmt(*self.inner) }
}
}
struct StaticStrPayload(&'static str);
unsafe impl PanicPayload for StaticStrPayload {
@ -645,25 +641,31 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
fn get(&mut self) -> &(dyn Any + Send) {
&self.0
}
fn as_str(&mut self) -> Option<&str> {
Some(self.0)
}
}
impl fmt::Display for StaticStrPayload {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.0)
}
}
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),
loc,
info.can_unwind(),
info.force_no_backtrace(),
);
} else {
rust_panic_with_hook(
&mut FormatStringPayload::new(msg),
info.message(),
&mut FormatStringPayload { inner: &msg, string: None },
loc,
info.can_unwind(),
info.force_no_backtrace(),
@ -689,27 +691,10 @@ pub const fn begin_panic<M: Any + Send>(msg: M) -> ! {
intrinsics::abort()
}
let loc = Location::caller();
return crate::sys_common::backtrace::__rust_end_short_backtrace(move || {
rust_panic_with_hook(
&mut Payload::new(msg),
None,
loc,
/* can_unwind */ true,
/* force_no_backtrace */ false,
)
});
struct Payload<A> {
inner: Option<A>,
}
impl<A: Send + 'static> Payload<A> {
fn new(inner: A) -> Payload<A> {
Payload { inner: Some(inner) }
}
}
unsafe impl<A: Send + 'static> PanicPayload for Payload<A> {
fn take_box(&mut self) -> *mut (dyn Any + Send) {
// Note that this should be the only allocation performed in this code path. Currently
@ -731,6 +716,35 @@ pub const fn begin_panic<M: Any + Send>(msg: M) -> ! {
}
}
}
impl<A: 'static> fmt::Display for Payload<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.inner {
Some(a) => f.write_str(payload_as_str(a)),
None => process::abort(),
}
}
}
let loc = Location::caller();
crate::sys_common::backtrace::__rust_end_short_backtrace(move || {
rust_panic_with_hook(
&mut Payload { inner: Some(msg) },
loc,
/* can_unwind */ true,
/* force_no_backtrace */ false,
)
})
}
fn payload_as_str(payload: &dyn Any) -> &str {
if let Some(&s) = payload.downcast_ref::<&'static str>() {
s
} else if let Some(s) = payload.downcast_ref::<String>() {
s.as_str()
} else {
"Box<dyn Any>"
}
}
/// Central point for dispatching panics.
@ -740,7 +754,6 @@ 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<'_>>,
location: &Location<'_>,
can_unwind: bool,
force_no_backtrace: bool,
@ -754,35 +767,21 @@ fn rust_panic_with_hook(
// Don't try to format the message in this case, perhaps that is causing the
// recursive panics. However if the message is just a string, no user-defined
// code is involved in printing it, so that is risk-free.
let msg_str = message.and_then(|m| m.as_str()).map(|m| [m]);
let message = msg_str.as_ref().map(|m| fmt::Arguments::new_const(m));
let panicinfo = PanicInfo::internal_constructor(
message.as_ref(),
location,
can_unwind,
force_no_backtrace,
let message: &str = payload.as_str().unwrap_or_default();
rtprintpanic!(
"panicked at {location}:\n{message}\nthread panicked while processing panic. aborting.\n"
);
rtprintpanic!("{panicinfo}\nthread panicked while processing panic. aborting.\n");
}
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");
rtprintpanic!("aborting due to panic at {location}:\n{payload}\n");
}
}
crate::sys::abort_internal();
}
let mut info =
PanicInfo::internal_constructor(message, location, can_unwind, force_no_backtrace);
let hook = HOOK.read().unwrap_or_else(PoisonError::into_inner);
match *hook {
match *HOOK.read().unwrap_or_else(PoisonError::into_inner) {
// Some platforms (like wasm) know that printing to stderr won't ever actually
// print anything, and if that's the case we can skip the default
// hook. Since string formatting happens lazily when calling `payload`
@ -791,15 +790,17 @@ fn rust_panic_with_hook(
// formatting.)
Hook::Default if panic_output().is_none() => {}
Hook::Default => {
info.set_payload(payload.get());
default_hook(&info);
default_hook(&PanicHookInfo::new(
location,
payload.get(),
can_unwind,
force_no_backtrace,
));
}
Hook::Custom(ref hook) => {
info.set_payload(payload.get());
hook(&info);
hook(&PanicHookInfo::new(location, payload.get(), can_unwind, force_no_backtrace));
}
};
drop(hook);
}
// Indicate that we have finished executing the panic hook. After this point
// it is fine if there is a panic while executing destructors, as long as it
@ -835,6 +836,12 @@ pub fn rust_panic_without_hook(payload: Box<dyn Any + Send>) -> ! {
}
}
impl fmt::Display for RewrapBox {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(payload_as_str(&self.0))
}
}
rust_panic(&mut RewrapBox(payload))
}

View File

@ -58,7 +58,7 @@ use std::{
env, io,
io::prelude::Write,
mem::ManuallyDrop,
panic::{self, catch_unwind, AssertUnwindSafe, PanicInfo},
panic::{self, catch_unwind, AssertUnwindSafe, PanicHookInfo},
process::{self, Command, Termination},
sync::mpsc::{channel, Sender},
sync::{Arc, Mutex},
@ -123,7 +123,7 @@ pub fn test_main(args: &[String], tests: Vec<TestDescAndFn>, options: Option<Opt
// from interleaving with the panic message or appearing after it.
let builtin_panic_hook = panic::take_hook();
let hook = Box::new({
move |info: &'_ PanicInfo<'_>| {
move |info: &'_ PanicHookInfo<'_>| {
if !info.can_unwind() {
std::mem::forget(std::io::stderr().lock());
let mut stdout = ManuallyDrop::new(std::io::stdout().lock());
@ -726,7 +726,7 @@ fn spawn_test_subprocess(
fn run_test_in_spawned_subprocess(desc: TestDesc, runnable_test: RunnableTest) -> ! {
let builtin_panic_hook = panic::take_hook();
let record_result = Arc::new(move |panic_info: Option<&'_ PanicInfo<'_>>| {
let record_result = Arc::new(move |panic_info: Option<&'_ PanicHookInfo<'_>>| {
let test_result = match panic_info {
Some(info) => calc_result(&desc, Err(info.payload()), &None, &None),
None => calc_result(&desc, Ok(()), &None, &None),

View File

@ -5,7 +5,9 @@
#![feature(lang_items)]
use std::panic::PanicInfo;
extern crate core;
use core::panic::PanicInfo;
#[lang = "panic_impl"]
fn panic_impl(info: &PanicInfo) -> ! {

View File

@ -1,5 +1,5 @@
error[E0152]: found duplicate lang item `panic_impl`
--> $DIR/duplicate_entry_error.rs:11:1
--> $DIR/duplicate_entry_error.rs:13:1
|
LL | / fn panic_impl(info: &PanicInfo) -> ! {
LL | |

View File

@ -1,8 +1,9 @@
//@ normalize-stderr-test "loaded from .*libstd-.*.rlib" -> "loaded from SYSROOT/libstd-*.rlib"
//@ error-pattern: found duplicate lang item `panic_impl`
extern crate core;
use std::panic::PanicInfo;
use core::panic::PanicInfo;
#[panic_handler]
fn panic(info: PanicInfo) -> ! {

View File

@ -1,5 +1,5 @@
error[E0152]: found duplicate lang item `panic_impl`
--> $DIR/panic-handler-std.rs:8:1
--> $DIR/panic-handler-std.rs:9:1
|
LL | / fn panic(info: PanicInfo) -> ! {
LL | | loop {}