Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggest pin!() instead of Pin::new() when appropriate #114654

Merged
merged 2 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 68 additions & 6 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2495,10 +2495,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Try alternative arbitrary self types that could fulfill this call.
// FIXME: probe for all types that *could* be arbitrary self-types, not
// just this list.
for (rcvr_ty, post) in &[
(rcvr_ty, ""),
(Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "),
(Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, rcvr_ty), "&"),
for (rcvr_ty, post, pin_call) in &[
estebank marked this conversation as resolved.
Show resolved Hide resolved
(rcvr_ty, "", None),
(
Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_erased, rcvr_ty),
"&mut ",
Some("as_mut"),
),
(
Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, rcvr_ty),
"&",
Some("as_ref"),
),
] {
match self.lookup_probe_for_diagnostic(
item_name,
Expand Down Expand Up @@ -2532,6 +2540,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Err(_) => (),
}

let pred = ty::TraitRef::new(
self.tcx,
self.tcx.lang_items().unpin_trait().unwrap(),
[*rcvr_ty],
);
let unpin = self.predicate_must_hold_considering_regions(&Obligation::new(
self.tcx,
ObligationCause::misc(rcvr.span, self.body_id),
self.param_env,
pred,
));
for (rcvr_ty, pre) in &[
(Ty::new_lang_item(self.tcx, *rcvr_ty, LangItem::OwnedBox), "Box::new"),
(Ty::new_lang_item(self.tcx, *rcvr_ty, LangItem::Pin), "Pin::new"),
Expand All @@ -2555,7 +2574,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Explicitly ignore the `Pin::as_ref()` method as `Pin` does not
// implement the `AsRef` trait.
let skip = skippable.contains(&did)
|| (("Pin::new" == *pre) && (sym::as_ref == item_name.name))
|| (("Pin::new" == *pre) && ((sym::as_ref == item_name.name) || !unpin))
|| inputs_len.is_some_and(|inputs_len| pick.item.kind == ty::AssocKind::Fn && self.tcx.fn_sig(pick.item.def_id).skip_binder().skip_binder().inputs().len() != inputs_len);
// Make sure the method is defined for the *actual* receiver: we don't
// want to treat `Box<Self>` as a receiver if it only works because of
Expand All @@ -2567,7 +2586,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
err.multipart_suggestion(
"consider wrapping the receiver expression with the \
appropriate type",
appropriate type",
vec![
(rcvr.span.shrink_to_lo(), format!("{pre}({post}")),
(rcvr.span.shrink_to_hi(), ")".to_string()),
Expand All @@ -2579,6 +2598,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
}
// We special case the situation where `Pin::new` wouldn't work, and instead
// suggest using the `pin!()` macro instead.
if let Some(new_rcvr_t) = Ty::new_lang_item(self.tcx, *rcvr_ty, LangItem::Pin)
// We didn't find an alternative receiver for the method.
&& !alt_rcvr_sugg
// `T: !Unpin`
&& !unpin
// The method isn't `as_ref`, as it would provide a wrong suggestion for `Pin`.
&& sym::as_ref != item_name.name
// Either `Pin::as_ref` or `Pin::as_mut`.
&& let Some(pin_call) = pin_call
// Search for `item_name` as a method accessible on `Pin<T>`.
&& let Ok(pick) = self.lookup_probe_for_diagnostic(
item_name,
new_rcvr_t,
rcvr,
ProbeScope::AllTraits,
return_type,
)
// We skip some common traits that we don't want to consider because autoderefs
// would take care of them.
&& !skippable.contains(&Some(pick.item.container_id(self.tcx)))
// We don't want to go through derefs.
&& pick.autoderefs == 0
// Check that the method of the same name that was found on the new `Pin<T>`
// receiver has the same number of arguments that appear in the user's code.
&& inputs_len.is_some_and(|inputs_len| pick.item.kind == ty::AssocKind::Fn && self.tcx.fn_sig(pick.item.def_id).skip_binder().skip_binder().inputs().len() == inputs_len)
{
let indent = self.tcx.sess
.source_map()
.indentation_before(rcvr.span)
.unwrap_or_else(|| " ".to_string());
err.multipart_suggestion(
"consider pinning the expression",
vec![
(rcvr.span.shrink_to_lo(), format!("let mut pinned = std::pin::pin!(")),
(rcvr.span.shrink_to_hi(), format!(");\n{indent}pinned.{pin_call}()")),
],
Applicability::MaybeIncorrect,
);
// We don't care about the other suggestions.
alt_rcvr_sugg = true;
}
}
}
if self.suggest_valid_traits(err, valid_out_of_scope_traits) {
Expand Down
16 changes: 16 additions & 0 deletions tests/ui/async-await/issue-108572.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// edition: 2021
// run-rustfix
#![allow(unused_must_use, dead_code)]

use std::future::Future;
fn foo() -> impl Future<Output=()> {
async { }
}

fn bar(cx: &mut std::task::Context<'_>) {
let fut = foo();
let mut pinned = std::pin::pin!(fut);
pinned.as_mut().poll(cx);
//~^ ERROR no method named `poll` found for opaque type `impl Future<Output = ()>` in the current scope [E0599]
}
fn main() {}
7 changes: 5 additions & 2 deletions tests/ui/async-await/issue-108572.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
// edition: 2021
// run-rustfix
#![allow(unused_must_use, dead_code)]

use std::future::Future;
fn foo() -> impl Future<Output=()> {
async { }
}

fn main() {
fn bar(cx: &mut std::task::Context<'_>) {
let fut = foo();
fut.poll();
fut.poll(cx);
//~^ ERROR no method named `poll` found for opaque type `impl Future<Output = ()>` in the current scope [E0599]
}
fn main() {}
9 changes: 7 additions & 2 deletions tests/ui/async-await/issue-108572.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
error[E0599]: no method named `poll` found for opaque type `impl Future<Output = ()>` in the current scope
--> $DIR/issue-108572.rs:10:9
--> $DIR/issue-108572.rs:12:9
|
LL | fut.poll();
LL | fut.poll(cx);
| ^^^^ method not found in `impl Future<Output = ()>`
|
= help: method `poll` found on `Pin<&mut impl Future<Output = ()>>`, see documentation for `std::pin::Pin`
= help: self type must be pinned to call `Future::poll`, see https://rust-lang.github.io/async-book/04_pinning/01_chapter.html#pinning-in-practice
help: consider pinning the expression
|
LL ~ let mut pinned = std::pin::pin!(fut);
LL ~ pinned.as_mut().poll(cx);
|

error: aborting due to previous error

Expand Down
8 changes: 3 additions & 5 deletions tests/ui/async-await/pin-needed-to-poll.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@ LL | struct Sleep;
...
LL | self.sleep.poll(cx)
| ^^^^ method not found in `Sleep`
--> $SRC_DIR/core/src/future/future.rs:LL:COL
|
= note: the method is available for `Pin<&mut Sleep>` here
help: consider pinning the expression
|
help: consider wrapping the receiver expression with the appropriate type
LL ~ let mut pinned = std::pin::pin!(self.sleep);
LL ~ pinned.as_mut().poll(cx)
|
LL | Pin::new(&mut self.sleep).poll(cx)
| +++++++++++++ +

error: aborting due to previous error

Expand Down
3 changes: 2 additions & 1 deletion tests/ui/self/arbitrary_self_types_needing_mut_pin.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ impl S {
}

fn main() {
Pin::new(&mut S).x(); //~ ERROR no method named `x` found
let mut pinned = std::pin::pin!(S);
pinned.as_mut().x(); //~ ERROR no method named `x` found
}
10 changes: 4 additions & 6 deletions tests/ui/self/arbitrary_self_types_needing_mut_pin.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,14 @@ error[E0599]: no method named `x` found for struct `S` in the current scope
LL | struct S;
| -------- method `x` not found for this struct
...
LL | fn x(self: Pin<&mut Self>) {
| - the method is available for `Pin<&mut S>` here
...
LL | S.x();
| ^ method not found in `S`
|
help: consider wrapping the receiver expression with the appropriate type
help: consider pinning the expression
|
LL ~ let mut pinned = std::pin::pin!(S);
LL ~ pinned.as_mut().x();
|
LL | Pin::new(&mut S).x();
| +++++++++++++ +

error: aborting due to previous error

Expand Down