From 5276fcd28eaaf3edbaa170990de622f3a9fe0bb1 Mon Sep 17 00:00:00 2001 From: dianne Date: Thu, 5 Mar 2026 10:28:12 -0800 Subject: [PATCH] prevent deref coercions in `pin!` --- library/core/src/lib.rs | 1 + library/core/src/pin.rs | 21 ++++++++++++-- .../iterators/iter-macro-not-async-closure.rs | 2 ++ .../iter-macro-not-async-closure.stderr | 28 +++++++++++++++++-- .../ui/pin/dont-deref-coerce-pinned-value.rs | 2 +- .../pin/dont-deref-coerce-pinned-value.stderr | 24 ++++++++++++++++ 6 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 tests/ui/pin/dont-deref-coerce-pinned-value.stderr diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 2b4ac66212e5..79694c46f2dc 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -157,6 +157,7 @@ #![feature(no_core)] #![feature(optimize_attribute)] #![feature(pattern_types)] +#![feature(pin_macro_internals)] #![feature(prelude_import)] #![feature(repr_simd)] #![feature(rustc_attrs)] diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index b65e40ef4675..f7224df849c8 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -2021,14 +2021,29 @@ unsafe impl PinCoerceUnsized for Pin {} /// [`Box::pin`]: ../../std/boxed/struct.Box.html#method.pin #[stable(feature = "pin_macro", since = "1.68.0")] #[rustc_macro_transparency = "semiopaque"] -#[allow_internal_unstable(super_let)] +#[allow_internal_unstable(pin_macro_internals, super_let)] #[rustc_diagnostic_item = "pin_macro"] // `super` gets removed by rustfmt #[rustfmt::skip] pub macro pin($value:expr $(,)?) { - { + 'p: { super let mut pinned = $value; // SAFETY: The value is pinned: it is the local above which cannot be named outside this macro. - unsafe { $crate::pin::Pin::new_unchecked(&mut pinned) } + break 'p unsafe { $crate::pin::Pin::new_unchecked(&mut pinned) }; + + // HACK: We need to ensure that, given `$value: T`, `pin!($value)` has type `Pin<&mut T>`. + // Otherwise, it's possible for a type annotation on the result of `pin!` to unsoundly add + // deref coercions. E.g. for `$value: &mut T`, we could get `pin!($value): Pin<&mut T>`, + // violating the pinning invariant; see . + #[expect(unreachable_code)] + $crate::pin::unreachable_pin_macro_type_constraint(pinned) } } + +/// Helper for `pin!` to enforce its type signature. +/// See . +#[unstable(feature = "pin_macro_internals", issue = "none")] +#[doc(hidden)] +pub fn unreachable_pin_macro_type_constraint<'a, T>(_: T) -> Pin<&'a mut T> { + unreachable!() +} diff --git a/tests/ui/iterators/iter-macro-not-async-closure.rs b/tests/ui/iterators/iter-macro-not-async-closure.rs index 634391883ea7..38ea33ccd773 100644 --- a/tests/ui/iterators/iter-macro-not-async-closure.rs +++ b/tests/ui/iterators/iter-macro-not-async-closure.rs @@ -27,6 +27,8 @@ fn main() { //~^^ ERROR AsyncFnOnce()` is not satisfied //~^^^ ERROR AsyncFnOnce()` is not satisfied //~^^^^ ERROR AsyncFnOnce()` is not satisfied + //~^^^^^ ERROR AsyncFnOnce()` is not satisfied + //~^^^^^^ ERROR AsyncFnOnce()` is not satisfied x.poll(&mut Context::from_waker(Waker::noop())); //~^ ERROR AsyncFnOnce()` is not satisfied } diff --git a/tests/ui/iterators/iter-macro-not-async-closure.stderr b/tests/ui/iterators/iter-macro-not-async-closure.stderr index 906ebd482fb6..735003207793 100644 --- a/tests/ui/iterators/iter-macro-not-async-closure.stderr +++ b/tests/ui/iterators/iter-macro-not-async-closure.stderr @@ -49,7 +49,31 @@ LL | async fn call_async_once(f: impl AsyncFnOnce()) { | ^^^^^^^^^^^^^ required by this bound in `call_async_once` error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}: AsyncFnOnce()` is not satisfied - --> $DIR/iter-macro-not-async-closure.rs:30:5 + --> $DIR/iter-macro-not-async-closure.rs:25:13 + | +LL | let x = pin!(call_async_once(f)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}` + | +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:14:34 + | +LL | async fn call_async_once(f: impl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:25:13 + | +LL | let x = pin!(call_async_once(f)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}` + | +note: required by a bound in `call_async_once` + --> $DIR/iter-macro-not-async-closure.rs:14:34 + | +LL | async fn call_async_once(f: impl AsyncFnOnce()) { + | ^^^^^^^^^^^^^ required by this bound in `call_async_once` + +error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}: AsyncFnOnce()` is not satisfied + --> $DIR/iter-macro-not-async-closure.rs:32:5 | LL | x.poll(&mut Context::from_waker(Waker::noop())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}` @@ -60,6 +84,6 @@ note: required by a bound in `call_async_once` LL | async fn call_async_once(f: impl AsyncFnOnce()) { | ^^^^^^^^^^^^^ required by this bound in `call_async_once` -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/pin/dont-deref-coerce-pinned-value.rs b/tests/ui/pin/dont-deref-coerce-pinned-value.rs index d902b68ac492..02730b54f407 100644 --- a/tests/ui/pin/dont-deref-coerce-pinned-value.rs +++ b/tests/ui/pin/dont-deref-coerce-pinned-value.rs @@ -2,12 +2,12 @@ //! expectation on `pin!`'s result, make sure we don't deref-coerce the argument to //! `Pin::new_unchecked` to get its type to match up. That violates the pinning invariant, leading //! to unsoundness! -//@ check-pass use std::pin::{Pin, pin}; fn wrong_pin(data: &mut T, callback: impl FnOnce(Pin<&mut T>)) { callback(pin!(data)); + //~^ ERROR: mismatched types } fn main() {} diff --git a/tests/ui/pin/dont-deref-coerce-pinned-value.stderr b/tests/ui/pin/dont-deref-coerce-pinned-value.stderr new file mode 100644 index 000000000000..1d5afaa3ad71 --- /dev/null +++ b/tests/ui/pin/dont-deref-coerce-pinned-value.stderr @@ -0,0 +1,24 @@ +error[E0308]: mismatched types + --> $DIR/dont-deref-coerce-pinned-value.rs:9:14 + | +LL | fn wrong_pin(data: &mut T, callback: impl FnOnce(Pin<&mut T>)) { + | - expected this type parameter +LL | callback(pin!(data)); + | ^^^^^^^^^^ + | | + | expected type parameter `T`, found `&mut T` + | arguments to this function are incorrect + | + = note: expected type parameter `_` + found mutable reference `&mut _` +help: the return type of this call is `&mut T` due to the type of the argument passed + --> $DIR/dont-deref-coerce-pinned-value.rs:9:14 + | +LL | callback(pin!(data)); + | ^^^^^^^^^^ this argument influences the return type of `unreachable_pin_macro_type_constraint` +note: function defined here + --> $SRC_DIR/core/src/pin.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`.