From 3dfb30c70a2a8da87eefa01a56d753d1698866c9 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Thu, 19 Sep 2024 19:35:01 -0700 Subject: [PATCH] Allow reborrowing pinned self methods --- .../rustc_hir_typeck/src/method/confirm.rs | 16 ++++-- compiler/rustc_hir_typeck/src/method/probe.rs | 49 ++++++++++++++++++- compiler/rustc_middle/src/ty/sty.rs | 10 ++++ tests/ui/async-await/pin-reborrow-self.rs | 3 +- .../feature-gate-pin_ergonomics.rs | 8 +++ .../feature-gate-pin_ergonomics.stderr | 47 ++++++++++++++++-- 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` 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> { + 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`.