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.
This commit is contained in:
commit
f9c9774aea
8 changed files with 86 additions and 29 deletions
|
@ -42,7 +42,7 @@ use rustc_hir as hir;
|
|||
use rustc_hir::def_id::DefId;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_infer::infer::{Coercion, InferOk, InferResult};
|
||||
use rustc_infer::traits::Obligation;
|
||||
use rustc_infer::traits::{Obligation, TraitEngine, TraitEngineExt};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::adjustment::{
|
||||
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast,
|
||||
|
@ -146,6 +146,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||
.and_then(|InferOk { value: ty, obligations }| success(f(ty), ty, obligations))
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
|
||||
// First, remove any resolved type variables (at the top level, at least):
|
||||
let a = self.shallow_resolve(a);
|
||||
|
@ -933,14 +934,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
/// Same as `try_coerce()`, but without side-effects.
|
||||
///
|
||||
/// Returns false if the coercion creates any obligations that result in
|
||||
/// errors.
|
||||
pub fn can_coerce(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> bool {
|
||||
let source = self.resolve_vars_with_obligations(expr_ty);
|
||||
debug!("coercion::can({:?} -> {:?})", source, target);
|
||||
debug!("coercion::can_with_predicates({:?} -> {:?})", source, target);
|
||||
|
||||
let cause = self.cause(rustc_span::DUMMY_SP, ObligationCauseCode::ExprAssignable);
|
||||
// We don't ever need two-phase here since we throw out the result of the coercion
|
||||
let coerce = Coerce::new(self, cause, AllowTwoPhase::No);
|
||||
self.probe(|_| coerce.coerce(source, target)).is_ok()
|
||||
self.probe(|_| {
|
||||
let ok = match coerce.coerce(source, target) {
|
||||
Ok(ok) => ok,
|
||||
_ => return false,
|
||||
};
|
||||
let mut fcx = traits::FulfillmentContext::new_in_snapshot();
|
||||
fcx.register_predicate_obligations(self, ok.obligations);
|
||||
fcx.select_where_possible(&self).is_ok()
|
||||
})
|
||||
}
|
||||
|
||||
/// Given a type and a target type, this function will calculate and return
|
||||
|
|
|
@ -341,7 +341,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
for (sp, label) in spans_and_labels {
|
||||
multi_span.push_span_label(sp, label);
|
||||
}
|
||||
err.span_note(multi_span, "closures can only be coerced to `fn` types if they do not capture any variables");
|
||||
err.span_note(
|
||||
multi_span,
|
||||
"closures can only be coerced to `fn` types if they do not capture any variables"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -361,15 +364,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
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,
|
||||
_ => {}
|
||||
// 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, LangItem::Pin).unwrap();
|
||||
if self.can_coerce(new_found, expected) {
|
||||
match expected.kind() {
|
||||
ty::Adt(def, _) if Some(def.did) == pin_did => (),
|
||||
_ => return false,
|
||||
}
|
||||
let box_found = self.tcx.mk_box(found);
|
||||
let pin_box_found = self.tcx.mk_lang_item(box_found, LangItem::Pin).unwrap();
|
||||
let pin_found = self.tcx.mk_lang_item(found, LangItem::Pin).unwrap();
|
||||
if self.can_coerce(pin_box_found, expected) {
|
||||
debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected);
|
||||
match found.kind() {
|
||||
ty::Adt(def, _) if def.is_box() => {
|
||||
err.help("use `Box::pin`");
|
||||
|
@ -381,11 +388,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
(expr.span.shrink_to_lo(), "Box::pin(".to_string()),
|
||||
(expr.span.shrink_to_hi(), ")".to_string()),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
true
|
||||
} else if self.can_coerce(pin_found, expected) {
|
||||
match found.kind() {
|
||||
ty::Adt(def, _) if def.is_box() => {
|
||||
err.help("use `Box::pin`");
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue