From 824a59ff510ebcf0373d9f6ada2ea728e03a687d Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Mon, 8 Jul 2019 15:14:43 +0200 Subject: [PATCH 1/6] Guarantee layout of some single variant enums Addresses part of #79 . --- reference/src/layout/enums.md | 108 ++++++++++++++-------------------- 1 file changed, 45 insertions(+), 63 deletions(-) diff --git a/reference/src/layout/enums.md b/reference/src/layout/enums.md index 286c1546..ff278bae 100644 --- a/reference/src/layout/enums.md +++ b/reference/src/layout/enums.md @@ -378,90 +378,72 @@ enum Enum2 { [niche]: ../glossary.html#niche -## Unresolved questions - ### Layout of single variant enums -[Issue #79.](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/79) - -Enums that contain a **single variant** and which do not have an -explicit `#[repr]` annotation are an important special case. Since -there is only a single variant, the enum must be instantiated with -that variant, which means that the enum is in fact equivalent to a -struct. The question then is to what extent we should **guarantee** -that the two share an equivalent layout, and also how to define the -interaction with uninhabited types. +**Single variant data-carrying*** enums without a `repr()` annotation have the +same layout as the variant field. **Single variant fieldless** enums have the +same layout as a unit struct. -As presently implemented, the compiler will use the same layout for -structs and for single variant enums (as long as they do not have a -`#[repr]` annotation that overrides that choice). So, for example, the -struct `SomeStruct` and the enum `SomeEnum` would have an equivalent -layout ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=3697ac684c3d021892694956df957653)): +Here: ```rust -struct SomeStruct; -enum SomeEnum { - SomeVariant, -} - +# use std::mem::{size_of, align_of}; +struct UnitStruct; +enum SingleVariantFieldless { FieldlessVariant } # fn main() { -# use std::mem; -let x = SomeStruct; -let y = SomeEnum::SomeVariant; -assert_eq!( - mem::size_of_val(&x), - mem::size_of_val(&y), - "types have different sizes", -); -println!("types both have size {}", std::mem::size_of_val(&x)); +assert_eq!(size_of::(), size_of::()); +assert_eq!(align_of::(), align_of::()); +// assert_eq!(call_abi_of::(), call_abi_of::()); +// assert_eq!(niches_of::(), niches_of::()); # } ``` -Similarly, the struct `SomeStruct` and the enum `SomeVariant` in this -example would also be equivalent in their layout -([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=924724764419f846c788a8763da45992)): +the single-variant fieldless enum `SingleVariantFieldless` has the same layout +as `UnitStruct`. + +Here: ```rust +# use std::mem::{size_of, align_of}; struct SomeStruct { x: u32 } -enum SomeEnum { - SomeVariant { x: u32 }, +enum SingleVariantDataCarrying { + DataCarryingVariant(SomeStruct), } - # fn main() { -# use std::mem; -let x = SomeStruct { x: 22 }; -let y = SomeEnum::SomeVariant { x: 22 }; -assert_eq!( - mem::size_of_val(&x), - mem::size_of_val(&y), - "types have different sizes", -); -println!("types both have size {}", mem::size_of_val(&x)); -} +# assert_eq!(size_of::(), size_of::()); +# assert_eq!(align_of::(), align_of::()); +# } ``` -In fact, the compiler will use this optimized layout even for enums -that define multiple variants, as long as all but one of the variants -is uninhabited -([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=3cc1484c5b91097f3dc2015b7c207a0e)): +the single-variant data-carrying enum `SingleVariantDataCarrying` has the same +layout as `SomeStruct`. + +### Layout of multi-variant enums with one inhabited variant + +The layout of **multi-variant** enums with **one inhabited variant** is the same +as that of the single-variant enum containing that same inhabited variant. + +Here: ```rust -# enum Void { } +# use std::mem::{size_of, align_of}; struct SomeStruct { x: u32 } -enum SomeEnum { - SomeVariant { x: u32 }, - UninhabitedVariant { y: Void }, +enum SingleVariantDataCarrying { + DataCarryingVariant(SomeStruct), +} +enum Void0 {} +enum Void1 {} +enum MultiVariantSingleHabited { + DataCarryingVariant(SomeStruct), + Uninhabited0(Void0), + Uninhabited1(Void1), } - # fn main() { # use std::mem; -let x = SomeStruct { x: 22 }; -let y = SomeEnum::SomeVariant { x: 22 }; -assert_eq!( - mem::size_of_val(&x), - mem::size_of_val(&y), - "types have different sizes", -); -println!("types both have size {}", mem::size_of_val(&x)); +# assert_eq!(size_of::(), size_of::()); +# assert_eq!(align_of::(), align_of::()); # } ``` + +the `MultiVariantSingleHabited` enum has the same layout as +`SingleVariantDataCarrying`. From 6b7294543393c357c43324d9026201f292c3fbe8 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Sat, 13 Jul 2019 12:43:48 +0200 Subject: [PATCH 2/6] Document that the new sections have not been approved by an RFC process --- reference/src/layout/enums.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/reference/src/layout/enums.md b/reference/src/layout/enums.md index ff278bae..48c49eed 100644 --- a/reference/src/layout/enums.md +++ b/reference/src/layout/enums.md @@ -380,6 +380,8 @@ enum Enum2 { ### Layout of single variant enums +> **NOTE**: the guarantees in this section have not been approved by an RFC process. + **Single variant data-carrying*** enums without a `repr()` annotation have the same layout as the variant field. **Single variant fieldless** enums have the same layout as a unit struct. @@ -420,6 +422,8 @@ layout as `SomeStruct`. ### Layout of multi-variant enums with one inhabited variant +> **NOTE**: the guarantees in this section have not been approved by an RFC process. + The layout of **multi-variant** enums with **one inhabited variant** is the same as that of the single-variant enum containing that same inhabited variant. From ec2e0d0a588cc9aeabc33448eef69360453e931e Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Thu, 15 Aug 2019 07:00:21 +0200 Subject: [PATCH 3/6] Amend data-carrying enums definition --- reference/src/layout/enums.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/reference/src/layout/enums.md b/reference/src/layout/enums.md index 48c49eed..fe56db78 100644 --- a/reference/src/layout/enums.md +++ b/reference/src/layout/enums.md @@ -46,10 +46,10 @@ As in C, discriminant values that are not specified are defined as either 0 (for the first variant) or as one more than the prior variant. -**Data-carrying enums.** Enums whose variants have fields are called -"data-carrying" enums. Note that for the purposes of this definition, -it is not relevant whether those fields are zero-sized. Therefore this -enum is considered "data-carrying": +**Data-carrying enums.** Enums with at least one variant with fields are called +"data-carrying" enums. Note that for the purposes of this definition, it is not +relevant whether the variant fields are zero-sized. Therefore this enum is +considered "data-carrying": ```rust enum Foo { From a8d29ad0150469f80e1b44d669bdc0bd1d7a25f5 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Thu, 15 Aug 2019 07:03:03 +0200 Subject: [PATCH 4/6] Clarify layout of enums with a single variant --- reference/src/layout/enums.md | 37 +++++++++-------------------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/reference/src/layout/enums.md b/reference/src/layout/enums.md index fe56db78..ff74ffa0 100644 --- a/reference/src/layout/enums.md +++ b/reference/src/layout/enums.md @@ -378,47 +378,28 @@ enum Enum2 { [niche]: ../glossary.html#niche -### Layout of single variant enums +### Layout of enums with a single variant > **NOTE**: the guarantees in this section have not been approved by an RFC process. -**Single variant data-carrying*** enums without a `repr()` annotation have the -same layout as the variant field. **Single variant fieldless** enums have the -same layout as a unit struct. +**Data-carrying** enums with a single variant without a `repr()` annotation have +the same layout as the variant field. **Fieldless** enums with a single variant +have the same layout as a unit struct. -Here: +For example, here: ```rust -# use std::mem::{size_of, align_of}; struct UnitStruct; -enum SingleVariantFieldless { FieldlessVariant } -# fn main() { -assert_eq!(size_of::(), size_of::()); -assert_eq!(align_of::(), align_of::()); -// assert_eq!(call_abi_of::(), call_abi_of::()); -// assert_eq!(niches_of::(), niches_of::()); -# } -``` - -the single-variant fieldless enum `SingleVariantFieldless` has the same layout -as `UnitStruct`. +enum FieldlessSingleVariant { FieldlessVariant } -Here: - -```rust -# use std::mem::{size_of, align_of}; struct SomeStruct { x: u32 } -enum SingleVariantDataCarrying { +enum DataCarryingSingleVariant { DataCarryingVariant(SomeStruct), } -# fn main() { -# assert_eq!(size_of::(), size_of::()); -# assert_eq!(align_of::(), align_of::()); -# } ``` -the single-variant data-carrying enum `SingleVariantDataCarrying` has the same -layout as `SomeStruct`. +* `FieldSingleVariant` has the same layout as `UnitStruct`, +* `DataCarryingSingleVariant` has the same layout as `SomeStruct`. ### Layout of multi-variant enums with one inhabited variant From e80b69d6898c5ffbe99abeac603d11ed04d65e8f Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Thu, 15 Aug 2019 07:14:58 +0200 Subject: [PATCH 5/6] Remove guarantees for enums with uninhabited variants --- reference/src/layout/enums.md | 34 ++++------------------------------ 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/reference/src/layout/enums.md b/reference/src/layout/enums.md index ff74ffa0..3053641d 100644 --- a/reference/src/layout/enums.md +++ b/reference/src/layout/enums.md @@ -401,34 +401,8 @@ enum DataCarryingSingleVariant { * `FieldSingleVariant` has the same layout as `UnitStruct`, * `DataCarryingSingleVariant` has the same layout as `SomeStruct`. -### Layout of multi-variant enums with one inhabited variant +## Unresolved questions -> **NOTE**: the guarantees in this section have not been approved by an RFC process. - -The layout of **multi-variant** enums with **one inhabited variant** is the same -as that of the single-variant enum containing that same inhabited variant. - -Here: - -```rust -# use std::mem::{size_of, align_of}; -struct SomeStruct { x: u32 } -enum SingleVariantDataCarrying { - DataCarryingVariant(SomeStruct), -} -enum Void0 {} -enum Void1 {} -enum MultiVariantSingleHabited { - DataCarryingVariant(SomeStruct), - Uninhabited0(Void0), - Uninhabited1(Void1), -} -# fn main() { -# use std::mem; -# assert_eq!(size_of::(), size_of::()); -# assert_eq!(align_of::(), align_of::()); -# } -``` - -the `MultiVariantSingleHabited` enum has the same layout as -`SingleVariantDataCarrying`. +See [Issue #79.](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/79): + +* Layout of multi-variant enums with one inhabited variant. From bac4066ea0d6905a17b7ca27ffb2af2551630a60 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 16 Aug 2019 15:19:16 +0200 Subject: [PATCH 6/6] Update reference/src/layout/enums.md Co-Authored-By: Ralf Jung --- reference/src/layout/enums.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/src/layout/enums.md b/reference/src/layout/enums.md index 3053641d..db0026a5 100644 --- a/reference/src/layout/enums.md +++ b/reference/src/layout/enums.md @@ -405,4 +405,4 @@ enum DataCarryingSingleVariant { See [Issue #79.](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/79): -* Layout of multi-variant enums with one inhabited variant. +* Layout of multi-variant enums where only one variant is inhabited.