diff --git a/src/libcore/panic.rs b/src/libcore/panic.rs index cdd38449a1b..99b372d92c8 100644 --- a/src/libcore/panic.rs +++ b/src/libcore/panic.rs @@ -266,6 +266,16 @@ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { #[unstable(feature = "std_internals", issue = "0")] #[doc(hidden)] pub unsafe trait BoxMeUp { - fn box_me_up(&mut self) -> *mut (dyn Any + Send); + /// Take full ownership of the contents. + /// The return type is actually `Box`, but we cannot use `Box` in libcore. + /// + /// After this method got called, only some dummy default value is left in `self`. + /// Calling this method twice, or calling `get` after calling this method, is an error. + /// + /// The argument is borrowed because the panic runtime (`__rust_start_panic`) only + /// gets a borrowed `dyn BoxMeUp`. + fn take_box(&mut self) -> *mut (dyn Any + Send); + + /// Just borrow the contents. fn get(&mut self) -> &(dyn Any + Send); } diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs index 4833194be37..5a8d647396d 100644 --- a/src/libcore/panicking.rs +++ b/src/libcore/panicking.rs @@ -11,13 +11,13 @@ //! ``` //! //! This definition allows for panicking with any general message, but it does not -//! allow for failing with a `Box` value. The reason for this is that libcore -//! is not allowed to allocate. +//! allow for failing with a `Box` 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 libcore 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. Currently, the actual symbol is declared in the standard -//! library, but the location of this may change over time. +//! one function. The actual symbol is declared through the `#[panic_handler]` attribute. // ignore-tidy-undocumented-unsafe @@ -72,6 +72,7 @@ pub fn panic_fmt(fmt: fmt::Arguments<'_>, location: &Location<'_>) -> ! { } // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call + // that gets resolved to the `#[panic_handler]` function. extern "Rust" { #[lang = "panic_impl"] fn panic_impl(pi: &PanicInfo<'_>) -> !; diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs index d97a7a8a87d..0c834e5c2a0 100644 --- a/src/libpanic_unwind/lib.rs +++ b/src/libpanic_unwind/lib.rs @@ -94,5 +94,5 @@ #[unwind(allowed)] pub unsafe extern "C" fn __rust_start_panic(payload: usize) -> u32 { let payload = payload as *mut &mut dyn BoxMeUp; - imp::panic(Box::from_raw((*payload).box_me_up())) + imp::panic(Box::from_raw((*payload).take_box())) } diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs index e36496d4c1c..cd024068d2d 100644 --- a/src/libstd/panic.rs +++ b/src/libstd/panic.rs @@ -425,5 +425,5 @@ pub fn catch_unwind R + UnwindSafe, R>(f: F) -> Result { /// ``` #[stable(feature = "resume_unwind", since = "1.9.0")] pub fn resume_unwind(payload: Box) -> ! { - panicking::update_count_then_panic(payload) + panicking::rust_panic_without_hook(payload) } diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 6819a4a04d7..c028ddcd676 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -20,6 +20,7 @@ use crate::sys_common::{thread_info, util}; use crate::sys_common::backtrace::{self, RustBacktrace}; use crate::thread; +use crate::process; #[cfg(not(test))] use crate::io::set_panic; @@ -46,6 +47,8 @@ fn __rust_maybe_catch_panic(f: fn(*mut u8), vtable_ptr: *mut usize) -> u32; /// `payload` is actually a `*mut &mut dyn BoxMeUp` but that would cause FFI warnings. + /// It cannot be `Box` because the other end of this call does not depend + /// on liballoc, and thus cannot use `Box`. #[unwind(allowed)] fn __rust_start_panic(payload: usize) -> u32; } @@ -296,14 +299,6 @@ pub fn panicking() -> bool { update_panic_count(0) != 0 } -/// Entry point of panic from the libcore crate (`panic_impl` lang item). -#[cfg(not(test))] -#[panic_handler] -#[unwind(allowed)] -pub fn rust_begin_panic(info: &PanicInfo<'_>) -> ! { - continue_panic_fmt(&info) -} - /// The entry point for panicking with a formatted message. /// /// This is designed to reduce the amount of code required at the call @@ -324,13 +319,17 @@ pub fn begin_panic_fmt(msg: &fmt::Arguments<'_>, unsafe { intrinsics::abort() } } + // Just package everything into a `PanicInfo` and continue like libcore panics. let (file, line, col) = *file_line_col; let location = Location::internal_constructor(file, line, col); let info = PanicInfo::internal_constructor(Some(msg), &location); - continue_panic_fmt(&info) + begin_panic_handler(&info) } -fn continue_panic_fmt(info: &PanicInfo<'_>) -> ! { +/// Entry point of panics from the libcore crate (`panic_impl` lang item). +#[cfg_attr(not(test), panic_handler)] +#[unwind(allowed)] +pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { struct PanicPayload<'a> { inner: &'a fmt::Arguments<'a>, string: Option, @@ -345,6 +344,7 @@ fn fill(&mut self) -> &mut String { use crate::fmt::Write; let inner = self.inner; + // Lazily, the first time this gets called, run the actual string formatting. self.string.get_or_insert_with(|| { let mut s = String::new(); drop(s.write_fmt(*inner)); @@ -354,7 +354,7 @@ fn fill(&mut self) -> &mut String { } unsafe impl<'a> BoxMeUp for PanicPayload<'a> { - fn box_me_up(&mut self) -> *mut (dyn Any + Send) { + fn take_box(&mut self) -> *mut (dyn Any + Send) { let contents = mem::take(self.fill()); Box::into_raw(Box::new(contents)) } @@ -378,7 +378,9 @@ fn get(&mut self) -> &(dyn Any + Send) { &file_line_col); } -/// This is the entry point of panicking for panic!() and assert!(). +/// This is the entry point of panicking for the non-format-string variants of +/// panic!() and assert!(). In particular, this is the only entry point that supports +/// arbitrary payloads, not just format strings. #[unstable(feature = "libstd_sys_internals", reason = "used by the panic! macro", issue = "0")] @@ -412,10 +414,10 @@ fn new(inner: A) -> PanicPayload { } unsafe impl BoxMeUp for PanicPayload { - fn box_me_up(&mut self) -> *mut (dyn Any + Send) { + fn take_box(&mut self) -> *mut (dyn Any + Send) { let data = match self.inner.take() { Some(a) => Box::new(a) as Box, - None => Box::new(()), + None => process::abort(), }; Box::into_raw(data) } @@ -423,7 +425,7 @@ fn box_me_up(&mut self) -> *mut (dyn Any + Send) { fn get(&mut self) -> &(dyn Any + Send) { match self.inner { Some(ref a) => a, - None => &(), + None => process::abort(), } } } @@ -457,9 +459,12 @@ fn rust_panic_with_hook(payload: &mut dyn BoxMeUp, let mut info = PanicInfo::internal_constructor(message, &location); HOOK_LOCK.read(); match HOOK { - // Some platforms know that printing to stderr won't ever actually + // 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. + // hook. Since string formatting happens lazily when calling `payload` + // methods, this means we avoid formatting the string at all! + // (The panic runtime might still call `payload.take_box()` though and trigger + // formatting.) Hook::Default if panic_output().is_none() => {} Hook::Default => { info.set_payload(payload.get()); @@ -486,14 +491,15 @@ fn rust_panic_with_hook(payload: &mut dyn BoxMeUp, rust_panic(payload) } -/// Shim around rust_panic. Called by resume_unwind. -pub fn update_count_then_panic(msg: Box) -> ! { +/// This is the entry point for `resume_unwind`. +/// It just forwards the payload to the panic runtime. +pub fn rust_panic_without_hook(payload: Box) -> ! { update_panic_count(1); struct RewrapBox(Box); unsafe impl BoxMeUp for RewrapBox { - fn box_me_up(&mut self) -> *mut (dyn Any + Send) { + fn take_box(&mut self) -> *mut (dyn Any + Send) { Box::into_raw(mem::replace(&mut self.0, Box::new(()))) } @@ -502,7 +508,7 @@ fn get(&mut self) -> &(dyn Any + Send) { } } - rust_panic(&mut RewrapBox(msg)) + rust_panic(&mut RewrapBox(payload)) } /// An unmangled function (through `rustc_std_internal_symbol`) on which to slap