Skip to main content

rustc_trait_selection/solve/
delegate.rs

1use std::collections::hash_map::Entry;
2use std::fmt::Debug;
3use std::mem;
4use std::ops::{ControlFlow, Deref};
5
6use rustc_attr_ir::lang_items::LangItem;
7use rustc_data_structures::fx::{FxHashMap, FxHashSet};
8use rustc_hir::CRATE_HIR_ID;
9use rustc_hir::def::Namespace;
10use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE};
11use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
12use rustc_infer::infer::canonical::{
13    Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarKind, CanonicalVarValues,
14    QueryRegionConstraint,
15};
16use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TyCtxtInferExt};
17use rustc_infer::traits::solve::{
18    ComputeGoalFastPathOutcome, FetchEligibleAssocItemResponse, Goal, SucceededInErased,
19};
20use rustc_lint_defs::builtin::RECURSION_DEPTH_EXCEEDING_LIMIT;
21use rustc_middle::traits::query::NoSolution;
22use rustc_middle::traits::solve::MaybeInfo;
23use rustc_middle::ty::print::{FmtPrinter, Print};
24use rustc_middle::ty::transmute::Answer;
25use rustc_middle::ty::{
26    self, CanonicalizerState, MayBeErased, Region, Ty, TyCtxt, TypeFlags, TypeFoldable,
27    TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode,
28};
29use rustc_next_trait_solver::solve::{GoalStalledOn, GoalStalledOnOpaques, TyOrConstInferVar};
30use rustc_span::{DUMMY_SP, Span};
31use rustc_structures::Limit;
32use rustc_transmute::TransmuteTypeEnv;
33use thin_vec::{ThinVec, thin_vec};
34
35use super::inspect::InferCtxtProofTreeExt;
36use crate::solve::inspect::{self, InspectConfig, ProofTreeVisitor};
37use crate::traits::{EvaluateConstErr, ObligationCause, sizedness_fast_path, specialization_graph};
38
39#[repr(transparent)]
40pub struct SolverDelegate<'tcx>(InferCtxt<'tcx>);
41
42impl<'a, 'tcx> From<&'a InferCtxt<'tcx>> for &'a SolverDelegate<'tcx> {
43    fn from(infcx: &'a InferCtxt<'tcx>) -> Self {
44        // SAFETY: `repr(transparent)`
45        unsafe { std::mem::transmute(infcx) }
46    }
47}
48
49impl<'tcx> Deref for SolverDelegate<'tcx> {
50    type Target = InferCtxt<'tcx>;
51
52    fn deref(&self) -> &Self::Target {
53        &self.0
54    }
55}
56
57impl<'tcx> SolverDelegate<'tcx> {
58    fn known_no_opaque_types_in_storage(&self) -> bool {
59        self.inner.borrow_mut().opaque_types().is_empty()
60            // in erased mode, observing that opaques are empty aren't enough to give a result
61            // here, so let's try the slow path instead.
62            && !self.typing_mode_raw().is_erased_not_coherence()
63    }
64}
65
66/// Create a [`ComputeGoalFastPathOutcome`] signalling the goal is stalled
67/// on a list of [`ty::GenericArg`]
68fn goal_stalled_on_args<'tcx>(
69    stalled_vars: ThinVec<TyOrConstInferVar>,
70) -> ComputeGoalFastPathOutcome<'tcx> {
71    ComputeGoalFastPathOutcome::TriviallyStalled {
72        stalled_on: GoalStalledOn {
73            stalled_vars,
74            sub_roots: ThinVec::new(),
75            stalled_maybe_info: MaybeInfo::AMBIGUOUS,
76            opaques: GoalStalledOnOpaques::No,
77        },
78    }
79}
80
81/// Create a [`ComputeGoalFastPathOutcome`] signalling the  goal is stalled
82/// on a list of [`ty::GenericArg`] *or* the opaque type storage being nonempty.
83///
84fn goal_stalled_on_args_or_nonempty_opaques<'tcx>(
85    stalled_vars: ThinVec<TyOrConstInferVar>,
86) -> ComputeGoalFastPathOutcome<'tcx> {
87    ComputeGoalFastPathOutcome::TriviallyStalled {
88        stalled_on: GoalStalledOn {
89            stalled_vars,
90            sub_roots: ThinVec::new(),
91            stalled_maybe_info: MaybeInfo::AMBIGUOUS,
92            opaques: GoalStalledOnOpaques::Yes {
93                num_opaques_in_storage: 0,
94                // This function should only be called when not in erased mode,
95                // otherwise this is wrong. The `compute_goal_fast_path` does this
96                // through `known_no_opaque_types_in_storage`
97                previously_succeeded_in_erased: SucceededInErased::No,
98            },
99        },
100    }
101}
102
103struct CollectNonRegionInfer<'tcx> {
104    infers: ThinVec<ty::GenericArg<'tcx>>,
105    visited: FxHashSet<Ty<'tcx>>,
106}
107
108impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for CollectNonRegionInfer<'tcx> {
109    fn visit_ty(&mut self, ty: Ty<'tcx>) {
110        if self.visited.contains(&ty) {
111            return;
112        }
113
114        match ty.kind() {
115            ty::Infer(_) => self.infers.push(ty.into()),
116            _ => ty.super_visit_with(self),
117        }
118
119        self.visited.insert(ty);
120    }
121
122    fn visit_const(&mut self, ct: ty::Const<'tcx>) {
123        match ct.kind() {
124            ty::ConstKind::Infer(_) => self.infers.push(ct.into()),
125            _ => ct.super_visit_with(self),
126        }
127    }
128}
129
130impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<'tcx> {
131    type Infcx = InferCtxt<'tcx>;
132    type Interner = TyCtxt<'tcx>;
133
134    fn cx(&self) -> TyCtxt<'tcx> {
135        self.0.tcx
136    }
137
138    fn build_with_canonical<V>(
139        interner: TyCtxt<'tcx>,
140        canonical: &CanonicalQueryInput<'tcx, V>,
141    ) -> (Self, V, CanonicalVarValues<'tcx>)
142    where
143        V: TypeFoldable<TyCtxt<'tcx>>,
144    {
145        let (infcx, value, vars) = interner
146            .infer_ctxt()
147            .with_next_trait_solver(true)
148            .build_with_canonical(DUMMY_SP, canonical);
149        (SolverDelegate(infcx), value, vars)
150    }
151
152    fn compute_goal_fast_path(
153        &self,
154        goal: Goal<'tcx, ty::Predicate<'tcx>>,
155        span: Span,
156    ) -> ComputeGoalFastPathOutcome<'tcx> {
157        use ComputeGoalFastPathOutcome as Outcome;
158
159        // FIXME(-Zassumptions-on-binders): actually handle fast path
160        if self.tcx.assumptions_on_binders() {
161            return Outcome::NoFastPath;
162        }
163
164        let pred = goal.predicate.kind();
165        match pred.skip_binder() {
166            ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => {
167                let trait_pred = pred.rebind(trait_pred);
168
169                let self_ty = self.shallow_resolve(trait_pred.self_ty().skip_binder());
170                if let Some(vid) = self_ty.ty_vid()
171                // We don't do this fast path when opaques are defined since we may
172                // eventually use opaques to incompletely guide inference via ty var
173                // self types.
174                // FIXME: Properly consider opaques here.
175                && self.known_no_opaque_types_in_storage()
176                {
177                    goal_stalled_on_args_or_nonempty_opaques({
    let len = [()].len();
    let mut vec = ::thin_vec::ThinVec::with_capacity(len);
    vec.push(TyOrConstInferVar::Ty(vid));
    vec
}thin_vec![TyOrConstInferVar::Ty(vid)])
178                } else if trait_pred.polarity() == ty::ClausePolarity::Positive {
179                    match self.0.tcx.as_lang_item(trait_pred.def_id()) {
180                        Some(LangItem::Sized) | Some(LangItem::MetaSized) => {
181                            let predicate = self.deeply_resolve_ignoring_regions(goal.predicate);
182                            if sizedness_fast_path(self.tcx, predicate, goal.param_env) {
183                                Outcome::TriviallyHolds
184                            } else {
185                                Outcome::NoFastPath
186                            }
187                        }
188                        Some(LangItem::Copy | LangItem::Clone) => {
189                            let self_ty = self.deeply_resolve_ignoring_regions(
190                                trait_pred.self_ty().skip_binder(),
191                            );
192                            // Unlike `Sized` traits, which always prefer the built-in impl,
193                            // `Copy`/`Clone` may be shadowed by a param-env candidate which
194                            // could force a lifetime error or guide inference. While that's
195                            // not generally desirable, it is observable, so for now let's
196                            // ignore this fast path for types that have regions or infer.
197                            if !self_ty
198                                .has_type_flags(TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_INFER)
199                                && self_ty.is_trivially_pure_clone_copy()
200                            {
201                                Outcome::TriviallyHolds
202                            } else {
203                                Outcome::NoFastPath
204                            }
205                        }
206                        _ => Outcome::NoFastPath,
207                    }
208                } else {
209                    Outcome::NoFastPath
210                }
211            }
212            ty::PredicateKind::DynCompatible(def_id) if self.0.tcx.is_dyn_compatible(def_id) => {
213                Outcome::TriviallyHolds
214            }
215            ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(outlives)) => {
216                if outlives.has_escaping_bound_vars() {
217                    return Outcome::NoFastPath;
218                }
219
220                // We use `VisibleForLeakCheck::Yes` for `RegionOutlives` goals without
221                // the fast path. However, we always change region constraints from nested
222                // goals to `VisibleForLeakCheck::No`, so to be consistent with this
223                // behavior as we avoid the nested goal boundary, we have to manually
224                // set the flag.
225                self.0.sub_regions(
226                    SubregionOrigin::RelateRegionParamBound(span, None),
227                    outlives.1,
228                    outlives.0,
229                    ty::VisibleForLeakCheck::No,
230                );
231                Outcome::TriviallyHolds
232            }
233            ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(outlives)) => {
234                if outlives.has_escaping_bound_vars() {
235                    return Outcome::NoFastPath;
236                }
237
238                let ty = self.deeply_resolve_ignoring_regions(outlives.0);
239                if ty.has_non_rigid_aliases() {
240                    return Outcome::NoFastPath;
241                }
242
243                let mut infer_collector = CollectNonRegionInfer {
244                    infers: Default::default(),
245                    visited: Default::default(),
246                };
247                ty.visit_with(&mut infer_collector);
248                let infers = infer_collector.infers;
249                if !infers.is_empty() {
250                    return goal_stalled_on_args(
251                        infers
252                            .into_iter()
253                            .map(|i| {
254                                TyOrConstInferVar::maybe_from_generic_arg::<Self::Interner>(i)
255                                    .unwrap()
256                            })
257                            .collect(),
258                    );
259                }
260
261                self.0.register_type_outlives_constraint(
262                    outlives.0,
263                    outlives.1,
264                    &ObligationCause::dummy_with_span(span),
265                );
266
267                Outcome::TriviallyHolds
268            }
269            ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, .. })
270            | ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => {
271                if a.has_escaping_bound_vars() || b.has_escaping_bound_vars() {
272                    return Outcome::NoFastPath;
273                }
274
275                match (self.shallow_resolve(a).kind(), self.shallow_resolve(b).kind()) {
276                    (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => {
277                        self.sub_unify_ty_vids_raw(a_vid, b_vid);
278                        goal_stalled_on_args({
    let len = [(), ()].len();
    let mut vec = ::thin_vec::ThinVec::with_capacity(len);
    vec.push(TyOrConstInferVar::Ty(a_vid));
    vec.push(TyOrConstInferVar::Ty(b_vid));
    vec
}thin_vec![
279                            TyOrConstInferVar::Ty(a_vid),
280                            TyOrConstInferVar::Ty(b_vid),
281                        ])
282                    }
283                    _ => Outcome::NoFastPath,
284                }
285            }
286            ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, _)) => {
287                if ct.has_escaping_bound_vars() {
288                    return Outcome::NoFastPath;
289                }
290
291                let arg = self.shallow_resolve_const(ct);
292                if let Some(vid) = arg.ct_vid() {
293                    goal_stalled_on_args({
    let len = [()].len();
    let mut vec = ::thin_vec::ThinVec::with_capacity(len);
    vec.push(TyOrConstInferVar::Const(vid));
    vec
}thin_vec![TyOrConstInferVar::Const(vid)])
294                } else {
295                    Outcome::NoFastPath
296                }
297            }
298            ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
299                if arg.has_escaping_bound_vars() {
300                    return Outcome::NoFastPath;
301                }
302
303                let arg = self.shallow_resolve_term(arg);
304                if arg.is_trivially_wf(self.tcx) {
305                    Outcome::TriviallyHolds
306                } else if arg.is_infer() {
307                    goal_stalled_on_args({
    let len = [()].len();
    let mut vec = ::thin_vec::ThinVec::with_capacity(len);
    vec.push(TyOrConstInferVar::maybe_from_term::<TyCtxt<'tcx>>(arg).expect("its an infer var"));
    vec
}thin_vec![
308                        TyOrConstInferVar::maybe_from_term::<TyCtxt<'tcx>>(arg)
309                            .expect("its an infer var"),
310                    ])
311                } else {
312                    Outcome::NoFastPath
313                }
314            }
315            _ => Outcome::NoFastPath,
316        }
317    }
318
319    fn fresh_var_for_kind(
320        &self,
321        arg: ty::GenericArg<'tcx>,
322        span: Span,
323        universe: ty::UniverseIndex,
324    ) -> ty::GenericArg<'tcx> {
325        match arg.kind() {
326            ty::GenericArgKind::Lifetime(_) => {
327                self.next_region_var_in_universe(RegionVariableOrigin::Misc(span), universe).into()
328            }
329            ty::GenericArgKind::Type(_) => self.next_ty_var_in_universe(span, universe).into(),
330            ty::GenericArgKind::Const(_) => self.next_const_var_in_universe(span, universe).into(),
331        }
332    }
333
334    fn leak_check(&self, max_input_universe: ty::UniverseIndex) -> Result<(), NoSolution> {
335        self.0.leak_check(max_input_universe, None).map_err(|_| NoSolution)
336    }
337
338    fn evaluate_const<E: Debug>(
339        &self,
340        param_env: ty::ParamEnv<'tcx>,
341        alias_const: ty::AliasConst<'tcx>,
342        normalize_ty: impl FnOnce(ty::Unnormalized<'tcx, Ty<'tcx>>) -> Result<Ty<'tcx>, E>,
343    ) -> Result<Option<ty::Const<'tcx>>, E> {
344        let ct = ty::Const::new_alias(self.tcx, ty::IsRigid::No, alias_const);
345
346        match crate::traits::try_evaluate_const(&self.0, ct, param_env, normalize_ty) {
347            Ok(ct) => Ok(Some(ct)),
348            Err(EvaluateConstErr::EvaluationFailure(e)) => {
349                Ok(Some(ty::Const::new_error(self.tcx, e)))
350            }
351            Err(
352                EvaluateConstErr::InvalidConstParamTy(_) | EvaluateConstErr::HasGenericsOrInfers,
353            ) => Ok(None),
354            Err(EvaluateConstErr::FailedNormalization(e)) => Err(e),
355        }
356    }
357
358    fn well_formed_goals(
359        &self,
360        param_env: ty::ParamEnv<'tcx>,
361        term: ty::Term<'tcx>,
362    ) -> Option<Vec<Goal<'tcx, ty::Predicate<'tcx>>>> {
363        crate::traits::wf::unnormalized_obligations(
364            &self.0,
365            param_env,
366            term,
367            DUMMY_SP,
368            CRATE_DEF_ID,
369        )
370        .map(|obligations| obligations.into_iter().map(|obligation| obligation.as_goal()).collect())
371    }
372
373    fn make_deduplicated_region_constraints(
374        &self,
375    ) -> Vec<(ty::RegionConstraint<'tcx>, ty::VisibleForLeakCheck)> {
376        // Cannot use `take_registered_region_obligations` as we may compute the response
377        // inside of a `probe` whenever we have multiple choices inside of the solver.
378        let region_obligations = self.0.inner.borrow().region_obligations().to_owned();
379        let region_assumptions = self.0.inner.borrow().region_assumptions().to_owned();
380        let region_constraints = self.0.with_region_constraints(|region_constraints| {
381            make_query_region_constraints(
382                region_obligations,
383                region_constraints,
384                region_assumptions,
385            )
386        });
387
388        let mut seen = FxHashMap::default();
389        let mut constraints = ::alloc::vec::Vec::new()vec![];
390        for QueryRegionConstraint { constraint: outlives, visible_for_leak_check: vis, .. } in
391            region_constraints.constraints
392        {
393            match seen.entry(outlives) {
394                Entry::Occupied(occupied) => {
395                    let idx = occupied.get();
396                    let (_, prev_vis): &mut (_, ty::VisibleForLeakCheck) =
397                        constraints.get_mut(*idx).unwrap();
398                    *prev_vis = (*prev_vis).or(vis);
399                }
400                Entry::Vacant(vacant) => {
401                    vacant.insert(constraints.len());
402                    constraints.push((outlives, vis));
403                }
404            }
405        }
406        constraints
407    }
408
409    fn instantiate_canonical<V>(
410        &self,
411        canonical: Canonical<'tcx, V>,
412        values: CanonicalVarValues<'tcx>,
413    ) -> V
414    where
415        V: TypeFoldable<TyCtxt<'tcx>>,
416    {
417        canonical.instantiate(self.tcx, &values)
418    }
419
420    fn instantiate_canonical_var(
421        &self,
422        kind: CanonicalVarKind<'tcx>,
423        span: Span,
424        var_values: &[ty::GenericArg<'tcx>],
425        universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
426    ) -> ty::GenericArg<'tcx> {
427        self.0.instantiate_canonical_var(span, kind, var_values, universe_map)
428    }
429
430    fn add_item_bounds_for_hidden_type(
431        &self,
432        def_id: DefId,
433        args: ty::GenericArgsRef<'tcx>,
434        param_env: ty::ParamEnv<'tcx>,
435        hidden_ty: Ty<'tcx>,
436        goals: &mut Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
437    ) {
438        self.0.add_item_bounds_for_hidden_type(def_id, args, param_env, hidden_ty, goals);
439    }
440
441    fn fetch_eligible_assoc_item(
442        &self,
443        goal_trait_ref: ty::TraitRef<'tcx>,
444        trait_assoc_def_id: DefId,
445        impl_def_id: DefId,
446    ) -> FetchEligibleAssocItemResponse<'tcx> {
447        let node_item =
448            match specialization_graph::assoc_def(self.tcx, impl_def_id, trait_assoc_def_id) {
449                Ok(i) => i,
450                Err(guar) => return FetchEligibleAssocItemResponse::Err(guar),
451            };
452
453        let typing_mode = self.typing_mode_raw();
454
455        let eligible = if node_item.is_final() {
456            // Non-specializable items are always projectable.
457            true
458        } else {
459            // Only reveal a specializable default if we're past type-checking
460            // and the obligation is monomorphic, otherwise passes such as
461            // transmute checking and polymorphic MIR optimizations could
462            // get a result which isn't correct for all monomorphizations.
463            match typing_mode {
464                TypingMode::Coherence
465                | TypingMode::Typeck { .. }
466                | TypingMode::PostTypeckUntilBorrowck { .. }
467                | TypingMode::Reflection
468                | TypingMode::PostBorrowck { .. } => false,
469                TypingMode::PostAnalysis | TypingMode::Codegen => {
470                    let poly_trait_ref = self.deeply_resolve_ignoring_regions(goal_trait_ref);
471                    !poly_trait_ref.still_further_specializable()
472                }
473                TypingMode::ErasedNotCoherence(MayBeErased) => {
474                    return FetchEligibleAssocItemResponse::NotFoundBecauseErased;
475                }
476            }
477        };
478
479        // FIXME: Check for defaultness here may cause diagnostics problems.
480        if eligible {
481            FetchEligibleAssocItemResponse::Found(node_item.item.def_id)
482        } else {
483            // We know it's not erased since then we'd have returned in the match above,
484            // or node_item.final() was true and eligible is always true.
485            FetchEligibleAssocItemResponse::NotFound(typing_mode.assert_not_erased())
486        }
487    }
488
489    // FIXME: This actually should destructure the `Result` we get from transmutability and
490    // register candidates. We probably need to register >1 since we may have an OR of ANDs.
491    fn is_transmutable(
492        &self,
493        src: Ty<'tcx>,
494        dst: Ty<'tcx>,
495        assume: ty::Const<'tcx>,
496    ) -> Result<Answer<Region<'tcx>, Ty<'tcx>>, NoSolution> {
497        rustc_transmute::Assume::from_const(self.tcx, assume)
498            .map(|assume| TransmuteTypeEnv::new(self.0.tcx).is_transmutable(src, dst, assume))
499            .ok_or(NoSolution)
500    }
501
502    fn obtain_canonicalizer_state(&self) -> CanonicalizerState<Self::Interner> {
503        // We temporarily take the canonicalizer state.
504        mem::take(&mut self.canonicalizer_state.borrow_mut())
505    }
506
507    fn release_canonicalizer_state(&self, mut state: CanonicalizerState<Self::Interner>) {
508        // Clear (don't deallocate) the state for later reuse.
509        state.clear();
510        *self.canonicalizer_state.borrow_mut() = state;
511    }
512
513    fn emit_next_solver_overflow_fcw(&self, goal: Goal<'tcx, ty::Predicate<'tcx>>, span: Span) {
514        let tcx = self.tcx;
515        tcx.emit_node_span_lint(
516            RECURSION_DEPTH_EXCEEDING_LIMIT,
517            CRATE_HIR_ID,
518            span,
519            rustc_errors::DiagDecorator(|diag| {
520                // We do this in the decorator as that avoids the work
521                // if we don't actually emit the lint.
522                let goal = self.deeply_resolve_ignoring_regions(goal);
523                let mut visitor = OverflowedGoalChain {
524                    span,
525                    predicates: ::alloc::vec::Vec::new()vec![],
526                    recursion_limit: usize::min(16, tcx.recursion_limit().0),
527                };
528
529                let _ = self.with_disabled_next_solver_overflow_fcw(|| {
530                    self.visit_proof_tree(goal, &mut visitor)
531                });
532
533                // FIXME: share this with overflow error in fulfillment instead of duplicating.
534                let pred_str = |pred: ty::Predicate<'tcx>| {
535                    let s = pred.to_string();
536                    if s.len() > 80 {
537                        let mut p: FmtPrinter<'_, '_> =
538                            FmtPrinter::new_with_limit(tcx, Namespace::TypeNS, Limit(10));
539                        pred.print(&mut p).unwrap();
540                        p.into_buffer()
541                    } else {
542                        s
543                    }
544                };
545                diag.primary_message(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("overflow evaluating the requirement `{0}`",
                pred_str(goal.predicate)))
    })format!(
546                    "overflow evaluating the requirement `{}`",
547                    pred_str(goal.predicate),
548                ));
549                for p in visitor.predicates.into_iter().skip(1) {
550                    diag.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("which requires `{0}`",
                pred_str(p)))
    })format!("which requires `{}`", pred_str(p)));
