about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEric Holk <ericholk@microsoft.com>2024-09-04 16:03:47 -0700
committerEric Holk <ericholk@microsoft.com>2024-09-18 12:36:31 -0700
commit7b7992fbcf5b8d9af945e32eac241252baa46fab (patch)
treeba13ee2c152fc84e0c5d6822a6f7819d71512792
parent13b5a4e43b92cf738acad403ea56900947f9d37b (diff)
downloadrust-7b7992fbcf5b8d9af945e32eac241252baa46fab.tar.gz
rust-7b7992fbcf5b8d9af945e32eac241252baa46fab.zip
Begin experimental support for pin reborrowing
This commit adds basic support for reborrowing `Pin` types in argument
position. At the moment it only supports reborrowing `Pin<&mut T>` as
`Pin<&mut T>` by inserting a call to `Pin::as_mut()`, and only in
argument position (not as the receiver in a method call).
-rw-r--r--compiler/rustc_borrowck/messages.ftl2
-rw-r--r--compiler/rustc_borrowck/src/session_diagnostics.rs2
-rw-r--r--compiler/rustc_feature/src/unstable.rs2
-rw-r--r--compiler/rustc_hir/src/lang_items.rs1
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs59
-rw-r--r--compiler/rustc_hir_typeck/src/expr_use_visitor.rs15
-rw-r--r--compiler/rustc_middle/src/ty/adjustment.rs3
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs45
-rw-r--r--compiler/rustc_span/src/symbol.rs2
-rw-r--r--library/core/src/pin.rs1
-rw-r--r--tests/ui/async-await/pin-reborrow-arg.rs27
-rw-r--r--tests/ui/async-await/pin-reborrow-self.rs24
-rw-r--r--tests/ui/feature-gates/feature-gate-pin_ergonomics.rs15
-rw-r--r--tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr21
14 files changed, 217 insertions, 2 deletions
diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl
index edb25e12864..ee4b2f95cb1 100644
--- a/compiler/rustc_borrowck/messages.ftl
+++ b/compiler/rustc_borrowck/messages.ftl
@@ -207,7 +207,7 @@ borrowck_simd_intrinsic_arg_const =
         *[other] {$arg}th
     } argument of `{$intrinsic}` is required to be a `const` item
 
-borrowck_suggest_create_freash_reborrow =
+borrowck_suggest_create_fresh_reborrow =
     consider reborrowing the `Pin` instead of moving it
 
 borrowck_suggest_iterate_over_slice =
diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs
index 4a50b0f0704..b6c6960d4ca 100644
--- a/compiler/rustc_borrowck/src/session_diagnostics.rs
+++ b/compiler/rustc_borrowck/src/session_diagnostics.rs
@@ -415,7 +415,7 @@ pub(crate) enum CaptureReasonSuggest<'tcx> {
         span: Span,
     },
     #[suggestion(
-        borrowck_suggest_create_freash_reborrow,
+        borrowck_suggest_create_fresh_reborrow,
         applicability = "maybe-incorrect",
         code = ".as_mut()",
         style = "verbose"
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index d0c0460ddfe..cf20b79dfa9 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -558,6 +558,8 @@ declare_features! (
     (unstable, optimize_attribute, "1.34.0", Some(54882)),
     /// Allows specifying nop padding on functions for dynamic patching.
     (unstable, patchable_function_entry, "1.81.0", Some(123115)),
+    /// Experimental features that make `Pin` more ergonomic.
+    (incomplete, pin_ergonomics, "CURRENT_RUSTC_VERSION", Some(130494)),
     /// Allows postfix match `expr.match { ... }`
     (unstable, postfix_match, "1.79.0", Some(121618)),
     /// Allows macro attributes on expressions, statements and non-inline modules.
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index e7398fd2226..60e024068e9 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -395,6 +395,7 @@ language_item_table! {
     IteratorNext,            sym::next,                next_fn,                    Target::Method(MethodKind::Trait { body: false}), GenericRequirement::None;
 
     PinNewUnchecked,         sym::new_unchecked,       new_unchecked_fn,           Target::Method(MethodKind::Inherent), GenericRequirement::None;
+    PinAsMut,                sym::pin_as_mut,              as_mut_fn,                  Target::Method(MethodKind::Inherent), GenericRequirement::None;
 
     RangeFrom,               sym::RangeFrom,           range_from_struct,          Target::Struct,         GenericRequirement::None;
     RangeFull,               sym::RangeFull,           range_full_struct,          Target::Struct,         GenericRequirement::None;
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 3bada1de148..3da4b2893d9 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -214,6 +214,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             ty::Dynamic(predicates, region, ty::DynStar) if self.tcx.features().dyn_star => {
                 return self.coerce_dyn_star(a, b, predicates, region);
             }
+            ty::Adt(pin, _)
+                if self.tcx.features().pin_ergonomics
+                    && pin.did() == self.tcx.lang_items().pin_type().unwrap() =>
+            {
+                return self.coerce_pin(a, b);
+            }
             _ => {}
         }
 
@@ -774,6 +780,59 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         })
     }
 
