Skip to content

Commit e43ec0c

Browse files
authored
Rollup merge of #157542 - Dnreikronos:pin_v2_ban_repr_packed, r=mejrs
Reject `#[repr(packed)]` on `#[pin_v2]` types Fixes #157011 `#[repr(packed)]` can store an over-aligned field below its alignment, and the drop glue for a packed type moves that field to a properly aligned spot before dropping it. When the field is structurally pinned, that move pulls it out from under a `Pin<&mut _>` we already handed out, which breaks the pin invariant. The repro in the issue makes it pretty clear: it prints one address during pinned access and a different one on drop. So the fix just rejects the combo at the type definition. If a type is both `#[pin_v2]` and `#[repr(packed)]`, it no longer compiles. The check sits in `check_packed` in `rustc_hir_analysis`, so it catches the concrete case and the generic one too (like `One<T>`, where we don't know `T`'s alignment yet), with no layout or monomorphization needed. One thing I want to be upfront about: this is necessary but don't solve the problem entirely. Sure, it stops the spelling the issue used, but you can still trigger the same move-on-drop through an explicit `&pin mut` / `ref pin mut` projection with no `#[pin_v2]` anywhere. Leaving that broader case open is intentional, imo the narrow ban is the right call for now, and the leftover stays tracked on the pin ergonomics tracking issue #130494. This is the direction we landed on with `@workingjubilee` over on Zulip, lgtm from their side: https://rust-lang.zulipchat.com/#narrow/channel/182449-t-compiler.2Fhelp/topic/.60pin_v2.60.20is.20unsound.20with.20.60packed.60/with/600522893 Tests live in `tests/ui/pin-ergonomics/pin_v2-packed.rs`: the three rejected shapes (packed struct, generic `packed(4)` struct, packed union), plus two controls that still compile fine, `#[pin_v2]` without packed and packed without `#[pin_v2]`. *Disclosure: AI tooling was used on the code changes, and everything was strictly validated by me before sending to remote.*
2 parents 2c65872 + 9663384 commit e43ec0c

4 files changed

Lines changed: 106 additions & 0 deletions

File tree

