Skip to content

Commit

Permalink
Rollup merge of rust-lang#69082 - estebank:boxfuture-box-pin, r=tmandry
Browse files Browse the repository at this point in the history
When expecting `BoxFuture` and using `async {}`, suggest `Box::pin`

Fix rust-lang#68197, cc rust-lang#69083.
  • Loading branch information
Dylan-DPC authored Feb 13, 2020
2 parents 2501a10 + 248f5a4 commit ec5bf15
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 10 deletions.
4 changes: 4 additions & 0 deletions src/libcore/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}

Expand Down
10 changes: 10 additions & 0 deletions src/librustc/traits/error_reporting/on_unimplemented.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 1 addition & 4 deletions src/librustc/traits/error_reporting/suggestions.rs
Original file line number Diff line number Diff line change
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
3 changes: 3 additions & 0 deletions src/librustc_typeck/check/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
54 changes: 50 additions & 4 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.,
///
/// ```
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/generator/static-not-unpin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
2 changes: 1 addition & 1 deletion src/test/ui/generator/static-not-unpin.stderr
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down
29 changes: 29 additions & 0 deletions src/test/ui/suggestions/expected-boxed-future-isnt-pinned.rs
Original file line number Diff line number Diff line change
@@ -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() {}
20 changes: 20 additions & 0 deletions src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr
Original file line number Diff line number Diff line change
@@ -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`.

0 comments on commit ec5bf15

Please sign in to comment.