Skip to content

Commit ec5bf15

Browse files
authored
Rollup merge of #69082 - estebank:boxfuture-box-pin, r=tmandry
When expecting `BoxFuture` and using `async {}`, suggest `Box::pin` Fix #68197, cc #69083.
2 parents 2501a10 + 248f5a4 commit ec5bf15

File tree

9 files changed

+119
-10
lines changed

9 files changed

+119
-10
lines changed

src/libcore/marker.rs

+4
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,10 @@ unsafe impl<T: ?Sized> Freeze for &mut T {}
727727
/// [`Pin<P>`]: ../pin/struct.Pin.html
728728
/// [`pin module`]: ../../std/pin/index.html
729729
#[stable(feature = "pin", since = "1.33.0")]
730+
#[rustc_on_unimplemented(
731+
on(_Self = "std::future::Future", note = "consider using `Box::pin`",),
732+
message = "`{Self}` cannot be unpinned"
733+
)]
730734
#[lang = "unpin"]
731735
pub auto trait Unpin {}
732736

src/librustc/traits/error_reporting/on_unimplemented.rs

+10
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
201201
}
202202
}
203203
}
204+
if let ty::Dynamic(traits, _) = self_ty.kind {
205+
for t in *traits.skip_binder() {
206+
match t {
207+
ty::ExistentialPredicate::Trait(trait_ref) => {
208+
flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id))))
209+
}
210+
_ => {}
211+
}
212+
}
213+
}
204214

205215
if let Ok(Some(command)) =
206216
OnUnimplementedDirective::of_item(self.tcx, trait_ref.def_id, def_id)

src/librustc/traits/error_reporting/suggestions.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -701,10 +701,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
701701
})
702702
.collect::<Vec<_>>();
703703
// Add the suggestion for the return type.
704-
suggestions.push((
705-
ret_ty.span,
706-
format!("Box<{}{}>", if has_dyn { "" } else { "dyn " }, snippet),
707-
));
704+
suggestions.push((ret_ty.span, format!("Box<dyn {}>", trait_obj)));
708705
err.multipart_suggestion(
709706
"return a boxed trait object instead",
710707
suggestions,

src/librustc_typeck/check/demand.rs

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2424
self.annotate_expected_due_to_let_ty(err, expr);
2525
self.suggest_compatible_variants(err, expr, expected, expr_ty);
2626
self.suggest_ref_or_into(err, expr, expected, expr_ty);
27+
if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) {
28+
return;
29+
}
2730
self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
2831
self.suggest_missing_await(err, expr, expected, expr_ty);
2932
}

src/librustc_typeck/check/mod.rs

+50-4
Original file line numberDiff line numberDiff line change
@@ -5038,14 +5038,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
50385038
Applicability::MachineApplicable,
50395039
);
50405040
err.note(
5041-
"for more on the distinction between the stack and the \
5042-
heap, read https://doc.rust-lang.org/book/ch15-01-box.html, \
5043-
https://doc.rust-lang.org/rust-by-example/std/box.html, and \
5044-
https://doc.rust-lang.org/std/boxed/index.html",
5041+
"for more on the distinction between the stack and the heap, read \
5042+
https://doc.rust-lang.org/book/ch15-01-box.html, \
5043+
https://doc.rust-lang.org/rust-by-example/std/box.html, and \
5044+
https://doc.rust-lang.org/std/boxed/index.html",
50455045
);
50465046
}
50475047
}
50485048

