Skip to content

Commit

Permalink
Remove "panic runtime" and emphasize panic_handler more
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ehuss committed Feb 10, 2025
1 parent 5fa03f3 commit ce5005d
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 92 deletions.
2 changes: 1 addition & 1 deletion src/attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/crates-and-source-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions src/destructors.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/items/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions src/linkage.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
90 changes: 71 additions & 19 deletions src/panic.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<!-- ignore: test infrastructure can't handle no_std -->
```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.
Expand All @@ -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:
Expand All @@ -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
79 changes: 14 additions & 65 deletions src/runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<!-- ignore: test infrastructure can't handle no_std -->
```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

Expand Down Expand Up @@ -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

<script>
(function() {
var fragments = {
"#the-panic_handler-attribute": "panic.html#the-panic_handler-attribute",
};
var target = fragments[window.location.hash];
if (target) {
var url = window.location.toString();
var base = url.substring(0, url.lastIndexOf('/'));
window.location.replace(base + "/" + target);
}
})();
</script>

0 comments on commit ce5005d

Please sign in to comment.