Closed
Description
This code panics, but it should not:
use std::mem;
struct Slice([u32]);
#[repr(packed, C)]
struct PackedSized {
f: u8,
d: [u32; 4],
}
#[repr(packed, C)]
struct PackedUnsized {
f: u8,
d: Slice,
}
impl PackedSized {
fn unsize(&self) -> &PackedUnsized {
// We can't unsize via a generic type since then we get the error
// that packed structs with unsized tail don't work if the tail
// might need dropping.
let len = 4usize;
unsafe { mem::transmute((self, len)) }
}
}
fn main() { unsafe {
let p = PackedSized { f: 0, d: [1, 2, 3, 4] };
let p = p.unsize() as *const PackedUnsized;
let d = std::ptr::addr_of!((*p).d);
assert_eq!(d.cast::<u32>().read_unaligned(), 1);
} }
The panic disappears when the Newtype
is inlined (because then we hit a different code path in this match).
What happens here is a bug in the logic that computes the offset of d
: since it is an unsized field, the offset is computed dynamically, by taking the statically known offset and then rounding it up to the dynamic alignment of the field. However, since the struct is packed, we need to cap the dynamic alignment of the field to the packing of the surrounding struct -- which we currently do not do. This is just yet another case where the non-compositional nature of repr(packed)
is causing issues...