+    /// Applies reborrowing for `Pin`
+    ///
+    /// We currently only support reborrowing `Pin<&mut T>` as `Pin<&mut T>`. This is accomplished
+    /// by inserting a call to `Pin::as_mut` during MIR building.
+    ///
+    /// In the future we might want to support other reborrowing coercions, such as:
+    /// - `Pin<&mut T>` as `Pin<&T>`
+    /// - `Pin<&T>` as `Pin<&T>`
+    /// - `Pin<Box<T>>` as `Pin<&T>`
+    /// - `Pin<Box<T>>` as `Pin<&mut T>`
+    #[instrument(skip(self), level = "trace")]
+    fn coerce_pin(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
+        // We need to make sure the two types are compatible for coercion.
+        // Then we will build a ReborrowPin adjustment and return that as an InferOk.
+
+        // Right now we can only reborrow if this is a `Pin<&mut T>`.
+        let can_reborrow = |ty: Ty<'tcx>| {
+            // Get the T out of Pin<T>
+            let ty = match ty.kind() {
+                ty::Adt(pin, args) if pin.did() == self.tcx.lang_items().pin_type().unwrap() => {
+                    args[0].expect_ty()
+                }
+                _ => {
+                    debug!("can't reborrow {:?} as pinned", ty);
+                    return None;
+                }
+            };
+            // Make sure the T is something we understand (just `&mut U` for now)
+            match ty.kind() {
+                ty::Ref(region, ty, ty::Mutability::Mut) => Some((*region, *ty)),
+                _ => {
+                    debug!("can't reborrow pin of inner type {:?}", ty);
+                    None
+                }
+            }
+        };
+
+        let (_, _a_ty) = can_reborrow(a).ok_or(TypeError::Mismatch)?;
+        let (b_region, _b_ty) = can_reborrow(b).ok_or(TypeError::Mismatch)?;
+
+        // To complete the reborrow, we need to make sure we can unify the inner types, and if so we
+        // add the adjustments.
+        self.unify_and(a, b, |_inner_ty| {
+            vec![Adjustment {
+                kind: Adjust::ReborrowPin(AutoBorrow::Ref(
+                    b_region,
+                    AutoBorrowMutability::Mut { allow_two_phase_borrow: AllowTwoPhase::No },
+                )),
+                target: b,
+            }]
+        })
+    }
+
     fn coerce_from_safe_fn<F, G>(
         &self,
         a: Ty<'tcx>,
diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
index da8c0ad3a30..103a4c72ea8 100644
--- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
@@ -780,6 +780,20 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
                 adjustment::Adjust::Borrow(ref autoref) => {
                     self.walk_autoref(expr, &place_with_id, autoref);
                 }
+
+                adjustment::Adjust::ReborrowPin(ref autoref) => {
+                    // Reborrowing a Pin is like a combinations of a deref and a borrow, so we do
+                    // both.
+                    let bk = match autoref {
+                        adjustment::AutoBorrow::Ref(_, m) => {
+                            ty::BorrowKind::from_mutbl((*m).into())
+                        }
+                        adjustment::AutoBorrow::RawPtr(m) => ty::BorrowKind::from_mutbl(*m),
+                    };
+                    self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk);
+
+                    self.walk_autoref(expr, &place_with_id, autoref);
+                }
             }
             place_with_id = self.cat_expr_adjusted(expr, place_with_id, adjustment)?;
         }
