Skip to content

Commit 203c57d

Browse files
committed
Auto merge of #115334 - RalfJung:transparent-aligned-zst, r=compiler-errors
repr(transparent): it's fine if the one non-1-ZST field is a ZST This code currently gets rejected: ```rust #[repr(transparent)] struct MyType([u16; 0]) ``` That clearly seems like a bug to me: `repr(transparent)` [got defined ](#77841 (comment)) as having any number of 1-ZST fields plus optionally one more field; `MyType` clearly satisfies that definition. This PR changes the `repr(transparent)` logic to actually match that definition.
2 parents db9c21f + a6ccd26 commit 203c57d

File tree

5 files changed

+91
-88
lines changed

5 files changed

+91
-88
lines changed

compiler/rustc_error_codes/src/error_codes/E0691.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
#### Note: this error code is no longer emitted by the compiler.
2+
13
A struct, enum, or union with the `repr(transparent)` representation hint
24
contains a zero-sized field that requires non-trivial alignment.
35

46
Erroneous code example:
57

6-
```compile_fail,E0691
8+
```ignore (error is no longer emitted)
79
#![feature(repr_align)]
810
911
#[repr(align(32))]

compiler/rustc_hir_analysis/messages.ftl

+6-6
Original file line numberDiff line numberDiff line change
@@ -304,13 +304,13 @@ hir_analysis_transparent_enum_variant = transparent enum needs exactly one varia
304304
.many_label = too many variants in `{$path}`
305305
.multi_label = variant here
306306
307-
hir_analysis_transparent_non_zero_sized = transparent {$desc} needs at most one non-zero-sized field, but has {$field_count}
308-
.label = needs at most one non-zero-sized field, but has {$field_count}
309-
.labels = this field is non-zero-sized
307+
hir_analysis_transparent_non_zero_sized = transparent {$desc} needs at most one field with non-trivial size or alignment, but has {$field_count}
308+
.label = needs at most one field with non-trivial size or alignment, but has {$field_count}
309+
.labels = this field has non-zero size or requires alignment
310310
311-
hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$desc} needs at most one non-zero-sized field, but has {$field_count}
312-
.label = needs at most one non-zero-sized field, but has {$field_count}
313-
.labels = this field is non-zero-sized
311+
hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$desc} needs at most one field with non-trivial size or alignment, but has {$field_count}
312+
.label = needs at most one field with non-trivial size or alignment, but has {$field_count}
313+
.labels = this field has non-zero size or requires alignment
314314
315315
hir_analysis_typeof_reserved_keyword_used =
316316
`typeof` is a reserved keyword but unimplemented

compiler/rustc_hir_analysis/src/check/check.rs

+21-37
Original file line numberDiff line numberDiff line change
@@ -1138,19 +1138,19 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
11381138
return;
11391139
}
11401140

1141-
// For each field, figure out if it's known to be a ZST and align(1), with "known"
1142-
// respecting #[non_exhaustive] attributes.
1141+
// For each field, figure out if it's known to have "trivial" layout (i.e., is a 1-ZST), with
1142+
// "known" respecting #[non_exhaustive] attributes.
11431143
let field_infos = adt.all_fields().map(|field| {
11441144
let ty = field.ty(tcx, GenericArgs::identity_for_item(tcx, field.did));
11451145
let param_env = tcx.param_env(field.did);
11461146
let layout = tcx.layout_of(param_env.and(ty));
11471147
// We are currently checking the type this field came from, so it must be local
11481148
let span = tcx.hir().span_if_local(field.did).unwrap();
1149-
let zst = layout.is_ok_and(|layout| layout.is_zst());
1150-
let align = layout.ok().map(|layout| layout.align.abi.bytes());
1151-
if !zst {
1152-
return (span, zst, align, None);
1149+
let trivial = layout.is_ok_and(|layout| layout.is_1zst());
1150+
if !trivial {
1151+
return (span, trivial, None);
11531152
}
1153+
// Even some 1-ZST fields are not allowed though, if they have `non_exhaustive`.
11541154

11551155
fn check_non_exhaustive<'tcx>(
11561156
tcx: TyCtxt<'tcx>,
@@ -1184,41 +1184,25 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
11841184
}
11851185
}
11861186

1187-
(span, zst, align, check_non_exhaustive(tcx, ty).break_value())
1187+
(span, trivial, check_non_exhaustive(tcx, ty).break_value())
11881188
});
11891189

