about summary refs log tree commit diff
path: root/src/doc/rustc-dev-guide
diff options
context:
space:
mode:
authorAaron Hill <aa1ronham@gmail.com>2019-10-29 18:18:04 -0400
committerWho? Me?! <mark-i-m@users.noreply.github.com>2020-01-02 21:54:14 -0600
commit441a9229fdc3e649807ef8a4bbd7648687572b26 (patch)
treef362b2f5388c4fe678453558cfad64cbf4e344ff /src/doc/rustc-dev-guide
parent66e8c4fd12a67baaab0ce42e27cdec9616c9bec8 (diff)
downloadrust-441a9229fdc3e649807ef8a4bbd7648687572b26.tar.gz
rust-441a9229fdc3e649807ef8a4bbd7648687572b26.zip
Add panic implementation docs
Diffstat (limited to 'src/doc/rustc-dev-guide')
-rw-r--r--src/doc/rustc-dev-guide/src/SUMMARY.md1
-rw-r--r--src/doc/rustc-dev-guide/src/panic-implementation.md105
2 files changed, 106 insertions, 0 deletions
diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md
index 7b334c36c51..125e20cf6fe 100644
--- a/src/doc/rustc-dev-guide/src/SUMMARY.md
+++ b/src/doc/rustc-dev-guide/src/SUMMARY.md
@@ -44,6 +44,7 @@
         - [Salsa](./salsa.md)
     - [Lexing and Parsing](./the-parser.md)
     - [`#[test]` Implementation](./test-implementation.md)
+    - [Panic Implementation](./panic-implementation.md)
     - [Macro expansion](./macro-expansion.md)
     - [Name resolution](./name-resolution.md)
     - [The HIR (High-level IR)](./hir.md)
diff --git a/src/doc/rustc-dev-guide/src/panic-implementation.md b/src/doc/rustc-dev-guide/src/panic-implementation.md
new file mode 100644
index 00000000000..d8bb4c315ae
--- /dev/null
+++ b/src/doc/rustc-dev-guide/src/panic-implementation.md
@@ -0,0 +1,105 @@
+### Panicking in rust ###
+
+#### Step 1: Invocation of the `panic!` macro.
+
+There are actually two panic macros - one defined in `libcore`, and one defined in `libstd`.
+This is due to the fact that code in `libcore` can panic. `libcore` is built before `libsd`,
+but we want panics to use the same machinery at runtime, whether they originate in `libcore` or `libstd`.
+
+##### libcore definition of panic!
+
+The `libcore` `panic!` macro eventually makes the following call (in `src/libcore/panicking.rs`):
+
+```rust
+// NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call
+extern "Rust" {
+    #[lang = "panic_impl"]
+    fn panic_impl(pi: &PanicInfo<'_>) -> !;
+}
+
+let pi = PanicInfo::internal_constructor(Some(&fmt), location);
+unsafe { panic_impl(&pi) }
+```
+
+Actually resolving this goes through several layers of indirection:
+
+1. In `src/librustc/middle/weak_lang_items.rs`, `panic_impl` is declared as 'weak lang item',
+   with the symbol `rust_begin_unwind`. This is used in `librustc_typeck/collect.rs`
+   to set the actual symbol name to `rust_begin_unwind`.
+
+   Note that `panic_impl` is declared in an `extern "Rust"` block,
+   which means that libcore will attempt to call a foreign symbol called `rust_begin_unwind`
+   (to be resolved at link time)
+
+2. In `src/libstd/panicking.rs`, we have this definition:
+
+```rust
+/// Entry point of panic from the libcore crate.
+[cfg(not(test))]
+[panic_handler]
+[unwind(allowed)]
+pub fn rust_begin_panic(info: &PanicInfo<'_>) -> ! {
+    continue_panic_fmt(&info)
+}
+```
+
+The special `panic_handler` attribute is resolved via `src/librustc/middle/lang_items`.
+The `extract` functions convers the `panic_handler` attribute to a `panic_impl` lang item.
+
+Now, we have a matching `panic_impl` lang item in the `libstd`. This function goes
+through the same process as the `extern { fn panic_impl }` definition in `libcore`, ending
+up with a symbol name of `rust_begin_unwind`. At link time, the symbol refernce in `libcore`
+will be resolved to the definition of `libstd` (the function called `rust_begin_panic` in the
+Rust source).
+
+Thus, control flow will pass from libcore to std at runtime. This allows panics from `libcore`
+to go through the same infratructure that other panics use (panic hooks, unwinding, etc)
+
+##### libstd implementation of panic!
+
+This is where the actual panic-related logic begins. In `src/libstd/pancking.rs`,
+control passes to `rust_panic_with_hook`. This method is responsible
+for checking for invoking the global panic hook, and checking for double panics. Finally,
+we call ```__rust_start_panic```, which is provided by the panic runtime.
+
+The call to ```__rust_start_panic``` is very weird - it is passed a ```*mut &mut dyn BoxMeUp```,
+converted to an `usize`. Let's break this type down:
+
+1. `BoxMeUp` is an internal trait. It is implemented for `PanicPayload` (a wrapper around the user-supplied
+payload type), and has a method ```fn box_me_up(&mut self) -> *mut (dyn Any + Send)```.
+This method takes the user-provided payload (`T: Any + Send`), boxes it, and convertes the box to a raw pointer.
+
+2. When we call ```__rust_start_panic```, we have an `&mut dyn BoxMeUp`. However, this is a fat pointer
+(twice the size of a `usize`). To pass this to the panic runtime across an FFI boundary, we take a mutable
+reference *to this mutable reference* (`&mut &mut dyn BoxMeUp`), and convert it to a raw pointer
+(`*mut &mut dyn BoxMeUp`). The outer raw pointer is a thin pointer, since it points to a `Sized`
+type (a mutable reference). Therefore, we can convert this thin pointer into a `usize`, which
+is suitable for passing across an FFI boundary.
+
+Finally, we call ```__rust_start_panic``` with this `usize`. We have now entered the panic runtime.
+
+#### Step 2: The panic runtime
+
+Rust provides two panic runtimes: `libpanic_abort` and `libpanic_unwind`. The user chooses
+between them at build time via their `Cargo.toml`
+
+`libpanic_abort` is extremely simple: its implementation of ```__rust_start_panic``` just aborts,
+as you would expect.
+
+`libpanic_unwind` is the more interesting case. 
+
+In its implementation of ```__rust_start_panic```, we take the `usize`, convert
+it back to a `*mut &mut dyn BoxMeUp`, dereference it, and call `box_me_up`
+on the `&mut dyn BoxMeUp`. At this point, we have a raw pointer to the payload
+itself (a `*mut (dyn Send + Any)`): that is, a raw pointer to the actual value
+provided by the user who called `panic!`.
+
+At this point, the platform-independent code ends. We now call into
+platform-specific unwinding logic (e.g `libunwind`). This code is
+responsible for unwinding the stack, running any 'landing pads' associated
+with each frame (currently, running destructors), and transferring control
+to the `catch_unwind` frame.
+
+Note that all panics either abort the process or get caught by some call to `catch_unwind`:
+in `src/libstd/rt.rs`, the call to the user-provided `main` function is wrapped in `catch_unwind
+