From 9dd682803fbf1528034ad0e254b29a8540205e02 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 29 Aug 2023 08:58:38 +0200 Subject: [PATCH] repr(transparent): it's fine if the one non-1-ZST field is a ZST --- compiler/rustc_hir_analysis/messages.ftl | 12 +-- .../rustc_hir_analysis/src/check/check.rs | 58 +++++-------- tests/ui/repr/repr-transparent.rs | 23 +++--- tests/ui/repr/repr-transparent.stderr | 82 +++++++++++-------- 4 files changed, 88 insertions(+), 87 deletions(-) diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 597cae6ff33ca..c99913ae049af 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -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 diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 46e8cf81bc1f7..3c00a246a3db0 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -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>, @@ -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()), diff --git a/tests/ui/repr/repr-transparent.rs b/tests/ui/repr/repr-transparent.rs index 8c9d1639c0a51..87cf59ce9af40 100644 --- a/tests/ui/repr/repr-transparent.rs +++ b/tests/ui/repr/repr-transparent.rs @@ -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 Mirror for T { type It = Self; } #[repr(transparent)] pub struct StructWithProjection(f32, ::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(PhantomData); #[repr(transparent)] -struct GenericAlign(ZstAlign32, u32); //~ ERROR alignment larger than 1 +struct GenericAlign(ZstAlign32, 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 @@ -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 @@ -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 { - Foo { bar: ZstAlign32, baz: u32 } //~ ERROR alignment larger than 1 +enum GenericAlignEnum { //~ ERROR needs at most one field with non-trivial size or alignment + Foo { bar: ZstAlign32, baz: u32 } } #[repr(transparent)] @@ -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 } diff --git a/tests/ui/repr/repr-transparent.stderr b/tests/ui/repr/repr-transparent.stderr index 028fc25db46cc..d0c78a8418a09 100644 --- a/tests/ui/repr/repr-transparent.stderr +++ b/tests/ui/repr/repr-transparent.stderr @@ -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 | 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, ::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(ZstAlign32, 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)] | ^^^^^^^^^^^^^^^^^^^^ @@ -37,23 +43,23 @@ 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 @@ -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 { + | ^^^^^^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2 LL | Foo { bar: ZstAlign32, 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`.