1190-
let non_zst_fields = field_infos
1190+
let non_trivial_fields = field_infos
11911191
.clone()
1192-
.filter_map(|(span, zst, _align, _non_exhaustive)| if !zst { Some(span) } else { None });
1193-
let non_zst_count = non_zst_fields.clone().count();
1194-
if non_zst_count >= 2 {
1195-
bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, tcx.def_span(adt.did()));
1192+
.filter_map(|(span, trivial, _non_exhaustive)| if !trivial { Some(span) } else { None });
1193+
let non_trivial_count = non_trivial_fields.clone().count();
1194+
if non_trivial_count >= 2 {
1195+
bad_non_zero_sized_fields(
1196+
tcx,
1197+
adt,
1198+
non_trivial_count,
1199+
non_trivial_fields,
1200+
tcx.def_span(adt.did()),
1201+
);
1202+
return;
11961203
}
1197-
let incompatible_zst_fields =
1198-
field_infos.clone().filter(|(_, _, _, opt)| opt.is_some()).count();
1199-
let incompat = incompatible_zst_fields + non_zst_count >= 2 && non_zst_count < 2;
1200-
for (span, zst, align, non_exhaustive) in field_infos {
1201-
if zst && align != Some(1) {
1202-
let mut err = struct_span_err!(
1203-
tcx.sess,
1204-
span,
1205-
E0691,
1206-
"zero-sized field in transparent {} has alignment larger than 1",
1207-
adt.descr(),
1208-
);
1209-
1210-
if let Some(align_bytes) = align {
1211-
err.span_label(
1212-
span,
1213-
format!("has alignment of {align_bytes}, which is larger than 1"),
1214-
);
1215-
} else {
1216-
err.span_label(span, "may have alignment larger than 1");
1217-
}
1218-
1219-
err.emit();
1220-
}
1221-
if incompat && let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive {
1204+
for (span, _trivial, non_exhaustive) in field_infos {
1205+
if let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive {
12221206
tcx.struct_span_lint_hir(
12231207
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
12241208
tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()),

tests/ui/repr/repr-transparent.rs

+13-10
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,26 @@ struct ContainsMultipleZst(PhantomData<*const i32>, NoFields);
2323
struct ContainsZstAndNonZst((), [i32; 2]);
2424

2525
#[repr(transparent)]
26-
struct MultipleNonZst(u8, u8); //~ ERROR needs at most one non-zero-sized field
26+
struct MultipleNonZst(u8, u8); //~ ERROR needs at most one field with non-trivial size or alignment
2727

2828
trait Mirror { type It: ?Sized; }
2929
impl<T: ?Sized> Mirror for T { type It = Self; }
3030

3131
#[repr(transparent)]
3232
pub struct StructWithProjection(f32, <f32 as Mirror>::It);
33-
//~^ ERROR needs at most one non-zero-sized field
33+
//~^ ERROR needs at most one field with non-trivial size or alignment
3434

3535
#[repr(transparent)]
36-
struct NontrivialAlignZst(u32, [u16; 0]); //~ ERROR alignment larger than 1
36+
struct NontrivialAlignZst(u32, [u16; 0]); //~ ERROR needs at most one field with non-trivial size or alignment
3737

3838
#[repr(align(32))]
3939
struct ZstAlign32<T>(PhantomData<T>);
4040

4141
#[repr(transparent)]
42-
struct GenericAlign<T>(ZstAlign32<T>, u32); //~ ERROR alignment larger than 1
42+
struct GenericAlign<T>(ZstAlign32<T>, u32); //~ ERROR needs at most one field with non-trivial size or alignment
43+
44+
#[repr(transparent)]
45+
struct WrapsZstWithAlignment([i32; 0]);
4346

4447
#[repr(transparent)] //~ ERROR unsupported representation for zero-variant enum
4548
enum Void {} //~ ERROR transparent enum needs exactly one variant, but has 0
@@ -58,7 +61,7 @@ enum UnitFieldEnum {
5861
enum TooManyFieldsEnum {
5962
Foo(u32, String),
6063
}
61-
//~^^^ ERROR transparent enum needs at most one non-zero-sized field, but has 2
64+
//~^^^ ERROR transparent enum needs at most one field with non-trivial size or alignment, but has 2
6265

6366
#[repr(transparent)]
6467
enum MultipleVariants { //~ ERROR transparent enum needs exactly one variant, but has 2
@@ -67,13 +70,13 @@ enum MultipleVariants { //~ ERROR transparent enum needs exactly one variant, bu
6770
}
6871

6972
#[repr(transparent)]
70-
enum NontrivialAlignZstEnum {
71-
Foo(u32, [u16; 0]), //~ ERROR alignment larger than 1
73+
enum NontrivialAlignZstEnum { //~ ERROR needs at most one field with non-trivial size or alignment
74+
Foo(u32, [u16; 0]),
7275
}
7376

7477
#[repr(transparent)]
75-
enum GenericAlignEnum<T> {
76-
Foo { bar: ZstAlign32<T>, baz: u32 } //~ ERROR alignment larger than 1
78+
enum GenericAlignEnum<T> { //~ ERROR needs at most one field with non-trivial size or alignment
79+
Foo { bar: ZstAlign32<T>, baz: u32 }
7780
}
7881

7982
#[repr(transparent)]
@@ -82,7 +85,7 @@ union UnitUnion {
8285
}
8386

8487
#[repr(transparent)]
85-
union TooManyFields { //~ ERROR transparent union needs at most one non-zero-sized field, but has 2
88+
union TooManyFields { //~ ERROR transparent union needs at most one field with non-trivial size or alignment, but has 2
8689
u: u32,
8790
s: i32
8891
}

tests/ui/repr/repr-transparent.stderr

+48-34
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,65 @@
1-
error[E0690]: transparent struct needs at most one non-zero-sized field, but has 2
1+
error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2
22
--> $DIR/repr-transparent.rs:26:1
33
|
44
LL | struct MultipleNonZst(u8, u8);
5-
| ^^^^^^^^^^^^^^^^^^^^^ -- -- this field is non-zero-sized
5+
| ^^^^^^^^^^^^^^^^^^^^^ -- -- this field has non-zero size or requires alignment
66
| | |
7-
| | this field is non-zero-sized
8-
| needs at most one non-zero-sized field, but has 2
7+
| | this field has non-zero size or requires alignment
8+
| needs at most one field with non-trivial size or alignment, but has 2
99

10-
error[E0690]: transparent struct needs at most one non-zero-sized field, but has 2
10+
error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2
1111
--> $DIR/repr-transparent.rs:32:1
1212
|
1313
LL | pub struct StructWithProjection(f32, <f32 as Mirror>::It);
14-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --- ------------------- this field is non-zero-sized
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --- ------------------- this field has non-zero size or requires alignment
1515
| | |
16-
| | this field is non-zero-sized
17-
| needs at most one non-zero-sized field, but has 2
16+
| | this field has non-zero size or requires alignment
17+
| needs at most one field with non-trivial size or alignment, but has 2
1818

19-
error[E0691]: zero-sized field in transparent struct has alignment larger than 1
20-
--> $DIR/repr-transparent.rs:36:32
19+
error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2
20+
--> $DIR/repr-transparent.rs:36:1
2121
|
2222
LL | struct NontrivialAlignZst(u32, [u16; 0]);
23-
| ^^^^^^^^ has alignment of 2, which is larger than 1
23+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ --- -------- this field has non-zero size or requires alignment
24+
| | |
25+
| | this field has non-zero size or requires alignment
26+
| needs at most one field with non-trivial size or alignment, but has 2
2427

25-
error[E0691]: zero-sized field in transparent struct has alignment larger than 1
26-
--> $DIR/repr-transparent.rs:42:24
28+
error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2
29+
--> $DIR/repr-transparent.rs:42:1
2730
|
2831
LL | struct GenericAlign<T>(ZstAlign32<T>, u32);
29-
| ^^^^^^^^^^^^^ has alignment of 32, which is larger than 1
32+
| ^^^^^^^^^^^^^^^^^^^^^^ ------------- --- this field has non-zero size or requires alignment
33+
| | |
34+
| | this field has non-zero size or requires alignment
35+
| needs at most one field with non-trivial size or alignment, but has 2
3036

3137
error[E0084]: unsupported representation for zero-variant enum
32-
--> $DIR/repr-transparent.rs:44:1
38+
--> $DIR/repr-transparent.rs:47:1
3339
|
3440
LL | #[repr(transparent)]
3541
| ^^^^^^^^^^^^^^^^^^^^
3642
LL | enum Void {}
3743
| --------- zero-variant enum
3844

3945
error[E0731]: transparent enum needs exactly one variant, but has 0
40-
--> $DIR/repr-transparent.rs:45:1
46+
--> $DIR/repr-transparent.rs:48:1
4147
|
4248
LL | enum Void {}
4349
| ^^^^^^^^^ needs exactly one variant, but has 0
4450

45-
error[E0690]: the variant of a transparent enum needs at most one non-zero-sized field, but has 2
46-
--> $DIR/repr-transparent.rs:58:1
51+
error[E0690]: the variant of a transparent enum needs at most one field with non-trivial size or alignment, but has 2
52+
--> $DIR/repr-transparent.rs:61:1
4753
|
4854
LL | enum TooManyFieldsEnum {
49-
| ^^^^^^^^^^^^^^^^^^^^^^ needs at most one non-zero-sized field, but has 2
55+
| ^^^^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2
5056
LL | Foo(u32, String),
51-
| --- ------ this field is non-zero-sized
57+
| --- ------ this field has non-zero size or requires alignment
5258
| |
53-
| this field is non-zero-sized
59+
| this field has non-zero size or requires alignment
5460

5561
error[E0731]: transparent enum needs exactly one variant, but has 2
56-
--> $DIR/repr-transparent.rs:64:1
62+
--> $DIR/repr-transparent.rs:67:1
5763
|
5864
LL | enum MultipleVariants {
5965
| ^^^^^^^^^^^^^^^^^^^^^ needs exactly one variant, but has 2
@@ -62,29 +68,37 @@ LL | Foo(String),
6268
LL | Bar,
6369
| --- too many variants in `MultipleVariants`
6470

65-
error[E0691]: zero-sized field in transparent enum has alignment larger than 1
66-
--> $DIR/repr-transparent.rs:71:14
71+
error[E0690]: the variant of a transparent enum needs at most one field with non-trivial size or alignment, but has 2
72+
--> $DIR/repr-transparent.rs:73:1
6773
|
74+
LL | enum NontrivialAlignZstEnum {
75+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2
6876
LL | Foo(u32, [u16; 0]),
69-
| ^^^^^^^^ has alignment of 2, which is larger than 1
77+
| --- -------- this field has non-zero size or requires alignment
78+
| |
79+
| this field has non-zero size or requires alignment
7080

71-
error[E0691]: zero-sized field in transparent enum has alignment larger than 1
72-
--> $DIR/repr-transparent.rs:76:11
81+
error[E0690]: the variant of a transparent enum needs at most one field with non-trivial size or alignment, but has 2
82+
--> $DIR/repr-transparent.rs:78:1
7383
|
84+
LL | enum GenericAlignEnum<T> {
85+
| ^^^^^^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2
7486
LL | Foo { bar: ZstAlign32<T>, baz: u32 }
75-
| ^^^^^^^^^^^^^^^^^^ has alignment of 32, which is larger than 1
87+
| ------------------ -------- this field has non-zero size or requires alignment
88+
| |
89+
| this field has non-zero size or requires alignment
7690

77-
error[E0690]: transparent union needs at most one non-zero-sized field, but has 2
78-
--> $DIR/repr-transparent.rs:85:1
91+
error[E0690]: transparent union needs at most one field with non-trivial size or alignment, but has 2
92+
--> $DIR/repr-transparent.rs:88:1
7993
|
8094
LL | union TooManyFields {
81-
| ^^^^^^^^^^^^^^^^^^^ needs at most one non-zero-sized field, but has 2
95+
| ^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2
8296
LL | u: u32,
83-
| ------ this field is non-zero-sized
97+
| ------ this field has non-zero size or requires alignment
8498
LL | s: i32
85-
| ------ this field is non-zero-sized
99+
| ------ this field has non-zero size or requires alignment
86100

87101
error: aborting due to 11 previous errors
88102

89-
Some errors have detailed explanations: E0084, E0690, E0691, E0731.
103+
Some errors have detailed explanations: E0084, E0690, E0731.
90104
For more information about an error, try `rustc --explain E0084`.

0 commit comments

Comments
 (0)