Skip to content

Commit

Permalink
error/E0691: include alignment in error message
Browse files Browse the repository at this point in the history
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<T>(u32, [T; 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 rust-lang#98071 as part of a bigger change.
This commit simply extracts this error-message change, to decouple it
from the other diagnostic improvements.
  • Loading branch information
dvdhrm committed Jul 21, 2023
1 parent 1a44b45 commit b0dadff
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 15 deletions.
3 changes: 2 additions & 1 deletion compiler/rustc_error_codes/src/error_codes/E0691.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 32, which
// is larger than 1
```

A transparent struct, enum, or union is supposed to be represented exactly like
Expand Down
29 changes: 19 additions & 10 deletions compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>(
Expand Down Expand Up @@ -1115,30 +1115,39 @@ 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()));
}
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(
Expand Down
8 changes: 4 additions & 4 deletions tests/ui/repr/repr-transparent.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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<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
--> $DIR/repr-transparent.rs:44:1
Expand Down Expand Up @@ -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<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
--> $DIR/repr-transparent.rs:85:1
Expand Down

0 comments on commit b0dadff

Please sign in to comment.