Skip to content

Commit 8e948df

Browse files
committed
Auto merge of #60463 - mjbshaw:transparent, r=varkor,rkruppe
Implement RFC 2645 (transparent enums and unions) Tracking issue: #60405
2 parents 912d22e + dac1c6a commit 8e948df

34 files changed

+730
-208
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# `transparent_enums`
2+
3+
The tracking issue for this feature is [#60405]
4+
5+
[60405]: https://github.com/rust-lang/rust/issues/60405
6+
7+
----
8+
9+
The `transparent_enums` feature allows you mark `enum`s as
10+
`#[repr(transparent)]`. An `enum` may be `#[repr(transparent)]` if it has
11+
exactly one variant, and that variant matches the same conditions which `struct`
12+
requires for transparency. Some concrete illustrations follow.
13+
14+
```rust
15+
#![feature(transparent_enums)]
16+
17+
// This enum has the same representation as `f32`.
18+
#[repr(transparent)]
19+
enum SingleFieldEnum {
20+
Variant(f32)
21+
}
22+
23+
// This enum has the same representation as `usize`.
24+
#[repr(transparent)]
25+
enum MultiFieldEnum {
26+
Variant { field: usize, nothing: () },
27+
}
28+
```
29+
30+
For consistency with transparent `struct`s, `enum`s must have exactly one
31+
non-zero-sized field. If all fields are zero-sized, the `enum` must not be
32+
`#[repr(transparent)]`:
33+
34+
```rust
35+
#![feature(transparent_enums)]
36+
37+
// This (non-transparent) enum is already valid in stable Rust:
38+
pub enum GoodEnum {
39+
Nothing,
40+
}
41+
42+
// Error: transparent enum needs exactly one non-zero-sized field, but has 0
43+
// #[repr(transparent)]
44+
// pub enum BadEnum {
45+
// Nothing(()),
46+
// }
47+
48+
// Error: transparent enum needs exactly one non-zero-sized field, but has 0
49+
// #[repr(transparent)]
50+
// pub enum BadEmptyEnum {
51+
// Nothing,
52+
// }
53+
```
54+
55+
The one exception is if the `enum` is generic over `T` and has a field of type
56+
`T`, it may be `#[repr(transparent)]` even if `T` is a zero-sized type:
57+
58+
```rust
59+
#![feature(transparent_enums)]
60+
61+
// This enum has the same representation as `T`.
62+
#[repr(transparent)]
63+
pub enum GenericEnum<T> {
64+
Variant(T, ()),
65+
}
66+
67+
// This is okay even though `()` is a zero-sized type.
68+
pub const THIS_IS_OKAY: GenericEnum<()> = GenericEnum::Variant((), ());
69+
```
70+
71+
Transparent `enum`s require exactly one variant:
72+
73+
```rust
74+
// Error: transparent enum needs exactly one variant, but has 0
75+
// #[repr(transparent)]
76+
// pub enum TooFewVariants {
77+
// }
78+
79+
// Error: transparent enum needs exactly one variant, but has 2
80+
// #[repr(transparent)]
81+
// pub enum TooManyVariants {
82+
// First(usize),
83+
// Second,
84+
// }
85+
```
86+
87+
Like transarent `struct`s, a transparent `enum` of type `E` has the same layout,
88+
size, and ABI as its single non-ZST field. If it is generic over a type `T`, and
89+
all its fields are ZSTs except for exactly one field of type `T`, then it has
90+
the same layout and ABI as `T` (even if `T` is a ZST when monomorphized).
91+
92+
Like transparent `struct`s, transparent `enum`s are FFI-safe if and only if
93+
their underlying representation type is also FFI-safe.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# `transparent_unions`
2+
3+
The tracking issue for this feature is [#60405]
4+
5+
[60405]: https://github.com/rust-lang/rust/issues/60405
6+
7+
----
8+
9+
The `transparent_unions` feature allows you mark `union`s as
10+
`#[repr(transparent)]`. A `union` may be `#[repr(transparent)]` in exactly the
11+
same conditions in which a `struct` may be `#[repr(transparent)]` (generally,
12+
this means the `union` must have exactly one non-zero-sized field). Some
13+
concrete illustrations follow.
14+
15+
```rust
16+
#![feature(transparent_unions)]
17+
18+
// This union has the same representation as `f32`.
19+
#[repr(transparent)]
20+
union SingleFieldUnion {
21+
field: f32,
22+
}
23+
24+
// This union has the same representation as `usize`.
25+
#[repr(transparent)]
26+
union MultiFieldUnion {
27+
field: usize,
28+
nothing: (),
29+
}
30+
```
31+
32+
For consistency with transparent `struct`s, `union`s must have exactly one
33+
non-zero-sized field. If all fields are zero-sized, the `union` must not be
34+
`#[repr(transparent)]`:
35+
36+
```rust
37+
#![feature(transparent_unions)]
38+
39+
// This (non-transparent) union is already valid in stable Rust:
40+
pub union GoodUnion {
41+
pub nothing: (),
42+
}
43+
44+
// Error: transparent union needs exactly one non-zero-sized field, but has 0
45+
// #[repr(transparent)]
46+
// pub union BadUnion {
47+
// pub nothing: (),
48+
// }
49+
```
50+
51+
The one exception is if the `union` is generic over `T` and has a field of type
52+
`T`, it may be `#[repr(transparent)]` even if `T` is a zero-sized type:
53+
54+
```rust
55+
#![feature(transparent_unions)]
56+
57+
// This union has the same representation as `T`.
58+
#[repr(transparent)]
59+
pub union GenericUnion<T: Copy> { // Unions with non-`Copy` fields are unstable.
60+
pub field: T,
61+
pub nothing: (),
62+
}
63+
64+
// This is okay even though `()` is a zero-sized type.
65+
pub const THIS_IS_OKAY: GenericUnion<()> = GenericUnion { field: () };
66+
```
67+
68+
Like transarent `struct`s, a transparent `union` of type `U` has the same
69+
layout, size, and ABI as its single non-ZST field. If it is generic over a type
70+
`T`, and all its fields are ZSTs except for exactly one field of type `T`, then
71+
it has the same layout and ABI as `T` (even if `T` is a ZST when monomorphized).
72+
73+
Like transparent `struct`s, transparent `union`s are FFI-safe if and only if
74+
their underlying representation type is also FFI-safe.
75+
76+
A `union` may not be eligible for the same nonnull-style optimizations that a
77+
`struct` or `enum` (with the same fields) are eligible for. Adding
78+
`#[repr(transparent)]` to `union` does not change this. To give a more concrete
79+
example, it is unspecified whether `size_of::<T>()` is equal to
80+
`size_of::<Option<T>>()`, where `T` is a `union` (regardless of whether or not
81+
it is transparent). The Rust compiler is free to perform this optimization if
82+
possible, but is not required to, and different compiler versions may differ in
83+
their application of these optimizations.

src/librustc/hir/check_attr.rs

+9-13
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,9 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
181181
let (article, allowed_targets) = match hint.name_or_empty() {
182182
name @ sym::C | name @ sym::align => {
183183
is_c |= name == sym::C;
184-
if target != Target::Struct &&
185-
target != Target::Union &&
186-
target != Target::Enum {
187-
("a", "struct, enum or union")
188-
} else {
189-
continue
184+
match target {
185+
Target::Struct | Target::Union | Target::Enum => continue,
186+
_ => ("a", "struct, enum, or union"),
190187
}
191188
}
192189
sym::packed => {
@@ -207,10 +204,9 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
207204
}
208205
sym::transparent => {
209206
is_transparent = true;
210-
if target != Target::Struct {
211-
("a", "struct")
212-
} else {
213-
continue
207+
match target {
208+
Target::Struct | Target::Union | Target::Enum => continue,
209+
_ => ("a", "struct, enum, or union"),
214210
}
215211
}
216212
sym::i8 | sym::u8 | sym::i16 | sym::u16 |
@@ -241,7 +237,7 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
241237
if is_transparent && hints.len() > 1 {
242238
let hint_spans: Vec<_> = hint_spans.clone().collect();
243239
span_err!(self.tcx.sess, hint_spans, E0692,
244-
"transparent struct cannot have other repr hints");
240+
"transparent {} cannot have other repr hints", target);
245241
}
246242
// Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
247243
if (int_reprs > 1)
@@ -277,7 +273,7 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
277273
attr.span,
278274
stmt.span,
279275
"attribute should not be applied to a statement",
280-
"not a struct, enum or union",
276+
"not a struct, enum, or union",
281277
);
282278
}
283279
}
@@ -298,7 +294,7 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
298294
attr.span,
299295
expr.span,
300296
"attribute should not be applied to an expression",
301-
"not defining a struct, enum or union",
297+
"not defining a struct, enum, or union",
302298
);
303299
}
304300
}

