about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEric Holk <ericholk@microsoft.com>2024-09-19 19:35:01 -0700
committerEric Holk <ericholk@microsoft.com>2024-09-23 09:12:52 -0700
commit3dfb30c70a2a8da87eefa01a56d753d1698866c9 (patch)
tree3aa0a1a31e0dbe8a34e1ae5ed8fa862889225d38
parent97fbcf6773fa2d00675cbd7ea8dcdac1d6772072 (diff)
downloadrust-3dfb30c70a2a8da87eefa01a56d753d1698866c9.tar.gz
rust-3dfb30c70a2a8da87eefa01a56d753d1698866c9.zip
Allow reborrowing pinned self methods
-rw-r--r--compiler/rustc_hir_typeck/src/method/confirm.rs16
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs49
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs10
-rw-r--r--tests/ui/async-await/pin-reborrow-self.rs3
-rw-r--r--tests/ui/feature-gates/feature-gate-pin_ergonomics.rs8
-rw-r--r--tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr47
6 files changed, 123 insertions, 10 deletions
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
index 0d11df11334..f955c62a443 100644
--- a/compiler/rustc_hir_typeck/src/method/confirm.rs
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -239,10 +239,18 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
             Some(probe::AutorefOrPtrAdjustment::ReborrowPin(mutbl)) => {
                 let region = self.next_region_var(infer::Autoref(self.span));
 
-                adjustments.push(Adjustment {
-                    kind: Adjust::ReborrowPin(region, mutbl),
-                    target,
-                });
+                target = match target.kind() {
+                    ty::Adt(pin, args) if self.tcx.is_lang_item(pin.did(), hir::LangItem::Pin) => {
+                        let inner_ty = match args[0].expect_ty().kind() {
+                            ty::Ref(_, ty, _) => *ty,
+                            _ => bug!("Expected a reference type for argument to Pin"),
+                        };
+                        Ty::new_pinned_ref(self.tcx, region, inner_ty, mutbl)
+                    }
+                    _ => bug!("Cannot adjust receiver type for reborrowing pin of {target:?}"),
+                };
+
+                adjustments.push(Adjustment { kind: Adjust::ReborrowPin(region, mutbl), target });
             }
             None => {}
         }
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 43481c9704a..86584860b79 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -1113,6 +1113,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                                 unstable_candidates.as_deref_mut(),
                             )
                         })
+                        .or_else(|| {
+                            self.pick_reborrow_pin_method(
+                                step,
+                                self_ty,
+                                unstable_candidates.as_deref_mut(),
+                            )
+                        })
                     })
             })
     }
@@ -1147,7 +1154,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                         })
                     }
 
-                    ty::Adt(def, args) if self.tcx.is_lang_item(def.did(), hir::LangItem::Pin) => {
+                    ty::Adt(def, args)
+                        if self.tcx.features().pin_ergonomics
+                            && self.tcx.is_lang_item(def.did(), hir::LangItem::Pin) =>
+                    {
                         // make sure this is a pinned reference (and not a `Pin<Box>` or something)
                         if let ty::Ref(_, _, mutbl) = args[0].expect_ty().kind() {
                             pick.autoref_or_ptr_adjustment =
@@ -1186,6 +1196,43 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         })
     }
 
+    /// Looks for applicable methods if we reborrow a `Pin<&mut T>` as a `Pin<&T>`.
+    #[instrument(level = "debug", skip(self, step, unstable_candidates))]
+    fn pick_reborrow_pin_method(
+        &self,
+        step: &CandidateStep<'tcx>,
+        self_ty: Ty<'tcx>,
+        unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
+    ) -> Option<PickResult<'tcx>> {
+        if !self.tcx.features().pin_ergonomics {
+            return None;
+        }
+
+        // make sure self is a Pin<&mut T>
+        let inner_ty = match self_ty.kind() {
+            ty::Adt(def, args) if self.tcx.is_lang_item(def.did(), hir::LangItem::Pin) => {
+                match args[0].expect_ty().kind() {
+                    ty::Ref(_, ty, hir::Mutability::Mut) => *ty,
+                    _ => {
+                        return None;
+                    }
+                }
+            }
+            _ => return None,
+        };
+
+        let region = self.tcx.lifetimes.re_erased;
+        let autopin_ty = Ty::new_pinned_ref(self.tcx, region, inner_ty, hir::Mutability::Not);
+        self.pick_method(autopin_ty, unstable_candidates).map(|r| {
+            r.map(|mut pick| {
+                pick.autoderefs = step.autoderefs;
+                pick.autoref_or_ptr_adjustment =
+                    Some(AutorefOrPtrAdjustment::ReborrowPin(hir::Mutability::Not));
+                pick
+            })
+        })
+    }
+
     /// If `self_ty` is `*mut T` then this picks `*const T` methods. The reason why we have a
     /// special case for this is because going from `*mut T` to `*const T` with autoderefs and
     /// autorefs would require dereferencing the pointer, which is not safe.
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index db9978a7f53..fc4fb917283 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -584,6 +584,16 @@ impl<'tcx> Ty<'tcx> {
         Ty::new_ref(tcx, r, ty, hir::Mutability::Not)
     }
 
