Skip to content

Commit f9c9774

Browse files
authored
Rollup merge of #89390 - tmandry:issue-72117, r=estebank
Fix incorrect Box::pin suggestion The suggestion checked if `Pin<Box<T>>` could be coeerced to the expected type, but did not check predicates created by the coercion. We now look for predicates that definitely cannot be satisfied before giving the suggestion. The suggestion is still marked MaybeIncorrect because we allow predicates that are still ambiguous and can't be proven. Fixes #72117.
2 parents c34ac87 + a8558e9 commit f9c9774

8 files changed

+85
-28
lines changed

compiler/rustc_typeck/src/check/coercion.rs

+15-3
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use rustc_hir as hir;
4242
use rustc_hir::def_id::DefId;
4343
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
4444
use rustc_infer::infer::{Coercion, InferOk, InferResult};
45-
use rustc_infer::traits::Obligation;
45+
use rustc_infer::traits::{Obligation, TraitEngine, TraitEngineExt};
4646
use rustc_middle::lint::in_external_macro;
4747
use rustc_middle::ty::adjustment::{
4848
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast,
@@ -146,6 +146,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
146146
.and_then(|InferOk { value: ty, obligations }| success(f(ty), ty, obligations))
147147
}
148148

149+
#[instrument(skip(self))]
149150
fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
150151
// First, remove any resolved type variables (at the top level, at least):
151152
let a = self.shallow_resolve(a);
@@ -933,14 +934,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
933934
}
934935

935936
/// Same as `try_coerce()`, but without side-effects.
937+
///
938+
/// Returns false if the coercion creates any obligations that result in
939+
/// errors.
936940
pub fn can_coerce(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> bool {
937941
let source = self.resolve_vars_with_obligations(expr_ty);
938-
debug!("coercion::can({:?} -> {:?})", source, target);
942+
debug!("coercion::can_with_predicates({:?} -> {:?})", source, target);
939943

940944
let cause = self.cause(rustc_span::DUMMY_SP, ObligationCauseCode::ExprAssignable);
941945
// We don't ever need two-phase here since we throw out the result of the coercion
942946
let coerce = Coerce::new(self, cause, AllowTwoPhase::No);
943-
self.probe(|_| coerce.coerce(source, target)).is_ok()
947+
self.probe(|_| {
948+
let ok = match coerce.coerce(source, target) {
949+
Ok(ok) => ok,
950+
_ => return false,
951+
};
952+
let mut fcx = traits::FulfillmentContext::new_in_snapshot();
953+
fcx.register_predicate_obligations(self, ok.obligations);
954+
fcx.select_where_possible(&self).is_ok()
955+
})
944956
}
945957

946958
/// Given a type and a target type, this function will calculate and return

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

+24-9
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
341341
for (sp, label) in spans_and_labels {
342342
multi_span.push_span_label(sp, label);
343343
}
344-
err.span_note(multi_span, "closures can only be coerced to `fn` types if they do not capture any variables");
344+
err.span_note(
345+
multi_span,
346+
"closures can only be coerced to `fn` types if they do not capture any variables"
347+
);
345348
}
346349
}
347350
}
@@ -361,15 +364,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
361364
return false;
362365
}
363366
let pin_did = self.tcx.lang_items().pin_type();
367+
// This guards the `unwrap` and `mk_box` below.
368+
if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() {
369+
return false;
370+
}
364371
match expected.kind() {
365-
ty::Adt(def, _) if Some(def.did) != pin_did => return false,
366-
// This guards the `unwrap` and `mk_box` below.
367-
_ if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() => return false,
368-
_ => {}
372+
ty::Adt(def, _) if Some(def.did) == pin_did => (),
373+
_ => return false,
369374
}
370-
let boxed_found = self.tcx.mk_box(found);
371-
let new_found = self.tcx.mk_lang_item(boxed_found, LangItem::Pin).unwrap();
372-
if self.can_coerce(new_found, expected) {
375+
let box_found = self.tcx.mk_box(found);
376+
let pin_box_found = self.tcx.mk_lang_item(box_found, LangItem::Pin).unwrap();
377+
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);
373380
match found.kind() {
374381
ty::Adt(def, _) if def.is_box() => {
375382
err.help("use `Box::pin`");
@@ -381,11 +388,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
381388
(expr.span.shrink_to_lo(), "Box::pin(".to_string()),
382389
(expr.span.shrink_to_hi(), ")".to_string()),
383390
],
384-
Applicability::MachineApplicable,
391+
Applicability::MaybeIncorrect,
385392
);
386393
}
387394
}
388395
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
401+
}
402+
_ => false,
403+
}
389404
} else {
390405
false
391406
}