compiler/rustc_hir_analysis/src/check/check.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1635,6 +1635,16 @@ fn check_scalable_vector(tcx: TyCtxt<'_>, span: Span, def_id: LocalDefId, scalab
16351635
pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) {
16361636
let repr = def.repr();
16371637
if repr.packed() {
1638+
// `#[pin_v2]` on a packed type is unsound: drop glue for a packed type moves an
1639+
// over-aligned field to an aligned location before running its destructor, which would
1640+
// move a structurally pinned field out from under a `Pin<&mut _>` that was handed out.
1641+
if def.is_pin_project() {
1642+
tcx.dcx().emit_err(errors::PinV2OnPacked {
1643+
span: sp,
1644+
pin_v2_span: find_attr!(tcx, def.did(), PinV2(span) => *span),
1645+
adt_name: tcx.item_name(def.did()),
1646+
});
1647+
}
16381648
if let Some(reprs) = find_attr!(tcx, def.did(), Repr { reprs, .. } => reprs) {
16391649
for (r, _) in reprs {
16401650
if let ReprPacked(pack) = r

compiler/rustc_hir_analysis/src/errors.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2035,3 +2035,16 @@ pub(crate) struct PinV2WithoutPinDrop {
20352035
pub pin_v2_span: Option<Span>,
20362036
pub adt_name: Symbol,
20372037
}
2038+
2039+
#[derive(Diagnostic)]
2040+
#[diag("`#[pin_v2]` types may not have `#[repr(packed)]`")]
2041+
#[note(
2042+
"fields of a `#[repr(packed)]` type can be under-aligned, so a structurally pinned field may be moved to a properly aligned location, which `Pin` does not allow"
2043+
)]
2044+
pub(crate) struct PinV2OnPacked {
2045+
#[primary_span]
2046+
pub span: Span,
2047+
#[note("`{$adt_name}` is marked `#[pin_v2]` here")]
2048+
pub pin_v2_span: Option<Span>,
2049+
pub adt_name: Symbol,
2050+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//! `#[pin_v2]` is not allowed on `#[repr(packed)]` types.
2+
//!
3+
//! Drop glue for a packed type moves an over-aligned field to an aligned location before running
4+
//! its destructor. That move carries along any structurally pinned leaf, so a value handed out as
5+
//! `Pin<&mut _>` would be moved before it is dropped, violating `Pin`'s invariant.
6+
//!
7+
//! Regression test for <https://github.com/rust-lang/rust/issues/157011>.
8+
9+
#![feature(pin_ergonomics)]
10+
#![allow(incomplete_features)]
11+
12+
use std::marker::PhantomPinned;
13+
14+
#[pin_v2]
15+
#[repr(packed)]
16+
struct Packed { //~ ERROR `#[pin_v2]` types may not have `#[repr(packed)]`
17+
field: PhantomPinned,
18+
}
19+
20+
// The generic case from the issue: alignment of `T` is unknown at definition time, so this is
21+
// rejected regardless of how it is later monomorphized.
22+
#[pin_v2]
23+
#[repr(C, packed(4))]
24+
struct PackedN<T>(T);
25+
//~^ ERROR `#[pin_v2]` types may not have `#[repr(packed)]`
26+
27+
#[pin_v2]
28+
#[repr(packed)]
29+
union PackedUnion { //~ ERROR `#[pin_v2]` types may not have `#[repr(packed)]`
30+
field: (),
31+
}
32+
33+
// Allowed: `#[pin_v2]` without `#[repr(packed)]` still compiles.
34+
#[pin_v2]
35+
#[repr(C)]
36+
struct Unpacked<T>(T);
37+
38+
// Allowed: `#[repr(packed)]` without `#[pin_v2]` is unaffected by the ban.
39+
#[repr(packed)]
40+
struct PackedNoPin(u8);
41+
42+
fn main() {}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
error: `#[pin_v2]` types may not have `#[repr(packed)]`
2+
--> $DIR/pin_v2-packed.rs:16:1
3+
|
4+
LL | struct Packed {
5+
| ^^^^^^^^^^^^^
6+
|
7+
= note: fields of a `#[repr(packed)]` type can be under-aligned, so a structurally pinned field may be moved to a properly aligned location, which `Pin` does not allow
8+
note: `Packed` is marked `#[pin_v2]` here
9+
--> $DIR/pin_v2-packed.rs:14:1
10+
|
11+
LL | #[pin_v2]
12+
| ^^^^^^^^^
13+
14+
error: `#[pin_v2]` types may not have `#[repr(packed)]`
15+
--> $DIR/pin_v2-packed.rs:24:1
16+
|
17+
LL | struct PackedN<T>(T);
18+
| ^^^^^^^^^^^^^^^^^
19+
|
20+
= note: fields of a `#[repr(packed)]` type can be under-aligned, so a structurally pinned field may be moved to a properly aligned location, which `Pin` does not allow
21+
note: `PackedN` is marked `#[pin_v2]` here
22+
--> $DIR/pin_v2-packed.rs:22:1
23+
|
24+
LL | #[pin_v2]
25+
| ^^^^^^^^^
26+
27+
error: `#[pin_v2]` types may not have `#[repr(packed)]`
28+
--> $DIR/pin_v2-packed.rs:29:1
29+
|
30+
LL | union PackedUnion {
31+
| ^^^^^^^^^^^^^^^^^
32+
|
33+
= note: fields of a `#[repr(packed)]` type can be under-aligned, so a structurally pinned field may be moved to a properly aligned location, which `Pin` does not allow
34+
note: `PackedUnion` is marked `#[pin_v2]` here
35+
--> $DIR/pin_v2-packed.rs:27:1
36+
|
37+
LL | #[pin_v2]
38+
| ^^^^^^^^^
39+
40+
error: aborting due to 3 previous errors
41+

0 commit comments

Comments
 (0)