From d25288122c122569b40ed053caf05247c08807ef Mon Sep 17 00:00:00 2001 From: Kyle Strand Date: Thu, 29 Aug 2019 14:10:56 -0600 Subject: [PATCH 01/16] Simple RFC with lots of 'TODO's and text from Josh Triplett --- text/0000-simple_unwind_annotation.md | 75 +++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 text/0000-simple_unwind_annotation.md diff --git a/text/0000-simple_unwind_annotation.md b/text/0000-simple_unwind_annotation.md new file mode 100644 index 00000000000..8756f74af0a --- /dev/null +++ b/text/0000-simple_unwind_annotation.md @@ -0,0 +1,75 @@ +- Feature Name: `simple_unwind_attribute` +- Start Date: 2019-08-29 +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) + +# Summary +[summary]: #summary + +Provides an annotation to permit functions with explicit ABI specifications +(such as `extern "C"`) to unwind + +# Motivation +[motivation]: #motivation + +TODO + +- soundness & optimization +- libjpeg/mozjpeg + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +TODO + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +TODO: incorporate changes from discussion here: +https://github.com/rust-lang/rust/pull/63909 + +By default, Rust assumes that an external function imported with extern "C" +cannot unwind, and Rust will abort if a panic would propagate out of a Rust +function exported with extern "C" function. If you specify the #[unwind] +attribute on an extern "C" function, Rust will instead allow an unwind (such as +a panic) to proceed through that function boundary using Rust's normal unwind +mechanism. This may potentially allow Rust code to call non-Rust code that +calls back into Rust code, and then allow a panic to propagate from Rust to +Rust across the non-Rust code. + +The Rust unwind mechanism is intentionally not specified here. Catching a Rust +panic in Rust code compiled with a different Rust toolchain or options is not +specified. Catching a Rust panic in another language or vice versa is not +specified. The Rust unwind may or may not run non-Rust destructors as it +unwinds. Propagating a Rust panic through non-Rust code may require +target-specific options for the non-Rust code, or may not be supported at all. + +# Drawbacks +[drawbacks]: #drawbacks + +TODO + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +TODO + +- https://github.com/rust-lang/rfcs/pull/2699 + +# Prior art +[prior-art]: #prior-art + +TODO + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +TODO + +# Future possibilities +[future-possibilities]: #future-possibilities + +TODO + +- `unwind(abort)` +- non-"C" ABIs From beb20e37ae8f7e4950a11d5fe436ce00233744d3 Mon Sep 17 00:00:00 2001 From: Kyle J Strand Date: Thu, 29 Aug 2019 14:32:36 -0600 Subject: [PATCH 02/16] Apply initial changes based on previous discussion --- text/0000-simple_unwind_annotation.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/text/0000-simple_unwind_annotation.md b/text/0000-simple_unwind_annotation.md index 8756f74af0a..962a6efdb28 100644 --- a/text/0000-simple_unwind_annotation.md +++ b/text/0000-simple_unwind_annotation.md @@ -20,7 +20,7 @@ TODO # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -TODO +The `#[unwind(allowed)]` attribute permits functions with non-Rust ABIs (e.g. `extern "C" fn`) to unwind rather than terminating the process. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation @@ -28,10 +28,10 @@ TODO TODO: incorporate changes from discussion here: https://github.com/rust-lang/rust/pull/63909 -By default, Rust assumes that an external function imported with extern "C" +By default, Rust assumes that an external function imported with extern "C" { ... } cannot unwind, and Rust will abort if a panic would propagate out of a Rust -function exported with extern "C" function. If you specify the #[unwind] -attribute on an extern "C" function, Rust will instead allow an unwind (such as +function with a non-"Rust" ABI ("`extern`") specification. If you specify the #[unwind(allowed)] +attribute on a function with a non-"Rust" ABI, Rust will instead allow an unwind (such as a panic) to proceed through that function boundary using Rust's normal unwind mechanism. This may potentially allow Rust code to call non-Rust code that calls back into Rust code, and then allow a panic to propagate from Rust to @@ -41,8 +41,9 @@ The Rust unwind mechanism is intentionally not specified here. Catching a Rust panic in Rust code compiled with a different Rust toolchain or options is not specified. Catching a Rust panic in another language or vice versa is not specified. The Rust unwind may or may not run non-Rust destructors as it -unwinds. Propagating a Rust panic through non-Rust code may require -target-specific options for the non-Rust code, or may not be supported at all. +unwinds. Propagating a Rust panic through non-Rust code is unspecified; implementations +that define the behavior may require +target-specific options for the non-Rust code, or this feature may not be supported at all. # Drawbacks [drawbacks]: #drawbacks From 4e892b1d527a2a874cc4ebec130819490f4cab03 Mon Sep 17 00:00:00 2001 From: Kyle Strand Date: Thu, 29 Aug 2019 14:35:10 -0600 Subject: [PATCH 03/16] Some formatting stuff, remove one TODO --- text/0000-simple_unwind_annotation.md | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/text/0000-simple_unwind_annotation.md b/text/0000-simple_unwind_annotation.md index 962a6efdb28..2268da69f90 100644 --- a/text/0000-simple_unwind_annotation.md +++ b/text/0000-simple_unwind_annotation.md @@ -25,25 +25,22 @@ The `#[unwind(allowed)]` attribute permits functions with non-Rust ABIs (e.g. `e # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -TODO: incorporate changes from discussion here: -https://github.com/rust-lang/rust/pull/63909 - -By default, Rust assumes that an external function imported with extern "C" { ... } -cannot unwind, and Rust will abort if a panic would propagate out of a Rust -function with a non-"Rust" ABI ("`extern`") specification. If you specify the #[unwind(allowed)] -attribute on a function with a non-"Rust" ABI, Rust will instead allow an unwind (such as -a panic) to proceed through that function boundary using Rust's normal unwind -mechanism. This may potentially allow Rust code to call non-Rust code that -calls back into Rust code, and then allow a panic to propagate from Rust to -Rust across the non-Rust code. +By default, Rust assumes that an external function imported with `extern "C" { +... }` cannot unwind, and Rust will abort if a panic would propagate out of a +Rust function with a non-"Rust" ABI ("`extern`") specification. If you specify +the `#[unwind(allowed)]` attribute on a function with a non-"Rust" ABI, Rust +will instead allow an unwind (such as a panic) to proceed through that function +boundary using Rust's normal unwind mechanism. This may potentially allow Rust +code to call non-Rust code that calls back into Rust code, and then allow a +panic to propagate from Rust to Rust across the non-Rust code. The Rust unwind mechanism is intentionally not specified here. Catching a Rust panic in Rust code compiled with a different Rust toolchain or options is not specified. Catching a Rust panic in another language or vice versa is not specified. The Rust unwind may or may not run non-Rust destructors as it -unwinds. Propagating a Rust panic through non-Rust code is unspecified; implementations -that define the behavior may require -target-specific options for the non-Rust code, or this feature may not be supported at all. +unwinds. Propagating a Rust panic through non-Rust code is unspecified; +implementations that define the behavior may require target-specific options +for the non-Rust code, or this feature may not be supported at all. # Drawbacks [drawbacks]: #drawbacks From 513a86ee599aa891b471944b6cc9b64f29e80142 Mon Sep 17 00:00:00 2001 From: Kyle J Strand Date: Thu, 29 Aug 2019 15:14:07 -0600 Subject: [PATCH 04/16] Update text/0000-simple_unwind_annotation.md Co-Authored-By: Ralf Jung --- text/0000-simple_unwind_annotation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-simple_unwind_annotation.md b/text/0000-simple_unwind_annotation.md index 2268da69f90..03bcb860f38 100644 --- a/text/0000-simple_unwind_annotation.md +++ b/text/0000-simple_unwind_annotation.md @@ -27,7 +27,7 @@ The `#[unwind(allowed)]` attribute permits functions with non-Rust ABIs (e.g. `e By default, Rust assumes that an external function imported with `extern "C" { ... }` cannot unwind, and Rust will abort if a panic would propagate out of a -Rust function with a non-"Rust" ABI ("`extern`") specification. If you specify +Rust function with a non-"Rust" ABI ("`extern "ABI" fn`") specification. If you specify the `#[unwind(allowed)]` attribute on a function with a non-"Rust" ABI, Rust will instead allow an unwind (such as a panic) to proceed through that function boundary using Rust's normal unwind mechanism. This may potentially allow Rust From 1d0cc81884db061fbf996cc9c313213219f03618 Mon Sep 17 00:00:00 2001 From: Kyle J Strand Date: Fri, 30 Aug 2019 14:42:56 -0600 Subject: [PATCH 05/16] Update text/0000-simple_unwind_annotation.md Drawbacks Co-Authored-By: Jan Hudec --- text/0000-simple_unwind_annotation.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/0000-simple_unwind_annotation.md b/text/0000-simple_unwind_annotation.md index 03bcb860f38..9df53faa5a9 100644 --- a/text/0000-simple_unwind_annotation.md +++ b/text/0000-simple_unwind_annotation.md @@ -45,7 +45,8 @@ for the non-Rust code, or this feature may not be supported at all. # Drawbacks [drawbacks]: #drawbacks -TODO +- Only works as long as Rust uses the same unwinding mechanism as C++. +- Does not allow external library bindings to specify whether callbacks they accept are expected to unwind. # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives From c3588d410fe9334c2821b0378843f7c3f89eb0f1 Mon Sep 17 00:00:00 2001 From: Kyle J Strand Date: Fri, 30 Aug 2019 14:44:07 -0600 Subject: [PATCH 06/16] Update text/0000-simple_unwind_annotation.md Co-Authored-By: Jan Hudec --- text/0000-simple_unwind_annotation.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/0000-simple_unwind_annotation.md b/text/0000-simple_unwind_annotation.md index 9df53faa5a9..7e89c80458c 100644 --- a/text/0000-simple_unwind_annotation.md +++ b/text/0000-simple_unwind_annotation.md @@ -70,5 +70,7 @@ TODO TODO +- https://github.com/rust-lang/rfcs/pull/2699 + - `unwind(abort)` - non-"C" ABIs From 42b7e2b5173a2eea74f91a4b9556e04fc9cfce3b Mon Sep 17 00:00:00 2001 From: Kyle J Strand Date: Fri, 30 Aug 2019 14:45:35 -0600 Subject: [PATCH 07/16] Update text/0000-simple_unwind_annotation.md Co-Authored-By: Jan Hudec --- text/0000-simple_unwind_annotation.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/text/0000-simple_unwind_annotation.md b/text/0000-simple_unwind_annotation.md index 7e89c80458c..ad7a0e28126 100644 --- a/text/0000-simple_unwind_annotation.md +++ b/text/0000-simple_unwind_annotation.md @@ -51,7 +51,9 @@ for the non-Rust code, or this feature may not be supported at all. # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives -TODO +The goal is to enable the current use-cases for unwinding panics through foreign code while providing safer behaviour towards bindings not expecting that, with minimal changes to the language. + +We should try not to prevent the RFC 2699 or other later RFC from resolving the above disadvantages on top of this one. - https://github.com/rust-lang/rfcs/pull/2699 From d5b3c952146dae341ba6ec1b7b988b90ff398d3b Mon Sep 17 00:00:00 2001 From: Kyle J Strand Date: Fri, 30 Aug 2019 14:47:06 -0600 Subject: [PATCH 08/16] Update text/0000-simple_unwind_annotation.md --- text/0000-simple_unwind_annotation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-simple_unwind_annotation.md b/text/0000-simple_unwind_annotation.md index ad7a0e28126..dedd9993158 100644 --- a/text/0000-simple_unwind_annotation.md +++ b/text/0000-simple_unwind_annotation.md @@ -26,7 +26,7 @@ The `#[unwind(allowed)]` attribute permits functions with non-Rust ABIs (e.g. `e [reference-level-explanation]: #reference-level-explanation By default, Rust assumes that an external function imported with `extern "C" { -... }` cannot unwind, and Rust will abort if a panic would propagate out of a +... }` (or another ABI other than `"Rust"`) cannot unwind, and Rust will abort if a panic would propagate out of a Rust function with a non-"Rust" ABI ("`extern "ABI" fn`") specification. If you specify the `#[unwind(allowed)]` attribute on a function with a non-"Rust" ABI, Rust will instead allow an unwind (such as a panic) to proceed through that function From b15587432562305de3b0ea97e38d4c9a5cf138b4 Mon Sep 17 00:00:00 2001 From: "Adam C. Foltzer" Date: Fri, 6 Sep 2019 12:54:12 -0700 Subject: [PATCH 09/16] expand motivations and alternatives; emphasize fn pointer behavior --- text/0000-simple_unwind_annotation.md | 74 +++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 3 deletions(-) diff --git a/text/0000-simple_unwind_annotation.md b/text/0000-simple_unwind_annotation.md index dedd9993158..c56ea670179 100644 --- a/text/0000-simple_unwind_annotation.md +++ b/text/0000-simple_unwind_annotation.md @@ -7,7 +7,7 @@ [summary]: #summary Provides an annotation to permit functions with explicit ABI specifications -(such as `extern "C"`) to unwind +(such as `extern "C"`) to unwind, and affirms that calls to function pointers with explicit ABI specifications may unwind. # Motivation [motivation]: #motivation @@ -17,10 +17,43 @@ TODO - soundness & optimization - libjpeg/mozjpeg +## Generated code + +When working with generated code, such as the result of a JIT compiler, calls to generated code +frequently must use the C ABI. Similarly to the `mozjpeg` case, we want it to be possible for panics +to unwind across calls to foreign code. However the dynamic nature of the foreign code distinguishes +this case from the `mozjpeg` case, specifically because the set of foreign functions that may be +called is not known at Rust compile time. + +This property makes wrappers that use `setjmp`/`longjmp` or C++ exceptions much more difficult to +generate, as we have no header files to provide to a wrapper generator like +[`ffi_wrapper_nounwind`][ffi_wrapper_nounwind]. It further means that a solution must accommodate +foreign function pointers that don't have an `extern "C" { ... }` declaration where we can attach an +attribute. + +[Lucet][lucet] and [Weld][weld] are two projects with these FFI use patterns that would concretely +benefit from permitting unwinding through FFI boundaries. + +[ffi_wrapper_nounwind]: https://docs.rs/ffi_wrapper_nounwind +[lucet]: https://github.com/fastly/lucet +[weld]: https://www.weld.rs/ + +## Less dependence on C/C++ + +Some of the [proposed workarounds][cffi-panic] to the lack of cross-FFI unwinding require the use of +wrappers written in C (for `setjmp` and `longjmp`) or C++ (for exceptions). This solution reduces +the amount of non-Rust code that must be generated and maintained when the foreign code is +compatible with the unspecified Rust unwinding mechanism. + +[cffi-panic]: https://github.com/gnzlbg/cffi-panic + # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -The `#[unwind(allowed)]` attribute permits functions with non-Rust ABIs (e.g. `extern "C" fn`) to unwind rather than terminating the process. +The `#[unwind(allowed)]` attribute permits function declarations and definitions with non-Rust ABIs (e.g. `extern "C" fn`) to unwind rather than terminating the process. + +Calls to function pointers with non-Rust ABIs, as opposed to declared or defined functions, +currently are permitted to unwind. This RFC does not change this behavior. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation @@ -45,7 +78,7 @@ for the non-Rust code, or this feature may not be supported at all. # Drawbacks [drawbacks]: #drawbacks -- Only works as long as Rust uses the same unwinding mechanism as C++. +- Only works as long as the foreign code supports the same unwinding mechanism as Rust. - Does not allow external library bindings to specify whether callbacks they accept are expected to unwind. # Rationale and alternatives @@ -57,6 +90,41 @@ We should try not to prevent the RFC 2699 or other later RFC from resolving the - https://github.com/rust-lang/rfcs/pull/2699 +The alternatives considered are: + +1. Solve the soundness bug by aborting instead of unwinding when reaching an FFI boundary + (https://github.com/rust-lang/rust/issues/52652). As a workaround for applications that would + otherwise need this unwinding, we would recommend the use of wrappers to translate Rust panics to + and from C ABI-compatible values at FFI boundaries, with a foreign control mechanism like + `setjmp`/`longjmp` or C++ exceptions to skip or unwind segments of foreign stack. + + While using these types of wrappers will likely continue to be a recommendation for + maximally-compatible code, it comes with a number of downsides: + + - Additional non-Rust code must be maintained. + - `setjmp`/`longjmp` incur runtime overhead even when no unwinding is required, whereas many + system unwinders incur runtime overhead only when unwinding. + - Wrappers that must be present at Rust compile time are not suitable for applications with + dynamic, generated code. + + If an application has enough control over its compilers and runtime environment to be assured + that its foreign components are compatible with the unspecified Rust unwinding mechanism, these + downsides can be avoided by allowing unwinding across FFI boundaries. + +2. Address unwinding more thoroughly, perhaps through the introduction of additional `extern "ABI"` + strings. This is the approach being pursued in #2699, and is widely seen as a better long-term + solution than adding a single attribute. However, work in #2699 has stalled due to a number of + thorny questions that will likely take significant time and effort to resolve, such as: + + - What should the syntax be for unwind-capable ABIs? + - What are the type system implications for new ABI strings? + - How should the semantics of an unwind-capable ABI be defined across different platforms? + + In the meantime, we are caught between wanting to fix the soundness bug in Rust, and not wanting + to disrupt current development on a number of projects that depend on unwinding. Adding an unwind + attribute means that we can address those current needs right away, and then transition to + #2699's eventual solution by converting the attribute into a deprecated proc-macro. + # Prior art [prior-art]: #prior-art From e950faac95ed5630c58fb991d311e3627cfe05a1 Mon Sep 17 00:00:00 2001 From: Kyle J Strand Date: Fri, 6 Sep 2019 22:17:19 -0600 Subject: [PATCH 10/16] Apply suggested changes to PR additions --- text/0000-simple_unwind_annotation.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/text/0000-simple_unwind_annotation.md b/text/0000-simple_unwind_annotation.md index c56ea670179..be1cb44d573 100644 --- a/text/0000-simple_unwind_annotation.md +++ b/text/0000-simple_unwind_annotation.md @@ -50,7 +50,9 @@ compatible with the unspecified Rust unwinding mechanism. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -The `#[unwind(allowed)]` attribute permits function declarations and definitions with non-Rust ABIs (e.g. `extern "C" fn`) to unwind rather than terminating the process. +When used on Rust functions with non-Rust ABIs (e.g. `extern "C" fn`), the `#[unwind(allowed)]` attribute permits unwinding out of the annotated function; without the attribute, the process will automatically be terminated at the function boundary. + +When used on declarations of imported functions (e.g. `extern "C" { fn ... }`), the `#[unwind(allowed)]` attribute ensures that if the function unwinds, the unwind will be propagated though any calling code; without the attribute, the behavior is undefined. Calls to function pointers with non-Rust ABIs, as opposed to declared or defined functions, currently are permitted to unwind. This RFC does not change this behavior. @@ -78,7 +80,7 @@ for the non-Rust code, or this feature may not be supported at all. # Drawbacks [drawbacks]: #drawbacks -- Only works as long as the foreign code supports the same unwinding mechanism as Rust. +- Only works as long as the foreign code supports the same unwinding mechanism as Rust. (Currently, Rust and C++ code compiled for ABI-compatible backends use the same mechanism.) - Does not allow external library bindings to specify whether callbacks they accept are expected to unwind. # Rationale and alternatives @@ -103,7 +105,7 @@ The alternatives considered are: - Additional non-Rust code must be maintained. - `setjmp`/`longjmp` incur runtime overhead even when no unwinding is required, whereas many - system unwinders incur runtime overhead only when unwinding. + unwinding mechanisms incur runtime overhead only when unwinding. - Wrappers that must be present at Rust compile time are not suitable for applications with dynamic, generated code. From dab4e7f5ff52324eaf813a6a152462a85b327b18 Mon Sep 17 00:00:00 2001 From: Kyle J Strand Date: Fri, 6 Sep 2019 22:21:53 -0600 Subject: [PATCH 11/16] Another suggested change to PR additions --- text/0000-simple_unwind_annotation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-simple_unwind_annotation.md b/text/0000-simple_unwind_annotation.md index be1cb44d573..ec91d0b4d32 100644 --- a/text/0000-simple_unwind_annotation.md +++ b/text/0000-simple_unwind_annotation.md @@ -94,8 +94,8 @@ We should try not to prevent the RFC 2699 or other later RFC from resolving the The alternatives considered are: -1. Solve the soundness bug by aborting instead of unwinding when reaching an FFI boundary - (https://github.com/rust-lang/rust/issues/52652). As a workaround for applications that would +1. Stabilize the [abort-on-FFI-boundary behavior](https://github.com/rust-lang/rust/issues/52652) + without providing any mechanism to change this behavior. As a workaround for applications that would otherwise need this unwinding, we would recommend the use of wrappers to translate Rust panics to and from C ABI-compatible values at FFI boundaries, with a foreign control mechanism like `setjmp`/`longjmp` or C++ exceptions to skip or unwind segments of foreign stack. From 233578e6767f7a9263bcab58d9d24d058d0d2b18 Mon Sep 17 00:00:00 2001 From: "Adam C. Foltzer" Date: Tue, 10 Sep 2019 12:23:37 -0700 Subject: [PATCH 12/16] switch from annotation approach to new ABI string approach --- text/0000-simple_unwind_annotation.md | 42 ++++++++++++++++++--------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/text/0000-simple_unwind_annotation.md b/text/0000-simple_unwind_annotation.md index ec91d0b4d32..29433591d11 100644 --- a/text/0000-simple_unwind_annotation.md +++ b/text/0000-simple_unwind_annotation.md @@ -1,4 +1,4 @@ -- Feature Name: `simple_unwind_attribute` +- Feature Name: `simple_c_panic_abi` - Start Date: 2019-08-29 - RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) @@ -6,8 +6,8 @@ # Summary [summary]: #summary -Provides an annotation to permit functions with explicit ABI specifications -(such as `extern "C"`) to unwind, and affirms that calls to function pointers with explicit ABI specifications may unwind. +Provides a new ABI string `extern "C+panic"` to denote functions that use the C ABI, but may also +unwind with a Rust panic. # Motivation [motivation]: #motivation @@ -50,21 +50,23 @@ compatible with the unspecified Rust unwinding mechanism. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -When used on Rust functions with non-Rust ABIs (e.g. `extern "C" fn`), the `#[unwind(allowed)]` attribute permits unwinding out of the annotated function; without the attribute, the process will automatically be terminated at the function boundary. +Rust function definitions with the `"C+panic"` ABI string (e.g., `extern "C+panic" fn`) are +permitted to unwind with a panic, as opposed to `extern "C" fn` functions which will abort the +process if a panic reaches the function boundary. -When used on declarations of imported functions (e.g. `extern "C" { fn ... }`), the `#[unwind(allowed)]` attribute ensures that if the function unwinds, the unwind will be propagated though any calling code; without the attribute, the behavior is undefined. - -Calls to function pointers with non-Rust ABIs, as opposed to declared or defined functions, -currently are permitted to unwind. This RFC does not change this behavior. +When used on declarations of imported functions (e.g., `extern "C+panic" { fn ... }`), or function +pointers (e.g., `extern "C+panic" fn()`) the `"C+panic"` ABI string means that if the function +unwinds, the unwind will be propagated though any calling code. If an `extern "C"` imported function +or function pointer unwinds, the behavior is undefined. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -By default, Rust assumes that an external function imported with `extern "C" { +Currently, Rust assumes that an external function imported with `extern "C" { ... }` (or another ABI other than `"Rust"`) cannot unwind, and Rust will abort if a panic would propagate out of a -Rust function with a non-"Rust" ABI ("`extern "ABI" fn`") specification. If you specify -the `#[unwind(allowed)]` attribute on a function with a non-"Rust" ABI, Rust -will instead allow an unwind (such as a panic) to proceed through that function +Rust function with a non-"Rust" ABI ("`extern "ABI" fn`") specification. Under this RFC, +functions with the `"C+panic"` ABI string +instead allows an unwind (such as a panic) to proceed through that function boundary using Rust's normal unwind mechanism. This may potentially allow Rust code to call non-Rust code that calls back into Rust code, and then allow a panic to propagate from Rust to Rust across the non-Rust code. @@ -77,11 +79,16 @@ unwinds. Propagating a Rust panic through non-Rust code is unspecified; implementations that define the behavior may require target-specific options for the non-Rust code, or this feature may not be supported at all. +For the purposes of the type system, `"C+panic"` is considered a totally distinct ABI string from +`"C"`. While there may be some circumstances where it is sensible to use an `extern "C" fn` in place +of an `extern "C+panic" fn`, and vice-versa, this introduces questions of subtyping and variance +that are beyond the scope of this RFC. This restrictive approach is forwards-compatible with more +permissive typing in future work like #2699. + # Drawbacks [drawbacks]: #drawbacks - Only works as long as the foreign code supports the same unwinding mechanism as Rust. (Currently, Rust and C++ code compiled for ABI-compatible backends use the same mechanism.) -- Does not allow external library bindings to specify whether callbacks they accept are expected to unwind. # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives @@ -127,6 +134,15 @@ The alternatives considered are: attribute means that we can address those current needs right away, and then transition to #2699's eventual solution by converting the attribute into a deprecated proc-macro. +3. Using an attribute on function definitions and declarations to indicate that unwinding should be + allowed, regardless of the ABI string. This would be easy to implement, as there is currently + such an attribute in unstable Rust. An attribute is not a complete solution, though, as there is + no current way to syntactically attach an attribute to a function pointer type. We considered + making all function pointers unwindable without changing the existing syntax (see #2602), because + Rust currently does not emit `nounwind` for calls to function pointers, however this would + require changes to the language reference that would codify inconsistency between function + pointers and definitions/declarations. + # Prior art [prior-art]: #prior-art From 85944045cdc1357d7af55f4c4b41c5f22e368338 Mon Sep 17 00:00:00 2001 From: "Adam C. Foltzer" Date: Tue, 10 Sep 2019 12:27:46 -0700 Subject: [PATCH 13/16] fix link to related RFC PR --- text/0000-simple_unwind_annotation.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/text/0000-simple_unwind_annotation.md b/text/0000-simple_unwind_annotation.md index 29433591d11..c7a023f32d0 100644 --- a/text/0000-simple_unwind_annotation.md +++ b/text/0000-simple_unwind_annotation.md @@ -137,11 +137,11 @@ The alternatives considered are: 3. Using an attribute on function definitions and declarations to indicate that unwinding should be allowed, regardless of the ABI string. This would be easy to implement, as there is currently such an attribute in unstable Rust. An attribute is not a complete solution, though, as there is - no current way to syntactically attach an attribute to a function pointer type. We considered - making all function pointers unwindable without changing the existing syntax (see #2602), because - Rust currently does not emit `nounwind` for calls to function pointers, however this would - require changes to the language reference that would codify inconsistency between function - pointers and definitions/declarations. + no current way to syntactically attach an attribute to a function pointer type (see + https://github.com/rust-lang/rfcs/pull/2602). We considered making all function pointers + unwindable without changing the existing syntax, because Rust currently does not emit `nounwind` + for calls to function pointers, however this would require changes to the language reference that + would codify inconsistency between function pointers and definitions/declarations. # Prior art [prior-art]: #prior-art From 8c78012d264e46ccfad58fccca810c6b7ea156fb Mon Sep 17 00:00:00 2001 From: "Adam C. Foltzer" Date: Tue, 10 Sep 2019 12:34:09 -0700 Subject: [PATCH 14/16] `"C+panic"` -> `"C panic"` per current discussion --- text/0000-simple_unwind_annotation.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/text/0000-simple_unwind_annotation.md b/text/0000-simple_unwind_annotation.md index c7a023f32d0..6509c65ea08 100644 --- a/text/0000-simple_unwind_annotation.md +++ b/text/0000-simple_unwind_annotation.md @@ -6,7 +6,7 @@ # Summary [summary]: #summary -Provides a new ABI string `extern "C+panic"` to denote functions that use the C ABI, but may also +Provides a new ABI string `extern "C panic"` to denote functions that use the C ABI, but may also unwind with a Rust panic. # Motivation @@ -50,12 +50,12 @@ compatible with the unspecified Rust unwinding mechanism. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -Rust function definitions with the `"C+panic"` ABI string (e.g., `extern "C+panic" fn`) are +Rust function definitions with the `"C panic"` ABI string (e.g., `extern "C panic" fn`) are permitted to unwind with a panic, as opposed to `extern "C" fn` functions which will abort the process if a panic reaches the function boundary. -When used on declarations of imported functions (e.g., `extern "C+panic" { fn ... }`), or function -pointers (e.g., `extern "C+panic" fn()`) the `"C+panic"` ABI string means that if the function +When used on declarations of imported functions (e.g., `extern "C panic" { fn ... }`), or function +pointers (e.g., `extern "C panic" fn()`) the `"C panic"` ABI string means that if the function unwinds, the unwind will be propagated though any calling code. If an `extern "C"` imported function or function pointer unwinds, the behavior is undefined. @@ -65,7 +65,7 @@ or function pointer unwinds, the behavior is undefined. Currently, Rust assumes that an external function imported with `extern "C" { ... }` (or another ABI other than `"Rust"`) cannot unwind, and Rust will abort if a panic would propagate out of a Rust function with a non-"Rust" ABI ("`extern "ABI" fn`") specification. Under this RFC, -functions with the `"C+panic"` ABI string +functions with the `"C panic"` ABI string instead allows an unwind (such as a panic) to proceed through that function boundary using Rust's normal unwind mechanism. This may potentially allow Rust code to call non-Rust code that calls back into Rust code, and then allow a @@ -79,9 +79,9 @@ unwinds. Propagating a Rust panic through non-Rust code is unspecified; implementations that define the behavior may require target-specific options for the non-Rust code, or this feature may not be supported at all. -For the purposes of the type system, `"C+panic"` is considered a totally distinct ABI string from +For the purposes of the type system, `"C panic"` is considered a totally distinct ABI string from `"C"`. While there may be some circumstances where it is sensible to use an `extern "C" fn` in place -of an `extern "C+panic" fn`, and vice-versa, this introduces questions of subtyping and variance +of an `extern "C panic" fn`, and vice-versa, this introduces questions of subtyping and variance that are beyond the scope of this RFC. This restrictive approach is forwards-compatible with more permissive typing in future work like #2699. From cdd2ab11627c0f82bd434bf89d22ed958ebdbb7f Mon Sep 17 00:00:00 2001 From: "Adam C. Foltzer" Date: Tue, 10 Sep 2019 12:42:14 -0700 Subject: [PATCH 15/16] Apply suggestions from code review Co-Authored-By: Kyle J Strand --- text/0000-simple_unwind_annotation.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/text/0000-simple_unwind_annotation.md b/text/0000-simple_unwind_annotation.md index 6509c65ea08..9126f8c044c 100644 --- a/text/0000-simple_unwind_annotation.md +++ b/text/0000-simple_unwind_annotation.md @@ -54,8 +54,8 @@ Rust function definitions with the `"C panic"` ABI string (e.g., `extern "C pani permitted to unwind with a panic, as opposed to `extern "C" fn` functions which will abort the process if a panic reaches the function boundary. -When used on declarations of imported functions (e.g., `extern "C panic" { fn ... }`), or function -pointers (e.g., `extern "C panic" fn()`) the `"C panic"` ABI string means that if the function +When used on declarations of imported functions (e.g., `extern "C panic" { fn ... }`) or function +pointers (e.g., `extern "C panic" fn()`), the `"C panic"` ABI string means that if the function unwinds, the unwind will be propagated though any calling code. If an `extern "C"` imported function or function pointer unwinds, the behavior is undefined. @@ -80,8 +80,8 @@ implementations that define the behavior may require target-specific options for the non-Rust code, or this feature may not be supported at all. For the purposes of the type system, `"C panic"` is considered a totally distinct ABI string from -`"C"`. While there may be some circumstances where it is sensible to use an `extern "C" fn` in place -of an `extern "C panic" fn`, and vice-versa, this introduces questions of subtyping and variance +`"C"`. While there may be some circumstances for which an `extern "C" fn` in place +of an `extern "C panic" fn` (or vice-versa) would be useful, this introduces questions of subtyping and variance that are beyond the scope of this RFC. This restrictive approach is forwards-compatible with more permissive typing in future work like #2699. From 090838351107d85e6f121fba42925de0df9bac22 Mon Sep 17 00:00:00 2001 From: "Adam C. Foltzer" Date: Tue, 10 Sep 2019 12:43:53 -0700 Subject: [PATCH 16/16] fix up grammar --- text/0000-simple_unwind_annotation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-simple_unwind_annotation.md b/text/0000-simple_unwind_annotation.md index 9126f8c044c..1abef470032 100644 --- a/text/0000-simple_unwind_annotation.md +++ b/text/0000-simple_unwind_annotation.md @@ -66,7 +66,7 @@ Currently, Rust assumes that an external function imported with `extern "C" { ... }` (or another ABI other than `"Rust"`) cannot unwind, and Rust will abort if a panic would propagate out of a Rust function with a non-"Rust" ABI ("`extern "ABI" fn`") specification. Under this RFC, functions with the `"C panic"` ABI string -instead allows an unwind (such as a panic) to proceed through that function +instead allow Rust panic unwinding to proceed through that function boundary using Rust's normal unwind mechanism. This may potentially allow Rust code to call non-Rust code that calls back into Rust code, and then allow a panic to propagate from Rust to Rust across the non-Rust code.