about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2020-02-13 02:52:57 +0100
committerGitHub <noreply@github.com>2020-02-13 02:52:57 +0100
commitec5bf15c5d1b5969908251017f1e61d2f2961a87 (patch)
tree5fef428c2e3ded862487e18f4e4e4fa439a9c9a5
parent2501a10670c331ad5fb51c64c6d991a9a66f8f31 (diff)
parent248f5a4046ab5a90189f37c305c759b7cde8acb3 (diff)
downloadrust-ec5bf15c5d1b5969908251017f1e61d2f2961a87.tar.gz
rust-ec5bf15c5d1b5969908251017f1e61d2f2961a87.zip
Rollup merge of #69082 - estebank:boxfuture-box-pin, r=tmandry
When expecting `BoxFuture` and using `async {}`, suggest `Box::pin`

Fix #68197, cc #69083.
-rw-r--r--src/libcore/marker.rs4
-rw-r--r--src/librustc/traits/error_reporting/on_unimplemented.rs10
-rw-r--r--src/librustc/traits/error_reporting/suggestions.rs5
-rw-r--r--src/librustc_typeck/check/demand.rs3
-rw-r--r--src/librustc_typeck/check/mod.rs54
-rw-r--r--src/test/ui/generator/static-not-unpin.rs2
-rw-r--r--src/test/ui/generator/static-not-unpin.stderr2
-rw-r--r--src/test/ui/suggestions/expected-boxed-future-isnt-pinned.rs29
-rw-r--r--src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr20
9 files changed, 119 insertions, 10 deletions
diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs
index b4b595f330e..2800f11cc01 100644
--- a/src/libcore/marker.rs
+++ b/src/libcore/marker.rs
@@ -727,6 +727,10 @@ unsafe impl<T: ?Sized> Freeze for &mut T {}
 /// [`Pin<P>`]: ../pin/struct.Pin.html
 /// [`pin module`]: ../../std/pin/index.html
 #[stable(feature = "pin", since = "1.33.0")]
+#[rustc_on_unimplemented(
+    on(_Self = "std::future::Future", note = "consider using `Box::pin`",),
+    message = "`{Self}` cannot be unpinned"
+)]
 #[lang = "unpin"]
 pub auto trait Unpin {}
 
diff --git a/src/librustc/traits/error_reporting/on_unimplemented.rs b/src/librustc/traits/error_reporting/on_unimplemented.rs
index 2ba12baaf6d..ab2d74b1c8d 100644
--- a/src/librustc/traits/error_reporting/on_unimplemented.rs
+++ b/src/librustc/traits/error_reporting/on_unimplemented.rs
@@ -201,6 +201,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 }
             }
         }
+        if let ty::Dynamic(traits, _) = self_ty.kind {
+            for t in *traits.skip_binder() {
+                match t {
+                    ty::ExistentialPredicate::Trait(trait_ref) => {
+                        flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id))))
+                    }
+                    _ => {}
+                }
+            }
+        }
 
         if let Ok(Some(command)) =
             OnUnimplementedDirective::of_item(self.tcx, trait_ref.def_id, def_id)
diff --git a/src/librustc/traits/error_reporting/suggestions.rs b/src/librustc/traits/error_reporting/suggestions.rs
index 60e55bd7bd9..82b73518d09 100644
--- a/src/librustc/traits/error_reporting/suggestions.rs
+++ b/src/librustc/traits/error_reporting/suggestions.rs
@@ -701,10 +701,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     })
                     .collect::<Vec<_>>();
                 // Add the suggestion for the return type.
-                suggestions.push((
-                    ret_ty.span,
-                    format!("Box<{}{}>", if has_dyn { "" } else { "dyn " }, snippet),
-                ));
+                suggestions.push((ret_ty.span, format!("Box<dyn {}>", trait_obj)));
                 err.multipart_suggestion(
                     "return a boxed trait object instead",
                     suggestions,
diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs
index 3c2e02fc79b..4a98095ec89 100644
--- a/src/librustc_typeck/check/demand.rs
+++ b/src/librustc_typeck/check/demand.rs
@@ -24,6 +24,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.annotate_expected_due_to_let_ty(err, expr);
         self.suggest_compatible_variants(err, expr, expected, expr_ty);
         self.suggest_ref_or_into(err, expr, expected, expr_ty);
+        if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) {
+            return;
+        }
         self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
         self.suggest_missing_await(err, expr, expected, expr_ty);
     }
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 0bdf7ba1a56..fd0c994a6ea 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -5038,14 +5038,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 Applicability::MachineApplicable,
             );
             err.note(
-                "for more on the distinction between the stack and the \
-                        heap, read https://doc.rust-lang.org/book/ch15-01-box.html, \
-                        https://doc.rust-lang.org/rust-by-example/std/box.html, and \
-                        https://doc.rust-lang.org/std/boxed/index.html",
+                "for more on the distinction between the stack and the heap, read \
+                 https://doc.rust-lang.org/book/ch15-01-box.html, \
+                 https://doc.rust-lang.org/rust-by-example/std/box.html, and \
+                 https://doc.rust-lang.org/std/boxed/index.html",
             );
         }
     }
 
