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 ](https://github.com/rust-lang/rust/issues/77841#issuecomment-716575747) 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.
This commit is contained in:
bors 2023-09-17 15:20:44 +00:00
commit 203c57dbe2
5 changed files with 91 additions and 88 deletions

View File

@ -1,9 +1,11 @@
#### Note: this error code is no longer emitted by the compiler.
A struct, enum, or union with the `repr(transparent)` representation hint A struct, enum, or union with the `repr(transparent)` representation hint
contains a zero-sized field that requires non-trivial alignment. contains a zero-sized field that requires non-trivial alignment.
Erroneous code example: Erroneous code example:
```compile_fail,E0691 ```ignore (error is no longer emitted)
#![feature(repr_align)] #![feature(repr_align)]
#[repr(align(32))] #[repr(align(32))]

View File

@ -304,13 +304,13 @@ hir_analysis_transparent_enum_variant = transparent enum needs exactly one varia
.many_label = too many variants in `{$path}` .many_label = too many variants in `{$path}`
.multi_label = variant here .multi_label = variant here
hir_analysis_transparent_non_zero_sized = transparent {$desc} needs at most one non-zero-sized field, but has {$field_count} hir_analysis_transparent_non_zero_sized = transparent {$desc} needs at most one field with non-trivial size or alignment, but has {$field_count}
.label = needs at most one non-zero-sized field, but has {$field_count} .label = needs at most one field with non-trivial size or alignment, but has {$field_count}
.labels = this field is non-zero-sized .labels = this field has non-zero size or requires alignment
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} 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}
.label = needs at most one non-zero-sized field, but has {$field_count} .label = needs at most one field with non-trivial size or alignment, but has {$field_count}
.labels = this field is non-zero-sized .labels = this field has non-zero size or requires alignment
hir_analysis_typeof_reserved_keyword_used = hir_analysis_typeof_reserved_keyword_used =
`typeof` is a reserved keyword but unimplemented `typeof` is a reserved keyword but unimplemented

View File

