Skip to content

Commit 5c14433

Browse files
committed
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 marked MaybeIncorrect because we allow predicates that are still ambiguous and can't be proven.
1 parent 2c31c31 commit 5c14433

File tree

4 files changed

+73
-5
lines changed

4 files changed

+73
-5
lines changed

compiler/rustc_typeck/src/check/coercion.rs

Lines changed: 23 additions & 1 deletion
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);
@@ -943,6 +944,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
943944
self.probe(|_| coerce.coerce(source, target)).is_ok()
944945
}
945946

947+
/// Same as `try_coerce()`, but without side-effects and attempts to select
948+
/// all predicates created by the coercion. This is useful for e.g. checking
949+
/// that associated types are correct.
950+
pub fn can_coerce_and_satisfy_predicates(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> bool {
951+
let source = self.resolve_vars_with_obligations(expr_ty);
952+
debug!("coercion::can_with_predicates({:?} -> {:?})", source, target);
953+
954+
let cause = self.cause(rustc_span::DUMMY_SP, ObligationCauseCode::ExprAssignable);
955+
// We don't ever need two-phase here since we throw out the result of the coercion
956+
let coerce = Coerce::new(self, cause, AllowTwoPhase::No);
957+
self.probe(|_| {
958+
let ok = match coerce.coerce(source, target) {
959+
Ok(ok) => ok,
960+
_ => return false,
961+
};
962+
let mut fcx = traits::FulfillmentContext::new_in_snapshot();
963+
fcx.register_predicate_obligations(self, ok.obligations);
964+
fcx.select_where_possible(&self).is_ok()
965+
})
966+
}
967+
946968
/// Given a type and a target type, this function will calculate and return
947969
/// how many dereference steps needed to achieve `expr_ty <: target`. If
948970
/// it's not possible, return `None`.

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -370,9 +370,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
370370
_ if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() => return false,
371371
_ => {}
372372
}
373-
let boxed_found = self.tcx.mk_box(found);
374-
let new_found = self.tcx.mk_lang_item(boxed_found, LangItem::Pin).unwrap();
375-
if self.can_coerce(new_found, expected) {
373+
let box_found = self.tcx.mk_box(found);
374+
let pin_box_found = self.tcx.mk_lang_item(box_found, LangItem::Pin).unwrap();
375+
let pin_found = self.tcx.mk_lang_item(found, LangItem::Pin).unwrap();
376+
if self.can_coerce_and_satisfy_predicates(pin_box_found, expected) {
377+
debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected);
376378
match found.kind() {
377379
ty::Adt(def, _) if def.is_box() => {
378380
err.help("use `Box::pin`");
@@ -384,11 +386,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
384386
(expr.span.shrink_to_lo(), "Box::pin(".to_string()),
385387
(expr.span.shrink_to_hi(), ")".to_string()),
386388
],
387-
Applicability::MachineApplicable,
389+
Applicability::MaybeIncorrect,
388390
);
389391
}
390392
}
391393
true
394+
} else if self.can_coerce_and_satisfy_predicates(pin_found, expected) {
395+
match found.kind() {
396+
ty::Adt(def, _) if def.is_box() => {
397+
err.help("use `Box::pin`");
398+
true
399+
}
400+
_ => false,
401+
}
392402
} else {
393403
false
394404
}
Lines changed: 22 additions & 0 deletions
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+
}
Lines changed: 14 additions & 0 deletions
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`.

0 commit comments

Comments
 (0)