Skip to content

Commit

Permalink
fix computing the dynamic alignment of packed structs with dyn trait …
Browse files Browse the repository at this point in the history
…tails
  • Loading branch information
RalfJung committed Dec 13, 2023
1 parent fe45ba6 commit 46d86aa
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 15 deletions.
27 changes: 12 additions & 15 deletions compiler/rustc_codegen_ssa/src/size_of_val.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,19 +100,18 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(

// # First compute the dynamic alignment

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

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

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

Expand Down
21 changes: 21 additions & 0 deletions src/tools/miri/tests/pass/packed-struct-dyn-trait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// run-pass
use std::ptr::addr_of;

// When the unsized tail is a `dyn Trait`, its alignments is only dynamically known. This means the
// packed(2) needs to be applied at runtime: the actual alignment of the field is `min(2,
// usual_alignment)`. Here we check that we do this right by comparing size, alignment, and field
// offset before and after unsizing.
fn main() {
#[repr(C, packed(2))]
struct Packed<T: ?Sized>(u8, core::mem::ManuallyDrop<T>);

let p = Packed(0, core::mem::ManuallyDrop::new(1));
let p: &Packed<usize> = &p;
let sized = (core::mem::size_of_val(p), core::mem::align_of_val(p));
let sized_offset = unsafe { addr_of!(p.1).cast::<u8>().offset_from(addr_of!(p.0)) };
let p: &Packed<dyn Send> = p;
let un_sized = (core::mem::size_of_val(p), core::mem::align_of_val(p));
let un_sized_offset = unsafe { addr_of!(p.1).cast::<u8>().offset_from(addr_of!(p.0)) };
assert_eq!(sized, un_sized);
assert_eq!(sized_offset, un_sized_offset);
}
21 changes: 21 additions & 0 deletions tests/ui/packed/dyn-trait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// run-pass
use std::ptr::addr_of;

// When the unsized tail is a `dyn Trait`, its alignments is only dynamically known. This means the
// packed(2) needs to be applied at runtime: the actual alignment of the field is `min(2,
// usual_alignment)`. Here we check that we do this right by comparing size, alignment, and field
// offset before and after unsizing.
fn main() {
#[repr(C, packed(2))]
struct Packed<T: ?Sized>(u8, core::mem::ManuallyDrop<T>);

let p = Packed(0, core::mem::ManuallyDrop::new(1));
let p: &Packed<usize> = &p;
let sized = (core::mem::size_of_val(p), core::mem::align_of_val(p));
let sized_offset = unsafe { addr_of!(p.1).cast::<u8>().offset_from(addr_of!(p.0)) };
let p: &Packed<dyn Send> = p;
let un_sized = (core::mem::size_of_val(p), core::mem::align_of_val(p));
let un_sized_offset = unsafe { addr_of!(p.1).cast::<u8>().offset_from(addr_of!(p.0)) };
assert_eq!(sized, un_sized);
assert_eq!(sized_offset, un_sized_offset);
}

0 comments on commit 46d86aa

Please sign in to comment.