Skip to content

Commit e2c28ad

Browse files
authored
Rollup merge of #89870 - tmandry:box-pin, r=estebank
Suggest Box::pin when Pin::new is used instead This fixes an incorrect diagnostic. **Based on #89390**; only the last commit is specific to this PR. "Ignore whitespace changes" also helps here.
2 parents 345d483 + d18502d commit e2c28ad

File tree

3 files changed

+67
-43
lines changed

3 files changed

+67
-43
lines changed

compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs

+60-30
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ use rustc_errors::{Applicability, DiagnosticBuilder};
88
use rustc_hir as hir;
99
use rustc_hir::def::{CtorOf, DefKind};
1010
use rustc_hir::lang_items::LangItem;
11-
use rustc_hir::{Expr, ExprKind, ItemKind, Node, Stmt, StmtKind};
11+
use rustc_hir::{Expr, ExprKind, ItemKind, Node, Path, QPath, Stmt, StmtKind, TyKind};
1212
use rustc_infer::infer;
1313
use rustc_middle::lint::in_external_macro;
1414
use rustc_middle::ty::{self, Binder, Ty};
15-
use rustc_span::symbol::kw;
15+
use rustc_span::symbol::{kw, sym};
1616

1717
use std::iter;
1818

@@ -350,6 +350,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
350350
}
351351

