X-Git-Url: https://git.ralfj.de/web.git/blobdiff_plain/7834da2ccb92d1b9a9af4d0df1559b764a982e12..c89c36772dcf86fecf1a7360bd81da746f864333:/ralf/_posts/2019-11-25-how-to-panic-in-rust.md diff --git a/ralf/_posts/2019-11-25-how-to-panic-in-rust.md b/ralf/_posts/2019-11-25-how-to-panic-in-rust.md index 0a9d2ba..3f5e2f1 100644 --- a/ralf/_posts/2019-11-25-how-to-panic-in-rust.md +++ b/ralf/_posts/2019-11-25-how-to-panic-in-rust.md @@ -18,6 +18,9 @@ There [are still some rough edges](https://github.com/rust-lang/miri/issues?q=is The purpose of this post is to document the high-level structure and the relevant interfaces that come into play on the Rust side of this. The actual mechanism of unwinding is a totally different matter (and one that I am not qualified to speak about). +*Note:* This post describes panicking as of [this commit](https://github.com/rust-lang/rust/commit/7d761fe0462ba0f671a237d0bb35e3579b8ba0e8). +Many of the interfaces described here are unstable internal details of libstd, and subject to change any time. + ## High-level structure When trying to figure out how panicking works by reading the code in libstd, one can easily get lost in the maze. @@ -71,7 +74,7 @@ On top of the panic *runtime* interface, libstd implements the default Rust pani #### `rust_panic_with_hook` -The key function that everything passes through is [`rust_panic_with_hook`](https://github.com/rust-lang/rust/blob/7d761fe0462ba0f671a237d0bb35e3579b8ba0e8/src/libstd/panicking.rs#L435-L437): +The key function that almost everything passes through is [`rust_panic_with_hook`](https://github.com/rust-lang/rust/blob/7d761fe0462ba0f671a237d0bb35e3579b8ba0e8/src/libstd/panicking.rs#L435-L437): {% highlight rust %} fn rust_panic_with_hook( payload: &mut dyn BoxMeUp, @@ -96,7 +99,7 @@ The `'static` bound is quite well hidden there, but after a while I realized tha #### libstd panicking entry points -`rust_panic_with_hook` is a private function to `std::panicking`; the module provides three separate entry points on top of this central function: +`rust_panic_with_hook` is a private function to `std::panicking`; the module provides three entry points on top of this central function, and one that circumvents it: * the [default panic handler implementation](https://github.com/rust-lang/rust/blob/7d761fe0462ba0f671a237d0bb35e3579b8ba0e8/src/libstd/panicking.rs#L301), backing (as we will see) panics from `core::panic!` and built-in panics (from arithmetic overflow or out-of-bounds array/slice indexing). This obtains as input a [`PanicInfo` ](https://doc.rust-lang.org/core/panic/struct.PanicInfo.html), and it has to turn that into arguments for `rust_panic_with_hook`. @@ -119,6 +122,11 @@ The `'static` bound is quite well hidden there, but after a while I realized tha In particular, a panic hook that looks at the `message` field of the `PanicData` it is passed will *not* be able to see the message in a `std::panic!("do panic")`, but it *will* see the message in a `std::panic!("panic with data: {}", data)` as the latter passes through `begin_panic_fmt` instead. That seems quite surprising. (But also note that `PanicData::message()` is not stable yet.) +* [`update_count_then_panic`](https://github.com/rust-lang/rust/blob/7d761fe0462ba0f671a237d0bb35e3579b8ba0e8/src/libstd/panicking.rs#L488) is the odd one out: this entry point backs [`resume_unwind`](https://doc.rust-lang.org/nightly/std/panic/fn.resume_unwind.html), and it actually does *not* call the panic hook. + Instead, it dispatches to the panic runtime immediately. + Like, `begin_panic`, it lets the caller pick an arbitrary payload. + Unlike `begin_panic`, the caller is responsible for boxing and unsizing the payload; `update_count_then_panic` just forwards that pretty much verbatim to the panic runtime. + ## Panic Handler All of the `std::panic!` machinery is really useful, but it relies on heap allocations through `Box` which is not always available.