5049+
/// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
5050+
fn suggest_calling_boxed_future_when_appropriate(
5051+
&self,
5052+
err: &mut DiagnosticBuilder<'_>,
5053+
expr: &hir::Expr<'_>,
5054+
expected: Ty<'tcx>,
5055+
found: Ty<'tcx>,
5056+
) -> bool {
5057+
// Handle #68197.
5058+
5059+
if self.tcx.hir().is_const_context(expr.hir_id) {
5060+
// Do not suggest `Box::new` in const context.
5061+
return false;
5062+
}
5063+
let pin_did = self.tcx.lang_items().pin_type();
5064+
match expected.kind {
5065+
ty::Adt(def, _) if Some(def.did) != pin_did => return false,
5066+
// This guards the `unwrap` and `mk_box` below.
5067+
_ if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() => return false,
5068+
_ => {}
5069+
}
5070+
let boxed_found = self.tcx.mk_box(found);
5071+
let new_found = self.tcx.mk_lang_item(boxed_found, lang_items::PinTypeLangItem).unwrap();
5072+
if let (true, Ok(snippet)) = (
5073+
self.can_coerce(new_found, expected),
5074+
self.sess().source_map().span_to_snippet(expr.span),
5075+
) {
5076+
match found.kind {
5077+
ty::Adt(def, _) if def.is_box() => {
5078+
err.help("use `Box::pin`");
5079+
}
5080+
_ => {
5081+
err.span_suggestion(
5082+
expr.span,
5083+
"you need to pin and box this expression",
5084+
format!("Box::pin({})", snippet),
5085+
Applicability::MachineApplicable,
5086+
);
5087+
}
5088+
}
5089+
true
5090+
} else {
5091+
false
5092+
}
5093+
}
5094+
50495095
/// A common error is to forget to add a semicolon at the end of a block, e.g.,
50505096
///
50515097
/// ```

src/test/ui/generator/static-not-unpin.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ fn main() {
1111
let mut generator = static || {
1212
yield;
1313
};
14-
assert_unpin(generator); //~ ERROR std::marker::Unpin` is not satisfied
14+
assert_unpin(generator); //~ ERROR E0277
1515
}

src/test/ui/generator/static-not-unpin.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error[E0277]: the trait bound `[static generator@$DIR/static-not-unpin.rs:11:25: 13:6 _]: std::marker::Unpin` is not satisfied
1+
error[E0277]: `[static generator@$DIR/static-not-unpin.rs:11:25: 13:6 _]` cannot be unpinned
22
--> $DIR/static-not-unpin.rs:14:18
33
|
44
LL | fn assert_unpin<T: Unpin>(_: T) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// edition:2018
2+
#![allow(dead_code)]
3+
use std::future::Future;
4+
use std::pin::Pin;
5+
6+
type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
7+
// ^^^^^^^^^ This would come from the `futures` crate in real code.
8+
9+
fn foo<F: Future<Output=i32> + Send + 'static>(x: F) -> BoxFuture<'static, i32> {
10+
// We could instead use an `async` block, but this way we have no std spans.
11+
x //~ ERROR mismatched types
12+
}
13+
14+
// FIXME: uncomment these once this commit is in Beta and we can rely on `rustc_on_unimplemented`
15+
// having filtering for `Self` being a trait.
16+
//
17+
// fn bar<F: Future<Output=i32> + Send + 'static>(x: F) -> BoxFuture<'static, i32> {
18+
// Box::new(x)
19+
// }
20+
//
21+
// fn baz<F: Future<Output=i32> + Send + 'static>(x: F) -> BoxFuture<'static, i32> {
22+
// Pin::new(x)
23+
// }
24+
//
25+
// fn qux<F: Future<Output=i32> + Send + 'static>(x: F) -> BoxFuture<'static, i32> {
26+
// Pin::new(Box::new(x))
27+
// }
28+
29+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/expected-boxed-future-isnt-pinned.rs:11:5
3+
|
4+
LL | fn foo<F: Future<Output=i32> + Send + 'static>(x: F) -> BoxFuture<'static, i32> {
5+
| - this type parameter ----------------------- expected `std::pin::Pin<std::boxed::Box<(dyn std::future::Future<Output = i32> + std::marker::Send + 'static)>>` because of return type
6+
LL | // We could instead use an `async` block, but this way we have no std spans.
7+
LL | x
8+
| ^
9+
| |
10+
| expected struct `std::pin::Pin`, found type parameter `F`
11+
| help: you need to pin and box this expression: `Box::pin(x)`
12+
|
13+
= note: expected struct `std::pin::Pin<std::boxed::Box<(dyn std::future::Future<Output = i32> + std::marker::Send + 'static)>>`
14+
found type parameter `F`
15+
= help: type parameters must be constrained to match other types
16+
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
17+
18+
error: aborting due to previous error
19+
20+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)