+    pub fn new_pinned_ref(
+        tcx: TyCtxt<'tcx>,
+        r: Region<'tcx>,
+        ty: Ty<'tcx>,
+        mutbl: ty::Mutability,
+    ) -> Ty<'tcx> {
+        let pin = tcx.adt_def(tcx.require_lang_item(LangItem::Pin, None));
+        Ty::new_adt(tcx, pin, tcx.mk_args(&[Ty::new_ref(tcx, r, ty, mutbl).into()]))
+    }
+
     #[inline]
     pub fn new_ptr(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, mutbl: ty::Mutability) -> Ty<'tcx> {
         Ty::new(tcx, ty::RawPtr(ty, mutbl))
diff --git a/tests/ui/async-await/pin-reborrow-self.rs b/tests/ui/async-await/pin-reborrow-self.rs
index ab36ce575e1..ee617617da0 100644
--- a/tests/ui/async-await/pin-reborrow-self.rs
+++ b/tests/ui/async-await/pin-reborrow-self.rs
@@ -22,8 +22,7 @@ pub fn bar(x: Pin<&mut Foo>) {
 
     Foo::baz(x);
 
-    // FIXME: We should allow downgrading a Pin<&mut T> to Pin<&T>
-    // x.baz();
+    x.baz();
 }
 
 pub fn baaz(x: Pin<&Foo>) {
diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs
index d694531d53a..d56a046fd62 100644
--- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs
+++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs
@@ -4,12 +4,20 @@ use std::pin::Pin;
 
 struct Foo;
 
+impl Foo {
+    fn foo(self: Pin<&mut Self>) {
+    }
+}
+
 fn foo(_: Pin<&mut Foo>) {
 }
 
 fn bar(mut x: Pin<&mut Foo>) {
     foo(x);
     foo(x); //~ ERROR use of moved value: `x`
+
+    x.foo(); //~ ERROR use of moved value: `x`
+    x.foo(); //~ 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
index 6c9029d8176..bc49088f3d7 100644
--- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr
+++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr
@@ -1,5 +1,5 @@
 error[E0382]: use of moved value: `x`
-  --> $DIR/feature-gate-pin_ergonomics.rs:12:9
+  --> $DIR/feature-gate-pin_ergonomics.rs:17: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
@@ -9,13 +9,54 @@ 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
+  --> $DIR/feature-gate-pin_ergonomics.rs:12:11
    |
 LL | fn foo(_: Pin<&mut Foo>) {
    |    ---    ^^^^^^^^^^^^^ this parameter takes ownership of the value
    |    |
    |    in this function
 
-error: aborting due to 1 previous error
+error[E0382]: use of moved value: `x`
+  --> $DIR/feature-gate-pin_ergonomics.rs:19:5
+   |
+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);
+LL |     foo(x);
+   |         - value moved here
+LL |
+LL |     x.foo();
+   |     ^ 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:12:11
+   |
+LL | fn foo(_: Pin<&mut Foo>) {
+   |    ---    ^^^^^^^^^^^^^ this parameter takes ownership of the value
+   |    |
+   |    in this function
+
+error[E0382]: use of moved value: `x`
+  --> $DIR/feature-gate-pin_ergonomics.rs:20:5
+   |
+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 |     x.foo();
+   |       ----- `x` moved due to this method call
+LL |     x.foo();
+   |     ^ value used here after move
+   |
+note: `Foo::foo` takes ownership of the receiver `self`, which moves `x`
+  --> $DIR/feature-gate-pin_ergonomics.rs:8:12
+   |
+LL |     fn foo(self: Pin<&mut Self>) {
+   |            ^^^^
+help: consider reborrowing the `Pin` instead of moving it
+   |
+LL |     x.as_mut().foo();
+   |      +++++++++
+
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0382`.