diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index fab22e4e6cf32..6b952e5aef13e 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -161,6 +161,9 @@ declare_features! ( (active, staged_api, "1.0.0", None, None), /// Added for testing E0705; perma-unstable. (active, test_2018_feature, "1.31.0", None, Some(Edition::Edition2018)), + /// Allows non-`unsafe` —and thus, unsound— access to `Pin` constructions. + /// Marked `incomplete` since perma-unstable and unsound. + (incomplete, unsafe_pin_internals, "1.60.0", None, None), /// Use for stable + negative coherence and strict coherence depending on trait's /// rustc_strict_coherence value. (active, with_negative_coherence, "1.60.0", None, None), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 2e45150226366..ebc0e72bba6ab 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1455,6 +1455,7 @@ symbols! { unsafe_block_in_unsafe_fn, unsafe_cell, unsafe_no_drop_flag, + unsafe_pin_internals, unsize, unsized_fn_params, unsized_locals, diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 09fc6df542975..dec1b5270d58b 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -406,7 +406,14 @@ use crate::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn, Receiver}; #[repr(transparent)] #[derive(Copy, Clone)] pub struct Pin

{ - pointer: P, + // FIXME(#93176): this field is made `#[unstable] #[doc(hidden)] pub` to: + // - deter downstream users from accessing it (which would be unsound!), + // - let the `pin!` macro access it (such a macro requires using struct + // literal syntax in order to benefit from lifetime extension). + // Long-term, `unsafe` fields or macro hygiene are expected to offer more robust alternatives. + #[unstable(feature = "unsafe_pin_internals", issue = "none")] + #[doc(hidden)] + pub pointer: P, } // The following implementations aren't derived in order to avoid soundness @@ -909,3 +916,243 @@ impl CoerceUnsized> for Pin

where P: CoerceUnsized {} #[stable(feature = "pin", since = "1.33.0")] impl DispatchFromDyn> for Pin

where P: DispatchFromDyn {} + +/// Constructs a [Pin]<[&mut] T>, by pinning[^1] a `value: T` _locally_[^2]. +/// +/// Unlike [`Box::pin`], this does not involve a heap allocation. +/// +/// [^1]: If the (type `T` of the) given value does not implement [`Unpin`], then this +/// effectively pins the `value` in memory, where it will be unable to be moved. +/// Otherwise, [Pin]<[&mut] T> behaves like [&mut] T, and operations such +/// as [`mem::replace()`][crate::mem::replace] will allow extracting that value, and therefore, +/// moving it. +/// See [the `Unpin` section of the `pin` module][self#unpin] for more info. +/// +/// [^2]: This is usually dubbed "stack"-pinning. And whilst local values are almost always located +/// in the stack (_e.g._, when within the body of a non-`async` function), the truth is that inside +/// the body of an `async fn` or block —more generally, the body of a generator— any locals crossing +/// an `.await` point —a `yield` point— end up being part of the state captured by the `Future` —by +/// the `Generator`—, and thus will be stored wherever that one is. +/// +/// ## Examples +/// +/// ### Basic usage +/// +/// ```rust +/// #![feature(pin_macro)] +/// # use core::marker::PhantomPinned as Foo; +/// use core::pin::{pin, Pin}; +/// +/// fn stuff(foo: Pin<&mut Foo>) { +/// // … +/// # let _ = foo; +/// } +/// +/// let pinned_foo = pin!(Foo { /* … */ }); +/// stuff(pinned_foo); +/// // or, directly: +/// stuff(pin!(Foo { /* … */ })); +/// ``` +/// +/// ### Manually polling a `Future` (wihout `Unpin` bounds) +/// +/// ```rust +/// #![feature(pin_macro)] +/// use std::{ +/// future::Future, +/// pin::pin, +/// task::{Context, Poll}, +/// thread, +/// }; +/// # use std::{sync::Arc, task::Wake, thread::Thread}; +/// +/// # /// A waker that wakes up the current thread when called. +/// # struct ThreadWaker(Thread); +/// # +/// # impl Wake for ThreadWaker { +/// # fn wake(self: Arc) { +/// # self.0.unpark(); +/// # } +/// # } +/// # +/// /// Runs a future to completion. +/// fn block_on(fut: Fut) -> Fut::Output { +/// let waker_that_unparks_thread = // … +/// # Arc::new(ThreadWaker(thread::current())).into(); +/// let mut cx = Context::from_waker(&waker_that_unparks_thread); +/// // Pin the future so it can be polled. +/// let mut pinned_fut = pin!(fut); +/// loop { +/// match pinned_fut.as_mut().poll(&mut cx) { +/// Poll::Pending => thread::park(), +/// Poll::Ready(res) => return res, +/// } +/// } +/// } +/// # +/// # assert_eq!(42, block_on(async { 42 })); +/// ``` +/// +/// ### With `Generator`s +/// +/// ```rust +/// #![feature(generators, generator_trait, pin_macro)] +/// use core::{ +/// ops::{Generator, GeneratorState}, +/// pin::pin, +/// }; +/// +/// fn generator_fn() -> impl Generator /* not Unpin */ { +/// // Allow generator to be self-referential (not `Unpin`) +/// // vvvvvv so that locals can cross yield points. +/// static || { +/// let foo = String::from("foo"); // --+ +/// yield 0; // | <- crosses yield point! +/// println!("{}", &foo); // <----------+ +/// yield foo.len(); +/// } +/// } +/// +/// fn main() { +/// let mut generator = pin!(generator_fn()); +/// match generator.as_mut().resume(()) { +/// GeneratorState::Yielded(0) => {}, +/// _ => unreachable!(), +/// } +/// match generator.as_mut().resume(()) { +/// GeneratorState::Yielded(3) => {}, +/// _ => unreachable!(), +/// } +/// match generator.resume(()) { +/// GeneratorState::Yielded(_) => unreachable!(), +/// GeneratorState::Complete(()) => {}, +/// } +/// } +/// ``` +/// +/// ## Remarks +/// +/// Precisely because a value is pinned to local storage, the resulting [Pin]<[&mut] T> +/// reference ends up borrowing a local tied to that block: it can't escape it. +/// +/// The following, for instance, fails to compile: +/// +/// ```rust,compile_fail +/// #![feature(pin_macro)] +/// use core::pin::{pin, Pin}; +/// # use core::{marker::PhantomPinned as Foo, mem::drop as stuff}; +/// +/// let x: Pin<&mut Foo> = { +/// let x: Pin<&mut Foo> = pin!(Foo { /* … */ }); +/// x +/// }; // <- Foo is dropped +/// stuff(x); // Error: use of dropped value +/// ``` +/// +///

Error message +/// +/// ```console +/// error[E0716]: temporary value dropped while borrowed +/// --> src/main.rs:9:28 +/// | +/// 8 | let x: Pin<&mut Foo> = { +/// | - borrow later stored here +/// 9 | let x: Pin<&mut Foo> = pin!(Foo { /* … */ }); +/// | ^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use +/// 10 | x +/// 11 | }; // <- Foo is dropped +/// | - temporary value is freed at the end of this statement +/// | +/// = note: consider using a `let` binding to create a longer lived value +/// ``` +/// +///
+/// +/// This makes [`pin!`] **unsuitable to pin values when intending to _return_ them**. Instead, the +/// value is expected to be passed around _unpinned_ until the point where it is to be consumed, +/// where it is then useful and even sensible to pin the value locally using [`pin!`]. +/// +/// If you really need to return a pinned value, consider using [`Box::pin`] instead. +/// +/// On the other hand, pinning to the stack[2](#fn2) using [`pin!`] is likely to be +/// cheaper than pinning into a fresh heap allocation using [`Box::pin`]. Moreover, by virtue of not +/// even needing an allocator, [`pin!`] is the main non-`unsafe` `#![no_std]`-compatible [`Pin`] +/// constructor. +/// +/// [`Box::pin`]: ../../std/boxed/struct.Box.html#method.pin +#[unstable(feature = "pin_macro", issue = "93178")] +#[rustc_macro_transparency = "semitransparent"] +#[allow_internal_unstable(unsafe_pin_internals)] +pub macro pin($value:expr $(,)?) { + // This is `Pin::new_unchecked(&mut { $value })`, so, for starters, let's + // review such a hypothetical macro (that any user-code could define): + // + // ```rust + // macro_rules! pin {( $value:expr ) => ( + // match &mut { $value } { at_value => unsafe { // Do not wrap `$value` in an `unsafe` block. + // $crate::pin::Pin::<&mut _>::new_unchecked(at_value) + // }} + // )} + // ``` + // + // Safety: + // - `type P = &mut _`. There are thus no pathological `Deref{,Mut}` impls + // that would break `Pin`'s invariants. + // - `{ $value }` is braced, making it a _block expression_, thus **moving** + // the given `$value`, and making it _become an **anonymous** temporary_. + // By virtue of being anonynomous, it can no longer be accessed, thus + // preventing any attemps to `mem::replace` it or `mem::forget` it, _etc._ + // + // This gives us a `pin!` definition that is sound, and which works, but only + // in certain scenarios: + // - If the `pin!(value)` expression is _directly_ fed to a function call: + // `let poll = pin!(fut).poll(cx);` + // - If the `pin!(value)` expression is part of a scrutinee: + // ```rust + // match pin!(fut) { pinned_fut => { + // pinned_fut.as_mut().poll(...); + // pinned_fut.as_mut().poll(...); + // }} // <- `fut` is dropped here. + // ``` + // Alas, it doesn't work for the more straight-forward use-case: `let` bindings. + // ```rust + // let pinned_fut = pin!(fut); // <- temporary value is freed at the end of this statement + // pinned_fut.poll(...) // error[E0716]: temporary value dropped while borrowed + // // note: consider using a `let` binding to create a longer lived value + // ``` + // - Issues such as this one are the ones motivating https://github.com/rust-lang/rfcs/pull/66 + // + // This makes such a macro incredibly unergonomic in practice, and the reason most macros + // out there had to take the path of being a statement/binding macro (_e.g._, `pin!(future);`) + // instead of featuring the more intuitive ergonomics of an expression macro. + // + // Luckily, there is a way to avoid the problem. Indeed, the problem stems from the fact that a + // temporary is dropped at the end of its enclosing statement when it is part of the parameters + // given to function call, which has precisely been the case with our `Pin::new_unchecked()`! + // For instance, + // ```rust + // let p = Pin::new_unchecked(&mut ); + // ``` + // becomes: + // ```rust + // let p = { let mut anon = ; &mut anon }; + // ``` + // + // However, when using a literal braced struct to construct the value, references to temporaries + // can then be taken. This makes Rust change the lifespan of such temporaries so that they are, + // instead, dropped _at the end of the enscoping block_. + // For instance, + // ```rust + // let p = Pin { pointer: &mut }; + // ``` + // becomes: + // ```rust + // let mut anon = ; + // let p = Pin { pointer: &mut anon }; + // ``` + // which is *exactly* what we want. + // + // See https://doc.rust-lang.org/1.58.1/reference/destructors.html#temporary-lifetime-extension + // for more info. + $crate::pin::Pin::<&mut _> { pointer: &mut { $value } } +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 1c512471c95cb..65be0c320c26f 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -45,6 +45,7 @@ #![feature(inline_const)] #![feature(is_sorted)] #![feature(pattern)] +#![feature(pin_macro)] #![feature(sort_internals)] #![feature(slice_take)] #![feature(maybe_uninit_uninit_array)] @@ -122,6 +123,7 @@ mod ops; mod option; mod pattern; mod pin; +mod pin_macro; mod ptr; mod result; mod simd; diff --git a/library/core/tests/pin_macro.rs b/library/core/tests/pin_macro.rs new file mode 100644 index 0000000000000..79c8c166c58d9 --- /dev/null +++ b/library/core/tests/pin_macro.rs @@ -0,0 +1,33 @@ +// edition:2021 +use core::{ + marker::PhantomPinned, + mem::{drop as stuff, transmute}, + pin::{pin, Pin}, +}; + +#[test] +fn basic() { + let it: Pin<&mut PhantomPinned> = pin!(PhantomPinned); + stuff(it); +} + +#[test] +fn extension_works_through_block() { + let it: Pin<&mut PhantomPinned> = { pin!(PhantomPinned) }; + stuff(it); +} + +#[test] +fn extension_works_through_unsafe_block() { + // "retro-type-inference" works as well. + let it: Pin<&mut PhantomPinned> = unsafe { pin!(transmute(())) }; + stuff(it); +} + +#[test] +fn unsize_coercion() { + let slice: Pin<&mut [PhantomPinned]> = pin!([PhantomPinned; 2]); + stuff(slice); + let dyn_obj: Pin<&mut dyn Send> = pin!([PhantomPinned; 2]); + stuff(dyn_obj); +} diff --git a/src/test/rustdoc-js-std/typed-query.js b/src/test/rustdoc-js-std/typed-query.js index f656aa72986fd..3915ee7dc5d23 100644 --- a/src/test/rustdoc-js-std/typed-query.js +++ b/src/test/rustdoc-js-std/typed-query.js @@ -8,5 +8,7 @@ const EXPECTED = { { 'path': 'std', 'name': 'eprint' }, { 'path': 'std', 'name': 'println' }, { 'path': 'std', 'name': 'eprintln' }, + { 'path': 'std::pin', 'name': 'pin' }, + { 'path': 'core::pin', 'name': 'pin' }, ], }; diff --git a/src/test/ui/feature-gates/feature-gate-unsafe_pin_internals.rs b/src/test/ui/feature-gates/feature-gate-unsafe_pin_internals.rs new file mode 100644 index 0000000000000..0680d23440304 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-unsafe_pin_internals.rs @@ -0,0 +1,17 @@ +// edition:2018 +#![forbid(incomplete_features, unsafe_code)] +#![feature(unsafe_pin_internals)] +//~^ ERROR the feature `unsafe_pin_internals` is incomplete and may not be safe to use + +use core::{marker::PhantomPinned, pin::Pin}; + +/// The `unsafe_pin_internals` is indeed unsound. +fn non_unsafe_pin_new_unchecked(pointer: &mut T) -> Pin<&mut T> { + Pin { pointer } +} + +fn main() { + let mut self_referential = PhantomPinned; + let _: Pin<&mut PhantomPinned> = non_unsafe_pin_new_unchecked(&mut self_referential); + core::mem::forget(self_referential); // move and disable drop glue! +} diff --git a/src/test/ui/feature-gates/feature-gate-unsafe_pin_internals.stderr b/src/test/ui/feature-gates/feature-gate-unsafe_pin_internals.stderr new file mode 100644 index 0000000000000..4d0c931b404e6 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-unsafe_pin_internals.stderr @@ -0,0 +1,14 @@ +error: the feature `unsafe_pin_internals` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/feature-gate-unsafe_pin_internals.rs:3:12 + | +LL | #![feature(unsafe_pin_internals)] + | ^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/feature-gate-unsafe_pin_internals.rs:2:11 + | +LL | #![forbid(incomplete_features, unsafe_code)] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/pin-macro/cant_access_internals.rs b/src/test/ui/pin-macro/cant_access_internals.rs new file mode 100644 index 0000000000000..120d08894f8f7 --- /dev/null +++ b/src/test/ui/pin-macro/cant_access_internals.rs @@ -0,0 +1,13 @@ +// edition:2018 +#![feature(pin_macro)] + +use core::{ + marker::PhantomPinned, + mem, + pin::{pin, Pin}, +}; + +fn main() { + let mut phantom_pinned = pin!(PhantomPinned); + mem::take(phantom_pinned.pointer); //~ ERROR use of unstable library feature 'unsafe_pin_internals' +} diff --git a/src/test/ui/pin-macro/cant_access_internals.stderr b/src/test/ui/pin-macro/cant_access_internals.stderr new file mode 100644 index 0000000000000..060c9c48c21c8 --- /dev/null +++ b/src/test/ui/pin-macro/cant_access_internals.stderr @@ -0,0 +1,11 @@ +error[E0658]: use of unstable library feature 'unsafe_pin_internals' + --> $DIR/cant_access_internals.rs:12:15 + | +LL | mem::take(phantom_pinned.pointer); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unsafe_pin_internals)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/pin-macro/lifetime_errors_on_promotion_misusage.rs b/src/test/ui/pin-macro/lifetime_errors_on_promotion_misusage.rs new file mode 100644 index 0000000000000..ca2b6cf759376 --- /dev/null +++ b/src/test/ui/pin-macro/lifetime_errors_on_promotion_misusage.rs @@ -0,0 +1,29 @@ +// edition:2018 +#![feature(pin_macro)] + +use core::{ + convert::identity, + marker::PhantomPinned, + mem::drop as stuff, + pin::pin, +}; + +fn function_call_stops_borrow_extension() { + let phantom_pinned = identity(pin!(PhantomPinned)); + //~^ ERROR temporary value dropped while borrowed + stuff(phantom_pinned) +} + +fn promotion_only_works_for_the_innermost_block() { + let phantom_pinned = { + let phantom_pinned = pin!(PhantomPinned); + //~^ ERROR temporary value dropped while borrowed + phantom_pinned + }; + stuff(phantom_pinned) +} + +fn main() { + function_call_stops_borrow_extension(); + promotion_only_works_for_the_innermost_block(); +} diff --git a/src/test/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr b/src/test/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr new file mode 100644 index 0000000000000..4971263af08ad --- /dev/null +++ b/src/test/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr @@ -0,0 +1,31 @@ +error[E0716]: temporary value dropped while borrowed + --> $DIR/lifetime_errors_on_promotion_misusage.rs:12:35 + | +LL | let phantom_pinned = identity(pin!(PhantomPinned)); + | ^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement + | | + | creates a temporary which is freed while still in use +LL | +LL | stuff(phantom_pinned) + | -------------- borrow later used here + | + = note: consider using a `let` binding to create a longer lived value + = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0716]: temporary value dropped while borrowed + --> $DIR/lifetime_errors_on_promotion_misusage.rs:19:30 + | +LL | let phantom_pinned = { + | -------------- borrow later stored here +LL | let phantom_pinned = pin!(PhantomPinned); + | ^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use +... +LL | }; + | - temporary value is freed at the end of this statement + | + = note: consider using a `let` binding to create a longer lived value + = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0716`.