+    /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
+    fn suggest_calling_boxed_future_when_appropriate(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        expr: &hir::Expr<'_>,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+    ) -> bool {
+        // Handle #68197.
+
+        if self.tcx.hir().is_const_context(expr.hir_id) {
+            // Do not suggest `Box::new` in const context.
+            return false;
+        }
+        let pin_did = self.tcx.lang_items().pin_type();
+        match expected.kind {
+            ty::Adt(def, _) if Some(def.did) != pin_did => return false,
+            // This guards the `unwrap` and `mk_box` below.
+            _ if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() => return false,
+            _ => {}
+        }
+        let boxed_found = self.tcx.mk_box(found);
+        let new_found = self.tcx.mk_lang_item(boxed_found, lang_items::PinTypeLangItem).unwrap();
+        if let (true, Ok(snippet)) = (
+            self.can_coerce(new_found, expected),
+            self.sess().source_map().span_to_snippet(expr.span),
+        ) {
+            match found.kind {
+                ty::Adt(def, _) if def.is_box() => {
+                    err.help("use `Box::pin`");
+                }
+                _ => {
+                    err.span_suggestion(
+                        expr.span,
+                        "you need to pin and box this expression",
+                        format!("Box::pin({})", snippet),
+                        Applicability::MachineApplicable,
+                    );
+                }
+            }
+            true
+        } else {
+            false
+        }
+    }
+
     /// A common error is to forget to add a semicolon at the end of a block, e.g.,
     ///
     /// ```
diff --git a/src/test/ui/generator/static-not-unpin.rs b/src/test/ui/generator/static-not-unpin.rs
index b271e982fb4..cfcb94737be 100644
--- a/src/test/ui/generator/static-not-unpin.rs
+++ b/src/test/ui/generator/static-not-unpin.rs
@@ -11,5 +11,5 @@ fn main() {
     let mut generator = static || {
         yield;
     };
-    assert_unpin(generator); //~ ERROR std::marker::Unpin` is not satisfied
+    assert_unpin(generator); //~ ERROR E0277
 }
diff --git a/src/test/ui/generator/static-not-unpin.stderr b/src/test/ui/generator/static-not-unpin.stderr
index f2b1078e2b5..6512d67319b 100644
--- a/src/test/ui/generator/static-not-unpin.stderr
+++ b/src/test/ui/generator/static-not-unpin.stderr
@@ -1,4 +1,4 @@
-error[E0277]: the trait bound `[static generator@$DIR/static-not-unpin.rs:11:25: 13:6 _]: std::marker::Unpin` is not satisfied
+error[E0277]: `[static generator@$DIR/static-not-unpin.rs:11:25: 13:6 _]` cannot be unpinned
   --> $DIR/static-not-unpin.rs:14:18
    |
 LL | fn assert_unpin<T: Unpin>(_: T) {
diff --git a/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.rs b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.rs
new file mode 100644
index 00000000000..0a1686eac9d
--- /dev/null
+++ b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.rs
@@ -0,0 +1,29 @@
+// edition:2018
+#![allow(dead_code)]
+use std::future::Future;
+use std::pin::Pin;
+
+type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
+//   ^^^^^^^^^ This would come from the `futures` crate in real code.
+
+fn foo<F: Future<Output=i32> + Send + 'static>(x: F) -> BoxFuture<'static, i32> {
+    // We could instead use an `async` block, but this way we have no std spans.
+    x //~ ERROR mismatched types
+}
+
+// FIXME: uncomment these once this commit is in Beta and we can rely on `rustc_on_unimplemented`
+//        having filtering for `Self` being a trait.
+//
+// fn bar<F: Future<Output=i32> + Send + 'static>(x: F) -> BoxFuture<'static, i32> {
+//     Box::new(x)
+// }
+//
+// fn baz<F: Future<Output=i32> + Send + 'static>(x: F) -> BoxFuture<'static, i32> {
+//     Pin::new(x)
+// }
+//
+// fn qux<F: Future<Output=i32> + Send + 'static>(x: F) -> BoxFuture<'static, i32> {
+//     Pin::new(Box::new(x))
+// }
+
+fn main() {}
diff --git a/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr
new file mode 100644
index 00000000000..48d941283b6
--- /dev/null
+++ b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr
@@ -0,0 +1,20 @@
+error[E0308]: mismatched types
+  --> $DIR/expected-boxed-future-isnt-pinned.rs:11:5
+   |
+LL | fn foo<F: Future<Output=i32> + Send + 'static>(x: F) -> BoxFuture<'static, i32> {
+   |        - this type parameter                            ----------------------- expected `std::pin::Pin<std::boxed::Box<(dyn std::future::Future<Output = i32> + std::marker::Send + 'static)>>` because of return type
+LL |     // We could instead use an `async` block, but this way we have no std spans.
+LL |     x
+   |     ^
+   |     |
+   |     expected struct `std::pin::Pin`, found type parameter `F`
+   |     help: you need to pin and box this expression: `Box::pin(x)`
+   |
+   = note:      expected struct `std::pin::Pin<std::boxed::Box<(dyn std::future::Future<Output = i32> + std::marker::Send + 'static)>>`
+           found type parameter `F`
+   = help: type parameters must be constrained to match other types
+   = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.