prevent deref coercions in pin!

This commit is contained in:
dianne
2026-03-05 10:28:12 -08:00
parent e917fdccb4
commit 5276fcd28e
6 changed files with 72 additions and 6 deletions
+1
View File
@@ -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)]
+18 -3
View File
@@ -2021,14 +2021,29 @@ unsafe impl<T: PinCoerceUnsized> PinCoerceUnsized for Pin<T> {}
/// [`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 <https://github.com/rust-lang/rust/issues/153438>.
#[expect(unreachable_code)]
$crate::pin::unreachable_pin_macro_type_constraint(pinned)
}
}
/// Helper for `pin!` to enforce its type signature.
/// See <https://github.com/rust-lang/rust/issues/153438>.
#[unstable(feature = "pin_macro_internals", issue = "none")]
#[doc(hidden)]
pub fn unreachable_pin_macro_type_constraint<'a, T>(_: T) -> Pin<&'a mut T> {
unreachable!()
}
@@ -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
}
@@ -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`.
@@ -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<T>(data: &mut T, callback: impl FnOnce(Pin<&mut T>)) {
callback(pin!(data));
//~^ ERROR: mismatched types
}
fn main() {}
@@ -0,0 +1,24 @@
error[E0308]: mismatched types
--> $DIR/dont-deref-coerce-pinned-value.rs:9:14
|
LL | fn wrong_pin<T>(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`.