src/librustc/ty/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2303,7 +2303,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
23032303
/// Returns an iterator over all fields contained
23042304
/// by this ADT.
23052305
#[inline]
2306-
pub fn all_fields<'s>(&'s self) -> impl Iterator<Item = &'s FieldDef> {
2306+
pub fn all_fields<'s>(&'s self) -> impl Iterator<Item = &'s FieldDef> + Clone {
23072307
self.variants.iter().flat_map(|v| v.fields.iter())
23082308
}
23092309

src/librustc_lint/types.rs

+23-11
Original file line numberDiff line numberDiff line change
@@ -531,8 +531,8 @@ fn ty_is_known_nonnull<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> b
531531
match ty.sty {
532532
ty::FnPtr(_) => true,
533533
ty::Ref(..) => true,
534-
ty::Adt(field_def, substs) if field_def.repr.transparent() && field_def.is_struct() => {
535-
for field in &field_def.non_enum_variant().fields {
534+
ty::Adt(field_def, substs) if field_def.repr.transparent() && !field_def.is_union() => {
535+
for field in field_def.all_fields() {
536536
let field_ty = tcx.normalize_erasing_regions(
537537
ParamEnv::reveal_all(),
538538
field.ty(tcx, substs),
@@ -627,8 +627,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
627627
return FfiUnsafe {
628628
ty: ty,
629629
reason: "this struct has unspecified layout",
630-
help: Some("consider adding a #[repr(C)] or #[repr(transparent)] \
631-
attribute to this struct"),
630+
help: Some("consider adding a `#[repr(C)]` or \
631+
`#[repr(transparent)]` attribute to this struct"),
632632
};
633633
}
634634

@@ -668,11 +668,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
668668
if all_phantom { FfiPhantom(ty) } else { FfiSafe }
669669
}
670670
AdtKind::Union => {
671-
if !def.repr.c() {
671+
if !def.repr.c() && !def.repr.transparent() {
672672
return FfiUnsafe {
673673
ty: ty,
674674
reason: "this union has unspecified layout",
675-
help: Some("consider adding a #[repr(C)] attribute to this union"),
675+
help: Some("consider adding a `#[repr(C)]` or \
676+
`#[repr(transparent)]` attribute to this union"),
676677
};
677678
}
678679

@@ -690,6 +691,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
690691
ParamEnv::reveal_all(),
691692
field.ty(cx, substs),
692693
);
694+
// repr(transparent) types are allowed to have arbitrary ZSTs, not just
695+
// PhantomData -- skip checking all ZST fields.
696+
if def.repr.transparent() && is_zst(cx, field.did, field_ty) {
697+
continue;
698+
}
693699
let r = self.check_type_for_ffi(cache, field_ty);
694700
match r {
695701
FfiSafe => {
@@ -712,26 +718,32 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
712718

713719
// Check for a repr() attribute to specify the size of the
714720
// discriminant.
715-
if !def.repr.c() && def.repr.int.is_none() {
721+
if !def.repr.c() && !def.repr.transparent() && def.repr.int.is_none() {
716722
// Special-case types like `Option<extern fn()>`.
717723
if !is_repr_nullable_ptr(cx, ty, def, substs) {
718724
return FfiUnsafe {
719725
ty: ty,
720726
reason: "enum has no representation hint",
721-
help: Some("consider adding a #[repr(...)] attribute \
722-
to this enum"),
727+
help: Some("consider adding a `#[repr(C)]`, \
728+
`#[repr(transparent)]`, or integer `#[repr(...)]` \
729+
attribute to this enum"),
723730
};
724731
}
725732
}
726733

727734
// Check the contained variants.
728735
for variant in &def.variants {
729736
for field in &variant.fields {
730-
let arg = cx.normalize_erasing_regions(
737+
let field_ty = cx.normalize_erasing_regions(
731738
ParamEnv::reveal_all(),
732739
field.ty(cx, substs),
733740
);
734-
let r = self.check_type_for_ffi(cache, arg);
741+
// repr(transparent) types are allowed to have arbitrary ZSTs, not
742+
// just PhantomData -- skip checking all ZST fields.
743+
if def.repr.transparent() && is_zst(cx, field.did, field_ty) {
744+
continue;
745+
}
746+
let r = self.check_type_for_ffi(cache, field_ty);
735747
match r {
736748
FfiSafe => {}
737749
FfiUnsafe { .. } => {

0 commit comments

Comments
 (0)