From ee9cd7bb6ac4ffd03d00549bff8285ed7ca0ea5b Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Fri, 21 Jan 2022 16:28:23 +0100 Subject: [PATCH 1/8] Add a stack-`pin!`-ning macro to the `pin` module. Add a type annotation to improve error messages with type mismatches Add a link to the temporary-lifetime-extension section of the reference --- library/core/src/pin.rs | 242 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 09fc6df542975..450ff752dab05 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -909,3 +909,245 @@ 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] +/// (≠ [in the heap][`Box::pin`]). +/// +/// [^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 +/// +/// ```rust +/// # const _IGNORE: &str = stringify! { +/// 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")] +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. + // + // Finally, we don't hit problems _w.r.t._ the privacy of the `pointer` field, or the + // unqualified `Pin` name, thanks to `decl_macro`s being _fully_ hygienic (`def_site` hygiene). + Pin::<&mut _> { pointer: &mut { $value } } +} From 42d69e279330e0aacf5aa6981f20f9c1ae323a37 Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Mon, 24 Jan 2022 01:41:37 +0100 Subject: [PATCH 2/8] Write {ui,} tests for `pin_macro` and `pin!` --- library/core/tests/lib.rs | 2 ++ library/core/tests/pin_macro.rs | 33 +++++++++++++++++++ .../ui/pin-macro/cant_access_internals.rs | 13 ++++++++ .../ui/pin-macro/cant_access_internals.stderr | 11 +++++++ .../lifetime_errors_on_promotion_misusage.rs | 29 ++++++++++++++++ ...fetime_errors_on_promotion_misusage.stderr | 31 +++++++++++++++++ 6 files changed, 119 insertions(+) create mode 100644 library/core/tests/pin_macro.rs create mode 100644 src/test/ui/pin-macro/cant_access_internals.rs create mode 100644 src/test/ui/pin-macro/cant_access_internals.stderr create mode 100644 src/test/ui/pin-macro/lifetime_errors_on_promotion_misusage.rs create mode 100644 src/test/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr 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/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`. From 5360469b8d6d2e3cac52c2d145df38aca763c61d Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Mon, 31 Jan 2022 17:48:47 +0100 Subject: [PATCH 3/8] Update `macro:print` typed-query rustdoc test to include `pin!` results --- src/test/rustdoc-js-std/typed-query.js | 2 ++ 1 file changed, 2 insertions(+) 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' }, ], }; From 54e443dceb8d7eb11553fe19ea1fcaf5eedc9004 Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Sat, 22 Jan 2022 14:47:49 +0100 Subject: [PATCH 4/8] Improve documentation. Co-Authored-By: Mara Bos --- library/core/src/pin.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 450ff752dab05..90f4a5390e768 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -910,8 +910,9 @@ 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] -/// (≠ [in the heap][`Box::pin`]). +/// 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. @@ -1043,8 +1044,7 @@ impl DispatchFromDyn> for Pin

where P: DispatchFromDyn {} /// ///

Error message /// -/// ```rust -/// # const _IGNORE: &str = stringify! { +/// ```console /// error[E0716]: temporary value dropped while borrowed /// --> src/main.rs:9:28 /// | @@ -1056,8 +1056,7 @@ impl DispatchFromDyn> for Pin

where P: DispatchFromDyn {} /// 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 -/// # }; +/// = note: consider using a `let` binding to create a longer lived value /// ``` /// ///

From 6df63cc148d58fb87797f3dc1fc201b789e7fb0d Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Sat, 22 Jan 2022 21:07:00 +0100 Subject: [PATCH 5/8] Replace `def_site`-&-privacy implementation with a stability-based one. Since `decl_macro`s and/or `Span::def_site()` is deemed quite unstable, no public-facing macro that relies on it can hope to be, itself, stabilized. We circumvent the issue by no longer relying on field privacy for safety and, instead, relying on an unstable feature-gate to act as the gate keeper for non users of the macro (thanks to `allow_internal_unstable`). This is technically not correct (since a `nightly` user could technically enable the feature and cause unsoundness with it); or, in other words, this makes the feature-gate used to gate the access to the field be (technically unsound, and in practice) `unsafe`. Hence it having `unsafe` in its name. Back to the macro, we go back to `macro_rules!` / `mixed_site()`-span rules thanks to declaring the `decl_macro` as `semitransparent`, which is a hack to basically have `pub macro_rules!` Co-Authored-By: Mara Bos --- library/core/src/pin.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 90f4a5390e768..84fed4ed24364 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -406,7 +406,9 @@ use crate::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn, Receiver}; #[repr(transparent)] #[derive(Copy, Clone)] pub struct Pin

{ - pointer: P, + #[unstable(feature = "unsafe_pin_internals", issue = "none")] + #[doc(hidden)] + pub pointer: P, } // The following implementations aren't derived in order to avoid soundness @@ -1074,6 +1076,8 @@ impl DispatchFromDyn> for Pin

where P: DispatchFromDyn {} /// /// [`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): @@ -1145,8 +1149,5 @@ pub macro pin($value:expr $(,)?) { // // See https://doc.rust-lang.org/1.58.1/reference/destructors.html#temporary-lifetime-extension // for more info. - // - // Finally, we don't hit problems _w.r.t._ the privacy of the `pointer` field, or the - // unqualified `Pin` name, thanks to `decl_macro`s being _fully_ hygienic (`def_site` hygiene). - Pin::<&mut _> { pointer: &mut { $value } } + $crate::pin::Pin::<&mut _> { pointer: &mut { $value } } } From c93968aee896ff2e2ccba1b195e778eb32d6d6e3 Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Mon, 24 Jan 2022 01:34:46 +0100 Subject: [PATCH 6/8] Mark `unsafe_pin_internals` as `incomplete`. This thus still makes it technically possible to enable the feature, and thus to trigger UB without `unsafe`, but this is fine since incomplete features are known to be potentially unsound (labelled "may not be safe"). This follows from the discussion at https://github.com/rust-lang/rust/pull/93176#discussion_r799413561 --- compiler/rustc_feature/src/active.rs | 3 +++ compiler/rustc_span/src/symbol.rs | 1 + .../feature-gate-unsafe_pin_internals.rs | 17 +++++++++++++++++ .../feature-gate-unsafe_pin_internals.stderr | 14 ++++++++++++++ 4 files changed, 35 insertions(+) create mode 100644 src/test/ui/feature-gates/feature-gate-unsafe_pin_internals.rs create mode 100644 src/test/ui/feature-gates/feature-gate-unsafe_pin_internals.stderr diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index fab22e4e6cf32..ad0e1eac4b9e3 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.61.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/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 + From 002f627d383597a792afa14a8b9046659dabdaa2 Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Mon, 14 Feb 2022 17:35:27 +0100 Subject: [PATCH 7/8] Add a comment to justify why the `pointer` field is `pub`. Addresses https://github.com/rust-lang/rust/pull/93176/files#r795258110. --- library/core/src/pin.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 84fed4ed24364..dec1b5270d58b 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -406,6 +406,11 @@ use crate::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn, Receiver}; #[repr(transparent)] #[derive(Copy, Clone)] pub struct Pin

{ + // 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, From bf2a9dc3750a69ec952be6eec75cb312a15c94b1 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Mon, 14 Feb 2022 19:17:21 +0000 Subject: [PATCH 8/8] Update unsafe_pin_internals unstable version. --- compiler/rustc_feature/src/active.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index ad0e1eac4b9e3..6b952e5aef13e 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -163,7 +163,7 @@ declare_features! ( (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.61.0", None, None), + (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),