From 38719cda47803124398c0acd32995f3b0e44e126 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 10 Feb 2025 14:36:31 -0800 Subject: [PATCH] Remove "panic runtime" and emphasize panic_handler more This is a general rework to avoid using the term "panic runtime" (and "panic mode"), since it is poorly defined, potentially confusing, and potentially exposes some internal terminology and details that we may not want to expose. The general outline here is: - Remove the "panic runtime" section. - Move the `panic_handler` attribute to the Panic chapter. - The `panic_handler` attribute text has some editorial changes, and the rule names have changed. - The "Standard behavior" section for `panic_handler` has been reworked, incorporating some of the content from the old panic runtime section. - Added panic.panic_handler.std.no_std - Reword sentences that refer to "runtime", usually pointing to the "handler" instead. Note that there are a few subtle cases where this is not absolutely true. --- src/attributes.md | 2 +- src/crates-and-source-files.md | 2 +- src/destructors.md | 3 +- src/items/functions.md | 4 +- src/linkage.md | 3 +- src/panic.md | 90 +++++++++++++++++++++++++++------- src/runtime.md | 79 ++++++----------------------- 7 files changed, 91 insertions(+), 92 deletions(-) diff --git a/src/attributes.md b/src/attributes.md index af3285edc..6d286c60d 100644 --- a/src/attributes.md +++ b/src/attributes.md @@ -370,7 +370,7 @@ The following is an index of all built-in attributes. [`no_mangle`]: abi.md#the-no_mangle-attribute [`no_std`]: names/preludes.md#the-no_std-attribute [`non_exhaustive`]: attributes/type_system.md#the-non_exhaustive-attribute -[`panic_handler`]: runtime.md#the-panic_handler-attribute +[`panic_handler`]: panic.md#the-panic_handler-attribute [`path`]: items/modules.md#the-path-attribute [`proc_macro_attribute`]: procedural-macros.md#attribute-macros [`proc_macro_derive`]: procedural-macros.md#derive-macros diff --git a/src/crates-and-source-files.md b/src/crates-and-source-files.md index dbae80fa9..80ae41aca 100644 --- a/src/crates-and-source-files.md +++ b/src/crates-and-source-files.md @@ -126,7 +126,7 @@ r[crate.uncaught-foreign-unwinding] ### Uncaught foreign unwinding When a "foreign" unwind (e.g. an exception thrown from C++ code, or a `panic!` -in Rust code compiled or linked with a different runtime) propagates beyond +in Rust code using a different panic handler) propagates beyond the `main` function, the process will be safely terminated. This may take the form of an abort, in which case it is not guaranteed that any `Drop` calls will be executed, and the error output may be less informative than if the diff --git a/src/destructors.md b/src/destructors.md index 8e445e406..85c513647 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -444,7 +444,7 @@ destructors will not be run. The standard library provides [`std::process::exit`] and [`std::process::abort`] to do this explicitly. Additionally, if the -[panic-mode] is set to `abort`, panicking will always terminate the process +[panic handler][panic.panic_handler.std] is set to `abort`, panicking will always terminate the process without destructors being run. There is one additional case to be aware of: when a panic reaches a @@ -462,7 +462,6 @@ destructors up until the ABI boundary will run. [lazy boolean expression]: expressions/operator-expr.md#lazy-boolean-operators [non-unwinding ABI boundary]: items/functions.md#unwinding [panic]: panic.md -[panic-mode]: panic.md#panic-runtimes [place context]: expressions.md#place-expressions-and-value-expressions [promoted]: destructors.md#constant-promotion [scrutinee]: glossary.md#scrutinee diff --git a/src/items/functions.md b/src/items/functions.md index a9f6316d2..f48d6acfb 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -259,7 +259,7 @@ r[items.fn.extern.unwind] r[items.fn.extern.unwind.intro] Most ABI strings come in two variants, one with an `-unwind` suffix and one without. The `Rust` ABI always permits unwinding, so there is no `Rust-unwind` ABI. The -choice of ABI, together with the runtime [panic mode][panic-modes], determines +choice of ABI, together with the runtime [panic handler], determines the behavior when unwinding out of a function. r[items.fn.extern.unwind.behavior] @@ -301,7 +301,7 @@ For other considerations and limitations regarding unwinding across FFI boundaries, see the [relevant section in the Panic documentation][panic-ffi]. [forced-unwinding]: https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html#forced-unwinding -[panic-modes]: ../panic.md#panic-runtimes +[panic handler]: ../panic.md#the-panic_handler-attribute [panic-ffi]: ../panic.md#unwinding-across-ffi-boundaries [panicking]: ../panic.md [undefined behavior]: ../behavior-considered-undefined.md diff --git a/src/linkage.md b/src/linkage.md index e10740796..5751ce817 100644 --- a/src/linkage.md +++ b/src/linkage.md @@ -283,7 +283,7 @@ Panic unwinding can only be used if the binary is built consistently according t r[link.unwinding.potential] A Rust artifact is called *potentially unwinding* if any of the following conditions is met: -- The artifact is linked with [the `unwind` panic runtime][panic-runtime]. +- The artifact uses the [`unwind` panic handler][panic.panic_handler]. - The artifact contains a crate built with the `unwind` [panic strategy] that makes a call to a function using a `-unwind` ABI. - The artifact makes a `"Rust"` ABI call to code running in another Rust @@ -313,7 +313,6 @@ Otherwise, unwinding can cause undefined behavior. [`cfg` attribute `target_feature` option]: conditional-compilation.md#target_feature [`ffi_unwind_calls` lint]: ../rustc/lints/listing/allowed-by-default.html#ffi-unwind-calls [configuration option]: conditional-compilation.md -[panic-runtime]: panic.md#panic-runtimes [procedural macros]: procedural-macros.md [panic strategy]: panic.md#panic-strategy [`-C panic`]: ../rustc/codegen-options/index.html#panic diff --git a/src/panic.md b/src/panic.md index 6725094f9..3bac38482 100644 --- a/src/panic.md +++ b/src/panic.md @@ -10,56 +10,104 @@ Some language constructs, such as out-of-bounds [array indexing], panic automati r[panic.control] There are also language features that provide a level of control over panic behavior: -* A [_panic runtime_](#panic-runtimes) defined how a panic is handled during runtime. +* A [_panic handler_][panic handler] defines the behavior of a panic. * [FFI ABIs](items/functions.md#unwinding) may alter how panics behave. > [!NOTE] > The standard library provides the capability to explicitly panic via the [`panic!` macro][panic!]. -r[panic.runtime] -## Panic runtimes +r[panic.panic_handler] +## The `panic_handler` attribute -r[panic.runtime.intro] -The actual behavior and implementation of a panic is controlled by the _panic runtime_. The panic runtime is a handler linked into the output which provides the necessary implementation for panicking. +r[panic.panic_handler.intro] +The *`panic_handler` attribute* can be applied to a function to define the behavior of panics. -r[panic.runtime.kinds] -The following panic runtimes are provided by the standard library: +r[panic.panic_handler.allowed-positions] +The `panic_handler` attribute can only be applied to a function with signature `fn(&PanicInfo) -> !`. + +> [!NOTE] +> The [`PanicInfo`] struct contains information about the location of the panic. + +r[panic.panic_handler.unique] +There must be a single `panic_handler` function in the dependency graph. + +Below is shown a `panic_handler` function that logs the panic message and then halts the thread. + + +```rust,ignore +#![no_std] + +use core::fmt::{self, Write}; +use core::panic::PanicInfo; + +struct Sink { + // .. +# _0: (), +} +# +# impl Sink { +# fn new() -> Sink { Sink { _0: () }} +# } +# +# impl fmt::Write for Sink { +# fn write_str(&mut self, _: &str) -> fmt::Result { Ok(()) } +# } + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + let mut sink = Sink::new(); + + // logs "panicked at '$reason', src/main.rs:27:4" to some `sink` + let _ = writeln!(sink, "{}", info); + + loop {} +} +``` + +r[panic.panic_handler.std] +### Standard behavior + +r[panic.panic_handler.std.kinds] +`std` provides two different panic handlers: * `unwind` --- unwinds the stack and is potentially recoverable. * `abort` ---- aborts the process and is non-recoverable. -Not all targets may provide the `unwind` runtime. - -The default runtime depends on the target platform, but is generally `unwind` on platforms with native support for C++ exceptions. +Not all targets may provide the `unwind` handler. > [!NOTE] -> The panic runtime can be chosen in `rustc` with the [`-C panic`] CLI flag when building any crate type except an rlib. +> The panic handler used when linking with `std` can be set with the [`-C panic`] CLI flag. The default for most targets is `unwind`. +> +> The standard library's panic behavior can be modified at runtime with the [`std::panic::set_hook`] function. -See also the [`panic_handler` attribute](runtime.md#the-panic_handler-attribute) which can be used to change the behavior of panics. +r[panic.panic_handler.std.no_std] +Linking a [`no_std`] binary, dylib, cdylib, or staticlib will require specifying your own panic handler. r[panic.strategy] ## Panic strategy r[panic.strategy.intro] -The _panic strategy_ defines the kind of panic runtime that a crate is built to support. +The _panic strategy_ defines the kind of panic behavior that a crate is built to support. > [!NOTE] > The panic strategy can be chosen in `rustc` with the [`-C panic`] CLI flag. +> +> When generating a binary, dylib, cdylib, or staticlib and linking with `std`, the `-C panic` CLI flag also influences which [panic handler] is used. > [!NOTE] > When compiling code with the `abort` panic strategy, the optimizer may assume that unwinding across Rust frames is impossible, which can result in both code-size and runtime speed improvements. > [!NOTE] -> See [link.unwinding] for restrictions on linking crates with different panic strategies. An implication is that crates built with the `unwind` strategy can use the `abort` runtime, but not vice-versa. +> See [link.unwinding] for restrictions on linking crates with different panic strategies. An implication is that crates built with the `unwind` strategy can use the `abort` panic handler, but the `abort` strategy cannot use the `unwind` panic handler. r[panic.unwind] ## Unwinding r[panic.unwind.intro] -Panicking may either be recoverable or non-recoverable, though it can be configured (by choosing the `abort` panic runtime) to always be non-recoverable. (The converse is not true: the `unwind` runtime does not guarantee that all panics are recoverable, only that panicking via the `panic!` macro and similar standard library mechanisms is recoverable.) +Panicking may either be recoverable or non-recoverable, though it can be configured (by choosing a non-unwinding panic handler) to always be non-recoverable. (The converse is not true: the `unwind` handler does not guarantee that all panics are recoverable, only that panicking via the `panic!` macro and similar standard library mechanisms is recoverable.) r[panic.unwind.destruction] -When panic recovery occurs, the `unwind` runtime "unwinds" Rust frames, just as C++'s `throw` unwinds C++ frames, until the panic reaches the point of recovery (for instance at a thread boundary). This means that as the panic traverses Rust frames, live objects in those frames that [implement `Drop`][destructors] will have their `drop` methods called. Thus, when normal execution resumes, no-longer-accessible objects will have been "cleaned up" just as if they had gone out of scope normally. +When a panic occurs, the `unwind` handler "unwinds" Rust frames, just as C++'s `throw` unwinds C++ frames, until the panic reaches the point of recovery (for instance at a thread boundary). This means that as the panic traverses Rust frames, live objects in those frames that [implement `Drop`][destructors] will have their `drop` methods called. Thus, when normal execution resumes, no-longer-accessible objects will have been "cleaned up" just as if they had gone out of scope normally. > [!NOTE] > As long as this guarantee of resource-cleanup is preserved, "unwinding" may be implemented without actually using the mechanism used by C++ for the target platform. @@ -77,7 +125,7 @@ r[panic.unwind.ffi.undefined] Unwinding with the wrong ABI is undefined behavior: * Causing an unwind into Rust code from a foreign function that was called via a function declaration or pointer declared with a non-unwinding ABI, such as `"C"`, `"system"`, etc. (For example, this case occurs when such a function written in C++ throws an exception that is uncaught and propagates to Rust.) -* Calling a Rust `extern` function that unwinds (with `extern "C-unwind"` or another ABI that permits unwinding) from a runtime that does not support unwinding, such as code compiled with GCC or Clang using `-fno-exceptions` +* Calling a Rust `extern` function that unwinds (with `extern "C-unwind"` or another ABI that permits unwinding) from code that does not support unwinding, such as code compiled with GCC or Clang using `-fno-exceptions` r[panic.unwind.ffi.catch-foreign] Catching a foreign unwinding operation (such as a C++ exception) using [`std::panic::catch_unwind`], [`std::thread::JoinHandle::join`], or by letting it propagate beyond the Rust `main()` function or thread root will have one of two behaviors, and it is unspecified which will occur: @@ -86,13 +134,17 @@ Catching a foreign unwinding operation (such as a C++ exception) using [`std::pa * The function returns a [`Result::Err`] containing an opaque type. > [!NOTE] -> Rust code compiled or linked with a different instance of the Rust runtime counts as a "foreign exception" for the purpose of this guarantee. Thus, a library that uses `panic!` and is linked against one version of the Rust standard library, invoked from an application that uses a different version of the standard library, may cause the entire application to crash even if the library is only used within a child thread. +> Rust code compiled or linked with a different instance of the Rust standard library counts as a "foreign exception" for the purpose of this guarantee. Thus, a library that uses `panic!` and is linked against one version of the Rust standard library, invoked from an application that uses a different version of the standard library, may cause the entire application to abort even if the library is only used within a child thread. r[panic.unwind.ffi.dispose-panic] There are currently no guarantees about the behavior that occurs when a foreign runtime attempts to dispose of, or rethrow, a Rust `panic` payload. In other words, an unwind originated from a Rust runtime must either lead to termination of the process or be caught by the same runtime. +[`-C panic`]: ../rustc/codegen-options/index.html#panic +[`no_std`]: names/preludes.md#the-no_std-attribute +[`PanicInfo`]: core::panic::PanicInfo [array indexing]: expressions/array-expr.md#array-and-slice-indexing-expressions +[attribute]: attributes.md [destructors]: destructors.md +[panic handler]: #the-panic_handler-attribute [runtime]: runtime.md [unwind-abi]: items/functions.md#unwinding -[`-C panic`]: ../rustc/codegen-options/index.html#panic diff --git a/src/runtime.md b/src/runtime.md index 27d088878..986c1db88 100644 --- a/src/runtime.md +++ b/src/runtime.md @@ -3,66 +3,6 @@ r[runtime] This section documents features that define some aspects of the Rust runtime. -r[runtime.panic_handler] -## The `panic_handler` attribute - -r[runtime.panic_handler.allowed-positions] -The *`panic_handler` attribute* can only be applied to a function with signature -`fn(&PanicInfo) -> !`. - -r[runtime.panic_handler.intro] -The function marked with this [attribute] defines the behavior of panics. - -r[runtime.panic_handler.panic-info] -The [`PanicInfo`] struct contains information about the location of the panic. - -r[runtime.panic_handler.unique] -There must be a single `panic_handler` function in the dependency graph of a binary, dylib or cdylib crate. - -Below is shown a `panic_handler` function that logs the panic message and then halts the -thread. - - -```rust,ignore -#![no_std] - -use core::fmt::{self, Write}; -use core::panic::PanicInfo; - -struct Sink { - // .. -# _0: (), -} -# -# impl Sink { -# fn new() -> Sink { Sink { _0: () }} -# } -# -# impl fmt::Write for Sink { -# fn write_str(&mut self, _: &str) -> fmt::Result { Ok(()) } -# } - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - let mut sink = Sink::new(); - - // logs "panicked at '$reason', src/main.rs:27:4" to some `sink` - let _ = writeln!(sink, "{}", info); - - loop {} -} -``` - -r[runtime.panic_handler.std] -### Standard behavior - -The standard library provides an implementation of `panic_handler` that -defaults to unwinding the stack but that can be [changed to abort the -process][abort]. See [panic runtimes] for more details. - -The standard library's panic behavior can be modified at runtime with the -[`set_hook` function][set_hook]. - r[runtime.global_allocator] ## The `global_allocator` attribute @@ -98,11 +38,20 @@ display a console window on startup. It will run detached from any existing cons [_MetaNameValueStr_]: attributes.md#meta-item-attribute-syntax [`GlobalAlloc`]: alloc::alloc::GlobalAlloc -[`PanicInfo`]: core::panic::PanicInfo -[abort]: ../book/ch09-01-unrecoverable-errors-with-panic.html -[attribute]: attributes.md [crate types]: linkage.md -[panic runtimes]: panic.md#panic-runtimes -[set_hook]: std::panic::set_hook [static item]: items/static-items.md [subsystem]: https://msdn.microsoft.com/en-us/library/fcc1zstk.aspx + +