Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 52258fe

Browse files
committedMar 18, 2025·
Adapt pin! to the new lifespan-extension rules of 2024 edition
1 parent 75530e9 commit 52258fe

File tree

1 file changed

+148
-3
lines changed

1 file changed

+148
-3
lines changed
 

‎library/core/src/pin.rs

+148-3
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,11 @@ pub struct Pin<Ptr> {
11011101
#[unstable(feature = "unsafe_pin_internals", issue = "none")]
11021102
#[doc(hidden)]
11031103
pub __pointer: Ptr,
1104+
1105+
/// See the docs of [`lifetime_extension`] for more info.
1106+
#[unstable(feature = "unsafe_pin_internals", issue = "none")]
1107+
#[doc(hidden)]
1108+
pub __phantom: crate::marker::PhantomData<Ptr>,
11041109
}
11051110

11061111
// The following implementations aren't derived in order to avoid soundness
@@ -1355,7 +1360,7 @@ impl<Ptr: Deref> Pin<Ptr> {
13551360
#[rustc_const_stable(feature = "const_pin", since = "1.84.0")]
13561361
#[stable(feature = "pin", since = "1.33.0")]
13571362
pub const unsafe fn new_unchecked(pointer: Ptr) -> Pin<Ptr> {
1358-
Pin { __pointer: pointer }
1363+
Pin { __pointer: pointer, __phantom: crate::marker::PhantomData }
13591364
}
13601365

13611366
/// Gets a shared reference to the pinned value this [`Pin`] points to.
@@ -1575,7 +1580,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> {
15751580
#[rustc_const_stable(feature = "const_pin", since = "1.84.0")]
15761581
#[stable(feature = "pin", since = "1.33.0")]
15771582
pub const fn into_ref(self) -> Pin<&'a T> {
1578-
Pin { __pointer: self.__pointer }
1583+
Pin { __pointer: self.__pointer, __phantom: crate::marker::PhantomData }
15791584
}
15801585

15811586
/// Gets a mutable reference to the data inside of this `Pin`.
@@ -2014,5 +2019,145 @@ pub macro pin($value:expr $(,)?) {
20142019
//
20152020
// See https://doc.rust-lang.org/1.58.1/reference/destructors.html#temporary-lifetime-extension
20162021
// for more info.
2017-
$crate::pin::Pin::<&mut _> { __pointer: &mut { $value } }
2022+
$crate::pin::Pin::<&mut _> {
2023+
__pointer: &mut $crate::pin::__Pinned {
2024+
value_expression_preserving_lifespan_extension_of_temporaries: $value,
2025+
} as &mut _,
2026+
// ^^^^^^^^
2027+
// DerefMut coërcion, because of --+
2028+
__phantom: { // <------------------+
2029+
let pin = $crate::marker::PhantomData;
2030+
if false {
2031+
loop {}
2032+
// We use dead code to disable move semantics (and borrowck) to allow us to refer to
2033+
// `$value` again even when `$value` happens to consume non-`Copy` stuff or whatnot.
2034+
#[allow(unreachable_code)] {
2035+
$crate::pin::__phantomdata_set_typeof_pointee(pin, $value);
2036+
}
2037+
}
2038+
pin
2039+
},
2040+
}
2041+
}
2042+
2043+
/// Since the 2024 edition, the rules for lifespan-of-temporaries extension have changed,
2044+
/// and `{ $value }` no longer cuts it for all the scenarios where it did in the 2021 edition and
2045+
/// before.
2046+
///
2047+
/// - aside: throughout this documentation the expression "lifespan extension" shall be preferred
2048+
/// to that of "lifetime extension", so as to clarify we are firstly talking of the lifespan of
2049+
/// a temporary, which, when extended, results in the lifetime of a borrow to it to be allowed
2050+
/// to be bigger. This nit seems more in line with the idea that lifetimes themselves are just
2051+
/// descriptive, not prescriptive, so it does not make that much sense to talk of extending one
2052+
/// _directly_. We would not need to lift this ambiguity had Rust chosen a term other than
2053+
/// "lifetime" to talk of the duration of borrows.
2054+
///
2055+
/// Indeed, things such as the following regressed:
2056+
///
2057+
/// ```rust
2058+
/// // Regression test for #138596
2059+
///
2060+
/// fn main() {
2061+
/// match core::pin::pin!(foo(&mut 0)) {
2062+
/// _f => {}
2063+
/// }
2064+
/// }
2065+
///
2066+
/// async fn foo(_s: &mut usize) {}
2067+
/// ```
2068+
///
2069+
/// The reason for that is that we'd end up with `{ foo(&mut temporary(0)) }` among the expansion
2070+
/// of `pin!`, and since this happens in `match`-scrutinee position, temporaries get not to outlive
2071+
/// their encompassing braced block(s), so `temporary(0)` dies before that `}`, and the resulting
2072+
/// `Pin { … }` is born dangling.
2073+
///
2074+
/// But we do need a _value expression_ behind our `Pin { __pointer: &mut`, lest Rust treat it as a
2075+
/// _place expression_, and allow things such as `$value = *&mut non_unpin`, which would break
2076+
/// the soundness requirement of `Pin` (see
2077+
/// https://github.com/rust-lang/rust/issues/138596#issuecomment-2729894350).
2078+
///
2079+
/// This naïvely leaves us with things such as `identity($value)` or `($value, ).0` as
2080+
/// plausible alternatives.
2081+
///
2082+
/// But, alas, this now breaks the lifespan-of-temps extension happening _inside of `$value`_, which
2083+
/// has been an accidental, but stabilized and quite convenient, property of `pin!`.
2084+
/// For instance, the following code would break:
2085+
///
2086+
/// ```rust
2087+
/// fn temporary() {}
2088+
///
2089+
/// let p = core::pin::pin!(&mut temporary());
2090+
/// let _some_usage_of_p = (&p, );
2091+
/// ```
2092+
///
2093+
/// And there are several occurrences of `pin!(&mut …)` in the wild, and whilst most of them are
2094+
/// inlined/short usages of `Pin<&mut _>`, there is at least one instance of such code actually
2095+
/// being multi-statement lived, and thus relying on the lifespan-of-temps extension property of
2096+
/// `pin!()`.
2097+
///
2098+
/// So, unless/until we get `pin!()` —or a helper inner macro thereof— to be using edition 2021
2099+
/// rules for its `{ $value }` expression, the only 2024-edition-compatible workaround found so far
2100+
/// hinges on the following observation:
2101+
///
2102+
/// > `&mut Wrapper { field: $value }` happens to preserve the lifespan-of-temps-extending
2103+
/// > rules of 2021 edition `&mut $value`, and also funnels `$value` through a value expression.
2104+
///
2105+
/// But we get a `&mut Wrapper<typeof<$value>>` rather than a `&mut typeof<$value>`.
2106+
/// So now the challenge is to get `DerefMut` to take place here, and we are good!
2107+
///
2108+
/// Only…, so far, lifespan-extension rules get broken by _explicit_ `Deref{,Mut}` such as `*`,
2109+
/// `&*`, or, in our case, `&mut *`!! 😩
2110+
///
2111+
/// But…, it does not get broken by _implicit_ `Deref{,Mut}`, such as the one triggered by a deref
2112+
/// coërcion!
2113+
///
2114+
/// That is, `&mut Wrapper { field: $value } as &mut typeof<$value>` does preserve lifespan-of-temps
2115+
/// extension!
2116+
///
2117+
/// So, our final challenge now is to express `&mut typeof<$value>` when `typeof` is not available.
2118+
///
2119+
/// The usual approach is
2120+
/// `if false { /* unreachable code to infer type */ } else { /* real code */ }`, but we cannot
2121+
/// do that either since the braced blocks here are the "same" as our originally problematic
2122+
/// `{ $value }` to begin with!
2123+
///
2124+
/// So the only remaining tool available to our disposable are the very fields of `Pin { … }`: we
2125+
/// can have an extra `PhantomData` field, which "redundantly" uses the `Ptr` type, thereby
2126+
/// introducing a type equality constraint between the two fields similar to that of an `if … else`,
2127+
/// and which is where we shall be able to insert our `/* unreachable code to infer type */`.
2128+
#[doc(hidden)]
2129+
#[unstable(feature = "unsafe_pin_internals", issue = "none")]
2130+
mod lifetime_extension {
2131+
use crate::marker::PhantomData;
2132+
use crate::ops::{Deref, DerefMut};
2133+
2134+
/// Used in an `unreachable_code` branch to guide type inference.
2135+
#[doc(hidden)]
2136+
pub fn __phantomdata_set_typeof_pointee<T>(_: PhantomData<&mut T>, _: T) {}
2137+
2138+
/// Our `DerefMut` helper wrapper.
2139+
#[doc(hidden)]
2140+
#[unstable(feature = "unsafe_pin_internals", issue = "none")]
2141+
#[allow(missing_debug_implementations)]
2142+
pub struct __Pinned<T> {
2143+
pub value_expression_preserving_lifespan_extension_of_temporaries: T,
2144+
}
2145+
2146+
impl<T> Deref for __Pinned<T> {
2147+
type Target = T;
2148+
2149+
fn deref(&self) -> &T {
2150+
unimplemented!("never to be called");
2151+
}
2152+
}
2153+
2154+
impl<T> DerefMut for __Pinned<T> {
2155+
#[inline]
2156+
fn deref_mut(&mut self) -> &mut T {
2157+
&mut self.value_expression_preserving_lifespan_extension_of_temporaries
2158+
}
2159+
}
20182160
}
2161+
2162+
#[unstable(feature = "unsafe_pin_internals", issue = "none")]
2163+
pub use lifetime_extension::{__Pinned, __phantomdata_set_typeof_pointee};

0 commit comments

Comments
 (0)
Please sign in to comment.