551                }
552                diag.note("and so on...");
553                diag.help(
554                    "consider adding a manual `impl` of auto traits like `Send` for intermediate types, if auto traits are involved",
555                );
556                diag.help(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("or consider increasing the recursion limit by adding a `#![recursion_limit = \"{0}\"]` attribute to your crate (`{1}`)",
                tcx.recursion_limit() * 2, tcx.crate_name(LOCAL_CRATE)))
    })format!(
557                    "or consider increasing the recursion limit by adding a \
558                     `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
559                    tcx.recursion_limit() * 2,
560                    tcx.crate_name(LOCAL_CRATE),
561                ));
562                diag.note("this lint is attached to the whole crate and can't be disabled on a per-function basis");
563            }),
564        )
565    }
566}
567
568struct OverflowedGoalChain<'tcx> {
569    span: Span,
570    predicates: Vec<ty::Predicate<'tcx>>,
571    recursion_limit: usize,
572}
573
574impl<'tcx> ProofTreeVisitor<'tcx> for OverflowedGoalChain<'tcx> {
575    type Result = ControlFlow<()>;
576
577    fn span(&self) -> Span {
578        self.span
579    }
580
581    fn config(&self) -> InspectConfig {
582        InspectConfig { max_depth: self.recursion_limit }
583    }
584
585    fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
586        self.predicates.push(goal.goal().predicate);
587        if let Some(cand) = goal.unique_applicable_candidate() {
588            goal.infcx().probe(|_| {
589                if let Some(nested_goal_with_largest_required_depth) = cand
590                    .instantiate_nested_goals(self.span)
591                    .into_iter()
592                    .max_by_key(|g| g.required_depth())
593                {
594                    nested_goal_with_largest_required_depth.visit_with(self)
595                } else {
596                    ControlFlow::Continue(())
597                }
598            })?;
599        }
600        ControlFlow::Continue(())
601    }
602
603    fn on_recursion_limit(&mut self) -> Self::Result {
604        ControlFlow::Break(())
605    }
606}