rustc_trait_selection/solve/fulfill/
derive_errors.rs

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