diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 9ed083d16003c..ba4630c8b80a5 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1633,6 +1633,16 @@ fn check_scalable_vector(tcx: TyCtxt<'_>, span: Span, def_id: LocalDefId, scalab pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) { let repr = def.repr(); if repr.packed() { + // `#[pin_v2]` on a packed type is unsound: drop glue for a packed type moves an + // over-aligned field to an aligned location before running its destructor, which would + // move a structurally pinned field out from under a `Pin<&mut _>` that was handed out. + if def.is_pin_project() { + tcx.dcx().emit_err(errors::PinV2OnPacked { + span: sp, + pin_v2_span: find_attr!(tcx, def.did(), PinV2(span) => *span), + adt_name: tcx.item_name(def.did()), + }); + } if let Some(reprs) = find_attr!(tcx, def.did(), Repr { reprs, .. } => reprs) { for (r, _) in reprs { if let ReprPacked(pack) = r diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index b968a626d398f..e7518e46e3654 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -2035,3 +2035,16 @@ pub(crate) struct PinV2WithoutPinDrop { pub pin_v2_span: Option, pub adt_name: Symbol, } + +#[derive(Diagnostic)] +#[diag("`#[pin_v2]` types may not have `#[repr(packed)]`")] +#[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" +)] +pub(crate) struct PinV2OnPacked { + #[primary_span] + pub span: Span, + #[note("`{$adt_name}` is marked `#[pin_v2]` here")] + pub pin_v2_span: Option, + pub adt_name: Symbol, +} diff --git a/tests/ui/pin-ergonomics/pin_v2-packed.rs b/tests/ui/pin-ergonomics/pin_v2-packed.rs new file mode 100644 index 0000000000000..b10c7565de0c9 --- /dev/null +++ b/tests/ui/pin-ergonomics/pin_v2-packed.rs @@ -0,0 +1,42 @@ +//! `#[pin_v2]` is not allowed on `#[repr(packed)]` types. +//! +//! Drop glue for a packed type moves an over-aligned field to an aligned location before running +//! its destructor. That move carries along any structurally pinned leaf, so a value handed out as +//! `Pin<&mut _>` would be moved before it is dropped, violating `Pin`'s invariant. +//! +//! Regression test for . + +#![feature(pin_ergonomics)] +#![allow(incomplete_features)] + +use std::marker::PhantomPinned; + +#[pin_v2] +#[repr(packed)] +struct Packed { //~ ERROR `#[pin_v2]` types may not have `#[repr(packed)]` + field: PhantomPinned, +} + +// The generic case from the issue: alignment of `T` is unknown at definition time, so this is +// rejected regardless of how it is later monomorphized. +#[pin_v2] +#[repr(C, packed(4))] +struct PackedN(T); +//~^ ERROR `#[pin_v2]` types may not have `#[repr(packed)]` + +#[pin_v2] +#[repr(packed)] +union PackedUnion { //~ ERROR `#[pin_v2]` types may not have `#[repr(packed)]` + field: (), +} + +// Allowed: `#[pin_v2]` without `#[repr(packed)]` still compiles. +#[pin_v2] +#[repr(C)] +struct Unpacked(T); + +// Allowed: `#[repr(packed)]` without `#[pin_v2]` is unaffected by the ban. +#[repr(packed)] +struct PackedNoPin(u8); + +fn main() {} diff --git a/tests/ui/pin-ergonomics/pin_v2-packed.stderr b/tests/ui/pin-ergonomics/pin_v2-packed.stderr new file mode 100644 index 0000000000000..975b74e419687 --- /dev/null +++ b/tests/ui/pin-ergonomics/pin_v2-packed.stderr @@ -0,0 +1,41 @@ +error: `#[pin_v2]` types may not have `#[repr(packed)]` + --> $DIR/pin_v2-packed.rs:16:1 + | +LL | struct Packed { + | ^^^^^^^^^^^^^ + | + = 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 +note: `Packed` is marked `#[pin_v2]` here + --> $DIR/pin_v2-packed.rs:14:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + +error: `#[pin_v2]` types may not have `#[repr(packed)]` + --> $DIR/pin_v2-packed.rs:24:1 + | +LL | struct PackedN(T); + | ^^^^^^^^^^^^^^^^^ + | + = 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 +note: `PackedN` is marked `#[pin_v2]` here + --> $DIR/pin_v2-packed.rs:22:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + +error: `#[pin_v2]` types may not have `#[repr(packed)]` + --> $DIR/pin_v2-packed.rs:29:1 + | +LL | union PackedUnion { + | ^^^^^^^^^^^^^^^^^ + | + = 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 +note: `PackedUnion` is marked `#[pin_v2]` here + --> $DIR/pin_v2-packed.rs:27:1 + | +LL | #[pin_v2] + | ^^^^^^^^^ + +error: aborting due to 3 previous errors +