rustc_hir_typeck/fn_ctxt/
inspect_obligations.rs

1//! A utility module to inspect currently ambiguous obligations in the current context.
2
3use rustc_infer::traits::{self, ObligationCause, PredicateObligations};
4use rustc_middle::traits::solve::GoalSource;
5use rustc_middle::ty::{self, Ty, TypeVisitableExt};
6use rustc_span::Span;
7use rustc_trait_selection::solve::Certainty;
8use rustc_trait_selection::solve::inspect::{
9    InspectConfig, InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor,
10};
11use tracing::{debug, instrument, trace};
12
13use crate::FnCtxt;
14
15impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16    /// Returns a list of all obligations whose self type has been unified
17    /// with the unconstrained type `self_ty`.
18    #[instrument(skip(self), level = "debug")]
19    pub(crate) fn obligations_for_self_ty(&self, self_ty: ty::TyVid) -> PredicateObligations<'tcx> {
20        if self.next_trait_solver() {
21            self.obligations_for_self_ty_next(self_ty)
22        } else {
23            let ty_var_root = self.root_var(self_ty);
24            let mut obligations = self.fulfillment_cx.borrow().pending_obligations();
25            trace!("pending_obligations = {:#?}", obligations);
26            obligations
27                .retain(|obligation| self.predicate_has_self_ty(obligation.predicate, ty_var_root));
28            obligations
29        }
30    }
31
32    #[instrument(level = "debug", skip(self), ret)]
33    fn predicate_has_self_ty(
34        &self,
35        predicate: ty::Predicate<'tcx>,
36        expected_vid: ty::TyVid,
37    ) -> bool {
38        match predicate.kind().skip_binder() {
39            ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
40                self.type_matches_expected_vid(expected_vid, data.self_ty())
41            }
42            ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
43                self.type_matches_expected_vid(expected_vid, data.projection_term.self_ty())
44            }
45            ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
46            | ty::PredicateKind::Subtype(..)
47            | ty::PredicateKind::Coerce(..)
48            | ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..))
49            | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..))
50            | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..))
51            | ty::PredicateKind::DynCompatible(..)
52            | ty::PredicateKind::NormalizesTo(..)
53            | ty::PredicateKind::AliasRelate(..)
54            | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
55            | ty::PredicateKind::ConstEquate(..)
56            | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..))
57            | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_))
58            | ty::PredicateKind::Ambiguous => false,
59        }
60    }
61
62    #[instrument(level = "debug", skip(self), ret)]
63    fn type_matches_expected_vid(&self, expected_vid: ty::TyVid, ty: Ty<'tcx>) -> bool {
64        let ty = self.shallow_resolve(ty);
65        debug!(?ty);
66
67        match *ty.kind() {
68            ty::Infer(ty::TyVar(found_vid)) => {
69                self.root_var(expected_vid) == self.root_var(found_vid)
70            }
71            _ => false,
72        }
73    }
74
75    pub(crate) fn obligations_for_self_ty_next(
76        &self,
77        self_ty: ty::TyVid,
78    ) -> PredicateObligations<'tcx> {
79        let obligations = self.fulfillment_cx.borrow().pending_obligations();
80        debug!(?obligations);
81        let mut obligations_for_self_ty = PredicateObligations::new();
82        for obligation in obligations {
83            let mut visitor = NestedObligationsForSelfTy {
84                fcx: self,
85                self_ty,
86                obligations_for_self_ty: &mut obligations_for_self_ty,
87                root_cause: &obligation.cause,
88            };
89
90            let goal = obligation.as_goal();
91            self.visit_proof_tree(goal, &mut visitor);
92        }
93
94        obligations_for_self_ty.retain_mut(|obligation| {
95            obligation.predicate = self.resolve_vars_if_possible(obligation.predicate);
96            !obligation.predicate.has_placeholders()
97        });
98        obligations_for_self_ty
99    }
100}
101
102struct NestedObligationsForSelfTy<'a, 'tcx> {
103    fcx: &'a FnCtxt<'a, 'tcx>,
104    self_ty: ty::TyVid,
105    root_cause: &'a ObligationCause<'tcx>,
106    obligations_for_self_ty: &'a mut PredicateObligations<'tcx>,
107}
108
109impl<'a, 'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'a, 'tcx> {
110    fn span(&self) -> Span {
111        self.root_cause.span
112    }
113
114    fn config(&self) -> InspectConfig {
115        // Using an intentionally low depth to minimize the chance of future
116        // breaking changes in case we adapt the approach later on. This also
117        // avoids any hangs for exponentially growing proof trees.
118        InspectConfig { max_depth: 5 }
119    }
120
121    fn visit_goal(&mut self, inspect_goal: &InspectGoal<'_, 'tcx>) {
122        // No need to walk into goal subtrees that certainly hold, since they
123        // wouldn't then be stalled on an infer var.
124        if inspect_goal.result() == Ok(Certainty::Yes) {
125            return;
126        }
127
128        let tcx = self.fcx.tcx;
129        let goal = inspect_goal.goal();
130        if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty)
131            // We do not push the instantiated forms of goals as it would cause any
132            // aliases referencing bound vars to go from having escaping bound vars to
133            // being able to be normalized to an inference variable.
134            //
135            // This is mostly just a hack as arbitrary nested goals could still contain
136            // such aliases while having a different `GoalSource`. Closure signature inference
137            // however can't really handle *every* higher ranked `Fn` goal also being present
138            // in the form of `?c: Fn<(<?x as Trait<'!a>>::Assoc)`.
139            //
140            // This also just better matches the behaviour of the old solver where we do not
141            // encounter instantiated forms of goals, only nested goals that referred to bound
142            // vars from instantiated goals.
143            && !matches!(inspect_goal.source(), GoalSource::InstantiateHigherRanked)
144        {
145            self.obligations_for_self_ty.push(traits::Obligation::new(
146                tcx,
147                self.root_cause.clone(),
148                goal.param_env,
149                goal.predicate,
150            ));
151        }
152
153        // If there's a unique way to prove a given goal, recurse into
154        // that candidate. This means that for `impl<F: FnOnce(u32)> Trait<F> for () {}`
155        // and a `(): Trait<?0>` goal we recurse into the impl and look at
156        // the nested `?0: FnOnce(u32)` goal.
157        if let Some(candidate) = inspect_goal.unique_applicable_candidate() {
158            candidate.visit_nested_no_probe(self)
159        }
160    }
161}