352352
/// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
353+
#[instrument(skip(self, err))]
353354
pub(in super::super) fn suggest_calling_boxed_future_when_appropriate(
354355
&self,
355356
err: &mut DiagnosticBuilder<'_>,
@@ -368,41 +369,70 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
368369
if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() {
369370
return false;
370371
}
371-
match expected.kind() {
372-
ty::Adt(def, _) if Some(def.did) == pin_did => (),
373-
_ => return false,
374-
}
375372
let box_found = self.tcx.mk_box(found);
376373
let pin_box_found = self.tcx.mk_lang_item(box_found, LangItem::Pin).unwrap();
377374
let pin_found = self.tcx.mk_lang_item(found, LangItem::Pin).unwrap();
378-
if self.can_coerce(pin_box_found, expected) {
379-
debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected);
380-
match found.kind() {
381-
ty::Adt(def, _) if def.is_box() => {
382-
err.help("use `Box::pin`");
383-
}
384-
_ => {
385-
err.multipart_suggestion(
386-
"you need to pin and box this expression",
387-
vec![
388-
(expr.span.shrink_to_lo(), "Box::pin(".to_string()),
389-
(expr.span.shrink_to_hi(), ")".to_string()),
390-
],
391-
Applicability::MaybeIncorrect,
392-
);
375+
match expected.kind() {
376+
ty::Adt(def, _) if Some(def.did) == pin_did => {
377+
if self.can_coerce(pin_box_found, expected) {
378+
debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected);
379+
match found.kind() {
380+
ty::Adt(def, _) if def.is_box() => {
381+
err.help("use `Box::pin`");
382+
}
383+
_ => {
384+
err.multipart_suggestion(
385+
"you need to pin and box this expression",
386+
vec![
387+
(expr.span.shrink_to_lo(), "Box::pin(".to_string()),
388+
(expr.span.shrink_to_hi(), ")".to_string()),
389+
],
390+
Applicability::MaybeIncorrect,
391+
);
392+
}
393+
}
394+
true
395+
} else if self.can_coerce(pin_found, expected) {
396+
match found.kind() {
397+
ty::Adt(def, _) if def.is_box() => {
398+
err.help("use `Box::pin`");
399+
true
400+
}
401+
_ => false,
402+
}
403+
} else {
404+
false
393405
}
394406
}
395-
true
396-
} else if self.can_coerce(pin_found, expected) {
397-
match found.kind() {
398-
ty::Adt(def, _) if def.is_box() => {
399-
err.help("use `Box::pin`");
400-
true
407+
ty::Adt(def, _) if def.is_box() && self.can_coerce(box_found, expected) => {
408+
// Check if the parent expression is a call to Pin::new. If it
409+
// is and we were expecting a Box, ergo Pin<Box<expected>>, we
410+
// can suggest Box::pin.
411+
let parent = self.tcx.hir().get_parent_node(expr.hir_id);
412+
let fn_name = match self.tcx.hir().find(parent) {
413+
Some(Node::Expr(Expr { kind: ExprKind::Call(fn_name, _), .. })) => fn_name,
414+
_ => return false,
415+
};
416+
match fn_name.kind {
417+
ExprKind::Path(QPath::TypeRelative(
418+
hir::Ty {
419+
kind: TyKind::Path(QPath::Resolved(_, Path { res: recv_ty, .. })),
420+
..
421+
},
422+
method,
423+
)) if Some(recv_ty.def_id()) == pin_did && method.ident.name == sym::new => {
424+
err.span_suggestion(
425+
fn_name.span,
426+
"use `Box::pin` to pin and box this expression",
427+
"Box::pin".to_string(),
428+
Applicability::MachineApplicable,
429+
);
430+
true
431+
}
432+
_ => false,
401433
}
402-
_ => false,
403434
}
404-
} else {
405-
false
435+
_ => false,
406436
}
407437
}
408438

src/test/ui/suggestions/expected-boxed-future-isnt-pinned.rs

-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ fn bar<F: Future<Output=i32> + Send + 'static>(x: F) -> BoxFuture<'static, i32>
1515
Box::new(x) //~ ERROR mismatched types
1616
}
1717

18-
// This case is still subpar:
19-
// `Pin::new(x)`: store this in the heap by calling `Box::new`: `Box::new(x)`
20-
// Should suggest changing the code from `Pin::new` to `Box::pin`.
2118
fn baz<F: Future<Output=i32> + Send + 'static>(x: F) -> BoxFuture<'static, i32> {
2219
Pin::new(x) //~ ERROR mismatched types
2320
//~^ ERROR E0277

src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr

+7-10
Original file line numberDiff line numberDiff line change
@@ -27,23 +27,20 @@ LL | Box::new(x)
2727
= help: use `Box::pin`
2828

2929
error[E0308]: mismatched types
30-
--> $DIR/expected-boxed-future-isnt-pinned.rs:22:14
30+
--> $DIR/expected-boxed-future-isnt-pinned.rs:19:14
3131
|
3232
LL | fn baz<F: Future<Output=i32> + Send + 'static>(x: F) -> BoxFuture<'static, i32> {
3333
| - this type parameter
3434
LL | Pin::new(x)
35-
| ^ expected struct `Box`, found type parameter `F`
35+
| -------- ^ expected struct `Box`, found type parameter `F`
36+
| |
37+
| help: use `Box::pin` to pin and box this expression: `Box::pin`
3638
|
3739
= note: expected struct `Box<dyn Future<Output = i32> + Send>`
3840
found type parameter `F`
39-
= 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
40-
help: store this in the heap by calling `Box::new`
41-
|
42-
LL | Pin::new(Box::new(x))
43-
| +++++++++ +
4441

4542
error[E0277]: `dyn Future<Output = i32> + Send` cannot be unpinned
46-
--> $DIR/expected-boxed-future-isnt-pinned.rs:22:5
43+
--> $DIR/expected-boxed-future-isnt-pinned.rs:19:5
4744
|
4845
LL | Pin::new(x)
4946
| ^^^^^^^^ the trait `Unpin` is not implemented for `dyn Future<Output = i32> + Send`
@@ -56,7 +53,7 @@ LL | pub const fn new(pointer: P) -> Pin<P> {
5653
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5754

5855
error[E0277]: `dyn Future<Output = i32> + Send` cannot be unpinned
59-
--> $DIR/expected-boxed-future-isnt-pinned.rs:27:5
56+
--> $DIR/expected-boxed-future-isnt-pinned.rs:24:5
6057
|
6158
LL | Pin::new(Box::new(x))
6259
| ^^^^^^^^ the trait `Unpin` is not implemented for `dyn Future<Output = i32> + Send`
@@ -69,7 +66,7 @@ LL | pub const fn new(pointer: P) -> Pin<P> {
6966
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
7067

7168
error[E0308]: mismatched types
72-
--> $DIR/expected-boxed-future-isnt-pinned.rs:31:5
69+
--> $DIR/expected-boxed-future-isnt-pinned.rs:28:5
7370
|
7471
LL | fn zap() -> BoxFuture<'static, i32> {
7572
| ----------------------- expected `Pin<Box<(dyn Future<Output = i32> + Send + 'static)>>` because of return type

0 commit comments

Comments
 (0)