Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

repr(transparent): it's fine if the one non-1-ZST field is a ZST #115334

Merged
merged 2 commits into from
Sep 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion compiler/rustc_error_codes/src/error_codes/E0691.md
Original file line number Diff line number Diff line change
@@ -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
contains a zero-sized field that requires non-trivial alignment.

Erroneous code example:

```compile_fail,E0691
```ignore (error is no longer emitted)
#![feature(repr_align)]

#[repr(align(32))]
Expand Down
12 changes: 6 additions & 6 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -270,13 +270,13 @@ hir_analysis_transparent_enum_variant = transparent enum needs exactly one varia
.many_label = too many variants in `{$path}`
.multi_label = variant here

hir_analysis_transparent_non_zero_sized = transparent {$desc} needs at most one non-zero-sized field, but has {$field_count}
.label = needs at most one non-zero-sized field, but has {$field_count}
.labels = this field is non-zero-sized
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 field with non-trivial size or alignment, but has {$field_count}
.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}
.label = needs at most one non-zero-sized field, but has {$field_count}
.labels = this field is non-zero-sized
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 field with non-trivial size or alignment, but has {$field_count}
.labels = this field has non-zero size or requires alignment

hir_analysis_typeof_reserved_keyword_used =
`typeof` is a reserved keyword but unimplemented
Expand Down
58 changes: 21 additions & 37 deletions compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1130,19 +1130,19 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
return;
}

// For each field, figure out if it's known to be a ZST and align(1), with "known"
// respecting #[non_exhaustive] attributes.
// For each field, figure out if it's known to have "trivial" layout (i.e., is a 1-ZST), with
// "known" respecting #[non_exhaustive] attributes.
let field_infos = adt.all_fields().map(|field| {
let ty = field.ty(tcx, GenericArgs::identity_for_item(tcx, field.did));
let param_env = tcx.param_env(field.did);
let layout = tcx.layout_of(param_env.and(ty));
// 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 zst = layout.is_ok_and(|layout| layout.is_zst());
let align = layout.ok().map(|layout| layout.align.abi.bytes());
if !zst {
return (span, zst, align, None);
let trivial = layout.is_ok_and(|layout| layout.is_1zst());
if !trivial {
return (span, trivial, None);
}
// Even some 1-ZST fields are not allowed though, if they have `non_exhaustive`.

fn check_non_exhaustive<'tcx>(
tcx: TyCtxt<'tcx>,
Expand Down Expand Up @@ -1176,41 +1176,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()
.filter_map(|(span, zst, _align, _non_exhaustive)| if !zst { Some(span) } else { None });
let non_zst_count = non_zst_fields.clone().count();
if non_zst_count >= 2 {
bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, tcx.def_span(adt.did()));
.filter_map(|(span, trivial, _non_exhaustive)| if !trivial { Some(span) } else { None });
let non_trivial_count = non_trivial_fields.clone().count();
if non_trivial_count >= 2 {
bad_non_zero_sized_fields(
tcx,
adt,
non_trivial_count,
non_trivial_fields,
tcx.def_span(adt.did()),
);
return;
}
let incompatible_zst_fields =
field_infos.clone().filter(|(_, _, _, opt)| opt.is_some()).count();
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 {
for (span, _trivial, non_exhaustive) in field_infos {
if let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive {
tcx.struct_span_lint_hir(
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()),
Expand Down
23 changes: 13 additions & 10 deletions tests/ui/repr/repr-transparent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,26 @@ struct ContainsMultipleZst(PhantomData<*const i32>, NoFields);
struct ContainsZstAndNonZst((), [i32; 2]);

#[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; }
impl<T: ?Sized> Mirror for T { type It = Self; }

#[repr(transparent)]
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)]
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))]
struct ZstAlign32<T>(PhantomData<T>);

#[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
enum Void {} //~ ERROR transparent enum needs exactly one variant, but has 0
Expand All @@ -58,7 +61,7 @@ enum UnitFieldEnum {
enum TooManyFieldsEnum {
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)]
enum MultipleVariants { //~ ERROR transparent enum needs exactly one variant, but has 2
Expand All @@ -67,13 +70,13 @@ enum MultipleVariants { //~ ERROR transparent enum needs exactly one variant, bu
}

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

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

#[repr(transparent)]
Expand All @@ -82,7 +85,7 @@ union UnitUnion {
}

#[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,
s: i32
}
Expand Down
82 changes: 48 additions & 34 deletions tests/ui/repr/repr-transparent.stderr
Original file line number Diff line number Diff line change
@@ -1,59 +1,65 @@
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
|
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
| needs at most one non-zero-sized field, but has 2
| | this field has non-zero size or requires alignment
| 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
|
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
| needs at most one non-zero-sized field, but has 2
| | 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
--> $DIR/repr-transparent.rs:36:32
error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2
--> $DIR/repr-transparent.rs:36:1
|
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
--> $DIR/repr-transparent.rs:42:24
error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2
--> $DIR/repr-transparent.rs:42:1
|
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
--> $DIR/repr-transparent.rs:44:1
--> $DIR/repr-transparent.rs:47:1
|
LL | #[repr(transparent)]
| ^^^^^^^^^^^^^^^^^^^^
LL | enum Void {}
| --------- zero-variant enum

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 {}
| ^^^^^^^^^ 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
--> $DIR/repr-transparent.rs:58: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:61:1
|
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),
| --- ------ 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
--> $DIR/repr-transparent.rs:64:1
--> $DIR/repr-transparent.rs:67:1
|
LL | enum MultipleVariants {
| ^^^^^^^^^^^^^^^^^^^^^ needs exactly one variant, but has 2
Expand All @@ -62,29 +68,37 @@ LL | Foo(String),
LL | Bar,
| --- too many variants in `MultipleVariants`

error[E0691]: zero-sized field in transparent enum has alignment larger than 1
--> $DIR/repr-transparent.rs:71:14
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:73:1
|
LL | enum NontrivialAlignZstEnum {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2
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
--> $DIR/repr-transparent.rs:76:11
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: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 }
| ^^^^^^^^^^^^^^^^^^ 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
--> $DIR/repr-transparent.rs:85:1
error[E0690]: transparent union needs at most one field with non-trivial size or alignment, but has 2
--> $DIR/repr-transparent.rs:88:1
|
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,
| ------ this field is non-zero-sized
| ------ this field has non-zero size or requires alignment
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

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`.