Skip to content

Commit 9b6bdf3

Browse files
authored
Rollup merge of rust-lang#110721 - lukas-code:panic-fmt, r=Amanieu
format panic message only once For `panic!` and friends, the `std` panic runtime will always set the `.payload()` of `PanicInfo` to the formatted string. The linked issues show that formatting the message twice can cause problems, so we simply print the already formatted message instead of formatting it again. We can't remove the preformatted payload, because it can be observed by custom panic hooks. fixes rust-lang#110717 fixes rust-itertools/itertools#694 cc `@Amanieu` who broke this in rust-lang#109507
2 parents 1a73464 + 7410960 commit 9b6bdf3

File tree

3 files changed

+37
-13
lines changed

3 files changed

+37
-13
lines changed

library/std/src/panicking.rs

+13-13
Original file line numberDiff line numberDiff line change
@@ -249,20 +249,20 @@ fn default_hook(info: &PanicInfo<'_>) {
249249
let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>");
250250

251251
let write = |err: &mut dyn crate::io::Write| {
252-
// Use the panic message directly if available, otherwise take it from
253-
// the payload.
254-
if let Some(msg) = info.message() {
255-
let _ = writeln!(err, "thread '{name}' panicked at '{msg}', {location}");
252+
// The std panic runtime always sets a `&str` or `String` payload for `panic!` and related
253+
// macros with the formatted message.
254+
// We try using the payload first to avoid formatting the message twice.
255+
let msg: &dyn fmt::Display = if let Some(s) = info.payload().downcast_ref::<&'static str>()
256+
{
257+
s
258+
} else if let Some(s) = info.payload().downcast_ref::<String>() {
259+
s
260+
} else if let Some(msg) = info.message() {
261+
msg
256262
} else {
257-
let msg = if let Some(s) = info.payload().downcast_ref::<&'static str>() {
258-
*s
259-
} else if let Some(s) = info.payload().downcast_ref::<String>() {
260-
&s[..]
261-
} else {
262-
"Box<dyn Any>"
263-
};
264-
let _ = writeln!(err, "thread '{name}' panicked at '{msg}', {location}");
265-
}
263+
&"Box<dyn Any>"
264+
};
265+
let _ = writeln!(err, "thread '{name}' panicked at '{msg}', {location}");
266266

267267
static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
268268

tests/ui/panics/fmt-only-once.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// run-fail
2+
// check-run-results
3+
// exec-env:RUST_BACKTRACE=0
4+
5+
// Test that we format the panic message only once.
6+
// Regression test for https://github.com/rust-lang/rust/issues/110717
7+
8+
use std::fmt;
9+
10+
struct PrintOnFmt;
11+
12+
impl fmt::Display for PrintOnFmt {
13+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14+
eprintln!("fmt");
15+
f.write_str("PrintOnFmt")
16+
}
17+
}
18+
19+
fn main() {
20+
panic!("{}", PrintOnFmt)
21+
}
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fmt
2+
thread 'main' panicked at 'PrintOnFmt', $DIR/fmt-only-once.rs:20:5
3+
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

0 commit comments

Comments
 (0)