src/test/ui/cross/cross-borrow-trait.stderr

+2-4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ error[E0308]: mismatched types
22
--> $DIR/cross-borrow-trait.rs:10:26
33
|
44
LL | let _y: &dyn Trait = x;
5-
| ---------- ^
6-
| | |
7-
| | expected `&dyn Trait`, found struct `Box`
8-
| | help: consider borrowing here: `&x`
5+
| ---------- ^ expected `&dyn Trait`, found struct `Box`
6+
| |
97
| expected due to this
108
|
119
= note: expected reference `&dyn Trait`

src/test/ui/dst/dst-bad-coercions.stderr

+4-8
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,8 @@ error[E0308]: mismatched types
1313
--> $DIR/dst-bad-coercions.rs:15:21
1414
|
1515
LL | let y: &dyn T = x;
16-
| ------ ^
17-
| | |
18-
| | expected `&dyn T`, found *-ptr
19-
| | help: consider borrowing here: `&x`
16+
| ------ ^ expected `&dyn T`, found *-ptr
17+
| |
2018
| expected due to this
2119
|
2220
= note: expected reference `&dyn T`
@@ -37,10 +35,8 @@ error[E0308]: mismatched types
3735
--> $DIR/dst-bad-coercions.rs:20:21
3836
|
3937
LL | let y: &dyn T = x;
40-
| ------ ^
41-
| | |
42-
| | expected `&dyn T`, found *-ptr
43-
| | help: consider borrowing here: `&x`
38+
| ------ ^ expected `&dyn T`, found *-ptr
39+
| |
4440
| expected due to this
4541
|
4642
= note: expected reference `&dyn T`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Issue #72117
2+
// edition:2018
3+
4+
use core::future::Future;
5+
use core::pin::Pin;
6+
7+
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
8+
9+
impl<T: ?Sized> FutureExt for T where T: Future {}
10+
trait FutureExt: Future {
11+
fn boxed<'a>(self) -> BoxFuture<'a, Self::Output>
12+
where
13+
Self: Sized + Send + 'a,
14+
{
15+
Box::pin(self)
16+
}
17+
}
18+
19+
fn main() {
20+
let _: BoxFuture<'static, bool> = async {}.boxed();
21+
//~^ ERROR: mismatched types
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/box-future-wrong-output.rs:20:39
3+
|
4+
LL | let _: BoxFuture<'static, bool> = async {}.boxed();
5+
| ------------------------ ^^^^^^^^^^^^^^^^ expected `bool`, found `()`
6+
| |
7+
| expected due to this
8+
|
9+
= note: expected struct `Pin<Box<(dyn Future<Output = bool> + Send + 'static)>>`
10+
found struct `Pin<Box<dyn Future<Output = ()> + Send>>`
11+
12+
error: aborting due to previous error
13+
14+
For more information about this error, try `rustc --explain E0308`.

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ fn foo<F: Future<Output=i32> + Send + 'static>(x: F) -> BoxFuture<'static, i32>
1111
x //~ ERROR mismatched types
1212
}
1313

14-
// This case is still subpar:
15-
// `Pin::new(x)`: store this in the heap by calling `Box::new`: `Box::new(x)`
16-
// Should suggest changing the code from `Pin::new` to `Box::pin`.
1714
fn bar<F: Future<Output=i32> + Send + 'static>(x: F) -> BoxFuture<'static, i32> {
1815
Box::new(x) //~ ERROR mismatched types
1916
}
2017

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`.
2121
fn baz<F: Future<Output=i32> + Send + 'static>(x: F) -> BoxFuture<'static, i32> {
2222
Pin::new(x) //~ ERROR mismatched types
2323
//~^ ERROR E0277

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ LL | Box::pin(x)
1515
| +++++++++ +
1616

1717
error[E0308]: mismatched types
18-
--> $DIR/expected-boxed-future-isnt-pinned.rs:18:5
18+
--> $DIR/expected-boxed-future-isnt-pinned.rs:15:5
1919
|
2020
LL | fn bar<F: Future<Output=i32> + Send + 'static>(x: F) -> BoxFuture<'static, i32> {
2121
| ----------------------- expected `Pin<Box<(dyn Future<Output = i32> + Send + 'static)>>` because of return type

0 commit comments

Comments
 (0)