Skip to content

Commit a594ae1

Browse files
committed
compiler/hir/check: be more clear about transparent layout violations
Extend the error-messages of rustc to distinguish between non-zero-sized fields and fields without layout when annotating `repr(transparent)` violations. When compiling `repr(transparent)` structures, the compiler checks that there is at most one field with non-zero size *OR* having unknown layout. However, the error message treats both conditions as the same, thus always annotating them as; a: u32, ------ this field is non-zero-sized In most situations, this is reasonable. However, if you consider generic fields, this can easily become confusing. Consider the following error message: a: [T; 0], --------- this field is non-zero-sized This field is clearly zero-sized, yet the compiler is correct to refuse it. This is because the field might raise the alignment requirements of the transparent field, thus change the overall layout. This patch changes the error message to distinguish non-zero-sized fields _with_ known layout from fields with _unknown_ layout. The logic _when_ to emit errors is not changed. Before this patch, it would emit an error when `non_zst_count >= 2` (but `non_zst_count` counted fields without layout *AND* fields with guaranteed non-zero layout). After this patch, `non_zst_count` is changed to only count fields with guaranteed non-zero layout, but `non_layout_count` counts fields without layout. The new `non_transparent_count` is the sum of both and should be equivalent to the previous `non_zst_count`. Long story short, the new error output looks like this: error[E0690]: transparent struct needs at most one field of non-zero size or alignment larger than 1, but has 2 --> src/foobar.rs:2:1 | 2 | struct Test<T: Sized> { | ^^^^^^^^^^^^^^^^^^^^^ needs at most one field of non-zero size or alignment larger than 1, but has 2 3 | a: u32, | ------ this field is non-zero-sized 4 | b: [T; 0], | --------- this field may have alignment larger than 1 Previously, any field without known layout was annotated as "non-zero size", now this is changed to annotate those fields as "may have alignment larger than 1". This is a catch-all that is, to my knowledge, true for all generic fields without layout, since one cannot control their alignment. This might need refinement in the future, when other cases of layout-violation occur. One might even argue that the entire error-message could be made more generic, ala: transparent struct needs at most one field with non-trivial layout However, I think the current error-message is much more helpful in stating the 2 obvious violations: non-ZST and non-alignment-of-1 Hence, this commit retains the original error-message style and simply extends it with the alignment-violation notice. Originally reported in: #98064
1 parent d34d7f1 commit a594ae1

File tree

7 files changed

+81
-52
lines changed

7 files changed

+81
-52
lines changed

compiler/rustc_error_codes/src/error_codes/E0690.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
A struct with the representation hint `repr(transparent)` had two or more fields
2-
that were not guaranteed to be zero-sized.
2+
that were not guaranteed to be zero-sized or have an alignment of 1.
33

44
Erroneous code example:
55

@@ -17,7 +17,8 @@ it is not clear how the struct should be represented.
1717
Note that fields of zero-sized types (e.g., `PhantomData`) can also exist
1818
alongside the field that contains the actual data, they do not count for this
1919
error. When generic types are involved (as in the above example), an error is
20-
reported because the type parameter could be non-zero-sized.
20+
reported because the type parameter could be non-zero-sized, or it could raise
21+
the alignment requirements of the type.
2122

2223
To combine `repr(transparent)` with type parameters, `PhantomData` may be
2324
useful:

compiler/rustc_hir_analysis/messages.ftl

+9-7
Original file line numberDiff line numberDiff line change
@@ -270,13 +270,15 @@ hir_analysis_transparent_enum_variant = transparent enum needs exactly one varia
270270
.many_label = too many variants in `{$path}`
271271
.multi_label = variant here
272272
273-
hir_analysis_transparent_non_zero_sized = transparent {$desc} needs at most one non-zero-sized field, but has {$field_count}
274-
.label = needs at most one non-zero-sized field, but has {$field_count}
275-
.labels = this field is non-zero-sized
276-
277-
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}
278-
.label = needs at most one non-zero-sized field, but has {$field_count}
279-
.labels = this field is non-zero-sized
273+
hir_analysis_transparent_layout = transparent {$desc} needs at most one field of non-zero size or alignment larger than 1, but has {$field_count}
274+
.label = needs at most one field of non-zero size or alignment larger than 1, but has {$field_count}
275+
.non_layout_labels = this field may have an alignment larger than 1
276+
.non_zst_labels = this field is non-zero-sized
277+
278+
hir_analysis_transparent_layout_enum = the variant of a transparent {$desc} needs at most one field of non-zero size or alignment larger than 1, but has {$field_count}
279+
.label = needs at most one field of non-zero size or alignment larger than 1, but has {$field_count}
280+
.non_layout_labels = this field may have an alignment larger than 1
281+
.non_zst_labels = this field is non-zero-sized
280282
281283
hir_analysis_typeof_reserved_keyword_used =
282284
`typeof` is a reserved keyword but unimplemented

