diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index b4b595f330e22..2800f11cc01b1 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -727,6 +727,10 @@ unsafe impl Freeze for &mut T {} /// [`Pin

`]: ../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 2ba12baaf6d6e..ab2d74b1c8deb 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 60e55bd7bd9ad..82b73518d09a8 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::>(); // 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", 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 3c2e02fc79b3a..4a98095ec89c6 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 be2052dce3c0b..6d37526df2c22 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 b271e982fb420..cfcb94737be6f 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 f2b1078e2b532..6512d67319b0b 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) { 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 0000000000000..0a1686eac9d34 --- /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 + Send + 'a>>; +// ^^^^^^^^^ This would come from the `futures` crate in real code. + +fn foo + 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 + Send + 'static>(x: F) -> BoxFuture<'static, i32> { +// Box::new(x) +// } +// +// fn baz + Send + 'static>(x: F) -> BoxFuture<'static, i32> { +// Pin::new(x) +// } +// +// fn qux + 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 0000000000000..48d941283b62d --- /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 + Send + 'static>(x: F) -> BoxFuture<'static, i32> { + | - this type parameter ----------------------- expected `std::pin::Pin + 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::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`.