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.resolve_vars_if_possible(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 =
188                                self.resolve_vars_if_possible(trait_pred.self_ty().skip_binder());
189                            // Unlike `Sized` traits, which always prefer the built-in impl,
190                            // `Copy`/`Clone` may be shadowed by a param-env candidate which
191                            // could force a lifetime error or guide inference. While that's
192                            // not generally desirable, it is observable, so for now let's
193                            // ignore this fast path for types that have regions or infer.
194                            if !self_ty
195                                .has_type_flags(TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_INFER)
196                                && self_ty.is_trivially_pure_clone_copy()
197                            {
198                                Outcome::TriviallyHolds
199                            } else {
200                                Outcome::NoFastPath
201                            }
202                        }
203                        _ => Outcome::NoFastPath,
204                    }
205                } else {
206                    Outcome::NoFastPath
207                }
208            }
209            ty::PredicateKind::DynCompatible(def_id) if self.0.tcx.is_dyn_compatible(def_id) => {
210                Outcome::TriviallyHolds
211            }
212            ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(outlives)) => {
213                if outlives.has_escaping_bound_vars() {
214                    return Outcome::NoFastPath;
215                }
216
217                self.0.sub_regions(
218                    SubregionOrigin::RelateRegionParamBound(span, None),
219                    outlives.1,
220                    outlives.0,
221                    ty::VisibleForLeakCheck::Yes,
222                );
223                Outcome::TriviallyHolds
224            }
225            ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(outlives)) => {
226                if outlives.has_escaping_bound_vars() {
227                    return Outcome::NoFastPath;
228                }
229
230                let ty = self.resolve_vars_if_possible(outlives.0);
231                let mut infer_collector = CollectNonRegionInfer {
232                    infers: Default::default(),
233                    visited: Default::default(),
234                };
235                ty.visit_with(&mut infer_collector);
236                let infers = infer_collector.infers;
237                if !infers.is_empty() {
238                    return goal_stalled_on_args(
239                        infers
240                            .into_iter()
241                            .map(|i| {
242                                TyOrConstInferVar::maybe_from_generic_arg::<Self::Interner>(i)
243                                    .unwrap()
244                            })
245                            .collect(),
246                    );
247                }
248
249                if ty.has_non_rigid_aliases() {
250                    return Outcome::NoFastPath;
251                }
252
253                self.0.register_type_outlives_constraint(
254                    outlives.0,
255                    outlives.1,
256                    &ObligationCause::dummy_with_span(span),
257                );
258
259                Outcome::TriviallyHolds
260            }
261            ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, .. })
262            | ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => {
263                if a.has_escaping_bound_vars() || b.has_escaping_bound_vars() {
264                    return Outcome::NoFastPath;
265                }
266
267                match (self.shallow_resolve(a).kind(), self.shallow_resolve(b).kind()) {
268                    (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => {
269                        self.sub_unify_ty_vids_raw(a_vid, b_vid);
270                        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![
271                            TyOrConstInferVar::Ty(a_vid),
272                            TyOrConstInferVar::Ty(b_vid),
273                        ])
274                    }
275                    _ => Outcome::NoFastPath,
276                }
277            }
278            ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, _)) => {
279                if ct.has_escaping_bound_vars() {
280                    return Outcome::NoFastPath;
281                }
282
283                let arg = self.shallow_resolve_const(ct);
284                if let Some(vid) = arg.ct_vid() {
285                    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)])
286                } else {
287                    Outcome::NoFastPath
288                }
289            }
290            ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
291                if arg.has_escaping_bound_vars() {
292                    return Outcome::NoFastPath;
293                }
294
295                let arg = self.shallow_resolve_term(arg);
296                if arg.is_trivially_wf(self.tcx) {
297                    Outcome::TriviallyHolds
298                } else if arg.is_infer() {
299                    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![
300                        TyOrConstInferVar::maybe_from_term::<TyCtxt<'tcx>>(arg)
301                            .expect("its an infer var"),
302                    ])
303                } else {
304                    Outcome::NoFastPath
305                }
306            }
307            _ => Outcome::NoFastPath,
308        }
309    }
310
311    fn fresh_var_for_kind(
312        &self,
313        arg: ty::GenericArg<'tcx>,
314        span: Span,
315        universe: ty::UniverseIndex,
316    ) -> ty::GenericArg<'tcx> {
317        match arg.kind() {
318            ty::GenericArgKind::Lifetime(_) => {
319                self.next_region_var_in_universe(RegionVariableOrigin::Misc(span), universe).into()
320            }
321            ty::GenericArgKind::Type(_) => self.next_ty_var_in_universe(span, universe).into(),
322            ty::GenericArgKind::Const(_) => self.next_const_var_in_universe(span, universe).into(),
323        }
324    }
325
326    fn leak_check(&self, max_input_universe: ty::UniverseIndex) -> Result<(), NoSolution> {
327        self.0.leak_check(max_input_universe, None).map_err(|_| NoSolution)
328    }
329
330    fn evaluate_const<E: Debug>(
331        &self,
332        param_env: ty::ParamEnv<'tcx>,
333        alias_const: ty::AliasConst<'tcx>,
334        normalize_ty: impl FnOnce(ty::Unnormalized<'tcx, Ty<'tcx>>) -> Result<Ty<'tcx>, E>,
335    ) -> Result<Option<ty::Const<'tcx>>, E> {
336        let ct = ty::Const::new_alias(self.tcx, ty::IsRigid::No, alias_const);
337
338        match crate::traits::try_evaluate_const(&self.0, ct, param_env, normalize_ty) {
339            Ok(ct) => Ok(Some(ct)),
340            Err(EvaluateConstErr::EvaluationFailure(e)) => {
341                Ok(Some(ty::Const::new_error(self.tcx, e)))
342            }
343            Err(
344                EvaluateConstErr::InvalidConstParamTy(_) | EvaluateConstErr::HasGenericsOrInfers,
345            ) => Ok(None),
346            Err(EvaluateConstErr::FailedNormalization(e)) => Err(e),
347        }
348    }
349
350    fn well_formed_goals(
351        &self,
352        param_env: ty::ParamEnv<'tcx>,
353        term: ty::Term<'tcx>,
354    ) -> Option<Vec<Goal<'tcx, ty::Predicate<'tcx>>>> {
355        crate::traits::wf::unnormalized_obligations(
356            &self.0,
357            param_env,
358            term,
359            DUMMY_SP,
360            CRATE_DEF_ID,
361        )
362        .map(|obligations| obligations.into_iter().map(|obligation| obligation.as_goal()).collect())
363    }
364
365    fn make_deduplicated_region_constraints(
366        &self,
367    ) -> Vec<(ty::RegionConstraint<'tcx>, ty::VisibleForLeakCheck)> {
368        // Cannot use `take_registered_region_obligations` as we may compute the response
369        // inside of a `probe` whenever we have multiple choices inside of the solver.
370        let region_obligations = self.0.inner.borrow().region_obligations().to_owned();
371        let region_assumptions = self.0.inner.borrow().region_assumptions().to_owned();
372        let region_constraints = self.0.with_region_constraints(|region_constraints| {
373            make_query_region_constraints(
374                region_obligations,
375                region_constraints,
376                region_assumptions,
377            )
378        });
379
380        let mut seen = FxHashMap::default();
381        let mut constraints = ::alloc::vec::Vec::new()vec![];
382        for QueryRegionConstraint { constraint: outlives, visible_for_leak_check: vis, .. } in
383            region_constraints.constraints
384        {
385            match seen.entry(outlives) {
386                Entry::Occupied(occupied) => {
387                    let idx = occupied.get();
388                    let (_, prev_vis): &mut (_, ty::VisibleForLeakCheck) =
389                        constraints.get_mut(*idx).unwrap();
390                    *prev_vis = (*prev_vis).or(vis);
391                }
392                Entry::Vacant(vacant) => {
393                    vacant.insert(constraints.len());
394                    constraints.push((outlives, vis));
395                }
396            }
397        }
398        constraints
399    }
400
401    fn instantiate_canonical<V>(
402        &self,
403        canonical: Canonical<'tcx, V>,
404        values: CanonicalVarValues<'tcx>,
405    ) -> V
406    where
407        V: TypeFoldable<TyCtxt<'tcx>>,
408    {
409        canonical.instantiate(self.tcx, &values)
410    }
411
412    fn instantiate_canonical_var(
413        &self,
414        kind: CanonicalVarKind<'tcx>,
415        span: Span,
416        var_values: &[ty::GenericArg<'tcx>],
417        universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
418    ) -> ty::GenericArg<'tcx> {
419        self.0.instantiate_canonical_var(span, kind, var_values, universe_map)
420    }
421
422    fn add_item_bounds_for_hidden_type(
423        &self,
424        def_id: DefId,
425        args: ty::GenericArgsRef<'tcx>,
426        param_env: ty::ParamEnv<'tcx>,
427        hidden_ty: Ty<'tcx>,
428        goals: &mut Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
429    ) {
430        self.0.add_item_bounds_for_hidden_type(def_id, args, param_env, hidden_ty, goals);
431    }
432
433    fn fetch_eligible_assoc_item(
434        &self,
435        goal_trait_ref: ty::TraitRef<'tcx>,
436        trait_assoc_def_id: DefId,
437        impl_def_id: DefId,
438    ) -> FetchEligibleAssocItemResponse<'tcx> {
439        let node_item =
440            match specialization_graph::assoc_def(self.tcx, impl_def_id, trait_assoc_def_id) {
441                Ok(i) => i,
442                Err(guar) => return FetchEligibleAssocItemResponse::Err(guar),
443            };
444
445        let typing_mode = self.typing_mode_raw();
446
447        let eligible = if node_item.is_final() {
448            // Non-specializable items are always projectable.
449            true
450        } else {
451            // Only reveal a specializable default if we're past type-checking
452            // and the obligation is monomorphic, otherwise passes such as
453            // transmute checking and polymorphic MIR optimizations could
454            // get a result which isn't correct for all monomorphizations.
455            match typing_mode {
456                TypingMode::Coherence
457                | TypingMode::Typeck { .. }
458                | TypingMode::PostTypeckUntilBorrowck { .. }
459                | TypingMode::Reflection
460                | TypingMode::PostBorrowck { .. } => false,
461                TypingMode::PostAnalysis | TypingMode::Codegen => {
462                    let poly_trait_ref = self.resolve_vars_if_possible(goal_trait_ref);
463                    !poly_trait_ref.still_further_specializable()
464                }
465                TypingMode::ErasedNotCoherence(MayBeErased) => {
466                    return FetchEligibleAssocItemResponse::NotFoundBecauseErased;
467                }
468            }
469        };
470
471        // FIXME: Check for defaultness here may cause diagnostics problems.
472        if eligible {
473            FetchEligibleAssocItemResponse::Found(node_item.item.def_id)
474        } else {
475            // We know it's not erased since then we'd have returned in the match above,
476            // or node_item.final() was true and eligible is always true.
477            FetchEligibleAssocItemResponse::NotFound(typing_mode.assert_not_erased())
478        }
479    }
480
481    // FIXME: This actually should destructure the `Result` we get from transmutability and
482    // register candidates. We probably need to register >1 since we may have an OR of ANDs.
483    fn is_transmutable(
484        &self,
485        src: Ty<'tcx>,
486        dst: Ty<'tcx>,
487        assume: ty::Const<'tcx>,
488    ) -> Result<Certainty, NoSolution> {
489        // Erase regions because we compute layouts in `rustc_transmute`,
490        // which will ICE for region vars.
491        let (dst, src) = self.tcx.erase_and_anonymize_regions((dst, src));
492
493        let Some(assume) = rustc_transmute::Assume::from_const(self.tcx, assume) else {
494            return Err(NoSolution);
495        };
496
497        // FIXME(transmutability): This really should be returning nested goals for `Answer::If*`
498        match rustc_transmute::TransmuteTypeEnv::new(self.0.tcx).is_transmutable(src, dst, assume) {
499            rustc_transmute::Answer::Yes => Ok(Certainty::Yes),
500            rustc_transmute::Answer::No(_) | rustc_transmute::Answer::If(_) => Err(NoSolution),
501        }
502    }
503
504    fn obtain_canonicalizer_state(&self) -> CanonicalizerState<Self::Interner> {
505        // We temporarily take the canonicalizer state.
506        mem::take(&mut self.canonicalizer_state.borrow_mut())
507    }
508
509    fn release_canonicalizer_state(&self, mut state: CanonicalizerState<Self::Interner>) {
510        // Clear (don't deallocate) the state for later reuse.
511        state.clear();
512        *self.canonicalizer_state.borrow_mut() = state;
513    }
514
515    fn emit_next_solver_overflow_fcw(&self, goal: Goal<'tcx, ty::Predicate<'tcx>>, span: Span) {
516        let tcx = self.tcx;
517        let goal = self.resolve_vars_if_possible(goal);
518        let mut visitor = OverflowedGoalChain {
519            span,
520            predicates: ::alloc::vec::Vec::new()vec![],
521            recursion_limit: usize::min(16, tcx.recursion_limit().0),
522        };
523        let _ = self
524            .with_disabled_next_solver_overflow_fcw(|| self.visit_proof_tree(goal, &mut visitor));
525        tcx.emit_node_span_lint(
526            RECURSION_DEPTH_EXCEEDING_LIMIT,
527            CRATE_HIR_ID,
528            span,
529            rustc_errors::DiagDecorator(|diag| {
530                // FIXME: share this with overflow error in fulfillment instead of duplicating.
531                let pred_str = |pred: ty::Predicate<'tcx>| {
532                    let s = pred.to_string();
533                    if s.len() > 80 {
534                        let mut p: FmtPrinter<'_, '_> =
535                            FmtPrinter::new_with_limit(tcx, Namespace::TypeNS, Limit(10));
536                        pred.print(&mut p).unwrap();
537                        p.into_buffer()
538                    } else {
539                        s
540                    }
541                };
542                diag.primary_message(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("overflow evaluating the requirement `{0}`",
                pred_str(goal.predicate)))
    })format!(
543                    "overflow evaluating the requirement `{}`",
544                    pred_str(goal.predicate),
545                ));
546                for p in visitor.predicates.into_iter().skip(1) {
547                    diag.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("which requires `{0}`",
                pred_str(p)))
    })format!("which requires `{}`", pred_str(p)));
