Skip to content

Commit d9deb38

Browse files
authored
Rollup merge of rust-lang#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 rust-lang#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`): https://github.com/rust-lang/rust/blob/0692db1a9082380e027f354912229dfd6af37e78/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: rust-lang#115561 (comment)) --- 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()`.
2 parents 3ea5e23 + a6e23b1 commit d9deb38

File tree

13 files changed

+353
-181
lines changed

13 files changed

+353
-181
lines changed

compiler/rustc_driver_impl/src/lib.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ use std::ffi::OsString;
5252
use std::fmt::Write as _;
5353
use std::fs::{self, File};
5454
use std::io::{self, IsTerminal, Read, Write};
55-
use std::panic::{self, catch_unwind, PanicInfo};
55+
use std::panic::{self, catch_unwind, PanicHookInfo};
5656
use std::path::PathBuf;
5757
use std::process::{self, Command, Stdio};
5858
use std::str;
@@ -1366,11 +1366,10 @@ pub fn install_ice_hook(
13661366
let using_internal_features = Arc::new(std::sync::atomic::AtomicBool::default());
13671367
let using_internal_features_hook = using_internal_features.clone();
13681368
panic::update_hook(Box::new(
1369-
move |default_hook: &(dyn Fn(&PanicInfo<'_>) + Send + Sync + 'static),
1370-
info: &PanicInfo<'_>| {
1369+
move |default_hook: &(dyn Fn(&PanicHookInfo<'_>) + Send + Sync + 'static),
1370+
info: &PanicHookInfo<'_>| {
13711371
// Lock stderr to prevent interleaving of concurrent panics.
13721372
let _guard = io::stderr().lock();
1373-
13741373
// If the error was caused by a broken pipe then this is not a bug.
13751374
// Write the error and return immediately. See #98700.
13761375
#[cfg(windows)]
@@ -1431,7 +1430,7 @@ pub fn install_ice_hook(
14311430
/// When `install_ice_hook` is called, this function will be called as the panic
14321431
/// hook.
14331432
fn report_ice(
1434-
info: &panic::PanicInfo<'_>,
1433+
info: &panic::PanicHookInfo<'_>,
14351434
bug_report_url: &str,
14361435
extra_info: fn(&DiagCtxt),
14371436
using_internal_features: &AtomicBool,

library/core/src/error.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ The following are the primary interfaces of the panic system and the
1717
responsibilities they cover:
1818

1919
* [`panic!`] and [`panic_any`] (Constructing, Propagated automatically)
20-
* [`PanicInfo`] (Reporting)
21-
* [`set_hook`], [`take_hook`], and [`#[panic_handler]`][panic-handler] (Reporting)
20+
* [`set_hook`], [`take_hook`], and [`PanicHookInfo`] (Reporting)
21+
* [`#[panic_handler]`][panic-handler] and [`PanicInfo`] (Reporting in no_std)
2222
* [`catch_unwind`] and [`resume_unwind`] (Discarding, Propagating)
2323

2424
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
125125
should be available and executable by the current user".
126126

127127
[`panic_any`]: ../../std/panic/fn.panic_any.html
128+
[`PanicHookInfo`]: ../../std/panic/struct.PanicHookInfo.html
128129
[`PanicInfo`]: crate::panic::PanicInfo
129130
[`catch_unwind`]: ../../std/panic/fn.catch_unwind.html
130131
[`resume_unwind`]: ../../std/panic/fn.resume_unwind.html

library/core/src/panic.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ pub macro unreachable_2021 {
144144
/// use.
145145
#[unstable(feature = "std_internals", issue = "none")]
146146
#[doc(hidden)]
147-
pub unsafe trait PanicPayload {
147+
pub unsafe trait PanicPayload: crate::fmt::Display {
148148
/// Take full ownership of the contents.
149149
/// The return type is actually `Box<dyn Any + Send>`, but we cannot use `Box` in core.
150150
///
@@ -157,4 +157,9 @@ pub unsafe trait PanicPayload {
157157

158158
/// Just borrow the contents.
159159
fn get(&mut self) -> &(dyn Any + Send);
160+
161+
/// Try to borrow the contents as `&str`, if possible without doing any allocations.
162+
fn as_str(&mut self) -> Option<&str> {
163+
None
164+
}
160165
}

library/core/src/panic/location.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ use crate::fmt;
22

33
/// A struct containing information about the location of a panic.
44
///
5-
/// This structure is created by [`PanicInfo::location()`].
5+
/// This structure is created by [`PanicHookInfo::location()`] and [`PanicInfo::location()`].
66
///
77
/// [`PanicInfo::location()`]: crate::panic::PanicInfo::location
8+
/// [`PanicHookInfo::location()`]: ../../std/panic/struct.PanicHookInfo.html#method.location
89
///
910
/// # Examples
1011
///

library/core/src/panic/panic_info.rs

+28-78
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,40 @@
1-
use crate::any::Any;
21
use crate::fmt;
32
use crate::panic::Location;
43

54
/// A struct providing information about a panic.
65
///
7-
/// `PanicInfo` structure is passed to a panic hook set by the [`set_hook`]
8-
/// function.
6+
/// A `PanicInfo` structure is passed to the panic handler defined by `#[panic_handler]`.
97
///
10-
/// [`set_hook`]: ../../std/panic/fn.set_hook.html
8+
/// For the type used by the panic hook mechanism in `std`, see [`std::panic::PanicHookInfo`].
119
///
12-
/// # Examples
13-
///
14-
/// ```should_panic
15-
/// use std::panic;
16-
///
17-
/// panic::set_hook(Box::new(|panic_info| {
18-
/// println!("panic occurred: {panic_info}");
19-
/// }));
20-
///
21-
/// panic!("critical system failure");
22-
/// ```
10+
/// [`std::panic::PanicHookInfo`]: ../../std/panic/struct.PanicHookInfo.html
2311
#[lang = "panic_info"]
2412
#[stable(feature = "panic_hooks", since = "1.10.0")]
2513
#[derive(Debug)]
2614
pub struct PanicInfo<'a> {
27-
payload: &'a (dyn Any + Send),
28-
message: Option<&'a fmt::Arguments<'a>>,
15+
message: fmt::Arguments<'a>,
2916
location: &'a Location<'a>,
3017
can_unwind: bool,
3118
force_no_backtrace: bool,
3219
}
3320

3421
impl<'a> PanicInfo<'a> {
35-
#[unstable(
36-
feature = "panic_internals",
37-
reason = "internal details of the implementation of the `panic!` and related macros",
38-
issue = "none"
39-
)]
40-
#[doc(hidden)]
4122
#[inline]
42-
pub fn internal_constructor(
43-
message: Option<&'a fmt::Arguments<'a>>,
23+
pub(crate) fn new(
24+
message: fmt::Arguments<'a>,
4425
location: &'a Location<'a>,
4526
can_unwind: bool,
4627
force_no_backtrace: bool,
4728
) -> Self {
48-
struct NoPayload;
49-
PanicInfo { location, message, payload: &NoPayload, can_unwind, force_no_backtrace }
50-
}
51-
52-
#[unstable(
53-
feature = "panic_internals",
54-
reason = "internal details of the implementation of the `panic!` and related macros",
55-
issue = "none"
56-
)]
57-
#[doc(hidden)]
58-
#[inline]
59-
pub fn set_payload(&mut self, info: &'a (dyn Any + Send)) {
60-
self.payload = info;
61-
}
62-
63-
/// Returns the payload associated with the panic.
64-
///
65-
/// This will commonly, but not always, be a `&'static str` or [`String`].
66-
///
67-
/// [`String`]: ../../std/string/struct.String.html
68-
///
69-
/// # Examples
70-
///
71-
/// ```should_panic
72-
/// use std::panic;
73-
///
74-
/// panic::set_hook(Box::new(|panic_info| {
75-
/// if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
76-
/// println!("panic occurred: {s:?}");
77-
/// } else {
78-
/// println!("panic occurred");
79-
/// }
80-
/// }));
81-
///
82-
/// panic!("Normal panic");
83-
/// ```
84-
#[must_use]
85-
#[stable(feature = "panic_hooks", since = "1.10.0")]
86-
pub fn payload(&self) -> &(dyn Any + Send) {
87-
self.payload
29+
PanicInfo { location, message, can_unwind, force_no_backtrace }
8830
}
8931

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

@@ -128,6 +70,24 @@ impl<'a> PanicInfo<'a> {
12870
Some(&self.location)
12971
}
13072

73+
/// Returns the payload associated with the panic.
74+
///
75+
/// On `core::panic::PanicInfo`, this method never returns anything useful.
76+
/// It only exists because of compatibility with [`std::panic::PanicHookInfo`],
77+
/// which used to be the same type.
78+
///
79+
/// See [`std::panic::PanicHookInfo::payload`].
80+
///
81+
/// [`std::panic::PanicHookInfo`]: ../../std/panic/struct.PanicHookInfo.html
82+
/// [`std::panic::PanicHookInfo::payload`]: ../../std/panic/struct.PanicHookInfo.html#method.payload
83+
#[deprecated(since = "1.77.0", note = "this never returns anything useful")]
84+
#[stable(feature = "panic_hooks", since = "1.10.0")]
85+
#[allow(deprecated, deprecated_in_future)]
86+
pub fn payload(&self) -> &(dyn crate::any::Any + Send) {
87+
struct NoPayload;
88+
&NoPayload
89+
}
90+
13191
/// Returns whether the panic handler is allowed to unwind the stack from
13292
/// the point where the panic occurred.
13393
///
@@ -161,18 +121,8 @@ impl fmt::Display for PanicInfo<'_> {
161121
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
162122
formatter.write_str("panicked at ")?;
163123
self.location.fmt(formatter)?;
164-
formatter.write_str(":")?;
165-
if let Some(message) = self.message {
166-
formatter.write_str("\n")?;
167-
formatter.write_fmt(*message)?;
168-
} else if let Some(payload) = self.payload.downcast_ref::<&'static str>() {
169-
formatter.write_str("\n")?;
170-
formatter.write_str(payload)?;
171-
}
172-
// NOTE: we cannot use downcast_ref::<String>() here
173-
// since String is not available in core!
174-
// The payload is a String when `std::panic!` is called with multiple arguments,
175-
// but in that case the message is also available.
124+
formatter.write_str(":\n")?;
125+
formatter.write_fmt(self.message)?;
176126
Ok(())
177127
}
178128
}

library/core/src/panicking.rs

+13-11
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
//! Panic support for core
22
//!
3-
//! The core library cannot define panicking, but it does *declare* panicking. This
4-
//! means that the functions inside of core are allowed to panic, but to be
3+
//! In core, panicking is always done with a message, resulting in a `core::panic::PanicInfo`
4+
//! containing a `fmt::Arguments`. In std, however, panicking can be done with panic_any, which
5+
//! throws a `Box<dyn Any>` containing any type of value. Because of this,
6+
//! `std::panic::PanicHookInfo` is a different type, which contains a `&dyn Any` instead of a
7+
//! `fmt::Arguments`. std's panic handler will convert the `fmt::Arguments` to a `&dyn Any`
8+
//! containing either a `&'static str` or `String` containing the formatted message.
9+
//!
10+
//! The core library cannot define any panic handler, but it can invoke it.
11+
//! This means that the functions inside of core are allowed to panic, but to be
512
//! useful an upstream crate must define panicking for core to use. The current
613
//! interface for panicking is:
714
//!
@@ -10,11 +17,6 @@
1017
//! # { loop {} }
1118
//! ```
1219
//!
13-
//! This definition allows for panicking with any general message, but it does not
14-
//! allow for failing with a `Box<Any>` value. (`PanicInfo` just contains a `&(dyn Any + Send)`,
15-
//! for which we fill in a dummy value in `PanicInfo::internal_constructor`.)
16-
//! The reason for this is that core is not allowed to allocate.
17-
//!
1820
//! This module contains a few other panicking functions, but these are just the
1921
//! necessary lang items for the compiler. All panics are funneled through this
2022
//! one function. The actual symbol is declared through the `#[panic_handler]` attribute.
@@ -61,8 +63,8 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
6163
fn panic_impl(pi: &PanicInfo<'_>) -> !;
6264
}
6365

64-
let pi = PanicInfo::internal_constructor(
65-
Some(&fmt),
66+
let pi = PanicInfo::new(
67+
fmt,
6668
Location::caller(),
6769
/* can_unwind */ true,
6870
/* force_no_backtrace */ false,
@@ -99,8 +101,8 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo
99101
}
100102

101103
// PanicInfo with the `can_unwind` flag set to false forces an abort.
102-
let pi = PanicInfo::internal_constructor(
103-
Some(&fmt),
104+
let pi = PanicInfo::new(
105+
fmt,
104106
Location::caller(),
105107
/* can_unwind */ false,
106108
force_no_backtrace,

0 commit comments

Comments
 (0)