Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Panic machinery comments and tweaks #66766

Merged
merged 8 commits into from
Nov 30, 2019
12 changes: 11 additions & 1 deletion src/libcore/panic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,16 @@ impl fmt::Display for Location<'_> {
#[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<dyn Any + Send>`, 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);
}
9 changes: 5 additions & 4 deletions src/libcore/panicking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
//! ```
//!
//! This definition allows for panicking with any general message, but it does not
//! allow for failing with a `Box<Any>` value. The reason for this is that libcore
//! is not allowed to allocate.
//! allow for failing with a `Box<Any>` 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

Expand Down Expand Up @@ -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<'_>) -> !;
Expand Down
2 changes: 1 addition & 1 deletion src/libpanic_unwind/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,5 @@ pub unsafe extern "C" fn __rust_maybe_catch_panic(f: fn(*mut u8),
#[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()))
}
41 changes: 23 additions & 18 deletions src/libstd/panicking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::sys_common::rwlock::RWLock;
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;
Expand All @@ -46,6 +47,8 @@ extern {
vtable_ptr: *mut usize) -> u32;

/// `payload` is actually a `*mut &mut dyn BoxMeUp` but that would cause FFI warnings.
/// It cannot be `Box<dyn BoxMeUp>` 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;
}
Expand Down Expand Up @@ -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
Expand All @@ -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<String>,
Expand All @@ -345,6 +344,7 @@ fn continue_panic_fmt(info: &PanicInfo<'_>) -> ! {
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));
Expand All @@ -354,7 +354,7 @@ fn continue_panic_fmt(info: &PanicInfo<'_>) -> ! {
}

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))
}
Expand All @@ -378,7 +378,9 @@ fn continue_panic_fmt(info: &PanicInfo<'_>) -> ! {
&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")]
Expand Down Expand Up @@ -412,18 +414,18 @@ pub fn begin_panic<M: Any + Send>(msg: M, file_line_col: &(&'static str, u32, u3
}

unsafe impl<A: Send + 'static> BoxMeUp for PanicPayload<A> {
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<dyn Any + Send>,
None => Box::new(()),
None => process::abort(),
};
Box::into_raw(data)
}

fn get(&mut self) -> &(dyn Any + Send) {
match self.inner {
Some(ref a) => a,
None => &(),
None => process::abort(),
}
}
}
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -493,7 +498,7 @@ pub fn update_count_then_panic(msg: Box<dyn Any + Send>) -> ! {
struct RewrapBox(Box<dyn Any + Send>);

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(())))
}

Expand Down