Rollup merge of #69082 - estebank:boxfuture-box-pin, r=tmandry
When expecting `BoxFuture` and using `async {}`, suggest `Box::pin` Fix #68197, cc #69083.
This commit is contained in:
commit
ec5bf15c5d
9 changed files with 119 additions and 10 deletions
|
@ -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 {}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.,
|
||||
///
|
||||
/// ```
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
29
src/test/ui/suggestions/expected-boxed-future-isnt-pinned.rs
Normal file
29
src/test/ui/suggestions/expected-boxed-future-isnt-pinned.rs
Normal file
|
@ -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() {}
|
|
@ -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`.
|
Loading…
Add table
Add a link
Reference in a new issue