From 965d40f8207806834e97dbaa718162a7da8e483d Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Thu, 30 Apr 2026 18:29:49 +0200 Subject: [PATCH] Gate direct pin-borrow ADT diagnostic on !Unpin --- compiler/rustc_hir_typeck/src/errors.rs | 2 +- compiler/rustc_hir_typeck/src/expr.rs | 27 ++++- .../direct-borrow-requires-pin-v2.rs | 78 ++++++++++++-- .../direct-borrow-requires-pin-v2.stderr | 100 +++++++++++++++--- 4 files changed, 183 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 5959f57128ae0..369d963cfdece 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -1386,7 +1386,7 @@ pub(crate) struct ProjectOnNonPinProjectType { } #[derive(Diagnostic)] -#[diag("cannot directly pin an ADT that is not `#[pin_v2]`")] +#[diag("cannot directly pin a type that is not structurally pinnable")] pub(crate) struct DirectPinBorrowOfNonPinProjectType { #[primary_span] pub span: Span, diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 890c0fdde085e..5f370cf68d6b1 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -529,10 +529,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pin_borrow_of_adt_requires_pin_v2(&self, ty: Ty<'tcx>, span: Span) { let ty = self.structurally_resolve_type(span, ty); - if let Some(adt) = ty.ty_adt_def() - && !adt.is_pin_project() - { - let def_span = self.tcx.hir_span_if_local(adt.did()); + if self.direct_pin_borrow_requires_pin_v2(ty, span) { + let def_span = ty.ty_adt_def().and_then(|adt| self.tcx.hir_span_if_local(adt.did())); let sugg_span = def_span.map(|span| span.shrink_to_lo()); self.dcx().emit_err(crate::errors::DirectPinBorrowOfNonPinProjectType { span, @@ -542,6 +540,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn direct_pin_borrow_requires_pin_v2(&self, ty: Ty<'tcx>, span: Span) -> bool { + if ty.references_error() { + return false; + } + + // Manual `Unpin` impls do not prove that pinned projections through a generic ADT + // are safe, so keep those cases aligned with pinned-pattern projection checks. + match ty.kind() { + ty::Adt(adt, args) if adt.is_pin_project() || adt.is_pin() => false, + ty::Adt(_, args) if args.types().next().is_some() => true, + ty::Adt(..) => !self.type_known_to_be_unpin(ty, span), + ty::Param(..) => !self.type_known_to_be_unpin(ty, span), + _ => ty.has_param() || ty.has_infer() || ty.has_aliases() || ty.has_placeholders(), + } + } + + fn type_known_to_be_unpin(&self, ty: Ty<'tcx>, span: Span) -> bool { + let unpin_def_id = self.tcx.require_lang_item(LangItem::Unpin, span); + self.type_implements_trait(unpin_def_id, [ty], self.param_env).must_apply_modulo_regions() + } + /// Does this expression refer to a place that either: /// * Is based on a local or static. /// * Contains a dereference diff --git a/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.rs b/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.rs index 4d5ab1630ea9a..53ebd236c357c 100644 --- a/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.rs +++ b/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.rs @@ -1,17 +1,83 @@ #![feature(pin_ergonomics)] -#![allow(incomplete_features)] +#![allow(dead_code, incomplete_features)] //@ normalize-stderr: "\n\n\z" -> "\n" -struct NotPinV2; +use std::marker::PhantomPinned; +use std::pin::Pin; -fn direct_pin_mut(mut value: NotPinV2) { +struct UnpinAdt; + +struct NotUnpin { + _pin: PhantomPinned, +} + +#[pin_v2] +struct PinV2NotUnpin { + _pin: PhantomPinned, +} + +struct GenericAdt { + x: T, +} + +impl Unpin for GenericAdt {} + +struct DropGenericAdt(T); + +impl Drop for DropGenericAdt { + fn drop(&mut self) {} +} + +fn direct_pin_mut_unpin() { + let _: Pin<&mut _> = &pin mut UnpinAdt; +} + +fn direct_pin_const_unpin() { + let _: Pin<&_> = &pin const UnpinAdt; +} + +fn direct_pin_mut_not_unpin() { + let _ = &pin mut NotUnpin { _pin: PhantomPinned }; + //~^ ERROR cannot directly pin a type that is not structurally pinnable +} + +fn direct_pin_const_not_unpin() { + let _ = &pin const NotUnpin { _pin: PhantomPinned }; + //~^ ERROR cannot directly pin a type that is not structurally pinnable +} + +fn direct_pin_mut_pin_v2_not_unpin() { + let _: Pin<&mut _> = &pin mut PinV2NotUnpin { _pin: PhantomPinned }; +} + +fn direct_pin_const_pin_v2_not_unpin() { + let _: Pin<&_> = &pin const PinV2NotUnpin { _pin: PhantomPinned }; +} + +fn direct_pin_mut_generic(mut value: GenericAdt) { let _ = &pin mut value; - //~^ ERROR cannot directly pin an ADT that is not `#[pin_v2]` + //~^ ERROR cannot directly pin a type that is not structurally pinnable } -fn direct_pin_const(value: NotPinV2) { +fn direct_pin_const_generic(value: GenericAdt) { let _ = &pin const value; - //~^ ERROR cannot directly pin an ADT that is not `#[pin_v2]` + //~^ ERROR cannot directly pin a type that is not structurally pinnable +} + +pub fn unsound_pin_borrow(input: &pin mut GenericAdt) -> &pin mut T { + &pin mut input.x + //~^ ERROR cannot directly pin a type that is not structurally pinnable +} + +fn direct_pin_mut_manual_unpin_generic() { + let mut value = GenericAdt { x: NotUnpin { _pin: PhantomPinned } }; + let _ = &pin mut value; + //~^ ERROR cannot directly pin a type that is not structurally pinnable +} + +fn direct_pin_mut_drop_generic(mut input: DropGenericAdt) { + let _: &pin mut _ = &pin mut input; + //~^ ERROR cannot directly pin a type that is not structurally pinnable } fn main() {} diff --git a/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.stderr b/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.stderr index 1e82a18f4a12c..342e5be183b87 100644 --- a/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.stderr +++ b/tests/ui/pin-ergonomics/direct-borrow-requires-pin-v2.stderr @@ -1,35 +1,109 @@ -error: cannot directly pin an ADT that is not `#[pin_v2]` - --> $DIR/direct-borrow-requires-pin-v2.rs:8:13 +error: cannot directly pin a type that is not structurally pinnable + --> $DIR/direct-borrow-requires-pin-v2.rs:40:13 + | +LL | let _ = &pin mut NotUnpin { _pin: PhantomPinned }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/direct-borrow-requires-pin-v2.rs:10:1 + | +LL | struct NotUnpin { + | ^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct NotUnpin { + | + +error: cannot directly pin a type that is not structurally pinnable + --> $DIR/direct-borrow-requires-pin-v2.rs:45:13 + | +LL | let _ = &pin const NotUnpin { _pin: PhantomPinned }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/direct-borrow-requires-pin-v2.rs:10:1 + | +LL | struct NotUnpin { + | ^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct NotUnpin { + | + +error: cannot directly pin a type that is not structurally pinnable + --> $DIR/direct-borrow-requires-pin-v2.rs:58:13 | LL | let _ = &pin mut value; | ^^^^^^^^^^^^^^ | note: type defined here - --> $DIR/direct-borrow-requires-pin-v2.rs:5:1 + --> $DIR/direct-borrow-requires-pin-v2.rs:19:1 | -LL | struct NotPinV2; - | ^^^^^^^^^^^^^^^ +LL | struct GenericAdt { + | ^^^^^^^^^^^^^^^^^^^^ help: add `#[pin_v2]` here | LL + #[pin_v2] -LL | struct NotPinV2; +LL | struct GenericAdt { | -error: cannot directly pin an ADT that is not `#[pin_v2]` - --> $DIR/direct-borrow-requires-pin-v2.rs:13:13 +error: cannot directly pin a type that is not structurally pinnable + --> $DIR/direct-borrow-requires-pin-v2.rs:63:13 | LL | let _ = &pin const value; | ^^^^^^^^^^^^^^^^ | note: type defined here - --> $DIR/direct-borrow-requires-pin-v2.rs:5:1 + --> $DIR/direct-borrow-requires-pin-v2.rs:19:1 | -LL | struct NotPinV2; - | ^^^^^^^^^^^^^^^ +LL | struct GenericAdt { + | ^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct GenericAdt { + | + +error: cannot directly pin a type that is not structurally pinnable + --> $DIR/direct-borrow-requires-pin-v2.rs:68:5 + | +LL | &pin mut input.x + | ^^^^^^^^^^^^^^^^ + +error: cannot directly pin a type that is not structurally pinnable + --> $DIR/direct-borrow-requires-pin-v2.rs:74:13 + | +LL | let _ = &pin mut value; + | ^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/direct-borrow-requires-pin-v2.rs:19:1 + | +LL | struct GenericAdt { + | ^^^^^^^^^^^^^^^^^^^^ +help: add `#[pin_v2]` here + | +LL + #[pin_v2] +LL | struct GenericAdt { + | + +error: cannot directly pin a type that is not structurally pinnable + --> $DIR/direct-borrow-requires-pin-v2.rs:79:25 + | +LL | let _: &pin mut _ = &pin mut input; + | ^^^^^^^^^^^^^^ + | +note: type defined here + --> $DIR/direct-borrow-requires-pin-v2.rs:25:1 + | +LL | struct DropGenericAdt(T); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add `#[pin_v2]` here | LL + #[pin_v2] -LL | struct NotPinV2; +LL | struct DropGenericAdt(T); | -error: aborting due to 2 previous errors +error: aborting due to 7 previous errors