548                }
549                diag.note("and so on...");
550                diag.help(
551                    "consider adding a manual `impl` of auto traits like `Send` for intermediate types, if auto traits are involved",
552                );
553                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!(
554                    "or consider increasing the recursion limit by adding a \
555                     `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
556                    tcx.recursion_limit() * 2,
557                    tcx.crate_name(LOCAL_CRATE),
558                ));
559                diag.note("this lint is attached to the whole crate and can't be disabled on a per-function basis");
560            }),
561        )
562    }
563}
564
565struct OverflowedGoalChain<'tcx> {
566    span: Span,
567    predicates: Vec<ty::Predicate<'tcx>>,
568    recursion_limit: usize,
569}
570
571impl<'tcx> ProofTreeVisitor<'tcx> for OverflowedGoalChain<'tcx> {
572    type Result = ControlFlow<()>;
573
574    fn span(&self) -> Span {
575        self.span
576    }
577
578    fn config(&self) -> InspectConfig {
579        InspectConfig { max_depth: self.recursion_limit }
580    }
581
582    fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
583        self.predicates.push(goal.goal().predicate);
584        if let Some(cand) = goal.unique_applicable_candidate() {
585            goal.infcx().probe(|_| {
586                if let Some(nested_goal_with_largest_required_depth) = cand
587                    .instantiate_nested_goals(self.span)
588                    .into_iter()
589                    .max_by_key(|g| g.required_depth())
590                {
591                    nested_goal_with_largest_required_depth.visit_with(self)
592                } else {
593                    ControlFlow::Continue(())
594                }
595            })?;
596        }
597        ControlFlow::Continue(())
598    }
599
600    fn on_recursion_limit(&mut self) -> Self::Result {
601        ControlFlow::Break(())
602    }
603}