Auto merge of #68122 - Centril:stabilize-transparent-enums, r=petrochenkov
Stabilize `#[repr(transparent)]` on `enum`s in Rust 1.42.0 # Stabilization report The following is the stabilization report for `#![feature(transparent_enums)]`. Tracking issue: https://github.com/rust-lang/rust/issues/60405 [Version target](https://forge.rust-lang.org/#current-release-versions): 1.42 (2020-01-30 => beta, 2020-03-12 => stable). ## User guide A `struct` with only a single non-ZST field (let's call it `foo`) can be marked as `#[repr(transparent)]`. Such a `struct` has the same layout and ABI as `foo`. Here, we also extend this ability to `enum`s with only one variant, subject to the same restrictions as for the equivalent `struct`. That is, you can now write: ```rust #[repr(transparent)] enum Foo { Bar(u8) } ``` which, in terms of layout and ABI, is equivalent to: ```rust #[repr(transparent)] struct Foo(u8); ``` ## Motivation This is not a major feature that will unlock new and important use-cases. The utility of `repr(transparent)` `enum`s is indeed limited. However, there is still some value in it: 1. It provides conceptual simplification of the language in terms of treating univariant `enum`s and `struct`s the same, as both are product types. Indeed, languages like Haskell only have `data` as the only way to construct user-defined ADTs in the language. 2. In rare occasions, it might be that the user started out with a univariant `enum` for whatever reason (e.g. they thought they might extend it later). Now they want to make this `enum` `transparent` without breaking users by turning it into a `struct`. By lifting the restriction here, now they can. ## Technical specification The reference specifies [`repr(transparent)` on a `struct`](https://doc.rust-lang.org/nightly/reference/type-layout.html#the-transparent-representation) as: > ### The transparent Representation > > The `transparent` representation can only be used on `struct`s that have: > - a single field with non-zero size, and > - any number of fields with size 0 and alignment 1 (e.g. `PhantomData<T>`). > > Structs with this representation have the same layout and ABI as the single non-zero sized field. > > This is different than the `C` representation because a struct with the `C` representation will always have the ABI of a `C` `struct` while, for example, a struct with the `transparent` representation with a primitive field will have the ABI of the primitive field. > > Because this representation delegates type layout to another type, it cannot be used with any other representation. Here, we amend this to include univariant `enum`s as well with the same static restrictions and the same effects on dynamic semantics. ## Tests All the relevant tests are adjusted in the PR diff but are recounted here: - `src/test/ui/repr/repr-transparent.rs` checks that `repr(transparent)` on an `enum` must be univariant, rather than having zero or more than one variant. Restrictions on the fields inside the only variants, like for those on `struct`s, are also checked here. - A number of codegen tests are provided as well: - `src/test/codegen/repr-transparent.rs` (the canonical test) - `src/test/codegen/repr-transparent-aggregates-1.rs` - `src/test/codegen/repr-transparent-aggregates-2.rs` - `src/test/codegen/repr-transparent-aggregates-3.rs` - `src/test/ui/lint/lint-ctypes-enum.rs` tests the interactions with the `improper_ctypes` lint. ## History - 2019-04-30, RFC https://github.com/rust-lang/rfcs/pull/2645 Author: @mjbshaw Reviewers: The Language Team This is the RFC that proposes allowing `#[repr(transparent)]` on `enum`s and `union`. - 2019-06-11, PR https://github.com/rust-lang/rust/pull/60463 Author: @mjbshaw Reviewers: @varkor and @rkruppe The PR implements the RFC aforementioned in full. - 2019, PR https://github.com/rust-lang/rust/pull/67323 Author: @Centril Reviewers: @davidtwco The PR reorganizes the static checks taking advantage of the fact that `struct`s and `union`s are internally represented as ADTs with a single variant. - This PR stabilizes `transparent_enums`. ## Related / possible future work The remaining work here is to figure out the semantics of `#[repr(transparent)]` on `union`s and stabilize those. This work continues to be tracked in https://github.com/rust-lang/rust/issues/60405.
This commit is contained in:
commit
c3681d62ee
@ -1,93 +0,0 @@
|
||||
# `transparent_enums`
|
||||
|
||||
The tracking issue for this feature is [#60405]
|
||||
|
||||
[60405]: https://github.com/rust-lang/rust/issues/60405
|
||||
|
||||
----
|
||||
|
||||
The `transparent_enums` feature allows you mark `enum`s as
|
||||
`#[repr(transparent)]`. An `enum` may be `#[repr(transparent)]` if it has
|
||||
exactly one variant, and that variant matches the same conditions which `struct`
|
||||
requires for transparency. Some concrete illustrations follow.
|
||||
|
||||
```rust
|
||||
#![feature(transparent_enums)]
|
||||
|
||||
// This enum has the same representation as `f32`.
|
||||
#[repr(transparent)]
|
||||
enum SingleFieldEnum {
|
||||
Variant(f32)
|
||||
}
|
||||
|
||||
// This enum has the same representation as `usize`.
|
||||
#[repr(transparent)]
|
||||
enum MultiFieldEnum {
|
||||
Variant { field: usize, nothing: () },
|
||||
}
|
||||
```
|
||||
|
||||
For consistency with transparent `struct`s, `enum`s must have exactly one
|
||||
non-zero-sized field. If all fields are zero-sized, the `enum` must not be
|
||||
`#[repr(transparent)]`:
|
||||
|
||||
```rust
|
||||
#![feature(transparent_enums)]
|
||||
|
||||
// This (non-transparent) enum is already valid in stable Rust:
|
||||
pub enum GoodEnum {
|
||||
Nothing,
|
||||
}
|
||||
|
||||
// Error: transparent enum needs exactly one non-zero-sized field, but has 0
|
||||
// #[repr(transparent)]
|
||||
// pub enum BadEnum {
|
||||
// Nothing(()),
|
||||
// }
|
||||
|
||||
// Error: transparent enum needs exactly one non-zero-sized field, but has 0
|
||||
// #[repr(transparent)]
|
||||
// pub enum BadEmptyEnum {
|
||||
// Nothing,
|
||||
// }
|
||||
```
|
||||
|
||||
The one exception is if the `enum` is generic over `T` and has a field of type
|
||||
`T`, it may be `#[repr(transparent)]` even if `T` is a zero-sized type:
|
||||
|
||||
```rust
|
||||
#![feature(transparent_enums)]
|
||||
|
||||
// This enum has the same representation as `T`.
|
||||
#[repr(transparent)]
|
||||
pub enum GenericEnum<T> {
|
||||
Variant(T, ()),
|
||||
}
|
||||
|
||||
// This is okay even though `()` is a zero-sized type.
|
||||
pub const THIS_IS_OKAY: GenericEnum<()> = GenericEnum::Variant((), ());
|
||||
```
|
||||
|
||||
Transparent `enum`s require exactly one variant:
|
||||
|
||||
```rust
|
||||
// Error: transparent enum needs exactly one variant, but has 0
|
||||
// #[repr(transparent)]
|
||||
// pub enum TooFewVariants {
|
||||
// }
|
||||
|
||||
// Error: transparent enum needs exactly one variant, but has 2
|
||||
// #[repr(transparent)]
|
||||
// pub enum TooManyVariants {
|
||||
// First(usize),
|
||||
// Second,
|
||||
// }
|
||||
```
|
||||
|
||||
Like transarent `struct`s, a transparent `enum` of type `E` has the same layout,
|
||||
size, and ABI as its single non-ZST field. If it is generic over a type `T`, and
|
||||
all its fields are ZSTs except for exactly one field of type `T`, then it has
|
||||
the same layout and ABI as `T` (even if `T` is a ZST when monomorphized).
|
||||
|
||||
Like transparent `struct`s, transparent `enum`s are FFI-safe if and only if
|
||||
their underlying representation type is also FFI-safe.
|
@ -257,6 +257,8 @@ declare_features! (
|
||||
/// Allows relaxing the coherence rules such that
|
||||
/// `impl<T> ForeignTrait<LocalType> for ForeignType<T>` is permitted.
|
||||
(accepted, re_rebalance_coherence, "1.41.0", Some(55437), None),
|
||||
/// Allows #[repr(transparent)] on univariant enums (RFC 2645).
|
||||
(accepted, transparent_enums, "1.42.0", Some(60405), None),
|
||||
/// Allows using subslice patterns, `[a, .., b]` and `[a, xs @ .., b]`.
|
||||
(accepted, slice_patterns, "1.42.0", Some(62254), None),
|
||||
|
||||
|
@ -468,9 +468,6 @@ declare_features! (
|
||||
/// Allows `if/while p && let q = r && ...` chains.
|
||||
(active, let_chains, "1.37.0", Some(53667), None),
|
||||
|
||||
/// Allows #[repr(transparent)] on enums (RFC 2645).
|
||||
(active, transparent_enums, "1.37.0", Some(60405), None),
|
||||
|
||||
/// Allows #[repr(transparent)] on unions (RFC 2645).
|
||||
(active, transparent_unions, "1.37.0", Some(60405), None),
|
||||
|
||||
|
@ -2434,16 +2434,6 @@ fn check_transparent(tcx: TyCtxt<'_>, sp: Span, def_id: DefId) {
|
||||
}
|
||||
let sp = tcx.sess.source_map().def_span(sp);
|
||||
|
||||
if adt.is_enum() && !tcx.features().transparent_enums {
|
||||
feature_err(
|
||||
&tcx.sess.parse_sess,
|
||||
sym::transparent_enums,
|
||||
sp,
|
||||
"transparent enums are unstable",
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
|
||||
if adt.is_union() && !tcx.features().transparent_unions {
|
||||
feature_err(
|
||||
&tcx.sess.parse_sess,
|
||||
|
@ -10,7 +10,7 @@
|
||||
// ignore-windows
|
||||
// See repr-transparent.rs
|
||||
|
||||
#![feature(transparent_enums, transparent_unions)]
|
||||
#![feature(transparent_unions)]
|
||||
|
||||
#![crate_type="lib"]
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
// ignore-x86_64
|
||||
// See repr-transparent.rs
|
||||
|
||||
#![feature(transparent_enums, transparent_unions)]
|
||||
#![feature(transparent_unions)]
|
||||
|
||||
#![crate_type="lib"]
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
// only-mips64
|
||||
// See repr-transparent.rs
|
||||
|
||||
#![feature(transparent_enums, transparent_unions)]
|
||||
#![feature(transparent_unions)]
|
||||
|
||||
#![crate_type="lib"]
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
// compile-flags: -C no-prepopulate-passes
|
||||
|
||||
#![crate_type="lib"]
|
||||
#![feature(repr_simd, transparent_enums, transparent_unions)]
|
||||
#![feature(repr_simd, transparent_unions)]
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
|
@ -1,6 +0,0 @@
|
||||
#[repr(transparent)]
|
||||
enum OkButUnstableEnum { //~ ERROR transparent enums are unstable
|
||||
Foo((), String, ()),
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -1,12 +0,0 @@
|
||||
error[E0658]: transparent enums are unstable
|
||||
--> $DIR/feature-gate-transparent_enums.rs:2:1
|
||||
|
|
||||
LL | enum OkButUnstableEnum {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: for more information, see https://github.com/rust-lang/rust/issues/60405
|
||||
= help: add `#![feature(transparent_enums)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
@ -1,4 +1,4 @@
|
||||
#![feature(transparent_enums, transparent_unions)]
|
||||
#![feature(transparent_unions)]
|
||||
#![feature(ptr_internals)]
|
||||
#![deny(improper_ctypes)]
|
||||
#![allow(dead_code)]
|
||||
|
@ -3,7 +3,7 @@
|
||||
// - repr-transparent-other-reprs.rs
|
||||
// - repr-transparent-other-items.rs
|
||||
|
||||
#![feature(repr_align, transparent_enums, transparent_unions)]
|
||||
#![feature(transparent_unions)]
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
@ -60,6 +60,16 @@ enum TooManyVariants { //~ ERROR transparent enum needs exactly one variant, but
|
||||
Bar,
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
enum NontrivialAlignZstEnum {
|
||||
Foo(u32, [u16; 0]), //~ ERROR alignment larger than 1
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
enum GenericAlignEnum<T> {
|
||||
Foo { bar: ZstAlign32<T>, baz: u32 } //~ ERROR alignment larger than 1
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
union UnitUnion { //~ ERROR transparent union needs exactly one non-zero-sized field, but has 0
|
||||
u: (),
|
||||
|
@ -94,14 +94,26 @@ LL | Foo(String),
|
||||
LL | Bar,
|
||||
| --- too many variants in `TooManyVariants`
|
||||
|
||||
error[E0691]: zero-sized field in transparent enum has alignment larger than 1
|
||||
--> $DIR/repr-transparent.rs:65:14
|
||||
|
|
||||
LL | Foo(u32, [u16; 0]),
|
||||
| ^^^^^^^^ has alignment larger than 1
|
||||
|
||||
error[E0691]: zero-sized field in transparent enum has alignment larger than 1
|
||||
--> $DIR/repr-transparent.rs:70:11
|
||||
|
|
||||
LL | Foo { bar: ZstAlign32<T>, baz: u32 }
|
||||
| ^^^^^^^^^^^^^^^^^^ has alignment larger than 1
|
||||
|
||||
error[E0690]: transparent union needs exactly one non-zero-sized field, but has 0
|
||||
--> $DIR/repr-transparent.rs:64:1
|
||||
--> $DIR/repr-transparent.rs:74:1
|
||||
|
|
||||
LL | union UnitUnion {
|
||||
| ^^^^^^^^^^^^^^^ needs exactly one non-zero-sized field, but has 0
|
||||
|
||||
error[E0690]: transparent union needs exactly one non-zero-sized field, but has 2
|
||||
--> $DIR/repr-transparent.rs:69:1
|
||||
--> $DIR/repr-transparent.rs:79:1
|
||||
|
|
||||
LL | union TooManyFields {
|
||||
| ^^^^^^^^^^^^^^^^^^^ needs exactly one non-zero-sized field, but has 2
|
||||
@ -110,7 +122,7 @@ LL | u: u32,
|
||||
LL | s: i32
|
||||
| ------ this field is non-zero-sized
|
||||
|
||||
error: aborting due to 15 previous errors
|
||||
error: aborting due to 17 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0084, E0690, E0691, E0731.
|
||||
For more information about an error, try `rustc --explain E0084`.
|
||||
|
Loading…
x
Reference in New Issue
Block a user