Skip to content

Commit 46d86aa

Browse files
committed
fix computing the dynamic alignment of packed structs with dyn trait tails
1 parent fe45ba6 commit 46d86aa

File tree

3 files changed

+54
-15
lines changed

3 files changed

+54
-15
lines changed

compiler/rustc_codegen_ssa/src/size_of_val.rs

+12-15
Original file line numberDiff line numberDiff line change
@@ -100,19 +100,18 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
100100

101101
// # First compute the dynamic alignment
102102

103-
// Packed type alignment would have to be capped, but their tails always have known alignment.
104-
// Therefore, their alignment has already been taken into account when computing `sized_align`
105-
// and `unsized_offset_unadjusted`, so no further adjustment is needed.
106-
if let ty::Adt(def, _) = t.kind() {
107-
if def.repr().packed() {
108-
let unsized_tail =
109-
bx.tcx().struct_tail_with_normalize(field_ty, |ty| ty, || {});
110-
assert!(matches!(unsized_tail.kind(), ty::Slice(..) | ty::Str));
111-
// Therefore we do not need to adjust anything.
112-
// It's not actually correct to say that the unsized field has this alignment, but the
113-
// code below works correctly if we set this and it avoids having to compute
114-
// the actual alignment (which is `unsized_align.min(packed)`).
115-
unsized_align = sized_align;
103+
// For packed types, we need to cap the alignment.
104+
if let ty::Adt(def, _) = t.kind()
105+
&& let Some(packed) = def.repr().pack
106+
{
107+
if packed.bytes() == 1 {
108+
// We know this will be capped to 1.
109+
unsized_align = bx.const_usize(1);
110+
} else {
111+
// We have to dynamically compute `min(unsized_align, packed)`.
112+
let packed = bx.const_usize(packed.bytes());
113+
let cmp = bx.icmp(IntPredicate::IntULT, unsized_align, packed);
114+
unsized_align = bx.select(cmp, unsized_align, packed);
116115
}
117116
}
118117

@@ -143,8 +142,6 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
143142
// let full_size = (offset + unsized_size).align_to(unsized_align).align_to(full_align);
144143
// Furthermore, `align >= unsized_align`, and therefore we only need to do:
145144
// let full_size = (offset + unsized_size).align_to(full_align);
146-
// This formula also has the advantage of working correctly when the type is packed
147-
// and we set `unsized_align = sized_align`.
148145

149146
let full_size = bx.add(unsized_offset_unadjusted, unsized_size);
150147

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// run-pass
2+
use std::ptr::addr_of;
3+
4+
// When the unsized tail is a `dyn Trait`, its alignments is only dynamically known. This means the
5+
// packed(2) needs to be applied at runtime: the actual alignment of the field is `min(2,
6+
// usual_alignment)`. Here we check that we do this right by comparing size, alignment, and field
7+
// offset before and after unsizing.
8+
fn main() {
9+
#[repr(C, packed(2))]
10+
struct Packed<T: ?Sized>(u8, core::mem::ManuallyDrop<T>);
11+
12+
let p = Packed(0, core::mem::ManuallyDrop::new(1));
13+
let p: &Packed<usize> = &p;
14+
let sized = (core::mem::size_of_val(p), core::mem::align_of_val(p));
15+
let sized_offset = unsafe { addr_of!(p.1).cast::<u8>().offset_from(addr_of!(p.0)) };
16+
let p: &Packed<dyn Send> = p;
17+
let un_sized = (core::mem::size_of_val(p), core::mem::align_of_val(p));
18+
let un_sized_offset = unsafe { addr_of!(p.1).cast::<u8>().offset_from(addr_of!(p.0)) };
19+
assert_eq!(sized, un_sized);
20+
assert_eq!(sized_offset, un_sized_offset);
21+
}

tests/ui/packed/dyn-trait.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// run-pass
2+
use std::ptr::addr_of;
3+
4+
// When the unsized tail is a `dyn Trait`, its alignments is only dynamically known. This means the
5+
// packed(2) needs to be applied at runtime: the actual alignment of the field is `min(2,
6+
// usual_alignment)`. Here we check that we do this right by comparing size, alignment, and field
7+
// offset before and after unsizing.
8+
fn main() {
9+
#[repr(C, packed(2))]
10+
struct Packed<T: ?Sized>(u8, core::mem::ManuallyDrop<T>);
11+
12+
let p = Packed(0, core::mem::ManuallyDrop::new(1));
13+
let p: &Packed<usize> = &p;
14+
let sized = (core::mem::size_of_val(p), core::mem::align_of_val(p));
15+
let sized_offset = unsafe { addr_of!(p.1).cast::<u8>().offset_from(addr_of!(p.0)) };
16+
let p: &Packed<dyn Send> = p;
17+
let un_sized = (core::mem::size_of_val(p), core::mem::align_of_val(p));
18+
let un_sized_offset = unsafe { addr_of!(p.1).cast::<u8>().offset_from(addr_of!(p.0)) };
19+
assert_eq!(sized, un_sized);
20+
assert_eq!(sized_offset, un_sized_offset);
21+
}

0 commit comments

Comments
 (0)