From 4b5a891141ed98b8ff85d8deef04950d80a62ec8 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Tue, 21 Feb 2023 08:54:23 -0700 Subject: [PATCH 1/7] rfc text --- text/0000-result_ffi_guarantees.md | 80 ++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 text/0000-result_ffi_guarantees.md diff --git a/text/0000-result_ffi_guarantees.md b/text/0000-result_ffi_guarantees.md new file mode 100644 index 00000000000..f69616bf3b8 --- /dev/null +++ b/text/0000-result_ffi_guarantees.md @@ -0,0 +1,80 @@ +# RFC: result_ffi_guarantees + +- Feature Name: `result_ffi_guarantees` +- Start Date: 2023-02-15 +- 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 + +This RFC gives specific layout and ABI guarantees when wrapping "non-zero" data types from `core` in `Option` or `Result`. This allows those data types to be used directly in FFI, in place of the primitive form of the data (eg: `Result<(), NonZeroI32>` instead of `i32`). + +# Motivation +[motivation]: #motivation + +Rust often needs to interact with foreign code. However, foreign function type signatures don't normally support any of Rust's rich type system. Particular function inputs and outputs will simply use 0 (or null) as a sentinel value and the programmer has to remember when that's happening. + +Though it's common for "raw bindings" crates to also have "high level wrapper" crates that go with them (eg: `windows-sys`/`windows`, or `sdl2-sys`/`sdl2`, etc), someone still has to write those wrapper crates which use the foreign functions directly. Allowing Rust programmers to use more detailed types with foreign functions makes their work easier. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +I'm not sure how to write a "guide" portion of this that's any simpler than the "reference" portion, which is already quite short. + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +When either of these two `core` types: + +* `Option` +* `Result` where either `T` or `E` are a zero-sized type with alignment 1 (a "1-ZST"). + +Is combined with a non-zero or non-null type (see the chart), the combination has the same layout (size and alignment) and the same ABI as the primitive form of the data. + +| Example combined Type | Primitive Type | +|:-|:-| +| `Result, ()>` | `*mut T` | +| `Result<&T, ()>` | `&T` | +| `Result<&mut T, ()>` | `&mut T` | +| `Result` | `fn()` | +| `Result` | `i8` | +| `Result` | `i16` | +| `Result` | `i32` | +| `Result` | `i64` | +| `Result` | `i128` | +| `Result` | `isize` | +| `Result` | `u8` | +| `Result` | `u16` | +| `Result` | `u32` | +| `Result` | `u64` | +| `Result` | `u128` | +| `Result` | `usize` | + +* While `fn()` is listed just once in the above table, this rule applies to all `fn` types (regardless of ABI, arguments, and return type). + +# Drawbacks +[drawbacks]: #drawbacks + +* The compiler has less flexibility with respect to discriminant computation and pattern matching optimizations when a type is niche-optimized. + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +It's always possible to *not* strengthen the guarantees of the language. + +# Prior art +[prior-art]: #prior-art + +The compiler already suports `Option` being combined with specific non-zero types, this RFC mostly expands the list of guaranteed support. + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +None at this time. + +# Future possibilities +[future-possibilities]: #future-possibilities + +* This could be expanded to include [ControlFlow](https://doc.rust-lang.org/nightly/core/ops/enum.ControlFlow.html) and [Poll](https://doc.rust-lang.org/nightly/core/task/enum.Poll.html). +* This could be extended to *all* similar enums in the future. However, without a way to opt-in to the special layout and ABI guarantees (eg: a trait or attribute) it becomes yet another semver hazard for library authors. The RFC is deliberately limited in scope to avoid bikesheding. From f647e37f51f35e07e1dde6aa9ebaab7add1a5d7d Mon Sep 17 00:00:00 2001 From: Lokathor Date: Tue, 28 Feb 2023 11:37:59 -0700 Subject: [PATCH 2/7] restrict the ZST rules, provide further explanation. --- text/0000-result_ffi_guarantees.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/text/0000-result_ffi_guarantees.md b/text/0000-result_ffi_guarantees.md index f69616bf3b8..f74e5a9edbe 100644 --- a/text/0000-result_ffi_guarantees.md +++ b/text/0000-result_ffi_guarantees.md @@ -28,7 +28,7 @@ I'm not sure how to write a "guide" portion of this that's any simpler than the When either of these two `core` types: * `Option` -* `Result` where either `T` or `E` are a zero-sized type with alignment 1 (a "1-ZST"). +* `Result` where either `T` or `E` are a zero-sized type with alignment 1 (a "1-ZST") and either no fields (eg: `()` or `struct Foo;`) or with `repr(transparent)` if there are fields. Is combined with a non-zero or non-null type (see the chart), the combination has the same layout (size and alignment) and the same ABI as the primitive form of the data. @@ -53,6 +53,17 @@ Is combined with a non-zero or non-null type (see the chart), the combination ha * While `fn()` is listed just once in the above table, this rule applies to all `fn` types (regardless of ABI, arguments, and return type). +For simplicity the table listing only uses `Result<_, ()>`, but swapping the `T` and `E` types, or using `Option` is also valid. +What changes are the implied semantics: +* `Result` is "a non-zero success value" +* `Result<(), NonZeroI32>` is "a non-zero error value" +* `Option` is "a non-zero value is present" +* they all pass over FFI as if they were an `i32`. + +Which type you should use with a particular FFI function signature still depends on the function. +Rust can't solve that part for you. +However, once you've decided on the type you want to use, the compiler's normal type checks can guide you everywhere else in the code. + # Drawbacks [drawbacks]: #drawbacks From e7295315e33cb320194a2560641bed288d916c50 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Tue, 28 Feb 2023 13:24:02 -0700 Subject: [PATCH 3/7] require that there NOT be the non_exhaustive attribute on the ZST --- text/0000-result_ffi_guarantees.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/text/0000-result_ffi_guarantees.md b/text/0000-result_ffi_guarantees.md index f74e5a9edbe..e8794177f5e 100644 --- a/text/0000-result_ffi_guarantees.md +++ b/text/0000-result_ffi_guarantees.md @@ -28,7 +28,10 @@ I'm not sure how to write a "guide" portion of this that's any simpler than the When either of these two `core` types: * `Option` -* `Result` where either `T` or `E` are a zero-sized type with alignment 1 (a "1-ZST") and either no fields (eg: `()` or `struct Foo;`) or with `repr(transparent)` if there are fields. +* `Result` where either `T` or `E`: + * Are a zero-sized type with alignment 1 (a "1-ZST"). + * Either have no fields (eg: `()` or `struct Foo;`) or have `repr(transparent)` if there are fields. + * Do not have the `#[non_exhaustive]` attribute. Is combined with a non-zero or non-null type (see the chart), the combination has the same layout (size and alignment) and the same ABI as the primitive form of the data. From 2c258cca22c080fd863e59c8ac7de2ae52ef27eb Mon Sep 17 00:00:00 2001 From: Lokathor Date: Wed, 1 Mar 2023 09:02:33 -0700 Subject: [PATCH 4/7] clarify that the condition list is an "all" list. --- text/0000-result_ffi_guarantees.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-result_ffi_guarantees.md b/text/0000-result_ffi_guarantees.md index e8794177f5e..f62d3fece7d 100644 --- a/text/0000-result_ffi_guarantees.md +++ b/text/0000-result_ffi_guarantees.md @@ -28,7 +28,7 @@ I'm not sure how to write a "guide" portion of this that's any simpler than the When either of these two `core` types: * `Option` -* `Result` where either `T` or `E`: +* `Result` where either `T` or `E` meet all of the following conditions: * Are a zero-sized type with alignment 1 (a "1-ZST"). * Either have no fields (eg: `()` or `struct Foo;`) or have `repr(transparent)` if there are fields. * Do not have the `#[non_exhaustive]` attribute. From e8ecdc4a85f6e30c349c98509b6a67c8f076c4c8 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Wed, 1 Mar 2023 19:16:46 -0700 Subject: [PATCH 5/7] restrict the guarantee to when the ZST has no fields at all. --- text/0000-result_ffi_guarantees.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/0000-result_ffi_guarantees.md b/text/0000-result_ffi_guarantees.md index f62d3fece7d..4fa8350e175 100644 --- a/text/0000-result_ffi_guarantees.md +++ b/text/0000-result_ffi_guarantees.md @@ -29,9 +29,9 @@ When either of these two `core` types: * `Option` * `Result` where either `T` or `E` meet all of the following conditions: - * Are a zero-sized type with alignment 1 (a "1-ZST"). - * Either have no fields (eg: `()` or `struct Foo;`) or have `repr(transparent)` if there are fields. - * Do not have the `#[non_exhaustive]` attribute. + * Is a zero-sized type with alignment 1 (a "1-ZST"). + * Has no fields. + * Does not have the `#[non_exhaustive]` attribute. Is combined with a non-zero or non-null type (see the chart), the combination has the same layout (size and alignment) and the same ABI as the primitive form of the data. From 40b65f307893f86961414d2a39e2a1f8be42222a Mon Sep 17 00:00:00 2001 From: Lokathor Date: Thu, 23 Mar 2023 15:54:05 -0600 Subject: [PATCH 6/7] missing comma --- text/0000-result_ffi_guarantees.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-result_ffi_guarantees.md b/text/0000-result_ffi_guarantees.md index 4fa8350e175..3278f986a19 100644 --- a/text/0000-result_ffi_guarantees.md +++ b/text/0000-result_ffi_guarantees.md @@ -56,7 +56,7 @@ Is combined with a non-zero or non-null type (see the chart), the combination ha * While `fn()` is listed just once in the above table, this rule applies to all `fn` types (regardless of ABI, arguments, and return type). -For simplicity the table listing only uses `Result<_, ()>`, but swapping the `T` and `E` types, or using `Option` is also valid. +For simplicity the table listing only uses `Result<_, ()>`, but swapping the `T` and `E` types, or using `Option`, is also valid. What changes are the implied semantics: * `Result` is "a non-zero success value" * `Result<(), NonZeroI32>` is "a non-zero error value" From 7320c013ae716b29f654503685350a3f2899a825 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Tue, 18 Apr 2023 15:46:02 -0400 Subject: [PATCH 7/7] Add RFC number and tracking issue --- ...result_ffi_guarantees.md => 3391-result_ffi_guarantees.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename text/{0000-result_ffi_guarantees.md => 3391-result_ffi_guarantees.md} (96%) diff --git a/text/0000-result_ffi_guarantees.md b/text/3391-result_ffi_guarantees.md similarity index 96% rename from text/0000-result_ffi_guarantees.md rename to text/3391-result_ffi_guarantees.md index 3278f986a19..4a7ddb06126 100644 --- a/text/0000-result_ffi_guarantees.md +++ b/text/3391-result_ffi_guarantees.md @@ -2,8 +2,8 @@ - Feature Name: `result_ffi_guarantees` - Start Date: 2023-02-15 -- 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) +- RFC PR: [rust-lang/rfcs#3391](https://github.com/rust-lang/rfcs/pull/3391) +- Rust Issue: [rust-lang/rust#110503](https://github.com/rust-lang/rust/issues/110503) # Summary [summary]: #summary