Skip to content

Commit 5e6ea43

Browse files
committed
show packed alignment in mir_transform_unaligned_packed_ref
1 parent fd847d4 commit 5e6ea43

23 files changed

+155
-141
lines changed
Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
use rustc_abi::Align;
22
use rustc_middle::mir::*;
3-
use rustc_middle::ty::{self, TyCtxt};
3+
use rustc_middle::ty::{self, AdtDef, TyCtxt};
44
use tracing::debug;
55

6-
/// Returns `true` if this place is allowed to be less aligned
7-
/// than its containing struct (because it is within a packed
8-
/// struct).
9-
pub fn is_disaligned<'tcx, L>(
6+
/// If the place may be less aligned than its type requires
7+
/// (because it is in a packed type), returns the AdtDef
8+
/// and packed alignment of its most-unaligned projection.
9+
pub fn unalignment<'tcx, L>(
1010
tcx: TyCtxt<'tcx>,
1111
local_decls: &L,
1212
typing_env: ty::TypingEnv<'tcx>,
1313
place: Place<'tcx>,
14-
) -> bool
14+
) -> Option<(AdtDef<'tcx>, Align)>
1515
where
1616
L: HasLocalDecls<'tcx>,
1717
{
18-
debug!("is_disaligned({:?})", place);
19-
let Some(pack) = is_within_packed(tcx, local_decls, place) else {
20-
debug!("is_disaligned({:?}) - not within packed", place);
21-
return false;
18+
debug!("unalignment({:?})", place);
19+
let Some((descr, pack)) = most_packed_projection(tcx, local_decls, place) else {
20+
debug!("unalignment({:?}) - not within packed", place);
21+
return None;
2222
};
2323

2424
let ty = place.ty(local_decls, tcx).ty;
@@ -30,31 +30,31 @@ where
3030
|| matches!(unsized_tail().kind(), ty::Slice(..) | ty::Str)) =>
3131
{
3232
// If the packed alignment is greater or equal to the field alignment, the type won't be
33-
// further disaligned.
33+
// further unaligned.
3434
// However we need to ensure the field is sized; for unsized fields, `layout.align` is
3535
// just an approximation -- except when the unsized tail is a slice, where the alignment
3636
// is fully determined by the type.
3737
debug!(
38-
"is_disaligned({:?}) - align = {}, packed = {}; not disaligned",
38+
"unalignment({:?}) - align = {}, packed = {}; not unaligned",
3939
place,
4040
layout.align.bytes(),
4141
pack.bytes()
4242
);
43-
false
43+
None
4444
}
4545
_ => {
46-
// We cannot figure out the layout. Conservatively assume that this is disaligned.
47-
debug!("is_disaligned({:?}) - true", place);
48-
true
46+
// We cannot figure out the layout. Conservatively assume that this is unaligned.
47+
debug!("unalignment({:?}) - unaligned", place);
48+
Some((descr, pack))
4949
}
5050
}
5151
}
5252

