Rollup merge of #113913 - dvdhrm:pr/transpalign, r=jackh726

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:

```text
 LL | struct Foobar(u32, [u32; 0]);
    |                    ^^^^^^^^ has alignment larger than 1
```

This patch changes the report to include the alignment of the violating field:

```text
 LL | struct Foobar(u32, [u32; 0]);
    |                    ^^^^^^^^ has alignment of 4, which is larger than 1
```

In case of unknown alignments, it will yield:

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

(Originally proposed by `@compiler-errors` in #98071)
This commit is contained in:
Matthias Krüger 2023-07-21 17:17:42 +02:00 committed by GitHub
commit 4a90553717
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 25 additions and 15 deletions

View File

@ -11,7 +11,8 @@ struct ForceAlign32;
#[repr(transparent)] #[repr(transparent)]
struct Wrapper(f32, ForceAlign32); // error: zero-sized field in transparent struct Wrapper(f32, ForceAlign32); // error: zero-sized field in transparent
// struct has alignment larger than 1 // struct has alignment of 32, which
// is larger than 1
``` ```
A transparent struct, enum, or union is supposed to be represented exactly like A transparent struct, enum, or union is supposed to be represented exactly like

View File

@ -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 // 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 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 { if !zst {
return (span, zst, align1, None); return (span, zst, align, None);
} }
fn check_non_exhaustive<'tcx>( fn check_non_exhaustive<'tcx>(
@ -1115,12 +1115,12 @@ fn check_non_exhaustive<'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 let non_zst_fields = field_infos
.clone() .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(); let non_zst_count = non_zst_fields.clone().count();
if non_zst_count >= 2 { if non_zst_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_zst_count, non_zst_fields, tcx.def_span(adt.did()));
@ -1128,17 +1128,26 @@ fn check_non_exhaustive<'tcx>(
let incompatible_zst_fields = let incompatible_zst_fields =
field_infos.clone().filter(|(_, _, _, opt)| opt.is_some()).count(); 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_zst_count >= 2 && non_zst_count < 2;
for (span, zst, align1, non_exhaustive) in field_infos { for (span, zst, align, non_exhaustive) in field_infos {
if zst && !align1 { if zst && align != Some(1) {
struct_span_err!( let mut err = struct_span_err!(
tcx.sess, tcx.sess,
span, span,
E0691, E0691,
"zero-sized field in transparent {} has alignment larger than 1", "zero-sized field in transparent {} has alignment larger than 1",
adt.descr(), 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 { if incompat && let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive {
tcx.struct_span_lint_hir( tcx.struct_span_lint_hir(

View File

@ -20,13 +20,13 @@ error[E0691]: zero-sized field in transparent struct has alignment larger than 1
--> $DIR/repr-transparent.rs:36:32 --> $DIR/repr-transparent.rs:36:32
| |
LL | struct NontrivialAlignZst(u32, [u16; 0]); 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 error[E0691]: zero-sized field in transparent struct has alignment larger than 1
--> $DIR/repr-transparent.rs:42:24 --> $DIR/repr-transparent.rs:42:24
| |
LL | struct GenericAlign<T>(ZstAlign32<T>, u32); LL | struct GenericAlign<T>(ZstAlign32<T>, u32);
| ^^^^^^^^^^^^^ has alignment larger than 1 | ^^^^^^^^^^^^^ has alignment of 32, which is larger than 1
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: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 --> $DIR/repr-transparent.rs:71:14
| |
LL | Foo(u32, [u16; 0]), 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 error[E0691]: zero-sized field in transparent enum has alignment larger than 1
--> $DIR/repr-transparent.rs:76:11 --> $DIR/repr-transparent.rs:76:11
| |
LL | Foo { bar: ZstAlign32<T>, baz: u32 } LL | Foo { bar: ZstAlign32<T>, baz: u32 }
| ^^^^^^^^^^^^^^^^^^ has alignment larger than 1 | ^^^^^^^^^^^^^^^^^^ has alignment of 32, which is larger than 1
error[E0690]: transparent union needs at most one non-zero-sized field, but has 2 error[E0690]: transparent union needs at most one non-zero-sized field, but has 2
--> $DIR/repr-transparent.rs:85:1 --> $DIR/repr-transparent.rs:85:1