compiler/rustc_hir_analysis/src/check/check.rs

+22-5
Original file line numberDiff line numberDiff line change
@@ -1178,16 +1178,33 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
11781178
(span, zst, layout, check_non_exhaustive(tcx, ty).break_value())
11791179
});
11801180

1181-
let non_zst_fields = field_infos
1181+
let non_layout_fields = field_infos
11821182
.clone()
1183-
.filter_map(|(span, zst, _layout, _non_exhaustive)| if !zst { Some(span) } else { None });
1183+
.filter_map(|(span, _, layout, _)| if layout.is_err() { Some(span) } else { None });
1184+
let non_layout_count = non_layout_fields.clone().count();
1185+
let non_zst_fields =
1186+
field_infos.clone().filter_map(
1187+
|(span, zst, layout, _)| if layout.is_ok() && !zst { Some(span) } else { None },
1188+
);
11841189
let non_zst_count = non_zst_fields.clone().count();
1185-
if non_zst_count >= 2 {
1186-
bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, tcx.def_span(adt.did()));
1190+
1191+
let non_transparent_count = non_layout_count + non_zst_count;
1192+
if non_transparent_count >= 2 {
1193+
bad_transparent_layout(
1194+
tcx,
1195+
adt,
1196+
non_transparent_count,
1197+
non_layout_fields,
1198+
non_zst_fields,
1199+
tcx.def_span(adt.did()),
1200+
);
11871201
}
1202+
11881203
let incompatible_zst_fields =
11891204
field_infos.clone().filter(|(_, _, _, opt)| opt.is_some()).count();
1190-
let incompat = incompatible_zst_fields + non_zst_count >= 2 && non_zst_count < 2;
1205+
let incompat =
1206+
incompatible_zst_fields + non_transparent_count >= 2 && non_transparent_count < 2;
1207+
11911208
for (span, zst, layout, non_exhaustive) in field_infos {
11921209
let align = layout.ok().map(|layout| layout.align.abi.bytes());
11931210
if zst && align != Some(1) {

compiler/rustc_hir_analysis/src/check/mod.rs

+11-8
Original file line numberDiff line numberDiff line change
@@ -516,26 +516,29 @@ fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>, sp: Span, d
516516
});
517517
}
518518