@@ -1284,6 +1298,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
             adjustment::Adjust::NeverToAny
             | adjustment::Adjust::Pointer(_)
             | adjustment::Adjust::Borrow(_)
+            | adjustment::Adjust::ReborrowPin(_)
             | adjustment::Adjust::DynStar => {
                 // Result is an rvalue.
                 Ok(self.cat_rvalue(expr.hir_id, target))
diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs
index 1236c9efb41..de3619f5b62 100644
--- a/compiler/rustc_middle/src/ty/adjustment.rs
+++ b/compiler/rustc_middle/src/ty/adjustment.rs
@@ -104,6 +104,9 @@ pub enum Adjust<'tcx> {
 
     /// Cast into a dyn* object.
     DynStar,
+
+    /// Take a Pin<Ptr> and call either as_mut() or as_ref() to get a Pin<&mut T> or Pin<&T>.
+    ReborrowPin(AutoBorrow<'tcx>),
 }
 
 /// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)`
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index aa8ccc8b7dd..8e6e13f94e0 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -74,6 +74,7 @@ impl<'tcx> Cx<'tcx> {
         self.thir.exprs.push(expr)
     }
 
+    #[instrument(level = "trace", skip(self, expr, span))]
     fn apply_adjustment(
         &mut self,
         hir_expr: &'tcx hir::Expr<'tcx>,
@@ -146,6 +147,50 @@ impl<'tcx> Cx<'tcx> {
                 ExprKind::RawBorrow { mutability, arg: self.thir.exprs.push(expr) }
             }
             Adjust::DynStar => ExprKind::Cast { source: self.thir.exprs.push(expr) },
+            Adjust::ReborrowPin(AutoBorrow::Ref(region, m)) => {
+                debug!("apply ReborrowPin adjustment");
+                match m {
+                    AutoBorrowMutability::Mut { .. } => {
+                        // Rewrite `$expr` as `Pin::as_mut(&mut $expr)`
+                        let as_mut_method =
+                            self.tcx().require_lang_item(rustc_hir::LangItem::PinAsMut, Some(span));
+                        let pin_ty_args = match expr.ty.kind() {
+                            ty::Adt(_, args) => args,
+                            _ => bug!("ReborrowPin with non-Pin type"),
+                        };
+                        let as_mut_ty =
+                            Ty::new_fn_def(self.tcx, as_mut_method, pin_ty_args.into_iter());
+
+                        let ty = Ty::new_ref(self.tcx, region, expr.ty, ty::Mutability::Mut);
+                        let arg = ExprKind::Borrow {
+                            borrow_kind: BorrowKind::Mut { kind: mir::MutBorrowKind::Default },
+                            arg: self.thir.exprs.push(expr),
+                        };
+                        debug!(?arg, "borrow arg");
+                        let arg = self.thir.exprs.push(Expr { temp_lifetime, ty, span, kind: arg });
+
+                        let kind = ExprKind::Call {
+                            ty: as_mut_ty,
+                            fun: self.thir.exprs.push(Expr {
+                                temp_lifetime,
+                                ty: as_mut_ty,
+                                span,
+                                kind: ExprKind::ZstLiteral { user_ty: None },
+                            }),
+                            args: Box::new([arg]),
+                            from_hir_call: true,
+                            fn_span: span,
+                        };
+                        debug!(?kind);
+                        kind
+                    }
+                    AutoBorrowMutability::Not => {
+                        // FIXME: We need to call Pin::as_ref on the expression
+                        bug!("ReborrowPin with shared reference is not implemented yet")
+                    }
+                }
+            }
+            Adjust::ReborrowPin(AutoBorrow::RawPtr(_)) => bug!("ReborrowPin with raw pointer"),
         };
 
         Expr { temp_lifetime, ty: adjustment.target, span, kind }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index cbe2bafef21..979294563d7 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1418,6 +1418,8 @@ symbols! {
         pic,
         pie,
         pin,
+        pin_as_mut,
+        pin_ergonomics,
         platform_intrinsics,
         plugin,
         plugin_registrar,
diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs
index 9c13662e08e..bb1196e82de 100644
--- a/library/core/src/pin.rs
+++ b/library/core/src/pin.rs
@@ -1408,6 +1408,7 @@ impl<Ptr: DerefMut> Pin<Ptr> {
     ///     }
     /// }
     /// ```
+    #[cfg_attr(not(bootstrap), lang = "pin_as_mut")]
     #[stable(feature = "pin", since = "1.33.0")]
     #[inline(always)]
     pub fn as_mut(&mut self) -> Pin<&mut Ptr::Target> {
diff --git a/tests/ui/async-await/pin-reborrow-arg.rs b/tests/ui/async-await/pin-reborrow-arg.rs
new file mode 100644
index 00000000000..34f23533b65
--- /dev/null
+++ b/tests/ui/async-await/pin-reborrow-arg.rs
@@ -0,0 +1,27 @@
+//@ check-pass
+
+#![feature(pin_ergonomics)]
+#![allow(dead_code, incomplete_features)]
+
+use std::pin::Pin;
+
+struct Foo;
+
+impl Foo {
+    fn baz(self: Pin<&mut Self>) {
+    }
+}
+
+fn foo(_: Pin<&mut Foo>) {
+}
+
+fn bar(mut x: Pin<&mut Foo>) {
+    foo(x);
+    foo(x); // for this to work we need to automatically reborrow,
+            // as if the user had written `foo(x.as_mut())`.
+
+    Foo::baz(x);
+    Foo::baz(x);
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/pin-reborrow-self.rs b/tests/ui/async-await/pin-reborrow-self.rs
new file mode 100644
index 00000000000..b60b6982bb8
--- /dev/null
+++ b/tests/ui/async-await/pin-reborrow-self.rs
@@ -0,0 +1,24 @@
+//@ check-pass
+//@ignore-test
+
+// Currently ignored due to self reborrowing not being implemented for Pin
+
+#![feature(pin_ergonomics)]
+#![allow(incomplete_features)]
+
+use std::pin::Pin;
+
+struct Foo;
+
+impl Foo {
+    fn foo(self: Pin<&mut Self>) {
+    }
+}
+
+fn bar(x: Pin<&mut Foo>) {
+    x.foo();
+    x.foo(); // for this to work we need to automatically reborrow,
+             // as if the user had written `x.as_mut().foo()`.
+}
+
+fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs
new file mode 100644
index 00000000000..d694531d53a
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs
@@ -0,0 +1,15 @@
+#![allow(dead_code, incomplete_features)]
+
+use std::pin::Pin;
+
+struct Foo;
+
+fn foo(_: Pin<&mut Foo>) {
+}
+
+fn bar(mut x: Pin<&mut Foo>) {
+    foo(x);
+    foo(x); //~ ERROR use of moved value: `x`
+}
+
+fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr
new file mode 100644
index 00000000000..6c9029d8176
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr
@@ -0,0 +1,21 @@
+error[E0382]: use of moved value: `x`
+  --> $DIR/feature-gate-pin_ergonomics.rs:12:9
+   |
+LL | fn bar(mut x: Pin<&mut Foo>) {
+   |        ----- move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait
+LL |     foo(x);
+   |         - value moved here
+LL |     foo(x);
+   |         ^ value used here after move
+   |
+note: consider changing this parameter type in function `foo` to borrow instead if owning the value isn't necessary
+  --> $DIR/feature-gate-pin_ergonomics.rs:7:11
+   |
+LL | fn foo(_: Pin<&mut Foo>) {
+   |    ---    ^^^^^^^^^^^^^ this parameter takes ownership of the value
+   |    |
+   |    in this function
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0382`.