From c39b04ea851b821359534b540c0babb97de24122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 11 Feb 2020 17:19:05 -0800 Subject: [PATCH] When expecting `BoxFuture` and using `async {}`, suggest `Box::pin` --- .../traits/error_reporting/suggestions.rs | 5 +-- src/librustc_typeck/check/demand.rs | 1 + src/librustc_typeck/check/mod.rs | 44 +++++++++++++++++-- .../expected-boxed-future-isnt-pinned.fixed | 16 +++++++ .../expected-boxed-future-isnt-pinned.rs | 16 +++++++ .../expected-boxed-future-isnt-pinned.stderr | 27 ++++++++++++ 6 files changed, 101 insertions(+), 8 deletions(-) create mode 100644 src/test/ui/suggestions/expected-boxed-future-isnt-pinned.fixed create mode 100644 src/test/ui/suggestions/expected-boxed-future-isnt-pinned.rs create mode 100644 src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr diff --git a/src/librustc/traits/error_reporting/suggestions.rs b/src/librustc/traits/error_reporting/suggestions.rs index 60e55bd7bd9a..82b73518d09a 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 3c2e02fc79b3..ac5214ca756b 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -25,6 +25,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_compatible_variants(err, expr, expected, expr_ty); self.suggest_ref_or_into(err, expr, expected, expr_ty); self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty); + self.suggest_calling_boxed_future_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 be2052dce3c0..03c1f97246df 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -5038,10 +5038,46 @@ 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>, + ) { + // Handle #68197. + + if self.tcx.hir().is_const_context(expr.hir_id) { + // Do not suggest `Box::new` in const context. + return; + } + let pin_did = self.tcx.lang_items().pin_type(); + match expected.kind { + ty::Adt(def, _) if Some(def.did) != pin_did => return, + // This guards the `unwrap` and `mk_box` below. + _ if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() => return, + _ => {} + } + 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), + ) { + err.span_suggestion( + expr.span, + "you need to pin and box this expression", + format!("Box::pin({})", snippet), + Applicability::MachineApplicable, ); } } diff --git a/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.fixed b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.fixed new file mode 100644 index 000000000000..bddfd3ac9ccf --- /dev/null +++ b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.fixed @@ -0,0 +1,16 @@ +// edition:2018 +// run-rustfix +#![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() -> BoxFuture<'static, i32> { + Box::pin(async { //~ ERROR mismatched types + 42 + }) +} + +fn main() {} 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 000000000000..51818d6ae8f6 --- /dev/null +++ b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.rs @@ -0,0 +1,16 @@ +// edition:2018 +// run-rustfix +#![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() -> BoxFuture<'static, i32> { + async { //~ ERROR mismatched types + 42 + } +} + +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 000000000000..5e6f5c13b7a6 --- /dev/null +++ b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr @@ -0,0 +1,27 @@ +error[E0308]: mismatched types + --> $DIR/expected-boxed-future-isnt-pinned.rs:11:5 + | +LL | fn foo() -> BoxFuture<'static, i32> { + | ----------------------- expected `std::pin::Pin + std::marker::Send + 'static)>>` because of return type +LL | / async { +LL | | 42 +LL | | } + | |_____^ expected struct `std::pin::Pin`, found opaque type + | + ::: $SRC_DIR/libstd/future.rs:LL:COL + | +LL | pub fn from_generator>(x: T) -> impl Future { + | ------------------------------- the found opaque type + | + = note: expected struct `std::pin::Pin + std::marker::Send + 'static)>>` + found opaque type `impl std::future::Future` +help: you need to pin and box this expression + | +LL | Box::pin(async { +LL | 42 +LL | }) + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`.