Is projecting to an align-1 field of a `repr(packed)` struct sound
Currently it's allowed to project to an align-1 field of a repr(packed) struct, and this seems to be fine because the compiler does not move the field's value before dropping it (playground):
use core::pin::Pin;
struct Bar(u8);
impl Bar {
fn log(self: Pin<&mut Self>) {
eprintln!("{self:p} {}", self.0);
}
}
impl Drop for Bar {
fn drop(&mut self) {
eprintln!("{self:p} {}", self.0);
}
}
pin_project_lite::pin_project! {
#[repr(packed)] struct Foo {
#[pin] bar: Bar,
}
}
fn main() {
let mut y = Box::pin(Foo { bar: Bar(5) });
y.as_mut().project().bar.log();
}
0x55e58b8b9b10 5
0x55e58b8b9b10 5
But, afaict this is not guaranteed anywhere, the only documentation I can find actually claims it does not happen with no carve-out for align-1 fields:
Unaligned values cannot be dropped in place, they must be copied to an aligned location first using ptr::read_unaligned. For packed structs, this move is done automatically by the compiler. This means the fields of packed structs are not dropped in-place. —https://doc.rust-lang.org/stable/std/ptr/fn.drop_in_place.html
-
The previous discussion was in https://github.com/taiki-e/pin-project/pull/34.
- pin-project has a test to check the behavior here
- Btw, this doesn't affected to pin-project (not lite), as said in https://github.com/taiki-e/pin-project/pull/34#issuecomment-688801015.
-
Relevant rustc code is
AddMovesForPackedDrops: This only adds moves ifis_disalignedis true. (is_disalignedis defined here, via an import here) -
Relevant section in the reference says:
For
packed, if the specified alignment is greater than the type’s alignment without the packed modifier, then the alignment and layout is unaffected.(I think it should say "greater than or equal to" instead of "greater than", as mentioned in the comment in
is_disaligned.)And, AFAIK, there is no mention about drop on packed types the reference https://doc.rust-lang.org/nightly/reference/type-layout.html#the-alignment-modifiers https://doc.rust-lang.org/nightly/reference/destructors.html.
I think we just need to get the lang-team to agree to guarantee this, and add a test to rust-lang/rust and fix the documentation. It is obviously inefficient to bother adding moves if aligned, so I don't think they would disagree to guarantee this.
cc @RalfJung @Aaron1011
See also https://github.com/rust-lang/rust/issues/143411
@theemathas I think your issue is bug in the is_disaligned=true case, and our issue is about is_disaligned=false case.
I agree for Foo it would make sense to guarantee this, since all its fields are unaffected by the pack attribute.
But more generally, if some fields are affected and some are not, I don't know if we'll then just move the affected fields or all of them.
Right, only the case where all fields are align-1 is supported by pin-project-lite, if any field has higher alignment then it will cause a compile-error.
It could come up in the future if support for by-value projected fields was ever added
pin_project_lite::pin_project! {
#[repr(packed)] struct Foo {
#[pin] bar: Bar,
#[by_value] x: u32,
}
}
becoming something like
struct FooProjected {
bar: Pin<&mut Bar>,
x: u32,
}
but I don't think I've seen something like that proposed.