Auto merge of #126330 - m-ou-se:panic-message-type, r=Amanieu

Return opaque type from PanicInfo::message()

This changes the return type of the (unstable) PanicInfo::message() method to an opaque type (that implements Display). This allows for a bit more flexibility in the future.

See https://github.com/rust-lang/rust/issues/66745
This commit is contained in:
bors 2024-06-18 07:20:26 +00:00
commit f873ae029e
3 changed files with 79 additions and 11 deletions

View File

@ -12,6 +12,8 @@ use crate::any::Any;
pub use self::location::Location; pub use self::location::Location;
#[stable(feature = "panic_hooks", since = "1.10.0")] #[stable(feature = "panic_hooks", since = "1.10.0")]
pub use self::panic_info::PanicInfo; pub use self::panic_info::PanicInfo;
#[unstable(feature = "panic_info_message", issue = "66745")]
pub use self::panic_info::PanicMessage;
#[stable(feature = "catch_unwind", since = "1.9.0")] #[stable(feature = "catch_unwind", since = "1.9.0")]
pub use self::unwind_safe::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe}; pub use self::unwind_safe::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe};

View File

@ -1,4 +1,4 @@
use crate::fmt; use crate::fmt::{self, Display};
use crate::panic::Location; use crate::panic::Location;
/// A struct providing information about a panic. /// A struct providing information about a panic.
@ -18,6 +18,17 @@ pub struct PanicInfo<'a> {
force_no_backtrace: bool, force_no_backtrace: bool,
} }
/// A message that was given to the `panic!()` macro.
///
/// The [`Display`] implementation of this type will format the message with the arguments
/// that were given to the `panic!()` macro.
///
/// See [`PanicInfo::message`].
#[unstable(feature = "panic_info_message", issue = "66745")]
pub struct PanicMessage<'a> {
message: fmt::Arguments<'a>,
}
impl<'a> PanicInfo<'a> { impl<'a> PanicInfo<'a> {
#[inline] #[inline]
pub(crate) fn new( pub(crate) fn new(
@ -29,12 +40,26 @@ impl<'a> PanicInfo<'a> {
PanicInfo { location, message, can_unwind, force_no_backtrace } PanicInfo { location, message, can_unwind, force_no_backtrace }
} }
/// The message that was given to the `panic!` macro, /// The message that was given to the `panic!` macro.
/// ready to be formatted with e.g. [`fmt::write`]. ///
/// # Example
///
/// The type returned by this method implements `Display`, so it can
/// be passed directly to [`write!()`] and similar macros.
///
/// [`write!()`]: core::write
///
/// ```ignore (no_std)
/// #[panic_handler]
/// fn panic_handler(panic_info: &PanicInfo<'_>) -> ! {
/// write!(DEBUG_OUTPUT, "panicked: {}", panic_info.message());
/// loop {}
/// }
/// ```
#[must_use] #[must_use]
#[unstable(feature = "panic_info_message", issue = "66745")] #[unstable(feature = "panic_info_message", issue = "66745")]
pub fn message(&self) -> fmt::Arguments<'_> { pub fn message(&self) -> PanicMessage<'_> {
self.message PanicMessage { message: self.message }
} }
/// Returns information about the location from which the panic originated, /// Returns information about the location from which the panic originated,
@ -116,7 +141,7 @@ impl<'a> PanicInfo<'a> {
} }
#[stable(feature = "panic_hook_display", since = "1.26.0")] #[stable(feature = "panic_hook_display", since = "1.26.0")]
impl fmt::Display for PanicInfo<'_> { impl Display for PanicInfo<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("panicked at ")?; formatter.write_str("panicked at ")?;
self.location.fmt(formatter)?; self.location.fmt(formatter)?;
@ -125,3 +150,41 @@ impl fmt::Display for PanicInfo<'_> {
Ok(()) Ok(())
} }
} }
impl<'a> PanicMessage<'a> {
/// Get the formatted message, if it has no arguments to be formatted at runtime.
///
/// This can be used to avoid allocations in some cases.
///
/// # Guarantees
///
/// For `panic!("just a literal")`, this function is guaranteed to
/// return `Some("just a literal")`.
///
/// For most cases with placeholders, this function will return `None`.
///
/// See [`fmt::Arguments::as_str`] for details.
#[unstable(feature = "panic_info_message", issue = "66745")]
#[rustc_const_unstable(feature = "const_arguments_as_str", issue = "103900")]
#[must_use]
#[inline]
pub const fn as_str(&self) -> Option<&'static str> {
self.message.as_str()
}
}
#[unstable(feature = "panic_info_message", issue = "66745")]
impl Display for PanicMessage<'_> {
#[inline]
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_fmt(self.message)
}
}
#[unstable(feature = "panic_info_message", issue = "66745")]
impl fmt::Debug for PanicMessage<'_> {
#[inline]
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_fmt(self.message)
}
}

View File

@ -593,19 +593,18 @@ pub fn panicking() -> bool {
#[panic_handler] #[panic_handler]
pub fn begin_panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { pub fn begin_panic_handler(info: &core::panic::PanicInfo<'_>) -> ! {
struct FormatStringPayload<'a> { struct FormatStringPayload<'a> {
inner: &'a fmt::Arguments<'a>, inner: &'a core::panic::PanicMessage<'a>,
string: Option<String>, string: Option<String>,
} }
impl FormatStringPayload<'_> { impl FormatStringPayload<'_> {
fn fill(&mut self) -> &mut String { fn fill(&mut self) -> &mut String {
use crate::fmt::Write;
let inner = self.inner; let inner = self.inner;
// Lazily, the first time this gets called, run the actual string formatting. // Lazily, the first time this gets called, run the actual string formatting.
self.string.get_or_insert_with(|| { self.string.get_or_insert_with(|| {
let mut s = String::new(); let mut s = String::new();
let _err = s.write_fmt(*inner); let mut fmt = fmt::Formatter::new(&mut s);
let _err = fmt::Display::fmt(&inner, &mut fmt);
s s
}) })
} }
@ -627,7 +626,11 @@ pub fn begin_panic_handler(info: &core::panic::PanicInfo<'_>) -> ! {
impl fmt::Display for FormatStringPayload<'_> { impl fmt::Display for FormatStringPayload<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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) } if let Some(s) = &self.string {
f.write_str(s)
} else {
fmt::Display::fmt(&self.inner, f)
}
} }
} }