Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/next_solver/infer/errors.rs')
-rw-r--r--crates/hir-ty/src/next_solver/infer/errors.rs130
1 files changed, 87 insertions, 43 deletions
diff --git a/crates/hir-ty/src/next_solver/infer/errors.rs b/crates/hir-ty/src/next_solver/infer/errors.rs
index 7d3f111f66..c5eccdafd5 100644
--- a/crates/hir-ty/src/next_solver/infer/errors.rs
+++ b/crates/hir-ty/src/next_solver/infer/errors.rs
@@ -15,7 +15,7 @@ use crate::{
db::GeneralConstId,
next_solver::{
AliasTerm, AnyImplId, Binder, ClauseKind, Const, ConstKind, DbInterner,
- HostEffectPredicate, PolyTraitPredicate, PredicateKind, SolverContext, Term,
+ HostEffectPredicate, PolyTraitPredicate, Predicate, PredicateKind, SolverContext, Term,
TraitPredicate, Ty, TyKind, TypeError,
fulfill::NextSolverError,
infer::{
@@ -32,10 +32,7 @@ use crate::{
pub struct FulfillmentError<'db> {
pub obligation: PredicateObligation<'db>,
pub code: FulfillmentErrorCode<'db>,
- /// Diagnostics only: the 'root' obligation which resulted in
- /// the failure to process `obligation`. This is the obligation
- /// that was initially passed to `register_predicate_obligation`
- pub root_obligation: PredicateObligation<'db>,
+ pub trait_obligation_chain: Vec<Predicate<'db>>,
}
impl<'db> FulfillmentError<'db> {
@@ -44,7 +41,8 @@ impl<'db> FulfillmentError<'db> {
code: FulfillmentErrorCode<'db>,
root_obligation: PredicateObligation<'db>,
) -> FulfillmentError<'db> {
- FulfillmentError { obligation, code, root_obligation }
+ let trait_obligation_chain = trait_obligation_chain(&root_obligation, &obligation);
+ FulfillmentError { obligation, code, trait_obligation_chain }
}
pub fn is_true_error(&self) -> bool {
@@ -130,7 +128,8 @@ fn fulfillment_error_for_no_solution<'db>(
) -> FulfillmentError<'db> {
let interner = infcx.interner;
let db = interner.db;
- let obligation = find_best_leaf_obligation(infcx, &root_obligation, false);
+ let (obligation, trait_obligation_chain) =
+ find_best_leaf_obligation(infcx, &root_obligation, false);
let code = match obligation.predicate.kind().skip_binder() {
PredicateKind::Clause(ClauseKind::Projection(_)) => {
@@ -189,7 +188,7 @@ fn fulfillment_error_for_no_solution<'db>(
}
};
- FulfillmentError { obligation, code, root_obligation }
+ FulfillmentError { obligation, code, trait_obligation_chain }
}
fn fulfillment_error_for_stalled<'db>(
@@ -239,25 +238,27 @@ fn fulfillment_error_for_stalled<'db>(
}
});
- FulfillmentError {
- obligation: if refine_obligation {
- find_best_leaf_obligation(infcx, &root_obligation, true)
- } else {
- root_obligation.clone()
- },
- code,
- root_obligation,
- }
+ let (obligation, trait_obligation_chain) = if refine_obligation {
+ find_best_leaf_obligation(infcx, &root_obligation, true)
+ } else {
+ let obligation = root_obligation.clone();
+ let trait_obligation_chain = trait_obligation_chain(&root_obligation, &obligation);
+ (obligation, trait_obligation_chain)
+ };
+
+ FulfillmentError { obligation, code, trait_obligation_chain }
}
fn fulfillment_error_for_overflow<'db>(
infcx: &InferCtxt<'db>,
root_obligation: PredicateObligation<'db>,
) -> FulfillmentError<'db> {
+ let (obligation, trait_obligation_chain) =
+ find_best_leaf_obligation(infcx, &root_obligation, true);
FulfillmentError {
- obligation: find_best_leaf_obligation(infcx, &root_obligation, true),
+ obligation,
code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) },
- root_obligation,
+ trait_obligation_chain,
}
}
@@ -266,34 +267,62 @@ fn find_best_leaf_obligation<'db>(
infcx: &InferCtxt<'db>,
obligation: &PredicateObligation<'db>,
consider_ambiguities: bool,
-) -> PredicateObligation<'db> {
+) -> (PredicateObligation<'db>, Vec<Predicate<'db>>) {
let obligation = infcx.resolve_vars_if_possible(obligation.clone());
// FIXME: we use a probe here as the `BestObligation` visitor does not
// check whether it uses candidates which get shadowed by where-bounds.
//
// We should probably fix the visitor to not do so instead, as this also
// means the leaf obligation may be incorrect.
- let obligation = infcx
+ let (obligation, trait_obligation_chain) = infcx
.fudge_inference_if_ok(|| {
+ let mut visitor = BestObligation {
+ obligation: obligation.clone(),
+ consider_ambiguities,
+ trait_obligation_chain: trait_obligation_chain(&obligation, &obligation),
+ };
infcx
- .visit_proof_tree(
- obligation.as_goal(),
- &mut BestObligation { obligation: obligation.clone(), consider_ambiguities },
- )
+ .visit_proof_tree(obligation.as_goal(), &mut visitor)
.break_value()
.ok_or(())
// walk around the fact that the cause in `Obligation` is ignored by folders so that
// we can properly fudge the infer vars in cause code.
- .map(|o| (o.cause, o))
+ .map(|(obligation, trait_obligation_chain)| {
+ (obligation.cause, obligation, trait_obligation_chain)
+ })
+ })
+ .map(|(cause, obligation, trait_obligation_chain)| {
+ (PredicateObligation { cause, ..obligation }, trait_obligation_chain)
})
- .map(|(cause, o)| PredicateObligation { cause, ..o })
- .unwrap_or(obligation);
- deeply_normalize_for_diagnostics(infcx, obligation.param_env, obligation)
+ .unwrap_or_else(|()| {
+ let trait_obligation_chain = trait_obligation_chain(&obligation, &obligation);
+ (obligation, trait_obligation_chain)
+ });
+ let trait_obligation_chain =
+ deeply_normalize_for_diagnostics(infcx, obligation.param_env, trait_obligation_chain);
+ let obligation = deeply_normalize_for_diagnostics(infcx, obligation.param_env, obligation);
+ (obligation, trait_obligation_chain)
+}
+
+fn trait_obligation_chain<'db>(
+ root_obligation: &PredicateObligation<'db>,
+ obligation: &PredicateObligation<'db>,
+) -> Vec<Predicate<'db>> {
+ let mut chain = Vec::new();
+ if root_obligation.predicate.as_trait_clause().is_none() {
+ return chain;
+ }
+ chain.push(root_obligation.predicate);
+ if chain.last() != Some(&obligation.predicate) {
+ chain.push(obligation.predicate);
+ }
+ chain
}
struct BestObligation<'db> {
obligation: PredicateObligation<'db>,
consider_ambiguities: bool,
+ trait_obligation_chain: Vec<Predicate<'db>>,
}
impl<'db> BestObligation<'db> {
@@ -302,10 +331,25 @@ impl<'db> BestObligation<'db> {
derived_obligation: PredicateObligation<'db>,
and_then: impl FnOnce(&mut Self) -> <Self as ProofTreeVisitor<'db>>::Result,
) -> <Self as ProofTreeVisitor<'db>>::Result {
+ let should_push = derived_obligation.predicate.as_trait_clause().is_some()
+ && self.trait_obligation_chain.last() != Some(&derived_obligation.predicate);
+ if should_push {
+ self.trait_obligation_chain.push(derived_obligation.predicate);
+ }
let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation);
- let res = and_then(self);
+ let result = and_then(self);
self.obligation = old_obligation;
- res
+ if should_push {
+ self.trait_obligation_chain.pop();
+ }
+ result
+ }
+
+ fn break_with_current_obligation(&mut self) -> <Self as ProofTreeVisitor<'db>>::Result {
+ ControlFlow::Break((
+ self.obligation.clone(),
+ std::mem::take(&mut self.trait_obligation_chain),
+ ))
}
/// Filter out the candidates that aren't interesting to visit for the
@@ -360,7 +404,7 @@ impl<'db> BestObligation<'db> {
&mut self,
candidate: &inspect::InspectCandidate<'_, 'db>,
term: Term<'db>,
- ) -> ControlFlow<PredicateObligation<'db>> {
+ ) -> <Self as ProofTreeVisitor<'db>>::Result {
let _ = (candidate, term);
// FIXME: rustc does this, but we don't process WF obligations yet:
// let infcx = candidate.goal().infcx();
@@ -386,7 +430,7 @@ impl<'db> BestObligation<'db> {
// self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
// }
- ControlFlow::Break(self.obligation.clone())
+ self.break_with_current_obligation()
}
/// If a normalization of an associated item or a trait goal fails without trying any
@@ -396,7 +440,7 @@ impl<'db> BestObligation<'db> {
&mut self,
goal: &inspect::InspectGoal<'_, 'db>,
self_ty: Ty<'db>,
- ) -> ControlFlow<PredicateObligation<'db>> {
+ ) -> <Self as ProofTreeVisitor<'db>>::Result {
assert!(!self.consider_ambiguities);
let interner = goal.infcx().interner;
if let TyKind::Alias(..) = self_ty.kind() {
@@ -430,7 +474,7 @@ impl<'db> BestObligation<'db> {
fn detect_trait_error_in_higher_ranked_projection(
&mut self,
goal: &inspect::InspectGoal<'_, 'db>,
- ) -> ControlFlow<PredicateObligation<'db>> {
+ ) -> <Self as ProofTreeVisitor<'db>>::Result {
let interner = goal.infcx().interner;
if let Some(projection_clause) = goal.goal().predicate.as_projection_clause()
&& !projection_clause.bound_vars().is_empty()
@@ -464,7 +508,7 @@ impl<'db> BestObligation<'db> {
&mut self,
goal: &inspect::InspectGoal<'_, 'db>,
alias: AliasTerm<'db>,
- ) -> ControlFlow<PredicateObligation<'db>> {
+ ) -> <Self as ProofTreeVisitor<'db>>::Result {
let interner = goal.infcx().interner;
let obligation = Obligation::new(
interner,
@@ -486,7 +530,7 @@ impl<'db> BestObligation<'db> {
fn detect_error_from_empty_candidates(
&mut self,
goal: &inspect::InspectGoal<'_, 'db>,
- ) -> ControlFlow<PredicateObligation<'db>> {
+ ) -> <Self as ProofTreeVisitor<'db>>::Result {
let interner = goal.infcx().interner;
let pred_kind = goal.goal().predicate.kind();
@@ -504,12 +548,12 @@ impl<'db> BestObligation<'db> {
Some(_) | None => {}
}
- ControlFlow::Break(self.obligation.clone())
+ self.break_with_current_obligation()
}
}
impl<'db> ProofTreeVisitor<'db> for BestObligation<'db> {
- type Result = ControlFlow<PredicateObligation<'db>>;
+ type Result = ControlFlow<(PredicateObligation<'db>, Vec<Predicate<'db>>)>;
fn span(&self) -> Span {
self.obligation.cause.span()
@@ -531,7 +575,7 @@ impl<'db> ProofTreeVisitor<'db> for BestObligation<'db> {
let candidate = match candidates.as_slice() {
[candidate] => candidate,
[] => return self.detect_error_from_empty_candidates(goal),
- _ => return ControlFlow::Break(self.obligation.clone()),
+ _ => return self.break_with_current_obligation(),
};
// Don't walk into impls that have `do_not_recommend`.
@@ -544,7 +588,7 @@ impl<'db> ProofTreeVisitor<'db> for BestObligation<'db> {
.contains(AttrFlags::DIAGNOSTIC_DO_NOT_RECOMMEND)
{
trace!("#[diagnostic::do_not_recommend] -> exit");
- return ControlFlow::Break(self.obligation.clone());
+ return self.break_with_current_obligation();
}
// FIXME: Also, what about considering >1 layer up the stack? May be necessary
@@ -587,7 +631,7 @@ impl<'db> ProofTreeVisitor<'db> for BestObligation<'db> {
&& Some(poly_trait_pred.def_id().0) == interner.lang_items().FnPtrTrait
&& let Err(NoSolution) = nested_goal.result()
{
- return ControlFlow::Break(self.obligation.clone());
+ return self.break_with_current_obligation();
}
}
@@ -661,7 +705,7 @@ impl<'db> ProofTreeVisitor<'db> for BestObligation<'db> {
self.detect_trait_error_in_higher_ranked_projection(goal)?;
- ControlFlow::Break(self.obligation.clone())
+ self.break_with_current_obligation()
}
}