@ -1138,19 +1138,19 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
return; return;
} }
// For each field, figure out if it's known to be a ZST and align(1), with "known" // For each field, figure out if it's known to have "trivial" layout (i.e., is a 1-ZST), with
// respecting #[non_exhaustive] attributes. // "known" respecting #[non_exhaustive] attributes.
let field_infos = adt.all_fields().map(|field| { let field_infos = adt.all_fields().map(|field| {
let ty = field.ty(tcx, GenericArgs::identity_for_item(tcx, field.did)); let ty = field.ty(tcx, GenericArgs::identity_for_item(tcx, field.did));
let param_env = tcx.param_env(field.did); let param_env = tcx.param_env(field.did);
let layout = tcx.layout_of(param_env.and(ty)); let layout = tcx.layout_of(param_env.and(ty));
// We are currently checking the type this field came from, so it must be local // We are currently checking the type this field came from, so it must be local
let span = tcx.hir().span_if_local(field.did).unwrap(); let span = tcx.hir().span_if_local(field.did).unwrap();
let zst = layout.is_ok_and(|layout| layout.is_zst()); let trivial = layout.is_ok_and(|layout| layout.is_1zst());
let align = layout.ok().map(|layout| layout.align.abi.bytes()); if !trivial {
if !zst { return (span, trivial, None);
return (span, zst, align, None);
} }
// Even some 1-ZST fields are not allowed though, if they have `non_exhaustive`.
fn check_non_exhaustive<'tcx>( fn check_non_exhaustive<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
@ -1184,41 +1184,25 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
} }
} }
(span, zst, align, check_non_exhaustive(tcx, ty).break_value()) (span, trivial, check_non_exhaustive(tcx, ty).break_value())
}); });
let non_zst_fields = field_infos let non_trivial_fields = field_infos
.clone() .clone()
.filter_map(|(span, zst, _align, _non_exhaustive)| if !zst { Some(span) } else { None }); .filter_map(|(span, trivial, _non_exhaustive)| if !trivial { Some(span) } else { None });
let non_zst_count = non_zst_fields.clone().count(); let non_trivial_count = non_trivial_fields.clone().count();
if non_zst_count >= 2 { if non_trivial_count >= 2 {
bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, tcx.def_span(adt.did())); bad_non_zero_sized_fields(
tcx,
adt,
non_trivial_count,
non_trivial_fields,
tcx.def_span(adt.did()),
);
return;
} }
let incompatible_zst_fields = for (span, _trivial, non_exhaustive) in field_infos {
field_infos.clone().filter(|(_, _, _, opt)| opt.is_some()).count(); if let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive {
let incompat = incompatible_zst_fields + non_zst_count >= 2 && non_zst_count < 2;
for (span, zst, align, non_exhaustive) in field_infos {
if zst && align != Some(1) {
let mut err = struct_span_err!(
tcx.sess,
span,
E0691,
"zero-sized field in transparent {} has alignment larger than 1",
adt.descr(),
);
if let Some(align_bytes) = align {
err.span_label(
span,
format!("has alignment of {align_bytes}, which is larger than 1"),
);
} else {
err.span_label(span, "may have alignment larger than 1");
}
err.emit();
}
if incompat && let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive {
tcx.struct_span_lint_hir( tcx.struct_span_lint_hir(
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()), tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()),

View File

@ -23,23 +23,26 @@ struct ContainsMultipleZst(PhantomData<*const i32>, NoFields);
struct ContainsZstAndNonZst((), [i32; 2]); struct ContainsZstAndNonZst((), [i32; 2]);
#[repr(transparent)] #[repr(transparent)]
struct MultipleNonZst(u8, u8); //~ ERROR needs at most one non-zero-sized field struct MultipleNonZst(u8, u8); //~ ERROR needs at most one field with non-trivial size or alignment
trait Mirror { type It: ?Sized; } trait Mirror { type It: ?Sized; }
impl<T: ?Sized> Mirror for T { type It = Self; } impl<T: ?Sized> Mirror for T { type It = Self; }
#[repr(transparent)] #[repr(transparent)]
pub struct StructWithProjection(f32, <f32 as Mirror>::It); pub struct StructWithProjection(f32, <f32 as Mirror>::It);
//~^ ERROR needs at most one non-zero-sized field //~^ ERROR needs at most one field with non-trivial size or alignment
#[repr(transparent)] #[repr(transparent)]
struct NontrivialAlignZst(u32, [u16; 0]); //~ ERROR alignment larger than 1 struct NontrivialAlignZst(u32, [u16; 0]); //~ ERROR needs at most one field with non-trivial size or alignment
#[repr(align(32))] #[repr(align(32))]
struct ZstAlign32<T>(PhantomData<T>); struct ZstAlign32<T>(PhantomData<T>);
#[repr(transparent)] #[repr(transparent)]
struct GenericAlign<T>(ZstAlign32<T>, u32); //~ ERROR alignment larger than 1 struct GenericAlign<T>(ZstAlign32<T>, u32); //~ ERROR needs at most one field with non-trivial size or alignment
#[repr(transparent)]
struct WrapsZstWithAlignment([i32; 0]);
#[repr(transparent)] //~ ERROR unsupported representation for zero-variant enum #[repr(transparent)] //~ ERROR unsupported representation for zero-variant enum
enum Void {} //~ ERROR transparent enum needs exactly one variant, but has 0 enum Void {} //~ ERROR transparent enum needs exactly one variant, but has 0
@ -58,7 +61,7 @@ enum UnitFieldEnum {
enum TooManyFieldsEnum { enum TooManyFieldsEnum {
Foo(u32, String), Foo(u32, String),
} }
//~^^^ ERROR transparent enum needs at most one non-zero-sized field, but has 2 //~^^^ ERROR transparent enum needs at most one field with non-trivial size or alignment, but has 2
#[repr(transparent)] #[repr(transparent)]
enum MultipleVariants { //~ ERROR transparent enum needs exactly one variant, but has 2 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
} }
#[repr(transparent)] #[repr(transparent)]
enum NontrivialAlignZstEnum { enum NontrivialAlignZstEnum { //~ ERROR needs at most one field with non-trivial size or alignment
Foo(u32, [u16; 0]), //~ ERROR alignment larger than 1 Foo(u32, [u16; 0]),
} }
#[repr(transparent)] #[repr(transparent)]
enum GenericAlignEnum<T> { enum GenericAlignEnum<T> { //~ ERROR needs at most one field with non-trivial size or alignment
Foo { bar: ZstAlign32<T>, baz: u32 } //~ ERROR alignment larger than 1 Foo { bar: ZstAlign32<T>, baz: u32 }
} }
#[repr(transparent)] #[repr(transparent)]
@ -82,7 +85,7 @@ union UnitUnion {
} }
#[repr(transparent)] #[repr(transparent)]
union TooManyFields { //~ ERROR transparent union needs at most one non-zero-sized field, but has 2 union TooManyFields { //~ ERROR transparent union needs at most one field with non-trivial size or alignment, but has 2
u: u32, u: u32,
s: i32 s: i32
} }

View File

@ -1,35 +1,41 @@
error[E0690]: transparent struct needs at most one non-zero-sized field, but has 2 error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2
--> $DIR/repr-transparent.rs:26:1 --> $DIR/repr-transparent.rs:26:1
| |
LL | struct MultipleNonZst(u8, u8); LL | struct MultipleNonZst(u8, u8);
| ^^^^^^^^^^^^^^^^^^^^^ -- -- this field is non-zero-sized | ^^^^^^^^^^^^^^^^^^^^^ -- -- this field has non-zero size or requires alignment
| | | | | |
| | this field is non-zero-sized | | this field has non-zero size or requires alignment
| needs at most one non-zero-sized field, but has 2 | needs at most one field with non-trivial size or alignment, but has 2
error[E0690]: transparent struct needs at most one non-zero-sized field, but has 2 error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2
--> $DIR/repr-transparent.rs:32:1 --> $DIR/repr-transparent.rs:32:1
| |
LL | pub struct StructWithProjection(f32, <f32 as Mirror>::It); LL | pub struct StructWithProjection(f32, <f32 as Mirror>::It);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --- ------------------- this field is non-zero-sized | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --- ------------------- this field has non-zero size or requires alignment
| | | | | |
| | this field is non-zero-sized | | this field has non-zero size or requires alignment
| needs at most one non-zero-sized field, but has 2 | needs at most one field with non-trivial size or alignment, but has 2
error[E0691]: zero-sized field in transparent struct has alignment larger than 1 error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2
--> $DIR/repr-transparent.rs:36:32 --> $DIR/repr-transparent.rs:36:1
| |
LL | struct NontrivialAlignZst(u32, [u16; 0]); LL | struct NontrivialAlignZst(u32, [u16; 0]);
| ^^^^^^^^ has alignment of 2, which is larger than 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^ --- -------- this field has non-zero size or requires alignment
| | |
| | this field has non-zero size or requires alignment
| needs at most one field with non-trivial size or alignment, but has 2
error[E0691]: zero-sized field in transparent struct has alignment larger than 1 error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2
--> $DIR/repr-transparent.rs:42:24 --> $DIR/repr-transparent.rs:42:1
| |
LL | struct GenericAlign<T>(ZstAlign32<T>, u32); LL | struct GenericAlign<T>(ZstAlign32<T>, u32);
| ^^^^^^^^^^^^^ has alignment of 32, which is larger than 1 | ^^^^^^^^^^^^^^^^^^^^^^ ------------- --- this field has non-zero size or requires alignment
| | |
| | this field has non-zero size or requires alignment
| needs at most one field with non-trivial size or alignment, but has 2
error[E0084]: unsupported representation for zero-variant enum error[E0084]: unsupported representation for zero-variant enum
--> $DIR/repr-transparent.rs:44:1 --> $DIR/repr-transparent.rs:47:1
| |
LL | #[repr(transparent)] LL | #[repr(transparent)]
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^
@ -37,23 +43,23 @@ LL | enum Void {}
| --------- zero-variant enum | --------- zero-variant enum
error[E0731]: transparent enum needs exactly one variant, but has 0 error[E0731]: transparent enum needs exactly one variant, but has 0
--> $DIR/repr-transparent.rs:45:1 --> $DIR/repr-transparent.rs:48:1
| |
LL | enum Void {} LL | enum Void {}
| ^^^^^^^^^ needs exactly one variant, but has 0 | ^^^^^^^^^ needs exactly one variant, but has 0
error[E0690]: the variant of a transparent enum needs at most one non-zero-sized field, but has 2 error[E0690]: the variant of a transparent enum needs at most one field with non-trivial size or alignment, but has 2
--> $DIR/repr-transparent.rs:58:1 --> $DIR/repr-transparent.rs:61:1
| |
LL | enum TooManyFieldsEnum { LL | enum TooManyFieldsEnum {
| ^^^^^^^^^^^^^^^^^^^^^^ needs at most one non-zero-sized field, but has 2 | ^^^^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2
LL | Foo(u32, String), LL | Foo(u32, String),
| --- ------ this field is non-zero-sized | --- ------ this field has non-zero size or requires alignment
| | | |
| this field is non-zero-sized | this field has non-zero size or requires alignment
error[E0731]: transparent enum needs exactly one variant, but has 2 error[E0731]: transparent enum needs exactly one variant, but has 2
--> $DIR/repr-transparent.rs:64:1 --> $DIR/repr-transparent.rs:67:1
| |
LL | enum MultipleVariants { LL | enum MultipleVariants {
| ^^^^^^^^^^^^^^^^^^^^^ needs exactly one variant, but has 2 | ^^^^^^^^^^^^^^^^^^^^^ needs exactly one variant, but has 2
@ -62,29 +68,37 @@ LL | Foo(String),
LL | Bar, LL | Bar,
| --- too many variants in `MultipleVariants` | --- too many variants in `MultipleVariants`
error[E0691]: zero-sized field in transparent enum has alignment larger than 1 error[E0690]: the variant of a transparent enum needs at most one field with non-trivial size or alignment, but has 2
--> $DIR/repr-transparent.rs:71:14 --> $DIR/repr-transparent.rs:73:1
| |
LL | enum NontrivialAlignZstEnum {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2
LL | Foo(u32, [u16; 0]), LL | Foo(u32, [u16; 0]),
| ^^^^^^^^ has alignment of 2, which is larger than 1 | --- -------- this field has non-zero size or requires alignment
| |
| this field has non-zero size or requires alignment
error[E0691]: zero-sized field in transparent enum has alignment larger than 1 error[E0690]: the variant of a transparent enum needs at most one field with non-trivial size or alignment, but has 2
--> $DIR/repr-transparent.rs:76:11 --> $DIR/repr-transparent.rs:78:1
| |
LL | enum GenericAlignEnum<T> {
| ^^^^^^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2
LL | Foo { bar: ZstAlign32<T>, baz: u32 } LL | Foo { bar: ZstAlign32<T>, baz: u32 }
| ^^^^^^^^^^^^^^^^^^ has alignment of 32, which is larger than 1 | ------------------ -------- this field has non-zero size or requires alignment
| |
| this field has non-zero size or requires alignment
error[E0690]: transparent union needs at most one non-zero-sized field, but has 2 error[E0690]: transparent union needs at most one field with non-trivial size or alignment, but has 2
--> $DIR/repr-transparent.rs:85:1 --> $DIR/repr-transparent.rs:88:1
| |
LL | union TooManyFields { LL | union TooManyFields {
| ^^^^^^^^^^^^^^^^^^^ needs at most one non-zero-sized field, but has 2 | ^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2
LL | u: u32, LL | u: u32,
| ------ this field is non-zero-sized | ------ this field has non-zero size or requires alignment
LL | s: i32 LL | s: i32
| ------ this field is non-zero-sized | ------ this field has non-zero size or requires alignment
error: aborting due to 11 previous errors error: aborting due to 11 previous errors
Some errors have detailed explanations: E0084, E0690, E0691, E0731. Some errors have detailed explanations: E0084, E0690, E0731.
For more information about an error, try `rustc --explain E0084`. For more information about an error, try `rustc --explain E0084`.