diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 91f343b8d..60787c339 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -114,6 +114,8 @@ - [Memory allocation and lifetime](memory-allocation-and-lifetime.md) - [Variables](variables.md) +- [Panic](panic.md) + - [Linkage](linkage.md) - [Inline assembly](inline-assembly.md) 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/attributes/codegen.md b/src/attributes/codegen.md index b9a24b8a7..5133d0bc3 100644 --- a/src/attributes/codegen.md +++ b/src/attributes/codegen.md @@ -132,7 +132,7 @@ r[attributes.codegen.target_feature.allowed-positions] The `#[target_feature]` attribute is not allowed on the following places: - [the `main` function][crate.main] -- a [`panic_handler` function][runtime.panic_handler] +- a [`panic_handler` function][panic.panic_handler] - safe trait methods - safe default functions in traits diff --git a/src/behavior-considered-undefined.md b/src/behavior-considered-undefined.md index ceb646937..a11e67aec 100644 --- a/src/behavior-considered-undefined.md +++ b/src/behavior-considered-undefined.md @@ -77,7 +77,7 @@ r[undefined.target-feature] does not support (see [`target_feature`]), *except* if the platform explicitly documents this to be safe. r[undefined.call] -* Calling a function with the wrong call ABI or unwinding from a function with the wrong unwind ABI. +* Calling a function with the wrong [call ABI][abi], or unwinding past a stack frame that does not allow unwinding (e.g. by calling a `"C-unwind"` function imported or transmuted as a `"C"` function or function pointer). r[undefined.invalid] * Producing an [invalid value][invalid-values]. "Producing" a @@ -96,6 +96,11 @@ r[undefined.const-transmute-ptr2int] 'Reinterpreting' refers to loading the pointer value at integer type without a cast, e.g. by doing raw pointer casts or using a union. +r[undefined.runtime] +* Violating assumptions of the Rust runtime. Most assumptions of the Rust runtime are currently not explicitly documented. + * For assumptions specifically related to unwinding, see the [panic documentation][unwinding-ffi]. + * The runtime assumes that a Rust stack frame is not deallocated without executing destructors for local variables owned by the stack frame. This assumption can be violated by C functions like `longjmp`. + > **Note**: Undefined behavior affects the entire program. For example, calling > a function in C that exhibits undefined behavior of C means your entire > program contains undefined behaviour that can also affect the Rust code. And @@ -245,6 +250,7 @@ reading uninitialized memory is permitted are inside `union`s and in "padding" [`const`]: items/constant-items.md [noalias]: http://llvm.org/docs/LangRef.html#noalias [pointer aliasing rules]: http://llvm.org/docs/LangRef.html#pointer-aliasing-rules +[abi]: items/external-blocks.md#abi [undef]: http://llvm.org/docs/LangRef.html#undefined-values [`target_feature`]: attributes/codegen.md#the-target_feature-attribute [`UnsafeCell`]: std::cell::UnsafeCell @@ -258,5 +264,6 @@ reading uninitialized memory is permitted are inside `union`s and in "padding" [project-field]: expressions/field-expr.md [project-tuple]: expressions/tuple-expr.md#tuple-indexing-expressions [project-slice]: expressions/array-expr.md#array-and-slice-indexing-expressions +[unwinding-ffi]: panic.md#unwinding-across-ffi-boundaries [const-promoted]: destructors.md#constant-promotion [lifetime-extended]: destructors.md#temporary-lifetime-extension diff --git a/src/conditional-compilation.md b/src/conditional-compilation.md index 6c2a505a4..95eafcaa0 100644 --- a/src/conditional-compilation.md +++ b/src/conditional-compilation.md @@ -304,7 +304,7 @@ r[cfg.panic] ### `panic` r[cfg.panic.general] -Key-value option set depending on the panic strategy. Note that more values may be added in the future. +Key-value option set depending on the [panic strategy]. Note that more values may be added in the future. r[cfg.panic.values] Example values: @@ -312,6 +312,8 @@ Example values: * `"abort"` * `"unwind"` +[panic strategy]: panic.md#panic-strategy + ## Forms of conditional compilation r[cfg.attr] diff --git a/src/crates-and-source-files.md b/src/crates-and-source-files.md index e91eadb87..64bff1616 100644 --- a/src/crates-and-source-files.md +++ b/src/crates-and-source-files.md @@ -122,13 +122,17 @@ use foo::bar as main; +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 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 runtime had been terminated by a "native" Rust `panic`. + +For more information, see the [panic documentation][panic-docs]. + r[crate.no_main] ### The `no_main` attribute - -The *`no_main` [attribute]* may be applied at the crate level to disable -emitting the `main` symbol for an executable binary. This is useful when some -other object being linked to defines `main`. +The *`no_main` [attribute]* may be applied at the crate level to disable emitting the `main` symbol for an executable binary. This is useful when some other object being linked to defines `main`. r[crate.crate_name] ## The `crate_name` attribute @@ -166,6 +170,7 @@ or `_` (U+005F) characters. [function]: items/functions.md [module]: items/modules.md [module path]: paths.md +[panic-docs]: panic.md#unwinding-across-ffi-boundaries [shebang]: input-format.md#shebang-removal [trait or lifetime bounds]: trait-bounds.md [where clauses]: items/generics.md#where-clauses diff --git a/src/destructors.md b/src/destructors.md index 7ec6d6fe1..856e15d4b 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -274,7 +274,7 @@ Temporaries are also created to hold the result of operands to an expression while the other operands are evaluated. The temporaries are associated to the scope of the expression with that operand. Since the temporaries are moved from once the expression is evaluated, dropping them has no effect unless one of the -operands to an expression breaks out of the expression, returns, or panics. +operands to an expression breaks out of the expression, returns, or [panics][panic]. ```rust # struct PrintOnDrop(&'static str); @@ -425,6 +425,8 @@ let x = (&temp()).use_temp(); // ERROR r[destructors.forget] ## Not running destructors +r[destructors.manually-suppressing] +### Manually suppressing destructors [`std::mem::forget`] can be used to prevent the destructor of a variable from being run, and [`std::mem::ManuallyDrop`] provides a wrapper to prevent a @@ -433,6 +435,15 @@ variable or field from being dropped automatically. > Note: Preventing a destructor from being run via [`std::mem::forget`] or other means is safe even if it has a type that isn't `'static`. > Besides the places where destructors are guaranteed to run as defined by this document, types may *not* safely rely on a destructor being run for soundness. +r[destructors.process-termination] +### Process termination without unwinding + +There are some ways to terminate the process without [unwinding], in which case destructors will not be run. + +The standard library provides [`std::process::exit`] and [`std::process::abort`] to do this explicitly. Additionally, if the [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 [non-unwinding ABI boundary], either no destructors will run, or all destructors up until the ABI boundary will run. + [Assignment]: expressions/operator-expr.md#assignment-expressions [binding modes]: patterns.md#binding-modes [closure]: types/closure.md @@ -442,11 +453,14 @@ variable or field from being dropped automatically. [initialized]: glossary.md#initialized [interior mutability]: interior-mutability.md [lazy boolean expression]: expressions/operator-expr.md#lazy-boolean-operators +[non-unwinding ABI boundary]: items/functions.md#unwinding +[panic]: panic.md [place context]: expressions.md#place-expressions-and-value-expressions [promoted]: destructors.md#constant-promotion [scrutinee]: glossary.md#scrutinee [statement]: statements.md [temporary]: expressions.md#temporaries +[unwinding]: panic.md#unwinding [variable]: variables.md [array]: types/array.md diff --git a/src/expressions/array-expr.md b/src/expressions/array-expr.md index b1f6c0526..aa6a911b2 100644 --- a/src/expressions/array-expr.md +++ b/src/expressions/array-expr.md @@ -83,7 +83,7 @@ Indices are zero-based for arrays and slices. r[expr.array.index.const] Array access is a [constant expression], so bounds can be checked at compile-time with a constant index value. -Otherwise a check will be performed at run-time that will put the thread in a _panicked state_ if it fails. +Otherwise a check will be performed at run-time that will put the thread in a [_panicked state_][panic] if it fails. ```rust,should_panic // lint is deny by default. @@ -115,5 +115,6 @@ The array index expression can be implemented for types other than arrays and sl [constant item]: ../items/constant-items.md [literal]: ../tokens.md#literals [memory location]: ../expressions.md#place-expressions-and-value-expressions +[panic]: ../panic.md [path]: path-expr.md [slice]: ../types/slice.md diff --git a/src/items/external-blocks.md b/src/items/external-blocks.md index 4e08bc17d..492621476 100644 --- a/src/items/external-blocks.md +++ b/src/items/external-blocks.md @@ -106,8 +106,7 @@ unsafe extern "stdcall" { } ``` r[items.extern.abi.standard] -There are three ABI strings which are cross-platform, and which all compilers -are guaranteed to support: +The following ABI strings are supported on all platforms: r[items.extern.abi.rust] * `unsafe extern "Rust"` -- The default ABI when you write a normal `fn foo()` in any @@ -122,6 +121,9 @@ r[items.extern.abi.system] which case it's `"stdcall"`, or what you should use to link to the Windows API itself +r[items.extern.abi.unwind] +* `extern "C-unwind"` and `extern "system-unwind"` -- identical to `"C"` and `"system"`, respectively, but with [different behavior][unwind-behavior] when the callee unwinds (by panicking or throwing a C++ style exception). + r[items.extern.abi.platform] There are also some platform-specific ABI strings: @@ -151,6 +153,17 @@ r[items.extern.abi.thiscall] r[items.extern.abi.efiapi] * `unsafe extern "efiapi"` -- The ABI used for [UEFI] functions. +r[items.extern.abi.platform-unwind-variants] +Like `"C"` and `"system"`, most platform-specific ABI strings also have a [corresponding `-unwind` variant][unwind-behavior]; specifically, these are: + +* `"aapcs-unwind"` +* `"cdecl-unwind"` +* `"fastcall-unwind"` +* `"stdcall-unwind"` +* `"sysv64-unwind"` +* `"thiscall-unwind"` +* `"win64-unwind"` + r[items.extern.variadic] ## Variadic functions @@ -428,10 +441,9 @@ Attributes on extern function parameters follow the same rules and restrictions as [regular function parameters]. [IDENTIFIER]: ../identifiers.md +[PE Format]: https://learn.microsoft.com/windows/win32/debug/pe-format#import-name-type [UEFI]: https://uefi.org/specifications [WebAssembly module]: https://webassembly.github.io/spec/core/syntax/modules.html -[functions]: functions.md -[statics]: static-items.md [_Abi_]: functions.md [_Function_]: functions.md [_InnerAttribute_]: ../attributes.md @@ -441,11 +453,13 @@ restrictions as [regular function parameters]. [_OuterAttribute_]: ../attributes.md [_StaticItem_]: static-items.md [_Visibility_]: ../visibility-and-privacy.md -[attributes]: ../attributes.md -[regular function parameters]: functions.md#attributes-on-function-parameters [`bundle` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-bundle -[`whole-archive` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-whole-archive -[`verbatim` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-verbatim [`dylib` versus `raw-dylib`]: #dylib-versus-raw-dylib -[PE Format]: https://learn.microsoft.com/windows/win32/debug/pe-format#import-name-type +[`verbatim` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-verbatim +[`whole-archive` documentation for rustc]: ../../rustc/command-line-arguments.html#linking-modifiers-whole-archive +[attributes]: ../attributes.md +[functions]: functions.md +[regular function parameters]: functions.md#attributes-on-function-parameters +[statics]: static-items.md +[unwind-behavior]: functions.md#unwinding [value namespace]: ../names/namespaces.md diff --git a/src/items/functions.md b/src/items/functions.md index a4d0ee6d0..049eadd6d 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -254,20 +254,43 @@ let fptr: extern "C" fn() -> i32 = new_i32; ``` r[items.fn.extern.unwind] -Functions with an ABI that differs from `"Rust"` do not support unwinding in the -exact same way that Rust does. Therefore, unwinding past the end of functions -with such ABIs causes the process to abort. +### Unwinding -> **Note**: The LLVM backend of the `rustc` implementation -aborts the process by executing an illegal instruction. +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 handler], determines the behavior when unwinding out of a function. + +r[items.fn.extern.unwind.behavior] +The table below indicates the behavior of an unwinding operation reaching each type of ABI boundary (function declaration or definition using the corresponding ABI string). Note that the Rust runtime is not affected by, and cannot have an effect on, any unwinding that occurs entirely within another language's runtime, that is, unwinds that are thrown and caught without reaching a Rust ABI boundary. + +The `panic`-unwind column refers to [panicking] via the `panic!` macro and similar standard library mechanisms, as well as to any other Rust operations that cause a panic, such as out-of-bounds array indexing or integer overflow. + +The "unwinding" ABI category refers to `"Rust"` (the implicit ABI of Rust functions not marked `extern`), `"C-unwind"`, and any other ABI with `-unwind` in its name. The "non-unwinding" ABI category refers to all other ABI strings, including `"C"` and `"stdcall"`. + +Native unwinding is defined per-target. On targets that support throwing and catching C++ exceptions, it refers to the mechanism used to implement this feature. Some platforms implement a form of unwinding referred to as ["forced unwinding"][forced-unwinding]; `longjmp` on Windows and `pthread_exit` in `glibc` are implemented this way. Forced unwinding is explicitly excluded from the "Native unwind" column in the table. + +| panic runtime | ABI | `panic`-unwind | Native unwind (unforced) | +| -------------- | ------------ | ------------------------------------- | ----------------------- | +| `panic=unwind` | unwinding | unwind | unwind | +| `panic=unwind` | non-unwinding | abort (see notes below) | [undefined behavior] | +| `panic=abort` | unwinding | `panic` aborts without unwinding | abort | +| `panic=abort` | non-unwinding | `panic` aborts without unwinding | [undefined behavior] | + +r[items.fn.extern.abort] +With `panic=unwind`, when a `panic` is turned into an abort by a non-unwinding ABI boundary, either no destructors (`Drop` calls) will run, or all destructors up until the ABI boundary will run. It is unspecified which of those two behaviors will happen. + +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 handler]: ../panic.md#the-panic_handler-attribute +[panic-ffi]: ../panic.md#unwinding-across-ffi-boundaries +[panicking]: ../panic.md +[undefined behavior]: ../behavior-considered-undefined.md r[items.fn.const] ## Const functions r[items.fn.const.intro] -Functions qualified with the `const` keyword are [const functions], as are -[tuple struct] and [tuple variant] constructors. _Const functions_ can be -called from within [const contexts]. +Functions qualified with the `const` keyword are [const functions], as are [tuple struct] and [tuple variant] constructors. _Const functions_ can be called from within [const contexts]. r[items.fn.const.extern] Const functions may use the [`extern`] function qualifier. diff --git a/src/linkage.md b/src/linkage.md index 23bd44c2c..6d0a69761 100644 --- a/src/linkage.md +++ b/src/linkage.md @@ -253,6 +253,9 @@ RUSTFLAGS='-C target-feature=+crt-static' cargo build --target x86_64-pc-windows ## Mixed Rust and foreign codebases +r[link.foreign-code] + +r[link.foreign-code.foreign-linkers] If you are mixing Rust with foreign code (e.g. C, C++) and wish to make a single binary containing both types of code, you have two approaches for the final binary link: @@ -268,6 +271,36 @@ binary link: Passing `rlib`s directly into your foreign linker is currently unsupported. +> [!NOTE] +> Rust code compiled or linked with a different instance of the Rust runtime counts as "foreign code" for the purpose of this section. + +r[link.unwinding] +### Prohibited linkage and unwinding + +r[link.unwinding.intro] +Panic unwinding can only be used if the binary is built consistently according to the following rules. + +r[link.unwinding.potential] +A Rust artifact is called *potentially unwinding* if any of the following conditions is met: +- 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 artifact that has a separate copy of the Rust runtime, and that other artifact is potentially unwinding. + +> [!NOTE] +> This definition captures whether a `"Rust"` ABI call inside a Rust artifact can ever unwind. + +r[link.unwinding.prohibited] +If a Rust artifact is potentially unwinding, then all its crates must be built with the `unwind` [panic strategy]. Otherwise, unwinding can cause undefined behavior. + +> [!NOTE] +> If you are using `rustc` to link, these rules are enforced automatically. If you are *not* using `rustc` to link, you must take care to ensure that unwinding is handled consistently across the entire binary. Linking without `rustc` includes using `dlopen` or similar facilities where linking is done by the system runtime without `rustc` being involved. This can only happen when mixing code with different [`-C panic`] flags, so most users do not have to be concerned about this. + +> [!NOTE] +> To guarantee that a library will be sound (and linkable with `rustc`) regardless of the panic runtime used at link-time, the [`ffi_unwind_calls` lint] may be used. The lint flags any calls to `-unwind` foreign functions or function pointers. + [`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 [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 new file mode 100644 index 000000000..3bac38482 --- /dev/null +++ b/src/panic.md @@ -0,0 +1,150 @@ +r[panic] +# Panic + +r[panic.intro] +Rust provides a mechanism to prevent a function from returning normally, and instead "panic," which is a response to an error condition that is typically not expected to be recoverable within the context in which the error is encountered. + +r[panic.lang-ops] +Some language constructs, such as out-of-bounds [array indexing], panic automatically. + +r[panic.control] +There are also language features that provide a level of control over panic behavior: + +* 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.panic_handler] +## The `panic_handler` attribute + +r[panic.panic_handler.intro] +The *`panic_handler` attribute* can be applied to a function to define the behavior of panics. + +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` handler. + +> [!NOTE] +> 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. + +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 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` 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 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 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. + +> [!NOTE] +> The standard library provides two mechanisms for recovering from a panic, [`std::panic::catch_unwind`] (which enables recovery within the panicking thread) and [`std::thread::spawn`] (which automatically sets up panic recovery for the spawned thread so that other threads may continue running). + +r[panic.unwind.ffi] +### Unwinding across FFI boundaries + +r[panic.unwind.ffi.intro] +It is possible to unwind across FFI boundaries using an [appropriate ABI declaration][unwind-abi]. While useful in certain cases, this creates unique opportunities for undefined behavior, especially when multiple language runtimes are involved. + +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 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: + +* The process aborts. +* The function returns a [`Result::Err`] containing an opaque type. + +> [!NOTE] +> 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 diff --git a/src/runtime.md b/src/runtime.md index 22bfd4672..986c1db88 100644 --- a/src/runtime.md +++ b/src/runtime.md @@ -3,64 +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]. The standard library's panic behavior can be modified at -runtime with the [set_hook] function. - r[runtime.global_allocator] ## The `global_allocator` attribute @@ -96,10 +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 -[set_hook]: std::panic::set_hook [static item]: items/static-items.md [subsystem]: https://msdn.microsoft.com/en-us/library/fcc1zstk.aspx + + diff --git a/src/types/function-pointer.md b/src/types/function-pointer.md index 640b3fcfc..37e7479ac 100644 --- a/src/types/function-pointer.md +++ b/src/types/function-pointer.md @@ -54,6 +54,8 @@ r[type.fn-pointer.constraint-variadic] Variadic parameters can only be specified with [`extern`] function types with the `"C"` or `"cdecl"` calling convention. +This also includes the corresponding [`-unwind` variants][items.fn.extern.unwind]. + r[type.fn-pointer.attributes] ## Attributes on function pointer parameters