Skip to content

Commit

Permalink
Allow reborrowing pinned self methods
Browse files Browse the repository at this point in the history
  • Loading branch information
eholk committed Sep 20, 2024
1 parent 220a6ac commit d3c53aa
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 10 deletions.
16 changes: 12 additions & 4 deletions compiler/rustc_hir_typeck/src/method/confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {}
}
Expand Down
49 changes: 48 additions & 1 deletion compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,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(),
)
})
})
})
}
Expand Down Expand Up @@ -1136,7 +1143,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 =
Expand Down Expand Up @@ -1175,6 +1185,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.
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
3 changes: 1 addition & 2 deletions tests/ui/async-await/pin-reborrow-self.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>) {
Expand Down
8 changes: 8 additions & 0 deletions tests/ui/feature-gates/feature-gate-pin_ergonomics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
47 changes: 44 additions & 3 deletions tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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`.

0 comments on commit d3c53aa

Please sign in to comment.