519-
/// Emit an error when encountering two or more non-zero-sized fields in a transparent
520-
/// enum.
521-
fn bad_non_zero_sized_fields<'tcx>(
519+
/// Emit an error when encountering two or more fields without layout or non-zero size
520+
/// in a transparent Adt.
521+
fn bad_transparent_layout<'tcx>(
522522
tcx: TyCtxt<'tcx>,
523523
adt: ty::AdtDef<'tcx>,
524524
field_count: usize,
525-
field_spans: impl Iterator<Item = Span>,
525+
non_layout_spans: impl Iterator<Item = Span>,
526+
non_zst_spans: impl Iterator<Item = Span>,
526527
sp: Span,
527528
) {
528529
if adt.is_enum() {
529-
tcx.sess.emit_err(errors::TransparentNonZeroSizedEnum {
530+
tcx.sess.emit_err(errors::TransparentLayoutEnum {
530531
span: sp,
531-
spans: field_spans.collect(),
532+
non_layout_spans: non_layout_spans.collect(),
533+
non_zst_spans: non_zst_spans.collect(),
532534
field_count,
533535
desc: adt.descr(),
534536
});
535537
} else {
536-
tcx.sess.emit_err(errors::TransparentNonZeroSized {
538+
tcx.sess.emit_err(errors::TransparentLayout {
537539
span: sp,
538-
spans: field_spans.collect(),
540+
non_layout_spans: non_layout_spans.collect(),
541+
non_zst_spans: non_zst_spans.collect(),
539542
field_count,
540543
desc: adt.descr(),
541544
});

compiler/rustc_hir_analysis/src/errors.rs

+12-8
Original file line numberDiff line numberDiff line change
@@ -777,25 +777,29 @@ pub(crate) struct TransparentEnumVariant {
777777
}
778778

779779
#[derive(Diagnostic)]
780-
#[diag(hir_analysis_transparent_non_zero_sized_enum, code = "E0690")]
781-
pub(crate) struct TransparentNonZeroSizedEnum<'a> {
780+
#[diag(hir_analysis_transparent_layout_enum, code = "E0690")]
781+
pub(crate) struct TransparentLayoutEnum<'a> {
782782
#[primary_span]
783783
#[label]
784784
pub span: Span,
785-
#[label(hir_analysis_labels)]
786-
pub spans: Vec<Span>,
785+
#[label(hir_analysis_non_layout_labels)]
786+
pub non_layout_spans: Vec<Span>,
787+
#[label(hir_analysis_non_zst_labels)]
788+
pub non_zst_spans: Vec<Span>,
787789
pub field_count: usize,
788790
pub desc: &'a str,
789791
}
790792

791793
#[derive(Diagnostic)]
792-
#[diag(hir_analysis_transparent_non_zero_sized, code = "E0690")]
793-
pub(crate) struct TransparentNonZeroSized<'a> {
794+
#[diag(hir_analysis_transparent_layout, code = "E0690")]
795+
pub(crate) struct TransparentLayout<'a> {
794796
#[primary_span]
795797
#[label]
796798
pub span: Span,
797-
#[label(hir_analysis_labels)]
798-
pub spans: Vec<Span>,
799+
#[label(hir_analysis_non_layout_labels)]
800+
pub non_layout_spans: Vec<Span>,
801+
#[label(hir_analysis_non_zst_labels)]
802+
pub non_zst_spans: Vec<Span>,
799803
pub field_count: usize,
800804
pub desc: &'a str,
801805
}

tests/ui/repr/repr-transparent.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,15 @@ 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);
27+
//~^ ERROR needs at most one field of non-zero size or alignment larger than 1
2728

2829
trait Mirror { type It: ?Sized; }
2930
impl<T: ?Sized> Mirror for T { type It = Self; }
3031

3132
#[repr(transparent)]
3233
pub struct StructWithProjection(f32, <f32 as Mirror>::It);
33-
//~^ ERROR needs at most one non-zero-sized field
34+
//~^ ERROR needs at most one field of non-zero size or alignment larger than 1
3435

3536
#[repr(transparent)]
3637
struct NontrivialAlignZst(u32, [u16; 0]); //~ ERROR alignment larger than 1
@@ -58,7 +59,7 @@ enum UnitFieldEnum {
5859
enum TooManyFieldsEnum {
5960
Foo(u32, String),
6061
}
61-
//~^^^ ERROR transparent enum needs at most one non-zero-sized field, but has 2
62+
//~^^^ ERROR needs at most one field of non-zero size or alignment larger than 1, but has 2
6263

6364
#[repr(transparent)]
6465
enum MultipleVariants { //~ ERROR transparent enum needs exactly one variant, but has 2
@@ -82,9 +83,10 @@ union UnitUnion {
8283
}
8384

8485
#[repr(transparent)]
85-
union TooManyFields { //~ ERROR transparent union needs at most one non-zero-sized field, but has 2
86+
union TooManyFields {
8687
u: u32,
8788
s: i32
8889
}
90+
//~^^^^ ERROR transparent union needs at most one field of non-zero size or alignment larger than 1, but has 2
8991

9092
fn main() {}

tests/ui/repr/repr-transparent.stderr

+18-18
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,59 @@
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 of non-zero size or alignment larger than 1, but has 2
22
--> $DIR/repr-transparent.rs:26:1
33
|
44
LL | struct MultipleNonZst(u8, u8);
55
| ^^^^^^^^^^^^^^^^^^^^^ -- -- this field is non-zero-sized
66
| | |
77
| | this field is non-zero-sized
8-
| needs at most one non-zero-sized field, but has 2
8+
| needs at most one field of non-zero size or alignment larger than 1, but has 2
99

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

1919
error[E0691]: zero-sized field in transparent struct has alignment larger than 1
20-
--> $DIR/repr-transparent.rs:36:32
20+
--> $DIR/repr-transparent.rs:37:32
2121
|
2222
LL | struct NontrivialAlignZst(u32, [u16; 0]);
2323
| ^^^^^^^^ has alignment of 2, which is larger than 1
2424

2525
error[E0691]: zero-sized field in transparent struct has alignment larger than 1
26-
--> $DIR/repr-transparent.rs:42:24
26+
--> $DIR/repr-transparent.rs:43:24
2727
|
2828
LL | struct GenericAlign<T>(ZstAlign32<T>, u32);
2929
| ^^^^^^^^^^^^^ has alignment of 32, which is larger than 1
3030

3131
error[E0084]: unsupported representation for zero-variant enum
32-
--> $DIR/repr-transparent.rs:44:1
32+
--> $DIR/repr-transparent.rs:45:1
3333
|
3434
LL | #[repr(transparent)]
3535
| ^^^^^^^^^^^^^^^^^^^^
3636
LL | enum Void {}
3737
| --------- zero-variant enum
3838

3939
error[E0731]: transparent enum needs exactly one variant, but has 0
40-
--> $DIR/repr-transparent.rs:45:1
40+
--> $DIR/repr-transparent.rs:46:1
4141
|
4242
LL | enum Void {}
4343
| ^^^^^^^^^ needs exactly one variant, but has 0
4444

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
45+
error[E0690]: the variant of a transparent enum needs at most one field of non-zero size or alignment larger than 1, but has 2
46+
--> $DIR/repr-transparent.rs:59:1
4747
|
4848
LL | enum TooManyFieldsEnum {
49-
| ^^^^^^^^^^^^^^^^^^^^^^ needs at most one non-zero-sized field, but has 2
49+
| ^^^^^^^^^^^^^^^^^^^^^^ needs at most one field of non-zero size or alignment larger than 1, but has 2
5050
LL | Foo(u32, String),
5151
| --- ------ this field is non-zero-sized
5252
| |
5353
| this field is non-zero-sized
5454

5555
error[E0731]: transparent enum needs exactly one variant, but has 2
56-
--> $DIR/repr-transparent.rs:64:1
56+
--> $DIR/repr-transparent.rs:65:1
5757
|
5858
LL | enum MultipleVariants {
5959
| ^^^^^^^^^^^^^^^^^^^^^ needs exactly one variant, but has 2
@@ -63,22 +63,22 @@ LL | Bar,
6363
| --- too many variants in `MultipleVariants`
6464

6565
error[E0691]: zero-sized field in transparent enum has alignment larger than 1
66-
--> $DIR/repr-transparent.rs:71:14
66+
--> $DIR/repr-transparent.rs:72:14
6767
|
6868
LL | Foo(u32, [u16; 0]),
6969
| ^^^^^^^^ has alignment of 2, which is larger than 1
7070

7171
error[E0691]: zero-sized field in transparent enum has alignment larger than 1
72-
--> $DIR/repr-transparent.rs:76:11
72+
--> $DIR/repr-transparent.rs:77:11
7373
|
7474
LL | Foo { bar: ZstAlign32<T>, baz: u32 }
7575
| ^^^^^^^^^^^^^^^^^^ has alignment of 32, which is larger than 1
7676

77-
error[E0690]: transparent union needs at most one non-zero-sized field, but has 2
78-
--> $DIR/repr-transparent.rs:85:1
77+
error[E0690]: transparent union needs at most one field of non-zero size or alignment larger than 1, but has 2
78+
--> $DIR/repr-transparent.rs:86:1
7979
|
8080
LL | union TooManyFields {
81-
| ^^^^^^^^^^^^^^^^^^^ needs at most one non-zero-sized field, but has 2
81+
| ^^^^^^^^^^^^^^^^^^^ needs at most one field of non-zero size or alignment larger than 1, but has 2
8282
LL | u: u32,
8383
| ------ this field is non-zero-sized
8484
LL | s: i32

0 commit comments

Comments
 (0)