From 29a19f84c2ad86f6f524f8012a7f2b756d7677f0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 13 Sep 2025 16:58:40 +0200 Subject: [PATCH 1/4] Result/Option layout guarantee clarifications --- library/core/src/option.rs | 7 +++++-- library/core/src/result.rs | 18 ++++++++++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 430ee3470ac3f..47c554d75edb1 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -119,8 +119,11 @@ //! # Representation //! //! Rust guarantees to optimize the following types `T` such that -//! [`Option`] has the same size, alignment, and [function call ABI] as `T`. In some -//! of these cases, Rust further guarantees the following: +//! [`Option`] has the same size, alignment, and [function call ABI] as `T`. +//! It is therefore sound to transmute `t: T` to `Option` (which will produce `Some(t)`), and +//! to transmute `Some(t): Option` to `T` (which will produce `t`). +//! +//! In some of these cases, Rust further guarantees the following: //! - `transmute::<_, Option>([0u8; size_of::()])` is sound and produces //! `Option::::None` //! - `transmute::<_, [u8; size_of::()]>(Option::::None)` is sound and produces diff --git a/library/core/src/result.rs b/library/core/src/result.rs index c69762a728598..324f288594b19 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -230,24 +230,30 @@ //! //! # Representation //! -//! In some cases, [`Result`] will gain the same size, alignment, and ABI -//! guarantees as [`Option`] has. One of either the `T` or `E` type must be a -//! type that qualifies for the `Option` [representation guarantees][opt-rep], -//! and the *other* type must meet all of the following conditions: +//! In some cases, [`Result`] comes with size, alignment, and ABI guarantees. +//! Specifically, one of either the `T` or `E` type must be a type that qualifies for the `Option` +//! [representation guarantees][opt-rep] (let's call that type `I`), and the *other* type must meet +//! all of the following conditions: //! * Is a zero-sized type with alignment 1 (a "1-ZST"). //! * Has no fields. //! * Does not have the `#[non_exhaustive]` attribute. //! +//! If that is the case, then `Result` has the same size, alignment, and [function call ABI] +//! as `I` (and therefore, as `Option`). If `I` is `T`, it is therefore sound to transmute `t: I` +//! to `Result` (which will produce `Ok(t)`), and to transmute `Ok(t): Result` to `I` +//! (which will produce `t`). If `I` is `E`, the same applies with `Ok` replaced by `Err`. +//! //! For example, `NonZeroI32` qualifies for the `Option` representation //! guarantees, and `()` is a zero-sized type with alignment 1, no fields, and //! it isn't `non_exhaustive`. This means that both `Result` and -//! `Result<(), NonZeroI32>` have the same size, alignment, and ABI guarantees -//! as `Option`. The only difference is the implied semantics: +//! `Result<(), NonZeroI32>` have the same size, alignment, and ABI +//! as `NonZeroI32` (and `Option`). The only difference is the implied semantics: //! * `Option` is "a non-zero i32 might be present" //! * `Result` is "a non-zero i32 success result, if any" //! * `Result<(), NonZeroI32>` is "a non-zero i32 error result, if any" //! //! [opt-rep]: ../option/index.html#representation "Option Representation" +//! [function call ABI]: ../primitive.fn.html#abi-compatibility //! //! # Method overview //! From 781432e355cda386ce7c394a728cd4704d78fbc5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 13 Sep 2025 17:23:29 +0200 Subject: [PATCH 2/4] clarify 'no fields' --- compiler/rustc_lint/src/types.rs | 4 ++-- library/core/src/result.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index eaec0c9857d28..7c66559269580 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -814,7 +814,7 @@ fn get_nullable_type<'tcx>( /// A type is niche-optimization candidate iff: /// - Is a zero-sized type with alignment 1 (a “1-ZST”). -/// - Has no fields. +/// - Is either a struct/tuple with no fields, or an enum with no variants. /// - Does not have the `#[non_exhaustive]` attribute. fn is_niche_optimization_candidate<'tcx>( tcx: TyCtxt<'tcx>, @@ -828,7 +828,7 @@ fn is_niche_optimization_candidate<'tcx>( match ty.kind() { ty::Adt(ty_def, _) => { let non_exhaustive = ty_def.is_variant_list_non_exhaustive(); - let empty = (ty_def.is_struct() && ty_def.all_fields().next().is_none()) + let empty = (ty_def.is_struct() && ty_def.non_enum_variant().fields.is_empty()) || (ty_def.is_enum() && ty_def.variants().is_empty()); !non_exhaustive && empty diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 324f288594b19..86602b6200040 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -235,7 +235,7 @@ //! [representation guarantees][opt-rep] (let's call that type `I`), and the *other* type must meet //! all of the following conditions: //! * Is a zero-sized type with alignment 1 (a "1-ZST"). -//! * Has no fields. +//! * Is either a struct/tuple with no fields, or an enum with no variants. //! * Does not have the `#[non_exhaustive]` attribute. //! //! If that is the case, then `Result` has the same size, alignment, and [function call ABI] From 058f08dd7835ef2f16b8a7b5389c361dbdf6bc4c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 13 Sep 2025 21:05:30 +0200 Subject: [PATCH 3/4] have Result docs match ABI docs --- library/core/src/result.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 86602b6200040..39689e657a580 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -232,20 +232,16 @@ //! //! In some cases, [`Result`] comes with size, alignment, and ABI guarantees. //! Specifically, one of either the `T` or `E` type must be a type that qualifies for the `Option` -//! [representation guarantees][opt-rep] (let's call that type `I`), and the *other* type must meet -//! all of the following conditions: -//! * Is a zero-sized type with alignment 1 (a "1-ZST"). -//! * Is either a struct/tuple with no fields, or an enum with no variants. -//! * Does not have the `#[non_exhaustive]` attribute. +//! [representation guarantees][opt-rep] (let's call that type `I`), and the *other* type +//! is a zero-sized type with alignment 1 (a "1-ZST"). //! //! If that is the case, then `Result` has the same size, alignment, and [function call ABI] //! as `I` (and therefore, as `Option`). If `I` is `T`, it is therefore sound to transmute `t: I` //! to `Result` (which will produce `Ok(t)`), and to transmute `Ok(t): Result` to `I` //! (which will produce `t`). If `I` is `E`, the same applies with `Ok` replaced by `Err`. //! -//! For example, `NonZeroI32` qualifies for the `Option` representation -//! guarantees, and `()` is a zero-sized type with alignment 1, no fields, and -//! it isn't `non_exhaustive`. This means that both `Result` and +//! For example, `NonZeroI32` qualifies for the `Option` representation guarantees, and `()` is a +//! zero-sized type with alignment 1. This means that both `Result` and //! `Result<(), NonZeroI32>` have the same size, alignment, and ABI //! as `NonZeroI32` (and `Option`). The only difference is the implied semantics: //! * `Option` is "a non-zero i32 might be present" From 73f5fe78d4b822124ca6082d49592314eb9d68a9 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sat, 18 Oct 2025 00:23:15 +0000 Subject: [PATCH 4/4] Revise `Result`/`Option` guarantee docs The notation used here (e.g. "transmute `t: T` to `Option`") felt maybe a bit heavy, in the context of the library documentation, so let's elaborate this a bit. Also, in the `Option` docs, we talk about this being true for "the following types `T`", but it felt this caveat might get a bit lost in the next sentence that talks about the valid transmutations, so let's reiterate the caveat. While we're touching the line, we can improve: > The only difference is the implied semantics: This sentence was a bit awkward due to the mismatched plurality and not identifying the difference being spoken to. We'll reword this to make it more clear. We'll wrap to 80, since the existing text and most of the doc comments in these files are wrapped this way. --- library/core/src/option.rs | 10 ++++++---- library/core/src/result.rs | 33 +++++++++++++++++++-------------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 47c554d75edb1..e3c4758bc6af5 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -118,10 +118,12 @@ //! //! # Representation //! -//! Rust guarantees to optimize the following types `T` such that -//! [`Option`] has the same size, alignment, and [function call ABI] as `T`. -//! It is therefore sound to transmute `t: T` to `Option` (which will produce `Some(t)`), and -//! to transmute `Some(t): Option` to `T` (which will produce `t`). +//! Rust guarantees to optimize the following types `T` such that [`Option`] +//! has the same size, alignment, and [function call ABI] as `T`. It is +//! therefore sound, when `T` is one of these types, to transmute a value `t` of +//! type `T` to type `Option` (producing the value `Some(t)`) and to +//! transmute a value `Some(t)` of type `Option` to type `T` (producing the +//! value `t`). //! //! In some of these cases, Rust further guarantees the following: //! - `transmute::<_, Option>([0u8; size_of::()])` is sound and produces diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 39689e657a580..6fee7febde38d 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -230,20 +230,25 @@ //! //! # Representation //! -//! In some cases, [`Result`] comes with size, alignment, and ABI guarantees. -//! Specifically, one of either the `T` or `E` type must be a type that qualifies for the `Option` -//! [representation guarantees][opt-rep] (let's call that type `I`), and the *other* type -//! is a zero-sized type with alignment 1 (a "1-ZST"). -//! -//! If that is the case, then `Result` has the same size, alignment, and [function call ABI] -//! as `I` (and therefore, as `Option`). If `I` is `T`, it is therefore sound to transmute `t: I` -//! to `Result` (which will produce `Ok(t)`), and to transmute `Ok(t): Result` to `I` -//! (which will produce `t`). If `I` is `E`, the same applies with `Ok` replaced by `Err`. -//! -//! For example, `NonZeroI32` qualifies for the `Option` representation guarantees, and `()` is a -//! zero-sized type with alignment 1. This means that both `Result` and -//! `Result<(), NonZeroI32>` have the same size, alignment, and ABI -//! as `NonZeroI32` (and `Option`). The only difference is the implied semantics: +//! In some cases, [`Result`] comes with size, alignment, and ABI +//! guarantees. Specifically, one of either the `T` or `E` type must be a type +//! that qualifies for the `Option` [representation guarantees][opt-rep] (let's +//! call that type `I`), and the *other* type is a zero-sized type with +//! alignment 1 (a "1-ZST"). +//! +//! If that is the case, then `Result` has the same size, alignment, and +//! [function call ABI] as `I` (and therefore, as `Option`). If `I` is `T`, +//! it is therefore sound to transmute a value `t` of type `I` to type +//! `Result` (producing the value `Ok(t)`) and to transmute a value +//! `Ok(t)` of type `Result` to type `I` (producing the value `t`). If `I` +//! is `E`, the same applies with `Ok` replaced by `Err`. +//! +//! For example, `NonZeroI32` qualifies for the `Option` representation +//! guarantees and `()` is a zero-sized type with alignment 1. This means that +//! both `Result` and `Result<(), NonZeroI32>` have the same +//! size, alignment, and ABI as `NonZeroI32` (and `Option`). The +//! only difference between these is in the implied semantics: +//! //! * `Option` is "a non-zero i32 might be present" //! * `Result` is "a non-zero i32 success result, if any" //! * `Result<(), NonZeroI32>` is "a non-zero i32 error result, if any"