53-
pub fn is_within_packed<'tcx, L>(
53+
pub fn most_packed_projection<'tcx, L>(
5454
tcx: TyCtxt<'tcx>,
5555
local_decls: &L,
5656
place: Place<'tcx>,
57-
) -> Option<Align>
57+
) -> Option<(AdtDef<'tcx>, Align)>
5858
where
5959
L: HasLocalDecls<'tcx>,
6060
{
@@ -65,9 +65,11 @@ where
6565
.take_while(|(_base, elem)| !matches!(elem, ProjectionElem::Deref))
6666
// Consider the packed alignments at play here...
6767
.filter_map(|(base, _elem)| {
68-
base.ty(local_decls, tcx).ty.ty_adt_def().and_then(|adt| adt.repr().pack)
68+
let adt = base.ty(local_decls, tcx).ty.ty_adt_def()?;
69+
let pack = adt.repr().pack?;
70+
Some((adt, pack))
6971
})
7072
// ... and compute their minimum.
7173
// The overall smallest alignment is what matters.
72-
.min()
74+
.min_by_key(|(_, align)| *align)
7375
}

compiler/rustc_const_eval/src/util/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ mod check_validity_requirement;
66
mod compare_types;
77
mod type_name;
88

9-
pub use self::alignment::{is_disaligned, is_within_packed};
9+
pub use self::alignment::{most_packed_projection, unalignment};
1010
pub use self::check_validity_requirement::check_validity_requirement;
1111
pub(crate) use self::check_validity_requirement::validate_scalar_in_layout;
1212
pub use self::compare_types::{relate_types, sub_types};

compiler/rustc_mir_transform/messages.ftl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ mir_transform_tail_expr_local = {$is_generated_name ->
6969
*[false] `{$name}` calls a custom destructor
7070
}
7171
72-
mir_transform_unaligned_packed_ref = reference to packed field is unaligned
73-
.note = packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
72+
mir_transform_unaligned_packed_ref = reference to field of packed {$ty_descr} is unaligned
73+
.note = this {$ty_descr} is at most {$align}-byte aligned, but the type of this field may require higher alignment
7474
.note_ub = creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
7575
.help = copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
7676

compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl<'tcx> crate::MirPass<'tcx> for AddMovesForPackedDrops {
5151

5252
match terminator.kind {
5353
TerminatorKind::Drop { place, .. }
54-
if util::is_disaligned(tcx, body, typing_env, place) =>
54+
if util::unalignment(tcx, body, typing_env, place).is_some() =>
5555
{
5656
add_move_for_packed_drop(
5757
tcx,

compiler/rustc_mir_transform/src/check_packed_ref.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> {
3737
}
3838

3939
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
40-
if context.is_borrow() && util::is_disaligned(self.tcx, self.body, self.typing_env, *place)
40+
if context.is_borrow()
41+
&& let Some((adt, pack)) =
42+
util::unalignment(self.tcx, self.body, self.typing_env, *place)
4143
{
4244
let def_id = self.body.source.instance.def_id();
4345
if let Some(impl_def_id) = self.tcx.trait_impl_of_assoc(def_id)
@@ -48,7 +50,11 @@ impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> {
4850
// shouldn't do.
4951
span_bug!(self.source_info.span, "builtin derive created an unaligned reference");
5052
} else {
51-
self.tcx.dcx().emit_err(errors::UnalignedPackedRef { span: self.source_info.span });
53+
self.tcx.dcx().emit_err(errors::UnalignedPackedRef {
54+
span: self.source_info.span,
55+
ty_descr: adt.descr(),
56+
align: pack.bytes(),
57+
});
5258
}
5359
}
5460
}

compiler/rustc_mir_transform/src/dead_store_elimination.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use rustc_mir_dataflow::impls::{
2323
};
2424

2525
use crate::simplify::UsedInStmtLocals;
26-
use crate::util::is_within_packed;
26+
use crate::util::most_packed_projection;
2727

2828
/// Performs the optimization on the body
2929
///
@@ -65,7 +65,7 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
6565
// the move may be codegened as a pointer to that field.
6666
// Using that disaligned pointer may trigger UB in the callee,
6767
// so do nothing.
68-
&& is_within_packed(tcx, body, place).is_none()
68+
&& most_packed_projection(tcx, body, place).is_none()
6969
{
7070
call_operands_to_move.push((bb, index));
7171
}

compiler/rustc_mir_transform/src/errors.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ pub(crate) enum ConstMutate {
9999
pub(crate) struct UnalignedPackedRef {
100100
#[primary_span]
101101
pub span: Span,
102+
pub ty_descr: &'static str,
103+
pub align: u64,
102104
}
103105

104106
#[derive(Diagnostic)]

compiler/rustc_mir_transform/src/validate.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use rustc_middle::{bug, span_bug};
2020
use rustc_mir_dataflow::debuginfo::debuginfo_locals;
2121
use rustc_trait_selection::traits::ObligationCtxt;
2222

23-
use crate::util::{self, is_within_packed};
23+
use crate::util::{self, most_packed_projection};
2424

2525
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
2626
enum EdgeKind {
@@ -409,7 +409,9 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
409409

410410
// The call destination place and Operand::Move place used as an argument might
411411
// be passed by a reference to the callee. Consequently they cannot be packed.
412-
if is_within_packed(self.tcx, &self.body.local_decls, destination).is_some() {
412+
if most_packed_projection(self.tcx, &self.body.local_decls, destination)
413+
.is_some()
414+
{
413415
// This is bad! The callee will expect the memory to be aligned.
414416
self.fail(
415417
location,
@@ -423,7 +425,9 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
423425

424426
for arg in args {
425427
if let Operand::Move(place) = &arg.node {
426-
if is_within_packed(self.tcx, &self.body.local_decls, *place).is_some() {
428+
if most_packed_projection(self.tcx, &self.body.local_decls, *place)
429+
.is_some()
430+
{
427431
// This is bad! The callee will expect the memory to be aligned.
428432
self.fail(
429433
location,

tests/ui/binding/issue-53114-safety-checks.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ fn let_wild_gets_unsafe_field() {
2020
let u1 = U { a: I(0) };
2121
let u2 = U { a: I(1) };
2222
let p = P { a: &2, b: &3 };
23-
let _ = &p.b; //~ ERROR reference to packed field
23+
let _ = &p.b; //~ ERROR reference to field of packed struct
2424
let _ = u1.a; //~ ERROR [E0133]
2525
let _ = &u2.a; //~ ERROR [E0133]
2626

2727
// variation on above with `_` in substructure
28-
let (_,) = (&p.b,); //~ ERROR reference to packed field
28+
let (_,) = (&p.b,); //~ ERROR reference to field of packed struct
2929
let (_,) = (u1.a,); //~ ERROR [E0133]
3030
let (_,) = (&u2.a,); //~ ERROR [E0133]
3131
}
@@ -34,12 +34,12 @@ fn let_ascribe_gets_unsafe_field() {
3434
let u1 = U { a: I(0) };
3535
let u2 = U { a: I(1) };
3636
let p = P { a: &2, b: &3 };
37-
let _: _ = &p.b; //~ ERROR reference to packed field
37+
let _: _ = &p.b; //~ ERROR reference to field of packed struct
3838
let _: _ = u1.a; //~ ERROR [E0133]
3939
let _: _ = &u2.a; //~ ERROR [E0133]
4040

4141
// variation on above with `_` in substructure
42-
let (_,): _ = (&p.b,); //~ ERROR reference to packed field
42+
let (_,): _ = (&p.b,); //~ ERROR reference to field of packed struct
4343
let (_,): _ = (u1.a,); //~ ERROR [E0133]
4444
let (_,): _ = (&u2.a,); //~ ERROR [E0133]
4545
}
@@ -48,12 +48,12 @@ fn match_unsafe_field_to_wild() {
4848
let u1 = U { a: I(0) };
4949
let u2 = U { a: I(1) };
5050
let p = P { a: &2, b: &3 };
51-
match &p.b { _ => { } } //~ ERROR reference to packed field
51+
match &p.b { _ => { } } //~ ERROR reference to field of packed struct
5252
match u1.a { _ => { } } //~ ERROR [E0133]
5353
match &u2.a { _ => { } } //~ ERROR [E0133]
5454

5555
// variation on above with `_` in substructure
56-
match (&p.b,) { (_,) => { } } //~ ERROR reference to packed field
56+
match (&p.b,) { (_,) => { } } //~ ERROR reference to field of packed struct
5757
match (u1.a,) { (_,) => { } } //~ ERROR [E0133]
5858
match (&u2.a,) { (_,) => { } } //~ ERROR [E0133]
5959
}

tests/ui/binding/issue-53114-safety-checks.stderr

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
error[E0793]: reference to packed field is unaligned
1+
error[E0793]: reference to field of packed struct is unaligned
22
--> $DIR/issue-53114-safety-checks.rs:23:13
33
|
44
LL | let _ = &p.b;
55
| ^^^^
66
|
7-
= note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
7+
= note: this struct is at most 1-byte aligned, but the type of this field may require higher alignment
88
= note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
99
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
1010

11-
error[E0793]: reference to packed field is unaligned
11+
error[E0793]: reference to field of packed struct is unaligned
1212
--> $DIR/issue-53114-safety-checks.rs:28:17
1313
|
1414
LL | let (_,) = (&p.b,);
1515
| ^^^^
1616
|
17-
= note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
17+
= note: this struct is at most 1-byte aligned, but the type of this field may require higher alignment
1818
= note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
1919
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
2020

@@ -50,23 +50,23 @@ LL | let (_,) = (&u2.a,);
5050
|
5151
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
5252

53-
error[E0793]: reference to packed field is unaligned
53+
error[E0793]: reference to field of packed struct is unaligned
5454
--> $DIR/issue-53114-safety-checks.rs:37:16
5555
|
5656
LL | let _: _ = &p.b;
5757
| ^^^^
5858
|
59-
= note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
59+
= note: this struct is at most 1-byte aligned, but the type of this field may require higher alignment
6060
= note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
6161
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
6262

63-
error[E0793]: reference to packed field is unaligned
63+
error[E0793]: reference to field of packed struct is unaligned
6464
--> $DIR/issue-53114-safety-checks.rs:42:20
6565
|
6666
LL | let (_,): _ = (&p.b,);
6767
| ^^^^
6868
|
69-
= note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
69+
= note: this struct is at most 1-byte aligned, but the type of this field may require higher alignment
7070
= note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
7171
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
7272

@@ -102,23 +102,23 @@ LL | let (_,): _ = (&u2.a,);
102102
|
103103
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
104104

105-
error[E0793]: reference to packed field is unaligned
105+
error[E0793]: reference to field of packed struct is unaligned
106106
--> $DIR/issue-53114-safety-checks.rs:51:11
107107
|
108108
LL | match &p.b { _ => { } }
109109
| ^^^^
110110
|
111-
= note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
111+
= note: this struct is at most 1-byte aligned, but the type of this field may require higher alignment
112112
= note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
113113
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
114114

115-
error[E0793]: reference to packed field is unaligned
115+
error[E0793]: reference to field of packed struct is unaligned
116116
--> $DIR/issue-53114-safety-checks.rs:56:12
117117
|
118118
LL | match (&p.b,) { (_,) => { } }
119119
| ^^^^
120120
|
121-
= note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
121+
= note: this struct is at most 1-byte aligned, but the type of this field may require higher alignment
122122
= note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
123123
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
124124

0 commit comments

Comments
 (0)