about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-10-28 23:27:33 +0000
committerbors <bors@rust-lang.org>2022-10-28 23:27:33 +0000
commit7174231ae66aa3e938cbe0b84e23e79d867fec20 (patch)
tree7460d6911aa4b59612ba0ca595d395fb0407eab1
parent9565dfeb4e6225177bbe78f18cd48a7982f34401 (diff)
parent17d78c4ef922f81e752feb077d66fbce80630c6c (diff)
downloadrust-7174231ae66aa3e938cbe0b84e23e79d867fec20.tar.gz
rust-7174231ae66aa3e938cbe0b84e23e79d867fec20.zip
Auto merge of #102737 - RalfJung:poll_fn_pin, r=Mark-Simulacrum
poll_fn and Unpin: fix pinning

See [IRLO](https://internals.rust-lang.org/t/surprising-soundness-trouble-around-pollfn/17484) for details: currently `poll_fn` is very subtle to use, since it does not pin the closure, so creating a `Pin::get_unchcked(&mut capture)` inside the closure is unsound. This leads to actual miscompilations with `futures::join!`.

IMO the proper fix is to pin the closure when the future is pinned, which is achieved by changing the `Unpin` implementation. This is a breaking change though. 1.64.0 was *just* released, so maybe this is still okay?

The alternative would be to add some strong comments to the docs saying that closure captures are *not pinned* and doing `Pin::get_unchecked` on them is unsound.
-rw-r--r--library/core/src/future/poll_fn.rs11
1 files changed, 7 insertions, 4 deletions
diff --git a/library/core/src/future/poll_fn.rs b/library/core/src/future/poll_fn.rs
index db2a523323b..90cb797391a 100644
--- a/library/core/src/future/poll_fn.rs
+++ b/library/core/src/future/poll_fn.rs
@@ -5,7 +5,9 @@ use crate::task::{Context, Poll};
 
 /// Creates a future that wraps a function returning [`Poll`].
 ///
-/// Polling the future delegates to the wrapped function.
+/// Polling the future delegates to the wrapped function. If the returned future is pinned, then the
+/// captured environment of the wrapped function is also pinned in-place, so as long as the closure
+/// does not move out of its captures it can soundly create pinned references to them.
 ///
 /// # Examples
 ///
@@ -41,7 +43,7 @@ pub struct PollFn<F> {
 }
 
 #[stable(feature = "future_poll_fn", since = "1.64.0")]
-impl<F> Unpin for PollFn<F> {}
+impl<F: Unpin> Unpin for PollFn<F> {}
 
 #[stable(feature = "future_poll_fn", since = "1.64.0")]
 impl<F> fmt::Debug for PollFn<F> {
@@ -57,7 +59,8 @@ where
 {
     type Output = T;
 
-    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
-        (&mut self.f)(cx)
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
+        // SAFETY: We are not moving out of the pinned field.
+        (unsafe { &mut self.get_unchecked_mut().f })(cx)
     }
 }