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