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