From 19d65099c01747a93de9cda213f344e46ecc6282 Mon Sep 17 00:00:00 2001 From: David Rheinsberg Date: Fri, 21 Jul 2023 09:32:28 +0200 Subject: [PATCH] error/E0691: include alignment in error message Include the computed alignment of the violating field when rejecting transparent types with non-trivially aligned ZSTs. ZST member fields in transparent types must have an alignment of 1 (to ensure it does not raise the layout requirements of the transparent field). The current error message looks like this: LL | struct Foobar(u32, [u32; 0]); | ^^^^^^^^ has alignment larger than 1 This patch changes the report to include the alignment of the violating field: LL | struct Foobar(u32, [u32; 0]); | ^^^^^^^^ has alignment of 4, which is larger than 1 In case of unknown alignments, it will yield: LL | struct Foobar(u32, [u32; 0]); | ^^^^^^^^ may have alignment larger than 1 This allows developers to get a better grasp why a specific field is rejected. Knowing the alignment of the violating field makes it easier to judge where that alignment-requirement originates, and thus hopefully provide better hints on how to mitigate the problem. This idea was proposed in 2022 in #98071 as part of a bigger change. This commit simply extracts this error-message change, to decouple it from the other diagnostic improvements. --- .../src/error_codes/E0691.md | 3 ++- .../rustc_hir_analysis/src/check/check.rs | 26 ++++++++++++------- tests/ui/repr/repr-transparent.stderr | 8 +++--- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0691.md b/compiler/rustc_error_codes/src/error_codes/E0691.md index 60060cacbd60b..92a305a806a67 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0691.md +++ b/compiler/rustc_error_codes/src/error_codes/E0691.md @@ -11,7 +11,8 @@ struct ForceAlign32; #[repr(transparent)] struct Wrapper(f32, ForceAlign32); // error: zero-sized field in transparent - // struct has alignment larger than 1 + // struct has alignment of 4, which + // is larger than 1 ``` A transparent struct, enum, or union is supposed to be represented exactly like diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index d9e14096954b0..f763f8f43a497 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1078,9 +1078,9 @@ 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 align1 = layout.is_ok_and(|layout| layout.align.abi.bytes() == 1); + let align = layout.ok().map(|layout| layout.align.abi.bytes()); if !zst { - return (span, zst, align1, None); + return (span, zst, align, None); } fn check_non_exhaustive<'tcx>( @@ -1115,12 +1115,12 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) } } - (span, zst, align1, check_non_exhaustive(tcx, ty).break_value()) + (span, zst, align, check_non_exhaustive(tcx, ty).break_value()) }); let non_zst_fields = field_infos .clone() - .filter_map(|(span, zst, _align1, _non_exhaustive)| if !zst { Some(span) } else { None }); + .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())); @@ -1128,17 +1128,23 @@ 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, align1, non_exhaustive) in field_infos { - if zst && !align1 { - struct_span_err!( + 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(), - ) - .span_label(span, "has alignment larger than 1") - .emit(); + ); + + 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( diff --git a/tests/ui/repr/repr-transparent.stderr b/tests/ui/repr/repr-transparent.stderr index cb1e233777656..445aef580294f 100644 --- a/tests/ui/repr/repr-transparent.stderr +++ b/tests/ui/repr/repr-transparent.stderr @@ -20,13 +20,13 @@ error[E0691]: zero-sized field in transparent struct has alignment larger than 1 --> $DIR/repr-transparent.rs:36:32 | LL | struct NontrivialAlignZst(u32, [u16; 0]); - | ^^^^^^^^ has alignment larger than 1 + | ^^^^^^^^ 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 | LL | struct GenericAlign(ZstAlign32, u32); - | ^^^^^^^^^^^^^ has alignment larger than 1 + | ^^^^^^^^^^^^^ has alignment of 4, which is larger than 1 error[E0084]: unsupported representation for zero-variant enum --> $DIR/repr-transparent.rs:44:1 @@ -66,13 +66,13 @@ error[E0691]: zero-sized field in transparent enum has alignment larger than 1 --> $DIR/repr-transparent.rs:71:14 | LL | Foo(u32, [u16; 0]), - | ^^^^^^^^ has alignment larger than 1 + | ^^^^^^^^ 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 | LL | Foo { bar: ZstAlign32, baz: u32 } - | ^^^^^^^^^^^^^^^^^^ has alignment larger than 1 + | ^^^^^^^^^^^^^^^^^^ has alignment of 4, 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