From d34d7f1d7695a7f5f8ee8c746e4e7e1e1face204 Mon Sep 17 00:00:00 2001 From: David Rheinsberg Date: Fri, 21 Jul 2023 11:39:28 +0200 Subject: [PATCH 1/3] compiler/hir/check: retain layout info through transparent type check Postpone the alignment calculation and retain the entire layout information when checking transparent types for violations. This allows for future diagnostic improvements because we now know the exact type layouts of violating fields, or know exactly why their layout was not available. --- compiler/rustc_hir_analysis/src/check/check.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index c0cf1ea34cf2d..075ca966e21b3 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1130,7 +1130,7 @@ 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" + // For each field, figure out if it's known to be a ZST with layout, 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)); @@ -1139,9 +1139,8 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) // 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); + return (span, zst, layout, None); } fn check_non_exhaustive<'tcx>( @@ -1176,12 +1175,12 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) } } - (span, zst, align, check_non_exhaustive(tcx, ty).break_value()) + (span, zst, layout, check_non_exhaustive(tcx, ty).break_value()) }); let non_zst_fields = field_infos .clone() - .filter_map(|(span, zst, _align, _non_exhaustive)| if !zst { Some(span) } else { None }); + .filter_map(|(span, zst, _layout, _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())); @@ -1189,7 +1188,8 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) 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 { + for (span, zst, layout, non_exhaustive) in field_infos { + let align = layout.ok().map(|layout| layout.align.abi.bytes()); if zst && align != Some(1) { let mut err = struct_span_err!( tcx.sess, From a594ae13365c738778e832686d526d4b7c417f89 Mon Sep 17 00:00:00 2001 From: David Rheinsberg Date: Mon, 24 Jul 2023 15:47:08 +0200 Subject: [PATCH 2/3] 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 { | ^^^^^^^^^^^^^^^^^^^^^ 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 --- .../src/error_codes/E0690.md | 5 +-- compiler/rustc_hir_analysis/messages.ftl | 16 +++++---- .../rustc_hir_analysis/src/check/check.rs | 27 +++++++++++--- compiler/rustc_hir_analysis/src/check/mod.rs | 19 +++++----- compiler/rustc_hir_analysis/src/errors.rs | 20 ++++++----- tests/ui/repr/repr-transparent.rs | 10 +++--- tests/ui/repr/repr-transparent.stderr | 36 +++++++++---------- 7 files changed, 81 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0690.md b/compiler/rustc_error_codes/src/error_codes/E0690.md index ba706ad2b020b..c1122638996f2 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0690.md +++ b/compiler/rustc_error_codes/src/error_codes/E0690.md @@ -1,5 +1,5 @@ A struct with the representation hint `repr(transparent)` had two or more fields -that were not guaranteed to be zero-sized. +that were not guaranteed to be zero-sized or have an alignment of 1. Erroneous code example: @@ -17,7 +17,8 @@ it is not clear how the struct should be represented. Note that fields of zero-sized types (e.g., `PhantomData`) can also exist alongside the field that contains the actual data, they do not count for this error. When generic types are involved (as in the above example), an error is -reported because the type parameter could be non-zero-sized. +reported because the type parameter could be non-zero-sized, or it could raise +the alignment requirements of the type. To combine `repr(transparent)` with type parameters, `PhantomData` may be useful: diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 597cae6ff33ca..da2aa6db64503 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -270,13 +270,15 @@ 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_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_layout = transparent {$desc} needs at most one field of non-zero size or alignment larger than 1, but has {$field_count} + .label = needs at most one field of non-zero size or alignment larger than 1, but has {$field_count} + .non_layout_labels = this field may have an alignment larger than 1 + .non_zst_labels = this field is non-zero-sized + +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} + .label = needs at most one field of non-zero size or alignment larger than 1, but has {$field_count} + .non_layout_labels = this field may have an alignment larger than 1 + .non_zst_labels = this field is non-zero-sized 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 075ca966e21b3..93266c9f19e16 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1178,16 +1178,33 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) (span, zst, layout, check_non_exhaustive(tcx, ty).break_value()) }); - let non_zst_fields = field_infos + let non_layout_fields = field_infos .clone() - .filter_map(|(span, zst, _layout, _non_exhaustive)| if !zst { Some(span) } else { None }); + .filter_map(|(span, _, layout, _)| if layout.is_err() { Some(span) } else { None }); + let non_layout_count = non_layout_fields.clone().count(); + let non_zst_fields = + field_infos.clone().filter_map( + |(span, zst, layout, _)| if layout.is_ok() && !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())); + + let non_transparent_count = non_layout_count + non_zst_count; + if non_transparent_count >= 2 { + bad_transparent_layout( + tcx, + adt, + non_transparent_count, + non_layout_fields, + non_zst_fields, + tcx.def_span(adt.did()), + ); } + 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; + let incompat = + incompatible_zst_fields + non_transparent_count >= 2 && non_transparent_count < 2; + for (span, zst, layout, non_exhaustive) in field_infos { let align = layout.ok().map(|layout| layout.align.abi.bytes()); if zst && align != Some(1) { diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index d833166136669..f02e24c739af2 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -516,26 +516,29 @@ fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>, sp: Span, d }); } -/// Emit an error when encountering two or more non-zero-sized fields in a transparent -/// enum. -fn bad_non_zero_sized_fields<'tcx>( +/// Emit an error when encountering two or more fields without layout or non-zero size +/// in a transparent Adt. +fn bad_transparent_layout<'tcx>( tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>, field_count: usize, - field_spans: impl Iterator, + non_layout_spans: impl Iterator, + non_zst_spans: impl Iterator, sp: Span, ) { if adt.is_enum() { - tcx.sess.emit_err(errors::TransparentNonZeroSizedEnum { + tcx.sess.emit_err(errors::TransparentLayoutEnum { span: sp, - spans: field_spans.collect(), + non_layout_spans: non_layout_spans.collect(), + non_zst_spans: non_zst_spans.collect(), field_count, desc: adt.descr(), }); } else { - tcx.sess.emit_err(errors::TransparentNonZeroSized { + tcx.sess.emit_err(errors::TransparentLayout { span: sp, - spans: field_spans.collect(), + non_layout_spans: non_layout_spans.collect(), + non_zst_spans: non_zst_spans.collect(), field_count, desc: adt.descr(), }); diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 9471ad9ca9063..e2ff8da6e62af 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -777,25 +777,29 @@ pub(crate) struct TransparentEnumVariant { } #[derive(Diagnostic)] -#[diag(hir_analysis_transparent_non_zero_sized_enum, code = "E0690")] -pub(crate) struct TransparentNonZeroSizedEnum<'a> { +#[diag(hir_analysis_transparent_layout_enum, code = "E0690")] +pub(crate) struct TransparentLayoutEnum<'a> { #[primary_span] #[label] pub span: Span, - #[label(hir_analysis_labels)] - pub spans: Vec, + #[label(hir_analysis_non_layout_labels)] + pub non_layout_spans: Vec, + #[label(hir_analysis_non_zst_labels)] + pub non_zst_spans: Vec, pub field_count: usize, pub desc: &'a str, } #[derive(Diagnostic)] -#[diag(hir_analysis_transparent_non_zero_sized, code = "E0690")] -pub(crate) struct TransparentNonZeroSized<'a> { +#[diag(hir_analysis_transparent_layout, code = "E0690")] +pub(crate) struct TransparentLayout<'a> { #[primary_span] #[label] pub span: Span, - #[label(hir_analysis_labels)] - pub spans: Vec, + #[label(hir_analysis_non_layout_labels)] + pub non_layout_spans: Vec, + #[label(hir_analysis_non_zst_labels)] + pub non_zst_spans: Vec, pub field_count: usize, pub desc: &'a str, } diff --git a/tests/ui/repr/repr-transparent.rs b/tests/ui/repr/repr-transparent.rs index 8c9d1639c0a51..aab14c587cbdd 100644 --- a/tests/ui/repr/repr-transparent.rs +++ b/tests/ui/repr/repr-transparent.rs @@ -23,14 +23,15 @@ 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 of non-zero size or alignment larger than 1 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 of non-zero size or alignment larger than 1 #[repr(transparent)] struct NontrivialAlignZst(u32, [u16; 0]); //~ ERROR alignment larger than 1 @@ -58,7 +59,7 @@ enum UnitFieldEnum { enum TooManyFieldsEnum { Foo(u32, String), } -//~^^^ ERROR transparent enum needs at most one non-zero-sized field, but has 2 +//~^^^ ERROR needs at most one field of non-zero size or alignment larger than 1, but has 2 #[repr(transparent)] enum MultipleVariants { //~ ERROR transparent enum needs exactly one variant, but has 2 @@ -82,9 +83,10 @@ union UnitUnion { } #[repr(transparent)] -union TooManyFields { //~ ERROR transparent union needs at most one non-zero-sized field, but has 2 +union TooManyFields { u: u32, s: i32 } +//~^^^^ ERROR transparent union needs at most one field of non-zero size or alignment larger than 1, but has 2 fn main() {} diff --git a/tests/ui/repr/repr-transparent.stderr b/tests/ui/repr/repr-transparent.stderr index 028fc25db46cc..c16e5b27ec4fe 100644 --- a/tests/ui/repr/repr-transparent.stderr +++ b/tests/ui/repr/repr-transparent.stderr @@ -1,35 +1,35 @@ -error[E0690]: transparent struct needs at most one non-zero-sized field, but has 2 +error[E0690]: transparent struct needs at most one field of non-zero size or alignment larger than 1, but has 2 --> $DIR/repr-transparent.rs:26:1 | LL | struct MultipleNonZst(u8, u8); | ^^^^^^^^^^^^^^^^^^^^^ -- -- this field is non-zero-sized | | | | | this field is non-zero-sized - | needs at most one non-zero-sized field, but has 2 + | needs at most one field of non-zero size or alignment larger than 1, but has 2 -error[E0690]: transparent struct needs at most one non-zero-sized field, but has 2 - --> $DIR/repr-transparent.rs:32:1 +error[E0690]: transparent struct needs at most one field of non-zero size or alignment larger than 1, but has 2 + --> $DIR/repr-transparent.rs:33:1 | LL | pub struct StructWithProjection(f32, ::It); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --- ------------------- this field is non-zero-sized | | | | | this field is non-zero-sized - | needs at most one non-zero-sized field, but has 2 + | needs at most one field of non-zero size or alignment larger than 1, but has 2 error[E0691]: zero-sized field in transparent struct has alignment larger than 1 - --> $DIR/repr-transparent.rs:36:32 + --> $DIR/repr-transparent.rs:37:32 | LL | struct NontrivialAlignZst(u32, [u16; 0]); | ^^^^^^^^ has alignment of 2, which is larger than 1 error[E0691]: zero-sized field in transparent struct has alignment larger than 1 - --> $DIR/repr-transparent.rs:42:24 + --> $DIR/repr-transparent.rs:43:24 | LL | struct GenericAlign(ZstAlign32, u32); | ^^^^^^^^^^^^^ has alignment of 32, which is larger than 1 error[E0084]: unsupported representation for zero-variant enum - --> $DIR/repr-transparent.rs:44:1 + --> $DIR/repr-transparent.rs:45:1 | LL | #[repr(transparent)] | ^^^^^^^^^^^^^^^^^^^^ @@ -37,23 +37,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:46: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 of non-zero size or alignment larger than 1, but has 2 + --> $DIR/repr-transparent.rs:59:1 | LL | enum TooManyFieldsEnum { - | ^^^^^^^^^^^^^^^^^^^^^^ needs at most one non-zero-sized field, but has 2 + | ^^^^^^^^^^^^^^^^^^^^^^ needs at most one field of non-zero size or alignment larger than 1, but has 2 LL | Foo(u32, String), | --- ------ this field is non-zero-sized | | | this field is non-zero-sized error[E0731]: transparent enum needs exactly one variant, but has 2 - --> $DIR/repr-transparent.rs:64:1 + --> $DIR/repr-transparent.rs:65:1 | LL | enum MultipleVariants { | ^^^^^^^^^^^^^^^^^^^^^ needs exactly one variant, but has 2 @@ -63,22 +63,22 @@ 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 + --> $DIR/repr-transparent.rs:72:14 | LL | Foo(u32, [u16; 0]), | ^^^^^^^^ has alignment of 2, which is larger than 1 error[E0691]: zero-sized field in transparent enum has alignment larger than 1 - --> $DIR/repr-transparent.rs:76:11 + --> $DIR/repr-transparent.rs:77:11 | LL | Foo { bar: ZstAlign32, baz: u32 } | ^^^^^^^^^^^^^^^^^^ has alignment of 32, which is larger than 1 -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 of non-zero size or alignment larger than 1, but has 2 + --> $DIR/repr-transparent.rs:86:1 | LL | union TooManyFields { - | ^^^^^^^^^^^^^^^^^^^ needs at most one non-zero-sized field, but has 2 + | ^^^^^^^^^^^^^^^^^^^ needs at most one field of non-zero size or alignment larger than 1, but has 2 LL | u: u32, | ------ this field is non-zero-sized LL | s: i32 From b3708a7a1cace816af7c38528dac277b5be9fd8b Mon Sep 17 00:00:00 2001 From: David Rheinsberg Date: Mon, 4 Sep 2023 13:36:26 +0200 Subject: [PATCH 3/3] compiler/hir/check: clarify error message on transparent ADT Replace the phrase "layout of non-zero size or alignment larger than 1" with "non-trivial layout", and then elaborate that a layout is trivial if, and only if, it is zero-sized with an alignment of 1. Additionally, add a comment to the check-function explaining which information is gathered to check transparent ADTs, and elaborate why it is needed. --- .../src/error_codes/E0690.md | 4 ++-- compiler/rustc_hir_analysis/messages.ftl | 10 ++++---- .../rustc_hir_analysis/src/check/check.rs | 11 +++++++-- compiler/rustc_hir_analysis/src/check/mod.rs | 4 +++- compiler/rustc_hir_analysis/src/errors.rs | 4 ++++ tests/ui/repr/repr-transparent.rs | 8 +++---- tests/ui/repr/repr-transparent.stderr | 24 ++++++++++++------- 7 files changed, 44 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0690.md b/compiler/rustc_error_codes/src/error_codes/E0690.md index c1122638996f2..9dea165c913d8 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0690.md +++ b/compiler/rustc_error_codes/src/error_codes/E0690.md @@ -5,8 +5,8 @@ Erroneous code example: ```compile_fail,E0690 #[repr(transparent)] -struct LengthWithUnit { // error: transparent struct needs at most one - value: f32, // non-zero-sized field, but has 2 +struct LengthWithUnit { // error: transparent struct needs at most one field + value: f32, // with non-trivial layout, but has 2 unit: U, } ``` diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index da2aa6db64503..dfaca159876d5 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -270,15 +270,17 @@ 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_layout = transparent {$desc} needs at most one field of non-zero size or alignment larger than 1, but has {$field_count} - .label = needs at most one field of non-zero size or alignment larger than 1, but has {$field_count} +hir_analysis_transparent_layout = transparent {$desc} needs at most one field with non-trivial layout, but has {$field_count} + .label = needs at most one field with non-trivial layout, but has {$field_count} .non_layout_labels = this field may have an alignment larger than 1 .non_zst_labels = this field is non-zero-sized + .note = a layout is trivial if, any only if, it is zero-sized with an alignment of 1 -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} - .label = needs at most one field of non-zero size or alignment larger than 1, but has {$field_count} +hir_analysis_transparent_layout_enum = the variant of a transparent {$desc} needs at most one field with non-trivial layout, but has {$field_count} + .label = needs at most one field with non-trivial layout, but has {$field_count} .non_layout_labels = this field may have an alignment larger than 1 .non_zst_labels = this field is non-zero-sized + .note = a layout is trivial if, any only if, it is zero-sized with an alignment of 1 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 93266c9f19e16..20d8d85cc3e51 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1130,8 +1130,8 @@ 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 with layout, with "known" - // respecting #[non_exhaustive] attributes. + // For each field, collect information on its layout and whether it has a #[non_exhaustive] + // annotation. 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); @@ -1178,6 +1178,13 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) (span, zst, layout, check_non_exhaustive(tcx, ty).break_value()) }); + // For a transparent ADT we must know which member field controls its layout. Hence, we only + // allow at most one field with non-trivial or unknown layout. A layout is trivial if, and only + // if, it is zero-sized with an alignment of 1. A layout is unknown if, for instance, it + // contains generic type parameters or has a #[non_exhaustive] annotation in a non-local type. + // We will reject any transparent ADT with more than one field with non-trivial or unknown + // layout, but we try our best to point out the violating properties. + let non_layout_fields = field_infos .clone() .filter_map(|(span, _, layout, _)| if layout.is_err() { Some(span) } else { None }); diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index f02e24c739af2..bce84dd69c72b 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -516,7 +516,7 @@ fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>, sp: Span, d }); } -/// Emit an error when encountering two or more fields without layout or non-zero size +/// Emit an error when encountering two or more fields with non-trivial layout /// in a transparent Adt. fn bad_transparent_layout<'tcx>( tcx: TyCtxt<'tcx>, @@ -533,6 +533,7 @@ fn bad_transparent_layout<'tcx>( non_zst_spans: non_zst_spans.collect(), field_count, desc: adt.descr(), + note: (), }); } else { tcx.sess.emit_err(errors::TransparentLayout { @@ -541,6 +542,7 @@ fn bad_transparent_layout<'tcx>( non_zst_spans: non_zst_spans.collect(), field_count, desc: adt.descr(), + note: (), }); } } diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index e2ff8da6e62af..53230f8e90b2f 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -788,6 +788,8 @@ pub(crate) struct TransparentLayoutEnum<'a> { pub non_zst_spans: Vec, pub field_count: usize, pub desc: &'a str, + #[note] + pub note: (), } #[derive(Diagnostic)] @@ -802,6 +804,8 @@ pub(crate) struct TransparentLayout<'a> { pub non_zst_spans: Vec, pub field_count: usize, pub desc: &'a str, + #[note] + pub note: (), } #[derive(Diagnostic)] diff --git a/tests/ui/repr/repr-transparent.rs b/tests/ui/repr/repr-transparent.rs index aab14c587cbdd..870d8a5d32424 100644 --- a/tests/ui/repr/repr-transparent.rs +++ b/tests/ui/repr/repr-transparent.rs @@ -24,14 +24,14 @@ struct ContainsZstAndNonZst((), [i32; 2]); #[repr(transparent)] struct MultipleNonZst(u8, u8); -//~^ ERROR needs at most one field of non-zero size or alignment larger than 1 +//~^ ERROR needs at most one field with non-trivial layout trait Mirror { type It: ?Sized; } impl Mirror for T { type It = Self; } #[repr(transparent)] pub struct StructWithProjection(f32, ::It); -//~^ ERROR needs at most one field of non-zero size or alignment larger than 1 +//~^ ERROR needs at most one field with non-trivial layout #[repr(transparent)] struct NontrivialAlignZst(u32, [u16; 0]); //~ ERROR alignment larger than 1 @@ -59,7 +59,7 @@ enum UnitFieldEnum { enum TooManyFieldsEnum { Foo(u32, String), } -//~^^^ ERROR needs at most one field of non-zero size or alignment larger than 1, but has 2 +//~^^^ ERROR needs at most one field with non-trivial layout, but has 2 #[repr(transparent)] enum MultipleVariants { //~ ERROR transparent enum needs exactly one variant, but has 2 @@ -87,6 +87,6 @@ union TooManyFields { u: u32, s: i32 } -//~^^^^ ERROR transparent union needs at most one field of non-zero size or alignment larger than 1, but has 2 +//~^^^^ ERROR transparent union needs at most one field with non-trivial layout, but has 2 fn main() {} diff --git a/tests/ui/repr/repr-transparent.stderr b/tests/ui/repr/repr-transparent.stderr index c16e5b27ec4fe..63086a61576a0 100644 --- a/tests/ui/repr/repr-transparent.stderr +++ b/tests/ui/repr/repr-transparent.stderr @@ -1,20 +1,24 @@ -error[E0690]: transparent struct needs at most one field of non-zero size or alignment larger than 1, but has 2 +error[E0690]: transparent struct needs at most one field with non-trivial layout, but has 2 --> $DIR/repr-transparent.rs:26:1 | LL | struct MultipleNonZst(u8, u8); | ^^^^^^^^^^^^^^^^^^^^^ -- -- this field is non-zero-sized | | | | | this field is non-zero-sized - | needs at most one field of non-zero size or alignment larger than 1, but has 2 + | needs at most one field with non-trivial layout, but has 2 + | + = note: a layout is trivial if, any only if, it is zero-sized with an alignment of 1 -error[E0690]: transparent struct needs at most one field of non-zero size or alignment larger than 1, but has 2 +error[E0690]: transparent struct needs at most one field with non-trivial layout, but has 2 --> $DIR/repr-transparent.rs:33:1 | LL | pub struct StructWithProjection(f32, ::It); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --- ------------------- this field is non-zero-sized | | | | | this field is non-zero-sized - | needs at most one field of non-zero size or alignment larger than 1, but has 2 + | needs at most one field with non-trivial layout, but has 2 + | + = note: a layout is trivial if, any only if, it is zero-sized with an alignment of 1 error[E0691]: zero-sized field in transparent struct has alignment larger than 1 --> $DIR/repr-transparent.rs:37:32 @@ -42,15 +46,17 @@ error[E0731]: transparent enum needs exactly one variant, but has 0 LL | enum Void {} | ^^^^^^^^^ needs exactly one variant, but has 0 -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 +error[E0690]: the variant of a transparent enum needs at most one field with non-trivial layout, but has 2 --> $DIR/repr-transparent.rs:59:1 | LL | enum TooManyFieldsEnum { - | ^^^^^^^^^^^^^^^^^^^^^^ needs at most one field of non-zero size or alignment larger than 1, but has 2 + | ^^^^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial layout, but has 2 LL | Foo(u32, String), | --- ------ this field is non-zero-sized | | | this field is non-zero-sized + | + = note: a layout is trivial if, any only if, it is zero-sized with an alignment of 1 error[E0731]: transparent enum needs exactly one variant, but has 2 --> $DIR/repr-transparent.rs:65:1 @@ -74,15 +80,17 @@ error[E0691]: zero-sized field in transparent enum has alignment larger than 1 LL | Foo { bar: ZstAlign32, baz: u32 } | ^^^^^^^^^^^^^^^^^^ has alignment of 32, which is larger than 1 -error[E0690]: transparent union needs at most one field of non-zero size or alignment larger than 1, but has 2 +error[E0690]: transparent union needs at most one field with non-trivial layout, but has 2 --> $DIR/repr-transparent.rs:86:1 | LL | union TooManyFields { - | ^^^^^^^^^^^^^^^^^^^ needs at most one field of non-zero size or alignment larger than 1, but has 2 + | ^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial layout, but has 2 LL | u: u32, | ------ this field is non-zero-sized LL | s: i32 | ------ this field is non-zero-sized + | + = note: a layout is trivial if, any only if, it is zero-sized with an alignment of 1 error: aborting due to 11 previous errors