From 741096061c5a3dd65677058e4ad00b50a600bf91 Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Sun, 23 Apr 2023 15:24:34 +0200 Subject: [PATCH] format panic message only once --- library/std/src/panicking.rs | 26 ++++++++++++------------ tests/ui/panics/fmt-only-once.rs | 21 +++++++++++++++++++ tests/ui/panics/fmt-only-once.run.stderr | 3 +++ 3 files changed, 37 insertions(+), 13 deletions(-) create mode 100644 tests/ui/panics/fmt-only-once.rs create mode 100644 tests/ui/panics/fmt-only-once.run.stderr diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index ca4cf68ad54e2..b51f513f46eeb 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -249,20 +249,20 @@ fn default_hook(info: &PanicInfo<'_>) { let name = thread.as_ref().and_then(|t| t.name()).unwrap_or(""); let write = |err: &mut dyn crate::io::Write| { - // Use the panic message directly if available, otherwise take it from - // the payload. - if let Some(msg) = info.message() { - let _ = writeln!(err, "thread '{name}' panicked at '{msg}', {location}"); + // The std panic runtime always sets a `&str` or `String` payload for `panic!` and related + // macros with the formatted message. + // We try using the payload first to avoid formatting the message twice. + let msg: &dyn fmt::Display = if let Some(s) = info.payload().downcast_ref::<&'static str>() + { + s + } else if let Some(s) = info.payload().downcast_ref::() { + s + } else if let Some(msg) = info.message() { + msg } else { - let msg = if let Some(s) = info.payload().downcast_ref::<&'static str>() { - *s - } else if let Some(s) = info.payload().downcast_ref::() { - &s[..] - } else { - "Box" - }; - let _ = writeln!(err, "thread '{name}' panicked at '{msg}', {location}"); - } + &"Box" + }; + let _ = writeln!(err, "thread '{name}' panicked at '{msg}', {location}"); static FIRST_PANIC: AtomicBool = AtomicBool::new(true); diff --git a/tests/ui/panics/fmt-only-once.rs b/tests/ui/panics/fmt-only-once.rs new file mode 100644 index 0000000000000..6211bf961b3bc --- /dev/null +++ b/tests/ui/panics/fmt-only-once.rs @@ -0,0 +1,21 @@ +// run-fail +// check-run-results +// exec-env:RUST_BACKTRACE=0 + +// Test that we format the panic message only once. +// Regression test for https://github.com/rust-lang/rust/issues/110717 + +use std::fmt; + +struct PrintOnFmt; + +impl fmt::Display for PrintOnFmt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + eprintln!("fmt"); + f.write_str("PrintOnFmt") + } +} + +fn main() { + panic!("{}", PrintOnFmt) +} diff --git a/tests/ui/panics/fmt-only-once.run.stderr b/tests/ui/panics/fmt-only-once.run.stderr new file mode 100644 index 0000000000000..39bd06881ad05 --- /dev/null +++ b/tests/ui/panics/fmt-only-once.run.stderr @@ -0,0 +1,3 @@ +fmt +thread 'main' panicked at 'PrintOnFmt', $DIR/fmt-only-once.rs:20:5 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace