Skip to main content

rustc_trait_selection/solve/inspect/
analyse.rs

1//! An infrastructure to mechanically analyse proof trees.
2//!
3//! It is unavoidable that this representation is somewhat
4//! lossy as it should hide quite a few semantically relevant things,
5//! e.g. canonicalization and the order of nested goals.
6//!
7//! @lcnr: However, a lot of the weirdness here is not strictly necessary
8//! and could be improved in the future. This is mostly good enough for
9//! coherence right now and was annoying to implement, so I am leaving it
10//! as is until we start using it for something else.
11
12use rustc_data_structures::assert_matches;
13use rustc_infer::infer::InferCtxt;
14use rustc_infer::traits::Obligation;
15use rustc_macros::extension;
16use rustc_middle::traits::ObligationCause;
17use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, NoSolution, QueryResult};
18use rustc_middle::ty::{TyCtxt, VisitorResult, try_visit};
19use rustc_middle::{bug, ty};
20use rustc_next_trait_solver::canonical::instantiate_canonical_state;
21use rustc_next_trait_solver::resolve::eager_resolve_vars;
22use rustc_next_trait_solver::solve::{MaybeCause, SolverDelegateEvalExt as _, inspect};
23use rustc_span::Span;
24use tracing::instrument;
25
26use crate::solve::delegate::SolverDelegate;
27use crate::traits::ObligationCtxt;
28
29pub struct InspectConfig {
30    pub max_depth: usize,
31}
32
33pub struct InspectGoal<'a, 'tcx> {
34    infcx: &'a SolverDelegate<'tcx>,
35    depth: usize,
36    orig_values: Vec<ty::GenericArg<'tcx>>,
37    goal: Goal<'tcx, ty::Predicate<'tcx>>,
38    result: Result<Certainty, NoSolution>,
39    final_revision: &'tcx inspect::Probe<TyCtxt<'tcx>>,
40    normalizes_to_term_hack: Option<NormalizesToTermHack<'tcx>>,
41    source: GoalSource,
42}
43
44/// The expected term of a `NormalizesTo` goal gets replaced
45/// with an unconstrained inference variable when computing
46/// `NormalizesTo` goals and we return the nested goals to the
47/// caller, who also equates the actual term with the expected.
48///
49/// This is an implementation detail of the trait solver and
50/// not something we want to leak to users. We therefore
51/// treat `NormalizesTo` goals as if they apply the expected
52/// type at the end of each candidate.
53#[derive(#[automatically_derived]
impl<'tcx> ::core::marker::Copy for NormalizesToTermHack<'tcx> { }Copy, #[automatically_derived]
impl<'tcx> ::core::clone::Clone for NormalizesToTermHack<'tcx> {
    #[inline]
    fn clone(&self) -> NormalizesToTermHack<'tcx> {
        let _: ::core::clone::AssertParamIsClone<ty::Term<'tcx>>;
        let _: ::core::clone::AssertParamIsClone<ty::Term<'tcx>>;
        *self
    }
}Clone)]
54struct NormalizesToTermHack<'tcx> {
55    term: ty::Term<'tcx>,
56    unconstrained_term: ty::Term<'tcx>,
57}
58
59impl<'tcx> NormalizesToTermHack<'tcx> {
60    /// Relate the `term` with the new `unconstrained_term` created
61    /// when computing the proof tree for this `NormalizesTo` goals.
62    /// This handles nested obligations.
63    fn constrain_and(
64        &self,
65        infcx: &InferCtxt<'tcx>,
66        span: Span,
67        param_env: ty::ParamEnv<'tcx>,
68        f: impl FnOnce(&ObligationCtxt<'_, 'tcx>),
69    ) -> Result<Certainty, NoSolution> {
70        let ocx = ObligationCtxt::new(infcx);
71        ocx.eq(
72            &ObligationCause::dummy_with_span(span),
73            param_env,
74            self.term,
75            self.unconstrained_term,
76        )?;
77        f(&ocx);
78        let errors = ocx.evaluate_obligations_error_on_ambiguity();
79        if errors.is_empty() {
80            Ok(Certainty::Yes)
81        } else if errors.iter().all(|e| !e.is_true_error()) {
82            Ok(Certainty::AMBIGUOUS)
83        } else {
84            Err(NoSolution)
85        }
86    }
87}
88
89pub struct InspectCandidate<'a, 'tcx> {
90    goal: &'a InspectGoal<'a, 'tcx>,
91    kind: inspect::ProbeKind<TyCtxt<'tcx>>,
92    steps: Vec<&'a inspect::ProbeStep<TyCtxt<'tcx>>>,
93    final_state: inspect::CanonicalState<TyCtxt<'tcx>, ()>,
94    result: QueryResult<'tcx>,
95    shallow_certainty: Certainty,
96}
97
98impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
99    pub fn kind(&self) -> inspect::ProbeKind<TyCtxt<'tcx>> {
100        self.kind
101    }
102
103    pub fn result(&self) -> Result<Certainty, NoSolution> {
104        self.result.map(|c| c.value.certainty)
105    }
106
107    pub fn goal(&self) -> &'a InspectGoal<'a, 'tcx> {
108        self.goal
109    }
110
111    /// Certainty passed into `evaluate_added_goals_and_make_canonical_response`.
112    ///
113    /// If this certainty is `Yes`, then we must be confident that the candidate
114    /// must hold iff it's nested goals hold. This is not true if the certainty is
115    /// `Maybe(..)`, which suggests we forced ambiguity instead.
116    ///
117    /// This is *not* the certainty of the candidate's full nested evaluation, which
118    /// can be accessed with [`Self::result`] instead.
119    pub fn shallow_certainty(&self) -> Certainty {
120        self.shallow_certainty
121    }
122
123    /// Visit all nested goals of this candidate without rolling
124    /// back their inference constraints. This function modifies
125    /// the state of the `infcx`.
126    pub fn visit_nested_no_probe<V: ProofTreeVisitor<'tcx>>(&self, visitor: &mut V) -> V::Result {
127        for goal in self.instantiate_nested_goals(visitor.span()) {
128            match ::rustc_ast_ir::visit::VisitorResult::branch(goal.visit_with(visitor)) {
    core::ops::ControlFlow::Continue(()) =>
        (),
        #[allow(unreachable_code)]
        core::ops::ControlFlow::Break(r) => {
        return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
    }
};try_visit!(goal.visit_with(visitor));
129        }
130
131        V::Result::output()
132    }
133
134    /// Instantiate the nested goals for the candidate without rolling back their
135    /// inference constraints. This function modifies the state of the `infcx`.
136    ///
137    /// See [`Self::instantiate_impl_args`] if you need the impl args too.
138    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("instantiate_nested_goals",
                                    "rustc_trait_selection::solve::inspect::analyse",
                                    ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_trait_selection/src/solve/inspect/analyse.rs"),
                                    ::tracing_core::__macro_support::Option::Some(138u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_trait_selection::solve::inspect::analyse"),
                                    ::tracing_core::field::FieldSet::new(&["goal", "steps"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                let mut iter = meta.fields().iter();
                                meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&debug(&self.goal.goal)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&debug(&self.steps)
                                                            as &dyn Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: Vec<InspectGoal<'a, 'tcx>> =
                loop {};
            return __tracing_attr_fake_return;
        }
        {
            let infcx = self.goal.infcx;
            let param_env = self.goal.goal.param_env;
            let mut orig_values = self.goal.orig_values.clone();
            let mut instantiated_goals = ::alloc::vec::Vec::new();
            for step in &self.steps {
                match **step {
                    inspect::ProbeStep::AddGoal(source, goal) =>
                        instantiated_goals.push((source,
                                instantiate_canonical_state(infcx, span, param_env,
                                    &mut orig_values, goal))),
                    inspect::ProbeStep::RecordImplArgs { .. } => {}
                    inspect::ProbeStep::MakeCanonicalResponse { .. } |
                        inspect::ProbeStep::NestedProbe(_) =>
                        ::core::panicking::panic("internal error: entered unreachable code"),
                }
            }
            let () =
                instantiate_canonical_state(infcx, span, param_env,
                    &mut orig_values, self.final_state);
            if let Some(term_hack) = &self.goal.normalizes_to_term_hack {
                let _ =
                    term_hack.constrain_and(infcx, span, param_env, |_| {});
            }
            instantiated_goals.into_iter().map(|(source, goal)|
                        self.instantiate_proof_tree_for_nested_goal(source, goal,
                            span)).collect()
        }
    }
}#[instrument(
139        level = "debug",
140        skip_all,
141        fields(goal = ?self.goal.goal, steps = ?self.steps)
142    )]
143    pub fn instantiate_nested_goals(&self, span: Span) -> Vec<InspectGoal<'a, 'tcx>> {
144        let infcx = self.goal.infcx;
145        let param_env = self.goal.goal.param_env;
146        let mut orig_values = self.goal.orig_values.clone();
147
148        let mut instantiated_goals = vec![];
149        for step in &self.steps {
150            match **step {
151                inspect::ProbeStep::AddGoal(source, goal) => instantiated_goals.push((
152                    source,
153                    instantiate_canonical_state(infcx, span, param_env, &mut orig_values, goal),
154                )),
155                inspect::ProbeStep::RecordImplArgs { .. } => {}
156                inspect::ProbeStep::MakeCanonicalResponse { .. }
157                | inspect::ProbeStep::NestedProbe(_) => unreachable!(),
158            }
159        }
160
161        let () =
162            instantiate_canonical_state(infcx, span, param_env, &mut orig_values, self.final_state);
163
164        if let Some(term_hack) = &self.goal.normalizes_to_term_hack {
165            // FIXME: We ignore the expected term of `NormalizesTo` goals
166            // when computing the result of its candidates. This is
167            // scuffed.
168            let _ = term_hack.constrain_and(infcx, span, param_env, |_| {});
169        }
170
171        instantiated_goals
172            .into_iter()
173            .map(|(source, goal)| self.instantiate_proof_tree_for_nested_goal(source, goal, span))
174            .collect()
175    }
176
177    /// Instantiate the args of an impl if this candidate came from a
178    /// `CandidateSource::Impl`. This function modifies the state of the
179    /// `infcx`.
180    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("instantiate_impl_args",
                                    "rustc_trait_selection::solve::inspect::analyse",
                                    ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_trait_selection/src/solve/inspect/analyse.rs"),
                                    ::tracing_core::__macro_support::Option::Some(180u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_trait_selection::solve::inspect::analyse"),
                                    ::tracing_core::field::FieldSet::new(&["goal", "steps"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                let mut iter = meta.fields().iter();
                                meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&debug(&self.goal.goal)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&debug(&self.steps)
                                                            as &dyn Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: ty::GenericArgsRef<'tcx> =
                loop {};
            return __tracing_attr_fake_return;
        }
        {
            let infcx = self.goal.infcx;
            let param_env = self.goal.goal.param_env;
            let mut orig_values = self.goal.orig_values.clone();
            for step in &self.steps {
                match **step {
                    inspect::ProbeStep::RecordImplArgs { impl_args } => {
                        let impl_args =
                            instantiate_canonical_state(infcx, span, param_env,
                                &mut orig_values, impl_args);
                        let () =
                            instantiate_canonical_state(infcx, span, param_env,
                                &mut orig_values, self.final_state);
                        if !self.goal.normalizes_to_term_hack.is_none() {
                            {
                                ::core::panicking::panic_fmt(format_args!("cannot use `instantiate_impl_args` with a `NormalizesTo` goal"));
                            }
                        };
                        return eager_resolve_vars(infcx, impl_args);
                    }
                    inspect::ProbeStep::AddGoal(..) => {}
                    inspect::ProbeStep::MakeCanonicalResponse { .. } |
                        inspect::ProbeStep::NestedProbe(_) =>
                        ::core::panicking::panic("internal error: entered unreachable code"),
                }
            }
            ::rustc_middle::util::bug::bug_fmt(format_args!("expected impl args probe step for `instantiate_impl_args`"));
        }
    }
}#[instrument(
181        level = "debug",
182        skip_all,
183        fields(goal = ?self.goal.goal, steps = ?self.steps)
184    )]
185    pub fn instantiate_impl_args(&self, span: Span) -> ty::GenericArgsRef<'tcx> {
186        let infcx = self.goal.infcx;
187        let param_env = self.goal.goal.param_env;
188        let mut orig_values = self.goal.orig_values.clone();
189
190        for step in &self.steps {
191            match **step {
192                inspect::ProbeStep::RecordImplArgs { impl_args } => {
193                    let impl_args = instantiate_canonical_state(
194                        infcx,
195                        span,
196                        param_env,
197                        &mut orig_values,
198                        impl_args,
199                    );
200
201                    let () = instantiate_canonical_state(
202                        infcx,
203                        span,
204                        param_env,
205                        &mut orig_values,
206                        self.final_state,
207                    );
208
209                    // No reason we couldn't support this, but we don't need to for select.
210                    assert!(
211                        self.goal.normalizes_to_term_hack.is_none(),
212                        "cannot use `instantiate_impl_args` with a `NormalizesTo` goal"
213                    );
214
215                    return eager_resolve_vars(infcx, impl_args);
216                }
217                inspect::ProbeStep::AddGoal(..) => {}
218                inspect::ProbeStep::MakeCanonicalResponse { .. }
219                | inspect::ProbeStep::NestedProbe(_) => unreachable!(),
220            }
221        }
222
223        bug!("expected impl args probe step for `instantiate_impl_args`");
224    }
225
226    pub fn instantiate_proof_tree_for_nested_goal(
227        &self,
228        source: GoalSource,
229        goal: Goal<'tcx, ty::Predicate<'tcx>>,
230        span: Span,
231    ) -> InspectGoal<'a, 'tcx> {
232        let infcx = self.goal.infcx;
233        match goal.predicate.kind().no_bound_vars() {
234            Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => {
235                let unconstrained_term = infcx.next_term_var_of_kind(term, span);
236                let goal =
237                    goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term });
238                // We have to use a `probe` here as evaluating a `NormalizesTo` can constrain the
239                // expected term. This means that candidates which only fail due to nested goals
240                // and which normalize to a different term then the final result could ICE: when
241                // building their proof tree, the expected term was unconstrained, but when
242                // instantiating the candidate it is already constrained to the result of another
243                // candidate.
244                let normalizes_to_term_hack = NormalizesToTermHack { term, unconstrained_term };
245                let (proof_tree, nested_goals_result) = infcx.probe(|_| {
246                    // Here, if we have any nested goals, then we make sure to apply them
247                    // considering the constrained RHS, and pass the resulting certainty to
248                    // `InspectGoal::new` so that the goal has the right result (and maintains
249                    // the impression that we don't do this normalizes-to infer hack at all).
250                    let (nested, proof_tree) = infcx.evaluate_root_goal_for_proof_tree(goal, span);
251                    let nested_goals_result = nested.and_then(|nested| {
252                        normalizes_to_term_hack.constrain_and(
253                            infcx,
254                            span,
255                            proof_tree.uncanonicalized_goal.param_env,
256                            |ocx| {
257                                ocx.register_obligations(nested.0.into_iter().map(|(_, goal)| {
258                                    Obligation::new(
259                                        infcx.tcx,
260                                        ObligationCause::dummy_with_span(span),
261                                        goal.param_env,
262                                        goal.predicate,
263                                    )
264                                }));
265                            },
266                        )
267                    });
268                    (proof_tree, nested_goals_result)
269                });
270                InspectGoal::new(
271                    infcx,
272                    self.goal.depth + 1,
273                    proof_tree,
274                    Some((normalizes_to_term_hack, nested_goals_result)),
275                    source,
276                )
277            }
278            _ => {
279                // We're using a probe here as evaluating a goal could constrain
280                // inference variables by choosing one candidate. If we then recurse
281                // into another candidate who ends up with different inference
282                // constraints, we get an ICE if we already applied the constraints
283                // from the chosen candidate.
284                let proof_tree =
285                    infcx.probe(|_| infcx.evaluate_root_goal_for_proof_tree(goal, span).1);
286                InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source)
287            }
288        }
289    }
290
291    /// Visit all nested goals of this candidate, rolling back
292    /// all inference constraints.
293    pub fn visit_nested_in_probe<V: ProofTreeVisitor<'tcx>>(&self, visitor: &mut V) -> V::Result {
294        self.goal.infcx.probe(|_| self.visit_nested_no_probe(visitor))
295    }
296}
297
298impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
299    pub fn infcx(&self) -> &'a InferCtxt<'tcx> {
300        self.infcx
301    }
302
303    pub fn goal(&self) -> Goal<'tcx, ty::Predicate<'tcx>> {
304        self.goal
305    }
306
307    pub fn result(&self) -> Result<Certainty, NoSolution> {
308        self.result
309    }
310
311    pub fn source(&self) -> GoalSource {
312        self.source
313    }
314
315    pub fn depth(&self) -> usize {
316        self.depth
317    }
318
319    fn candidates_recur(
320        &'a self,
321        candidates: &mut Vec<InspectCandidate<'a, 'tcx>>,
322        steps: &mut Vec<&'a inspect::ProbeStep<TyCtxt<'tcx>>>,
323        probe: &'a inspect::Probe<TyCtxt<'tcx>>,
324    ) {
325        let mut shallow_certainty = None;
326        for step in &probe.steps {
327            match *step {
328                inspect::ProbeStep::AddGoal(..) | inspect::ProbeStep::RecordImplArgs { .. } => {
329                    steps.push(step)
330                }
331                inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => {
332                    match shallow_certainty.replace(c) {
    None | Some(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. }) => {}
    ref left_val => {
        ::core::panicking::assert_matches_failed(left_val,
            "None | Some(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })",
            ::core::option::Option::None);
    }
};assert_matches!(
333                        shallow_certainty.replace(c),
334                        None | Some(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })
335                    );
336                }
337                inspect::ProbeStep::NestedProbe(ref probe) => {
338                    match probe.kind {
339                        // These never assemble candidates for the goal we're trying to solve.
340                        inspect::ProbeKind::ProjectionCompatibility
341                        | inspect::ProbeKind::ShadowedEnvProbing => continue,
342
343                        inspect::ProbeKind::NormalizedSelfTyAssembly
344                        | inspect::ProbeKind::UnsizeAssembly
345                        | inspect::ProbeKind::Root { .. }
346                        | inspect::ProbeKind::TraitCandidate { .. }
347                        | inspect::ProbeKind::OpaqueTypeStorageLookup { .. }
348                        | inspect::ProbeKind::RigidAlias { .. } => {
349                            // Nested probes have to prove goals added in their parent
350                            // but do not leak them, so we truncate the added goals
351                            // afterwards.
352                            let num_steps = steps.len();
353                            self.candidates_recur(candidates, steps, probe);
354                            steps.truncate(num_steps);
355                        }
356                    }
357                }
358            }
359        }
360
361        match probe.kind {
362            inspect::ProbeKind::ProjectionCompatibility
363            | inspect::ProbeKind::ShadowedEnvProbing => {
364                ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!()
365            }
366
367            inspect::ProbeKind::NormalizedSelfTyAssembly | inspect::ProbeKind::UnsizeAssembly => {}
368
369            // We add a candidate even for the root evaluation if there
370            // is only one way to prove a given goal, e.g. for `WellFormed`.
371            inspect::ProbeKind::Root { result }
372            | inspect::ProbeKind::TraitCandidate { source: _, result }
373            | inspect::ProbeKind::OpaqueTypeStorageLookup { result }
374            | inspect::ProbeKind::RigidAlias { result } => {
375                // We only add a candidate if `shallow_certainty` was set, which means
376                // that we ended up calling `evaluate_added_goals_and_make_canonical_response`.
377                if let Some(shallow_certainty) = shallow_certainty {
378                    candidates.push(InspectCandidate {
379                        goal: self,
380                        kind: probe.kind,
381                        steps: steps.clone(),
382                        final_state: probe.final_state,
383                        shallow_certainty,
384                        result,
385                    });
386                }
387            }
388        }
389    }
390
391    pub fn candidates(&'a self) -> Vec<InspectCandidate<'a, 'tcx>> {
392        let mut candidates = ::alloc::vec::Vec::new()vec![];
393        let mut nested_goals = ::alloc::vec::Vec::new()vec![];
394        self.candidates_recur(&mut candidates, &mut nested_goals, &self.final_revision);
395        candidates
396    }
397
398    /// Returns the single candidate applicable for the current goal, if it exists.
399    ///
400    /// Returns `None` if there are either no or multiple applicable candidates.
401    pub fn unique_applicable_candidate(&'a self) -> Option<InspectCandidate<'a, 'tcx>> {
402        // FIXME(-Znext-solver): This does not handle impl candidates
403        // hidden by env candidates.
404        let mut candidates = self.candidates();
405        candidates.retain(|c| c.result().is_ok());
406        candidates.pop().filter(|_| candidates.is_empty())
407    }
408
409    fn new(
410        infcx: &'a InferCtxt<'tcx>,
411        depth: usize,
412        root: inspect::GoalEvaluation<TyCtxt<'tcx>>,
413        term_hack_and_nested_certainty: Option<(
414            NormalizesToTermHack<'tcx>,
415            Result<Certainty, NoSolution>,
416        )>,
417        source: GoalSource,
418    ) -> Self {
419        let infcx = <&SolverDelegate<'tcx>>::from(infcx);
420
421        let inspect::GoalEvaluation { uncanonicalized_goal, orig_values, final_revision, result } =
422            root;
423        // If there's a normalizes-to goal, AND the evaluation result with the result of
424        // constraining the normalizes-to RHS and computing the nested goals.
425        let result = result.and_then(|ok| {
426            let nested_goals_certainty =
427                term_hack_and_nested_certainty.map_or(Ok(Certainty::Yes), |(_, c)| c)?;
428            Ok(ok.value.certainty.and(nested_goals_certainty))
429        });
430
431        InspectGoal {
432            infcx,
433            depth,
434            orig_values,
435            goal: eager_resolve_vars(infcx, uncanonicalized_goal),
436            result,
437            final_revision,
438            normalizes_to_term_hack: term_hack_and_nested_certainty.map(|(n, _)| n),
439            source,
440        }
441    }
442
443    pub(crate) fn visit_with<V: ProofTreeVisitor<'tcx>>(&self, visitor: &mut V) -> V::Result {
444        if self.depth < visitor.config().max_depth {
445            match ::rustc_ast_ir::visit::VisitorResult::branch(visitor.visit_goal(self)) {
    core::ops::ControlFlow::Continue(()) =>
        (),
        #[allow(unreachable_code)]
        core::ops::ControlFlow::Break(r) => {
        return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
    }
};try_visit!(visitor.visit_goal(self));
446        }
447
448        V::Result::output()
449    }
450}
451
452/// The public API to interact with proof trees.
453pub trait ProofTreeVisitor<'tcx> {
454    type Result: VisitorResult = ();
455
456    fn span(&self) -> Span;
457
458    fn config(&self) -> InspectConfig {
459        InspectConfig { max_depth: 10 }
460    }
461
462    fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) -> Self::Result;
463}
464
465impl<'tcx> InferCtxtProofTreeExt<'tcx> for InferCtxt<'tcx> {
    fn visit_proof_tree<V: ProofTreeVisitor<'tcx>>(&self,
        goal: Goal<'tcx, ty::Predicate<'tcx>>, visitor: &mut V) -> V::Result {
        self.visit_proof_tree_at_depth(goal, 0, visitor)
    }
    fn visit_proof_tree_at_depth<V: ProofTreeVisitor<'tcx>>(&self,
        goal: Goal<'tcx, ty::Predicate<'tcx>>, depth: usize, visitor: &mut V)
        -> V::Result {
        let (_, proof_tree) =
            <&SolverDelegate<'tcx>>::from(self).evaluate_root_goal_for_proof_tree(goal,
                visitor.span());
        visitor.visit_goal(&InspectGoal::new(self, depth, proof_tree, None,
                    GoalSource::Misc))
    }
}#[extension(pub trait InferCtxtProofTreeExt<'tcx>)]
466impl<'tcx> InferCtxt<'tcx> {
467    fn visit_proof_tree<V: ProofTreeVisitor<'tcx>>(
468        &self,
469        goal: Goal<'tcx, ty::Predicate<'tcx>>,
470        visitor: &mut V,
471    ) -> V::Result {
472        self.visit_proof_tree_at_depth(goal, 0, visitor)
473    }
474
475    fn visit_proof_tree_at_depth<V: ProofTreeVisitor<'tcx>>(
476        &self,
477        goal: Goal<'tcx, ty::Predicate<'tcx>>,
478        depth: usize,
479        visitor: &mut V,
480    ) -> V::Result {
481        let (_, proof_tree) = <&SolverDelegate<'tcx>>::from(self)
482            .evaluate_root_goal_for_proof_tree(goal, visitor.span());
483        visitor.visit_goal(&InspectGoal::new(self, depth, proof_tree, None, GoalSource::Misc))
484    }
485}