Skip to main content

hir_ty/next_solver/infer/
errors.rs

1use std::{fmt, ops::ControlFlow};
2
3use hir_def::attrs::AttrFlags;
4use rustc_next_trait_solver::solve::{GoalEvaluation, SolverDelegateEvalExt};
5use rustc_type_ir::{
6    AliasRelationDirection, AliasTermKind, PredicatePolarity,
7    error::ExpectedFound,
8    inherent::IntoKind as _,
9    solve::{CandidateSource, Certainty, GoalSource, MaybeCause, MaybeInfo, NoSolution},
10};
11use tracing::{instrument, trace};
12
13use crate::{
14    Span,
15    db::GeneralConstId,
16    next_solver::{
17        AliasTerm, AnyImplId, Binder, ClauseKind, Const, ConstKind, DbInterner,
18        HostEffectPredicate, PolyTraitPredicate, Predicate, PredicateKind, SolverContext, Term,
19        TraitPredicate, Ty, TyKind, TypeError,
20        fulfill::NextSolverError,
21        infer::{
22            InferCtxt,
23            select::SelectionError,
24            traits::{Obligation, ObligationCause, PredicateObligation, PredicateObligations},
25        },
26        inspect::{self, ProofTreeVisitor},
27        normalize::deeply_normalize_for_diagnostics,
28    },
29};
30
31#[derive(Debug)]
32pub struct FulfillmentError<'db> {
33    pub obligation: PredicateObligation<'db>,
34    pub code: FulfillmentErrorCode<'db>,
35    pub parent_trait_obligations: Vec<Predicate<'db>>,
36}
37
38impl FulfillmentError<'_> {
39    pub fn is_true_error(&self) -> bool {
40        match self.code {
41            FulfillmentErrorCode::Select(_)
42            | FulfillmentErrorCode::Project(_)
43            | FulfillmentErrorCode::Subtype(_, _)
44            | FulfillmentErrorCode::ConstEquate(_, _) => true,
45            FulfillmentErrorCode::Cycle(_) | FulfillmentErrorCode::Ambiguity { overflow: _ } => {
46                false
47            }
48        }
49    }
50}
51
52#[derive(Clone)]
53pub enum FulfillmentErrorCode<'db> {
54    /// Inherently impossible to fulfill; this trait is implemented if and only
55    /// if it is already implemented.
56    Cycle(PredicateObligations<'db>),
57    Select(SelectionError<'db>),
58    Project(MismatchedProjectionTypes<'db>),
59    Subtype(ExpectedFound<Ty<'db>>, TypeError<'db>), // always comes from a SubtypePredicate
60    ConstEquate(ExpectedFound<Const<'db>>, TypeError<'db>),
61    Ambiguity {
62        /// Overflow is only `Some(suggest_recursion_limit)` when using the next generation
63        /// trait solver `-Znext-solver`. With the old solver overflow is eagerly handled by
64        /// emitting a fatal error instead.
65        overflow: Option<bool>,
66    },
67}
68
69impl<'db> fmt::Debug for FulfillmentErrorCode<'db> {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        match *self {
72            FulfillmentErrorCode::Select(ref e) => write!(f, "{e:?}"),
73            FulfillmentErrorCode::Project(ref e) => write!(f, "{e:?}"),
74            FulfillmentErrorCode::Subtype(ref a, ref b) => {
75                write!(f, "CodeSubtypeError({a:?}, {b:?})")
76            }
77            FulfillmentErrorCode::ConstEquate(ref a, ref b) => {
78                write!(f, "CodeConstEquateError({a:?}, {b:?})")
79            }
80            FulfillmentErrorCode::Ambiguity { overflow: None } => write!(f, "Ambiguity"),
81            FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) } => {
82                write!(f, "Overflow({suggest_increasing_limit})")
83            }
84            FulfillmentErrorCode::Cycle(ref cycle) => write!(f, "Cycle({cycle:?})"),
85        }
86    }
87}
88
89#[derive(Clone)]
90pub struct MismatchedProjectionTypes<'db> {
91    pub err: TypeError<'db>,
92}
93
94impl<'db> fmt::Debug for MismatchedProjectionTypes<'db> {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        write!(f, "MismatchedProjectionTypes({:?})", self.err)
97    }
98}
99
100impl<'db> NextSolverError<'db> {
101    pub fn into_fulfillment_error(self, infcx: &InferCtxt<'db>) -> FulfillmentError<'db> {
102        match self {
103            NextSolverError::TrueError(obligation) => {
104                fulfillment_error_for_no_solution(infcx, obligation)
105            }
106            NextSolverError::Ambiguity(obligation) => {
107                fulfillment_error_for_stalled(infcx, obligation)
108            }
109            NextSolverError::Overflow(obligation) => {
110                fulfillment_error_for_overflow(infcx, obligation)
111            }
112        }
113    }
114}
115
116fn fulfillment_error_for_no_solution<'db>(
117    infcx: &InferCtxt<'db>,
118    root_obligation: PredicateObligation<'db>,
119) -> FulfillmentError<'db> {
120    let interner = infcx.interner;
121    let db = interner.db;
122    let (obligation, parent_trait_obligations) =
123        find_best_leaf_obligation(infcx, &root_obligation, false);
124
125    let code = match obligation.predicate.kind().skip_binder() {
126        PredicateKind::Clause(ClauseKind::Projection(_)) => {
127            FulfillmentErrorCode::Project(
128                // FIXME: This could be a `Sorts` if the term is a type
129                MismatchedProjectionTypes { err: TypeError::Mismatch },
130            )
131        }
132        PredicateKind::Clause(ClauseKind::ConstArgHasType(ct, expected_ty)) => {
133            let ct_ty = match ct.kind() {
134                ConstKind::Unevaluated(uv) => {
135                    let ct_ty = match uv.def.0 {
136                        GeneralConstId::ConstId(konst) => db.value_ty(konst.into()).unwrap(),
137                        GeneralConstId::StaticId(statik) => db.value_ty(statik.into()).unwrap(),
138                        GeneralConstId::AnonConstId(konst) => konst.loc(db).ty.get(),
139                    };
140                    ct_ty.instantiate(interner, uv.args).skip_norm_wip()
141                }
142                ConstKind::Param(param_ct) => param_ct.find_const_ty_from_env(obligation.param_env),
143                ConstKind::Value(cv) => cv.ty,
144                kind => panic!(
145                    "ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}"
146                ),
147            };
148            FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType {
149                ct,
150                ct_ty,
151                expected_ty,
152            })
153        }
154        PredicateKind::NormalizesTo(..) => {
155            FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
156        }
157        PredicateKind::AliasRelate(_, _, _) => {
158            FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
159        }
160        PredicateKind::Subtype(pred) => {
161            let (a, b) = infcx.enter_forall_and_leak_universe(
162                obligation.predicate.kind().rebind((pred.a, pred.b)),
163            );
164            let expected_found = ExpectedFound::new(a, b);
165            FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
166        }
167        PredicateKind::Coerce(pred) => {
168            let (a, b) = infcx.enter_forall_and_leak_universe(
169                obligation.predicate.kind().rebind((pred.a, pred.b)),
170            );
171            let expected_found = ExpectedFound::new(b, a);
172            FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
173        }
174        PredicateKind::Clause(_) | PredicateKind::DynCompatible(_) | PredicateKind::Ambiguous => {
175            FulfillmentErrorCode::Select(SelectionError::Unimplemented)
176        }
177        PredicateKind::ConstEquate(..) => {
178            panic!("unexpected goal: {obligation:?}")
179        }
180    };
181
182    FulfillmentError { obligation, code, parent_trait_obligations }
183}
184
185fn fulfillment_error_for_stalled<'db>(
186    infcx: &InferCtxt<'db>,
187    root_obligation: PredicateObligation<'db>,
188) -> FulfillmentError<'db> {
189    let (code, refine_obligation) = infcx.probe(|_| {
190        match <&SolverContext<'db>>::from(infcx).evaluate_root_goal(
191            root_obligation.as_goal(),
192            root_obligation.cause.span(),
193            None,
194        ) {
195            Ok(GoalEvaluation {
196                certainty: Certainty::Maybe(MaybeInfo { cause: MaybeCause::Ambiguity, .. }),
197                ..
198            }) => (FulfillmentErrorCode::Ambiguity { overflow: None }, true),
199            Ok(GoalEvaluation {
200                certainty:
201                    Certainty::Maybe(MaybeInfo {
202                        cause:
203                            MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: _ },
204                        ..
205                    }),
206                ..
207            }) => (
208                FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) },
209                // Don't look into overflows because we treat overflows weirdly anyways.
210                // We discard the inference constraints from overflowing goals, so
211                // recomputing the goal again during `find_best_leaf_obligation` may apply
212                // inference guidance that makes other goals go from ambig -> pass, for example.
213                //
214                // FIXME: We should probably just look into overflows here.
215                false,
216            ),
217            Ok(GoalEvaluation { certainty: Certainty::Yes, .. }) => {
218                panic!(
219                    "did not expect successful goal when collecting ambiguity errors for `{:?}`",
220                    infcx.resolve_vars_if_possible(root_obligation.predicate),
221                )
222            }
223            Err(_) => {
224                panic!(
225                    "did not expect selection error when collecting ambiguity errors for `{:?}`",
226                    infcx.resolve_vars_if_possible(root_obligation.predicate),
227                )
228            }
229        }
230    });
231
232    let (obligation, parent_trait_obligations) = if refine_obligation {
233        find_best_leaf_obligation(infcx, &root_obligation, true)
234    } else {
235        (root_obligation, Vec::new())
236    };
237
238    FulfillmentError { obligation, code, parent_trait_obligations }
239}
240
241fn fulfillment_error_for_overflow<'db>(
242    infcx: &InferCtxt<'db>,
243    root_obligation: PredicateObligation<'db>,
244) -> FulfillmentError<'db> {
245    let (obligation, parent_trait_obligations) =
246        find_best_leaf_obligation(infcx, &root_obligation, true);
247    FulfillmentError {
248        obligation,
249        code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) },
250        parent_trait_obligations,
251    }
252}
253
254#[instrument(level = "debug", skip(infcx), ret)]
255fn find_best_leaf_obligation<'db>(
256    infcx: &InferCtxt<'db>,
257    obligation: &PredicateObligation<'db>,
258    consider_ambiguities: bool,
259) -> (PredicateObligation<'db>, Vec<Predicate<'db>>) {
260    let obligation = infcx.resolve_vars_if_possible(obligation.clone());
261    // FIXME: we use a probe here as the `BestObligation` visitor does not
262    // check whether it uses candidates which get shadowed by where-bounds.
263    //
264    // We should probably fix the visitor to not do so instead, as this also
265    // means the leaf obligation may be incorrect.
266    let (obligation, parent_trait_obligations) = infcx
267        .fudge_inference_if_ok(|| {
268            let mut visitor = BestObligation {
269                obligation: obligation.clone(),
270                consider_ambiguities,
271                parent_trait_obligations: Vec::new(),
272            };
273            infcx
274                .visit_proof_tree(obligation.as_goal(), &mut visitor)
275                .break_value()
276                .ok_or(())
277                // walk around the fact that the cause in `Obligation` is ignored by folders so that
278                // we can properly fudge the infer vars in cause code.
279                .map(|(obligation, parent_trait_obligations)| {
280                    (obligation.cause, obligation, parent_trait_obligations)
281                })
282        })
283        .map(|(cause, obligation, parent_trait_obligations)| {
284            (PredicateObligation { cause, ..obligation }, parent_trait_obligations)
285        })
286        .unwrap_or((obligation, Vec::new()));
287    let parent_trait_obligations =
288        deeply_normalize_for_diagnostics(infcx, obligation.param_env, parent_trait_obligations);
289    let obligation = deeply_normalize_for_diagnostics(infcx, obligation.param_env, obligation);
290    (obligation, parent_trait_obligations)
291}
292
293struct BestObligation<'db> {
294    obligation: PredicateObligation<'db>,
295    consider_ambiguities: bool,
296    parent_trait_obligations: Vec<Predicate<'db>>,
297}
298
299impl<'db> BestObligation<'db> {
300    fn with_derived_obligation(
301        &mut self,
302        derived_obligation: PredicateObligation<'db>,
303        and_then: impl FnOnce(&mut Self) -> <Self as ProofTreeVisitor<'db>>::Result,
304    ) -> <Self as ProofTreeVisitor<'db>>::Result {
305        let parent_predicate = self.obligation.predicate;
306        let should_push = parent_predicate.as_trait_clause().is_some()
307            && self.parent_trait_obligations.last() != Some(&parent_predicate);
308        if should_push {
309            self.parent_trait_obligations.push(parent_predicate);
310        }
311        let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation);
312        let result = and_then(self);
313        self.obligation = old_obligation;
314        if should_push {
315            self.parent_trait_obligations.pop();
316        }
317        result
318    }
319
320    fn break_with_current_obligation(&mut self) -> <Self as ProofTreeVisitor<'db>>::Result {
321        ControlFlow::Break((
322            self.obligation.clone(),
323            std::mem::take(&mut self.parent_trait_obligations),
324        ))
325    }
326
327    /// Filter out the candidates that aren't interesting to visit for the
328    /// purposes of reporting errors. For ambiguities, we only consider
329    /// candidates that may hold. For errors, we only consider candidates that
330    /// *don't* hold and which have impl-where clauses that also don't hold.
331    fn non_trivial_candidates<'a>(
332        &self,
333        goal: &'a inspect::InspectGoal<'a, 'db>,
334    ) -> Vec<inspect::InspectCandidate<'a, 'db>> {
335        let mut candidates = goal.candidates();
336        match self.consider_ambiguities {
337            true => {
338                // If we have an ambiguous obligation, we must consider *all* candidates
339                // that hold, or else we may guide inference causing other goals to go
340                // from ambig -> pass/fail.
341                candidates.retain(|candidate| candidate.result().is_ok());
342            }
343            false => {
344                // We always handle rigid alias candidates separately as we may not add them for
345                // aliases whose trait bound doesn't hold.
346                candidates.retain(|c| !matches!(c.kind(), inspect::ProbeKind::RigidAlias { .. }));
347                // If we have >1 candidate, one may still be due to "boring" reasons, like
348                // an alias-relate that failed to hold when deeply evaluated. We really
349                // don't care about reasons like this.
350                if candidates.len() > 1 {
351                    candidates.retain(|candidate| {
352                        goal.infcx().probe(|_| {
353                            candidate.instantiate_nested_goals(self.span()).iter().any(
354                                |nested_goal| {
355                                    matches!(
356                                        nested_goal.source(),
357                                        GoalSource::ImplWhereBound
358                                            | GoalSource::AliasBoundConstCondition
359                                            | GoalSource::AliasWellFormed
360                                    ) && nested_goal.result().is_err()
361                                },
362                            )
363                        })
364                    });
365                }
366            }
367        }
368
369        candidates
370    }
371
372    /// HACK: We walk the nested obligations for a well-formed arg manually,
373    /// since there's nontrivial logic in `wf.rs` to set up an obligation cause.
374    /// Ideally we'd be able to track this better.
375    fn visit_well_formed_goal(
376        &mut self,
377        candidate: &inspect::InspectCandidate<'_, 'db>,
378        term: Term<'db>,
379    ) -> <Self as ProofTreeVisitor<'db>>::Result {
380        let _ = (candidate, term);
381        // FIXME: rustc does this, but we don't process WF obligations yet:
382        // let infcx = candidate.goal().infcx();
383        // let param_env = candidate.goal().goal().param_env;
384        // let body_id = self.obligation.cause.body_id;
385
386        // for obligation in wf::unnormalized_obligations(infcx, param_env, term, self.span(), body_id)
387        //     .into_iter()
388        //     .flatten()
389        // {
390        //     let nested_goal = candidate.instantiate_proof_tree_for_nested_goal(
391        //         GoalSource::Misc,
392        //         obligation.as_goal(),
393        //         self.span(),
394        //     );
395        //     // Skip nested goals that aren't the *reason* for our goal's failure.
396        //     match (self.consider_ambiguities, nested_goal.result()) {
397        //         (true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. }))
398        //         | (false, Err(_)) => {}
399        //         _ => continue,
400        //     }
401
402        //     self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
403        // }
404
405        self.break_with_current_obligation()
406    }
407
408    /// If a normalization of an associated item or a trait goal fails without trying any
409    /// candidates it's likely that normalizing its self type failed. We manually detect
410    /// such cases here.
411    fn detect_error_in_self_ty_normalization(
412        &mut self,
413        goal: &inspect::InspectGoal<'_, 'db>,
414        self_ty: Ty<'db>,
415    ) -> <Self as ProofTreeVisitor<'db>>::Result {
416        assert!(!self.consider_ambiguities);
417        let interner = goal.infcx().interner;
418        if let TyKind::Alias(..) = self_ty.kind() {
419            let infer_term = goal.infcx().next_ty_var(self.obligation.cause.span());
420            let pred = PredicateKind::AliasRelate(
421                self_ty.into(),
422                infer_term.into(),
423                AliasRelationDirection::Equate,
424            );
425            let obligation =
426                Obligation::new(interner, self.obligation.cause, goal.goal().param_env, pred);
427            self.with_derived_obligation(obligation, |this| {
428                goal.infcx().visit_proof_tree_at_depth(
429                    goal.goal().with(interner, pred),
430                    goal.depth() + 1,
431                    this,
432                )
433            })
434        } else {
435            ControlFlow::Continue(())
436        }
437    }
438
439    /// When a higher-ranked projection goal fails, check that the corresponding
440    /// higher-ranked trait goal holds or not. This is because the process of
441    /// instantiating and then re-canonicalizing the binder of the projection goal
442    /// forces us to be unable to see that the leak check failed in the nested
443    /// `NormalizesTo` goal, so we don't fall back to the rigid projection check
444    /// that should catch when a projection goal fails due to an unsatisfied trait
445    /// goal.
446    fn detect_trait_error_in_higher_ranked_projection(
447        &mut self,
448        goal: &inspect::InspectGoal<'_, 'db>,
449    ) -> <Self as ProofTreeVisitor<'db>>::Result {
450        let interner = goal.infcx().interner;
451        if let Some(projection_clause) = goal.goal().predicate.as_projection_clause()
452            && !projection_clause.bound_vars().is_empty()
453        {
454            let pred = projection_clause.map_bound(|proj| proj.projection_term.trait_ref(interner));
455            let obligation = Obligation::new(
456                interner,
457                self.obligation.cause,
458                goal.goal().param_env,
459                deeply_normalize_for_diagnostics(goal.infcx(), goal.goal().param_env, pred),
460            );
461            self.with_derived_obligation(obligation, |this| {
462                goal.infcx().visit_proof_tree_at_depth(
463                    goal.goal().with(interner, pred),
464                    goal.depth() + 1,
465                    this,
466                )
467            })
468        } else {
469            ControlFlow::Continue(())
470        }
471    }
472
473    /// It is likely that `NormalizesTo` failed without any applicable candidates
474    /// because the alias is not well-formed.
475    ///
476    /// As we only enter `RigidAlias` candidates if the trait bound of the associated type
477    /// holds, we discard these candidates in `non_trivial_candidates` and always manually
478    /// check this here.
479    fn detect_non_well_formed_assoc_item(
480        &mut self,
481        goal: &inspect::InspectGoal<'_, 'db>,
482        alias: AliasTerm<'db>,
483    ) -> <Self as ProofTreeVisitor<'db>>::Result {
484        let interner = goal.infcx().interner;
485        let obligation = Obligation::new(
486            interner,
487            self.obligation.cause,
488            goal.goal().param_env,
489            alias.trait_ref(interner),
490        );
491        self.with_derived_obligation(obligation, |this| {
492            goal.infcx().visit_proof_tree_at_depth(
493                goal.goal().with(interner, alias.trait_ref(interner)),
494                goal.depth() + 1,
495                this,
496            )
497        })
498    }
499
500    /// If we have no candidates, then it's likely that there is a
501    /// non-well-formed alias in the goal.
502    fn detect_error_from_empty_candidates(
503        &mut self,
504        goal: &inspect::InspectGoal<'_, 'db>,
505    ) -> <Self as ProofTreeVisitor<'db>>::Result {
506        let interner = goal.infcx().interner;
507        let pred_kind = goal.goal().predicate.kind();
508
509        match pred_kind.no_bound_vars() {
510            Some(PredicateKind::Clause(ClauseKind::Trait(pred))) => {
511                self.detect_error_in_self_ty_normalization(goal, pred.self_ty())?;
512            }
513            Some(PredicateKind::NormalizesTo(pred))
514                if let AliasTermKind::ProjectionTy { .. }
515                | AliasTermKind::ProjectionConst { .. } = pred.alias.kind(interner) =>
516            {
517                self.detect_error_in_self_ty_normalization(goal, pred.alias.self_ty())?;
518                self.detect_non_well_formed_assoc_item(goal, pred.alias)?;
519            }
520            Some(_) | None => {}
521        }
522
523        self.break_with_current_obligation()
524    }
525}
526
527impl<'db> ProofTreeVisitor<'db> for BestObligation<'db> {
528    type Result = ControlFlow<(PredicateObligation<'db>, Vec<Predicate<'db>>)>;
529
530    fn span(&self) -> Span {
531        self.obligation.cause.span()
532    }
533
534    #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))]
535    fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'db>) -> Self::Result {
536        let interner = goal.infcx().interner;
537        // Skip goals that aren't the *reason* for our goal's failure.
538        match (self.consider_ambiguities, goal.result()) {
539            (true, Ok(Certainty::Maybe(MaybeInfo { cause: MaybeCause::Ambiguity, .. })))
540            | (false, Err(_)) => {}
541            _ => return ControlFlow::Continue(()),
542        }
543
544        let pred = goal.goal().predicate;
545
546        let candidates = self.non_trivial_candidates(goal);
547        let candidate = match candidates.as_slice() {
548            [candidate] => candidate,
549            [] => return self.detect_error_from_empty_candidates(goal),
550            _ => return self.break_with_current_obligation(),
551        };
552
553        // Don't walk into impls that have `do_not_recommend`.
554        if let inspect::ProbeKind::TraitCandidate {
555            source: CandidateSource::Impl(impl_def_id),
556            result: _,
557        } = candidate.kind()
558            && let AnyImplId::ImplId(impl_def_id) = impl_def_id
559            && AttrFlags::query(interner.db, impl_def_id.into())
560                .contains(AttrFlags::DIAGNOSTIC_DO_NOT_RECOMMEND)
561        {
562            trace!("#[diagnostic::do_not_recommend] -> exit");
563            return self.break_with_current_obligation();
564        }
565
566        // FIXME: Also, what about considering >1 layer up the stack? May be necessary
567        // for normalizes-to.
568        let child_mode = match pred.kind().skip_binder() {
569            PredicateKind::Clause(ClauseKind::Trait(trait_pred)) => {
570                ChildMode::Trait(pred.kind().rebind(trait_pred))
571            }
572            PredicateKind::Clause(ClauseKind::HostEffect(host_pred)) => {
573                ChildMode::Host(pred.kind().rebind(host_pred))
574            }
575            PredicateKind::NormalizesTo(normalizes_to)
576                if matches!(
577                    normalizes_to.alias.kind(interner),
578                    AliasTermKind::ProjectionTy { .. } | AliasTermKind::ProjectionConst { .. }
579                ) =>
580            {
581                ChildMode::Trait(pred.kind().rebind(TraitPredicate {
582                    trait_ref: normalizes_to.alias.trait_ref(interner),
583                    polarity: PredicatePolarity::Positive,
584                }))
585            }
586            PredicateKind::Clause(ClauseKind::WellFormed(term)) => {
587                return self.visit_well_formed_goal(candidate, term);
588            }
589            _ => ChildMode::PassThrough,
590        };
591
592        let nested_goals = candidate.instantiate_nested_goals(self.span());
593
594        // If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as
595        // an actual candidate, instead we should treat them as if the impl was never considered to
596        // have potentially applied. As if `impl<A, R> Trait for for<..> fn(..A) -> R` was written
597        // instead of `impl<T: FnPtr> Trait for T`.
598        //
599        // We do this as a separate loop so that we do not choose to tell the user about some nested
600        // goal before we encounter a `T: FnPtr` nested goal.
601        for nested_goal in &nested_goals {
602            if let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause()
603                && Some(poly_trait_pred.def_id().0) == interner.lang_items().FnPtrTrait
604                && let Err(NoSolution) = nested_goal.result()
605            {
606                return self.break_with_current_obligation();
607            }
608        }
609
610        let mut impl_where_bound_count = 0;
611        for nested_goal in nested_goals {
612            trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result()));
613
614            let nested_pred = nested_goal.goal().predicate;
615
616            let make_obligation = |cause| Obligation {
617                cause,
618                param_env: nested_goal.goal().param_env,
619                predicate: nested_pred,
620                recursion_depth: self.obligation.recursion_depth + 1,
621            };
622
623            let obligation;
624            match (child_mode, nested_goal.source()) {
625                (
626                    ChildMode::Trait(_) | ChildMode::Host(_),
627                    GoalSource::Misc | GoalSource::TypeRelating | GoalSource::NormalizeGoal(_),
628                ) => {
629                    continue;
630                }
631                (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => {
632                    obligation = make_obligation(derive_cause(
633                        interner,
634                        candidate.kind(),
635                        self.obligation.cause,
636                        impl_where_bound_count,
637                        parent_trait_pred,
638                    ));
639                    impl_where_bound_count += 1;
640                }
641                (
642                    ChildMode::Host(parent_host_pred),
643                    GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition,
644                ) => {
645                    obligation = make_obligation(derive_host_cause(
646                        interner,
647                        candidate.kind(),
648                        self.obligation.cause,
649                        impl_where_bound_count,
650                        parent_host_pred,
651                    ));
652                    impl_where_bound_count += 1;
653                }
654                (ChildMode::PassThrough, _)
655                | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => {
656                    obligation = make_obligation(self.obligation.cause);
657                }
658            }
659
660            self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
661        }
662
663        // alias-relate may fail because the lhs or rhs can't be normalized,
664        // and therefore is treated as rigid.
665        if let Some(PredicateKind::AliasRelate(lhs, rhs, _)) = pred.kind().no_bound_vars() {
666            goal.infcx().visit_proof_tree_at_depth(
667                goal.goal().with(interner, ClauseKind::WellFormed(lhs)),
668                goal.depth() + 1,
669                self,
670            )?;
671            goal.infcx().visit_proof_tree_at_depth(
672                goal.goal().with(interner, ClauseKind::WellFormed(rhs)),
673                goal.depth() + 1,
674                self,
675            )?;
676        }
677
678        self.detect_trait_error_in_higher_ranked_projection(goal)?;
679
680        self.break_with_current_obligation()
681    }
682}
683
684#[derive(Debug, Copy, Clone)]
685enum ChildMode<'db> {
686    // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
687    // and skip all `GoalSource::Misc`, which represent useless obligations
688    // such as alias-eq which may not hold.
689    Trait(PolyTraitPredicate<'db>),
690    // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
691    // and skip all `GoalSource::Misc`, which represent useless obligations
692    // such as alias-eq which may not hold.
693    Host(Binder<'db, HostEffectPredicate<'db>>),
694    // Skip trying to derive an `ObligationCause` from this obligation, and
695    // report *all* sub-obligations as if they came directly from the parent
696    // obligation.
697    PassThrough,
698}
699
700fn derive_cause<'db>(
701    _interner: DbInterner<'db>,
702    _candidate_kind: inspect::ProbeKind<DbInterner<'db>>,
703    cause: ObligationCause,
704    _idx: usize,
705    _parent_trait_pred: PolyTraitPredicate<'db>,
706) -> ObligationCause {
707    cause
708}
709
710fn derive_host_cause<'db>(
711    _interner: DbInterner<'db>,
712    _candidate_kind: inspect::ProbeKind<DbInterner<'db>>,
713    cause: ObligationCause,
714    _idx: usize,
715    _parent_host_pred: Binder<'db, HostEffectPredicate<'db>>,
716) -> ObligationCause {
717    cause
718}