Skip to content

Commit

Permalink
When expecting BoxFuture and using async {}, suggest Box::pin
Browse files Browse the repository at this point in the history
  • Loading branch information
estebank committed Feb 12, 2020
1 parent 2d2be57 commit c39b04e
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 8 deletions.
5 changes: 1 addition & 4 deletions src/librustc/traits/error_reporting/suggestions.rs
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/librustc_typeck/check/demand.rs
Expand Up @@ -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);
}

Expand Down
44 changes: 40 additions & 4 deletions src/librustc_typeck/check/mod.rs
Expand Up @@ -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,
);
}
}
Expand Down
16 changes: 16 additions & 0 deletions 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<Box<dyn Future<Output = T> + 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() {}
16 changes: 16 additions & 0 deletions 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<Box<dyn Future<Output = T> + Send + 'a>>;
// ^^^^^^^^^ This would come from the `futures` crate in real code.

fn foo() -> BoxFuture<'static, i32> {
async { //~ ERROR mismatched types
42
}
}

fn main() {}
27 changes: 27 additions & 0 deletions 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::boxed::Box<(dyn std::future::Future<Output = i32> + 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<T: Generator<Yield = ()>>(x: T) -> impl Future<Output = T::Return> {
| ------------------------------- the found opaque type
|
= note: expected struct `std::pin::Pin<std::boxed::Box<(dyn std::future::Future<Output = i32> + 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`.

0 comments on commit c39b04e

Please sign in to comment.