about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs74
-rw-r--r--tests/ui/async-await/issue-108572.fixed16
-rw-r--r--tests/ui/async-await/issue-108572.rs7
-rw-r--r--tests/ui/async-await/issue-108572.stderr9
-rw-r--r--tests/ui/async-await/pin-needed-to-poll.stderr8
-rw-r--r--tests/ui/self/arbitrary_self_types_needing_mut_pin.fixed3
-rw-r--r--tests/ui/self/arbitrary_self_types_needing_mut_pin.stderr10
7 files changed, 105 insertions, 22 deletions
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 07c48ec6392..a4bbb16026a 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -2494,10 +2494,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 &[
+                (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,
@@ -2531,6 +2539,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"),
@@ -2554,7 +2573,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
@@ -2566,7 +2585,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()),
@@ -2578,6 +2597,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) {
diff --git a/tests/ui/async-await/issue-108572.fixed b/tests/ui/async-await/issue-108572.fixed
new file mode 100644
index 00000000000..8f0133d97b5
--- /dev/null
+++ b/tests/ui/async-await/issue-108572.fixed
@@ -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() {}
diff --git a/tests/ui/async-await/issue-108572.rs b/tests/ui/async-await/issue-108572.rs
index efcb8b8ebab..3596580763c 100644
--- a/tests/ui/async-await/issue-108572.rs
+++ b/tests/ui/async-await/issue-108572.rs
@@ -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() {}
diff --git a/tests/ui/async-await/issue-108572.stderr b/tests/ui/async-await/issue-108572.stderr
index 0dbcf4d660a..588669092b0 100644
--- a/tests/ui/async-await/issue-108572.stderr
+++ b/tests/ui/async-await/issue-108572.stderr
@@ -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
 
diff --git a/tests/ui/async-await/pin-needed-to-poll.stderr b/tests/ui/async-await/pin-needed-to-poll.stderr
index b1f4a73aafe..964709daeda 100644
--- a/tests/ui/async-await/pin-needed-to-poll.stderr
+++ b/tests/ui/async-await/pin-needed-to-poll.stderr
@@ -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
 
diff --git a/tests/ui/self/arbitrary_self_types_needing_mut_pin.fixed b/tests/ui/self/arbitrary_self_types_needing_mut_pin.fixed
index ccd65ff4091..a400a1672a4 100644
--- a/tests/ui/self/arbitrary_self_types_needing_mut_pin.fixed
+++ b/tests/ui/self/arbitrary_self_types_needing_mut_pin.fixed
@@ -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
 }
diff --git a/tests/ui/self/arbitrary_self_types_needing_mut_pin.stderr b/tests/ui/self/arbitrary_self_types_needing_mut_pin.stderr
index f34ce4dce49..5dcb5861120 100644
--- a/tests/ui/self/arbitrary_self_types_needing_mut_pin.stderr
+++ b/tests/ui/self/arbitrary_self_types_needing_mut_pin.stderr
@@ -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