Rollup merge of #130633 - eholk:pin-reborrow-self, r=compiler-errors
Add support for reborrowing pinned method receivers This builds on #130526 to add pinned reborrowing for method receivers. This enables the folllowing examples to work: ```rust #![feature(pin_ergonomics)] #![allow(incomplete_features)] use std::pin::Pin; pub struct Foo; impl Foo { fn foo(self: Pin<&mut Self>) { } fn baz(self: Pin<&Self>) { } } pub fn bar(x: Pin<&mut Foo>) { x.foo(); x.foo(); x.baz(); // Pin<&mut Foo> is downgraded to Pin<&Foo> } pub fn baaz(x: Pin<&Foo>) { x.baz(); x.baz(); } ``` This PR includes the original one, which is currently in the commit queue, but the only code changes are in the latest commit (d3c53aaa5c6fcb1018c58d229bc5d92202fa6880). #130494 r? `@compiler-errors`
This commit is contained in:
commit
68de7d11a9
7 changed files with 171 additions and 22 deletions
|
@ -235,6 +235,23 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
||||||
target,
|
target,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some(probe::AutorefOrPtrAdjustment::ReborrowPin(mutbl)) => {
|
||||||
|
let region = self.next_region_var(infer::Autoref(self.span));
|
||||||
|
|
||||||
|
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 => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,16 +121,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
mutbl.ref_prefix_str()
|
mutbl.ref_prefix_str()
|
||||||
}
|
}
|
||||||
Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
|
Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
|
||||||
|
Some(probe::AutorefOrPtrAdjustment::ReborrowPin(mutbl)) => match mutbl {
|
||||||
|
hir::Mutability::Mut => "Pin<&mut ",
|
||||||
|
hir::Mutability::Not => "Pin<&",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span)
|
if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span)
|
||||||
{
|
{
|
||||||
let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
|
let mut self_adjusted =
|
||||||
|
if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
|
||||||
|
pick.autoref_or_ptr_adjustment
|
||||||
|
{
|
||||||
|
format!("{derefs}{self_expr} as *const _")
|
||||||
|
} else {
|
||||||
|
format!("{autoref}{derefs}{self_expr}")
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(probe::AutorefOrPtrAdjustment::ReborrowPin(_)) =
|
||||||
pick.autoref_or_ptr_adjustment
|
pick.autoref_or_ptr_adjustment
|
||||||
{
|
{
|
||||||
format!("{derefs}{self_expr} as *const _")
|
self_adjusted.push('>');
|
||||||
} else {
|
}
|
||||||
format!("{autoref}{derefs}{self_expr}")
|
|
||||||
};
|
|
||||||
|
|
||||||
lint.span_suggestion(
|
lint.span_suggestion(
|
||||||
sp,
|
sp,
|
||||||
|
@ -400,6 +411,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
let autoref = match pick.autoref_or_ptr_adjustment {
|
let autoref = match pick.autoref_or_ptr_adjustment {
|
||||||
Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, .. }) => mutbl.ref_prefix_str(),
|
Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, .. }) => mutbl.ref_prefix_str(),
|
||||||
Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
|
Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
|
||||||
|
Some(probe::AutorefOrPtrAdjustment::ReborrowPin(mutbl)) => match mutbl {
|
||||||
|
hir::Mutability::Mut => "Pin<&mut ",
|
||||||
|
hir::Mutability::Not => "Pin<&",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let (expr_text, precise) = if let Some(expr_text) = expr
|
let (expr_text, precise) = if let Some(expr_text) = expr
|
||||||
|
@ -412,7 +427,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
("(..)".to_string(), false)
|
("(..)".to_string(), false)
|
||||||
};
|
};
|
||||||
|
|
||||||
let adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
|
let mut adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
|
||||||
pick.autoref_or_ptr_adjustment
|
pick.autoref_or_ptr_adjustment
|
||||||
{
|
{
|
||||||
format!("{derefs}{expr_text} as *const _")
|
format!("{derefs}{expr_text} as *const _")
|
||||||
|
@ -420,6 +435,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
format!("{autoref}{derefs}{expr_text}")
|
format!("{autoref}{derefs}{expr_text}")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Some(probe::AutorefOrPtrAdjustment::ReborrowPin(_)) = pick.autoref_or_ptr_adjustment
|
||||||
|
{
|
||||||
|
adjusted_text.push('>');
|
||||||
|
}
|
||||||
|
|
||||||
(adjusted_text, precise)
|
(adjusted_text, precise)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,7 +136,7 @@ enum ProbeResult {
|
||||||
/// `mut`), or it has type `*mut T` and we convert it to `*const T`.
|
/// `mut`), or it has type `*mut T` and we convert it to `*const T`.
|
||||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
pub(crate) enum AutorefOrPtrAdjustment {
|
pub(crate) enum AutorefOrPtrAdjustment {
|
||||||
/// Receiver has type `T`, add `&` or `&mut` (it `T` is `mut`), and maybe also "unsize" it.
|
/// Receiver has type `T`, add `&` or `&mut` (if `T` is `mut`), and maybe also "unsize" it.
|
||||||
/// Unsizing is used to convert a `[T; N]` to `[T]`, which only makes sense when autorefing.
|
/// Unsizing is used to convert a `[T; N]` to `[T]`, which only makes sense when autorefing.
|
||||||
Autoref {
|
Autoref {
|
||||||
mutbl: hir::Mutability,
|
mutbl: hir::Mutability,
|
||||||
|
@ -147,6 +147,9 @@ pub(crate) enum AutorefOrPtrAdjustment {
|
||||||
},
|
},
|
||||||
/// Receiver has type `*mut T`, convert to `*const T`
|
/// Receiver has type `*mut T`, convert to `*const T`
|
||||||
ToConstPtr,
|
ToConstPtr,
|
||||||
|
|
||||||
|
/// Reborrow a `Pin<&mut T>` or `Pin<&T>`.
|
||||||
|
ReborrowPin(hir::Mutability),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AutorefOrPtrAdjustment {
|
impl AutorefOrPtrAdjustment {
|
||||||
|
@ -154,6 +157,7 @@ impl AutorefOrPtrAdjustment {
|
||||||
match self {
|
match self {
|
||||||
AutorefOrPtrAdjustment::Autoref { mutbl: _, unsize } => *unsize,
|
AutorefOrPtrAdjustment::Autoref { mutbl: _, unsize } => *unsize,
|
||||||
AutorefOrPtrAdjustment::ToConstPtr => false,
|
AutorefOrPtrAdjustment::ToConstPtr => false,
|
||||||
|
AutorefOrPtrAdjustment::ReborrowPin(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1103,6 +1107,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
||||||
unstable_candidates.as_deref_mut(),
|
unstable_candidates.as_deref_mut(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
self.pick_reborrow_pin_method(
|
||||||
|
step,
|
||||||
|
self_ty,
|
||||||
|
unstable_candidates.as_deref_mut(),
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1127,13 +1138,28 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
||||||
r.map(|mut pick| {
|
r.map(|mut pick| {
|
||||||
pick.autoderefs = step.autoderefs;
|
pick.autoderefs = step.autoderefs;
|
||||||
|
|
||||||
// Insert a `&*` or `&mut *` if this is a reference type:
|
match *step.self_ty.value.value.kind() {
|
||||||
if let ty::Ref(_, _, mutbl) = *step.self_ty.value.value.kind() {
|
// Insert a `&*` or `&mut *` if this is a reference type:
|
||||||
pick.autoderefs += 1;
|
ty::Ref(_, _, mutbl) => {
|
||||||
pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref {
|
pick.autoderefs += 1;
|
||||||
mutbl,
|
pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref {
|
||||||
unsize: pick.autoref_or_ptr_adjustment.is_some_and(|a| a.get_unsize()),
|
mutbl,
|
||||||
})
|
unsize: pick.autoref_or_ptr_adjustment.is_some_and(|a| a.get_unsize()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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 =
|
||||||
|
Some(AutorefOrPtrAdjustment::ReborrowPin(*mutbl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
pick
|
pick
|
||||||
|
@ -1164,6 +1190,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
|
/// 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
|
/// 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.
|
/// autorefs would require dereferencing the pointer, which is not safe.
|
||||||
|
|
|
@ -584,6 +584,16 @@ impl<'tcx> Ty<'tcx> {
|
||||||
Ty::new_ref(tcx, r, ty, hir::Mutability::Not)
|
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]
|
#[inline]
|
||||||
pub fn new_ptr(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, mutbl: ty::Mutability) -> Ty<'tcx> {
|
pub fn new_ptr(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, mutbl: ty::Mutability) -> Ty<'tcx> {
|
||||||
Ty::new(tcx, ty::RawPtr(ty, mutbl))
|
Ty::new(tcx, ty::RawPtr(ty, mutbl))
|
||||||
|
|
|
@ -1,24 +1,33 @@
|
||||||
//@ check-pass
|
//@ check-pass
|
||||||
//@ignore-test
|
|
||||||
|
|
||||||
// Currently ignored due to self reborrowing not being implemented for Pin
|
|
||||||
|
|
||||||
#![feature(pin_ergonomics)]
|
#![feature(pin_ergonomics)]
|
||||||
#![allow(incomplete_features)]
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
struct Foo;
|
pub struct Foo;
|
||||||
|
|
||||||
impl Foo {
|
impl Foo {
|
||||||
fn foo(self: Pin<&mut Self>) {
|
fn foo(self: Pin<&mut Self>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn baz(self: Pin<&Self>) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bar(x: Pin<&mut Foo>) {
|
pub fn bar(x: Pin<&mut Foo>) {
|
||||||
x.foo();
|
x.foo();
|
||||||
x.foo(); // for this to work we need to automatically reborrow,
|
x.foo(); // for this to work we need to automatically reborrow,
|
||||||
// as if the user had written `x.as_mut().foo()`.
|
// as if the user had written `x.as_mut().foo()`.
|
||||||
|
|
||||||
|
Foo::baz(x);
|
||||||
|
|
||||||
|
x.baz();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn baaz(x: Pin<&Foo>) {
|
||||||
|
x.baz();
|
||||||
|
x.baz();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -4,6 +4,11 @@ use std::pin::Pin;
|
||||||
|
|
||||||
struct Foo;
|
struct Foo;
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn foo(self: Pin<&mut Self>) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn foo(_: Pin<&mut Foo>) {
|
fn foo(_: Pin<&mut Foo>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,4 +17,9 @@ fn bar(mut x: Pin<&mut Foo>) {
|
||||||
foo(x); //~ ERROR use of moved value: `x`
|
foo(x); //~ ERROR use of moved value: `x`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn baz(mut x: Pin<&mut Foo>) {
|
||||||
|
x.foo();
|
||||||
|
x.foo(); //~ ERROR use of moved value: `x`
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
error[E0382]: use of moved value: `x`
|
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>) {
|
LL | fn bar(mut x: Pin<&mut Foo>) {
|
||||||
| ----- move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait
|
| ----- move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait
|
||||||
|
@ -9,13 +9,33 @@ LL | foo(x);
|
||||||
| ^ value used here after move
|
| ^ value used here after move
|
||||||
|
|
|
|
||||||
note: consider changing this parameter type in function `foo` to borrow instead if owning the value isn't necessary
|
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>) {
|
LL | fn foo(_: Pin<&mut Foo>) {
|
||||||
| --- ^^^^^^^^^^^^^ this parameter takes ownership of the value
|
| --- ^^^^^^^^^^^^^ this parameter takes ownership of the value
|
||||||
| |
|
| |
|
||||||
| in this function
|
| in this function
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error[E0382]: use of moved value: `x`
|
||||||
|
--> $DIR/feature-gate-pin_ergonomics.rs:22:5
|
||||||
|
|
|
||||||
|
LL | fn baz(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 2 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0382`.
|
For more information about this error, try `rustc --explain E0382`.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue