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::ty::{self, Ty, TypeVisitableExt};
5use rustc_span::Span;
6use rustc_trait_selection::solve::Certainty;
7use rustc_trait_selection::solve::inspect::{
8    InferCtxtProofTreeExt, InspectConfig, InspectGoal, ProofTreeVisitor,
9};
10use tracing::{debug, instrument, trace};
11
12use crate::FnCtxt;
13
14impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15    /// Returns a list of all obligations whose self type has been unified
16    /// with the unconstrained type `self_ty`.
17    #[instrument(skip(self), level = "debug")]
18    pub(crate) fn obligations_for_self_ty(&self, self_ty: ty::TyVid) -> PredicateObligations<'tcx> {
19        if self.next_trait_solver() {
20            self.obligations_for_self_ty_next(self_ty)
21        } else {
22            let ty_var_root = self.root_var(self_ty);
23            let mut obligations = self.fulfillment_cx.borrow().pending_obligations();
24            trace!("pending_obligations = {:#?}", obligations);
25            obligations
26                .retain(|obligation| self.predicate_has_self_ty(obligation.predicate, ty_var_root));
27            obligations
28        }
29    }
30
31    #[instrument(level = "debug", skip(self), ret)]
32    fn predicate_has_self_ty(
33        &self,
34        predicate: ty::Predicate<'tcx>,
35        expected_vid: ty::TyVid,
36    ) -> bool {
37        match predicate.kind().skip_binder() {
38            ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
39                self.type_matches_expected_vid(expected_vid, data.self_ty())
40            }
41            ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
42                self.type_matches_expected_vid(expected_vid, data.projection_term.self_ty())
43            }
44            ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
45            | ty::PredicateKind::Subtype(..)
46            | ty::PredicateKind::Coerce(..)
47            | ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..))
48            | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..))
49            | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..))
50            | ty::PredicateKind::DynCompatible(..)
51            | ty::PredicateKind::NormalizesTo(..)
52            | ty::PredicateKind::AliasRelate(..)
53            | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
54            | ty::PredicateKind::ConstEquate(..)
55            | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..))
56            | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_))
57            | ty::PredicateKind::Ambiguous => false,
58        }
59    }
60
61    #[instrument(level = "debug", skip(self), ret)]
62    fn type_matches_expected_vid(&self, expected_vid: ty::TyVid, ty: Ty<'tcx>) -> bool {
63        let ty = self.shallow_resolve(ty);
64        debug!(?ty);
65
66        match *ty.kind() {
67            ty::Infer(ty::TyVar(found_vid)) => {
68                self.root_var(expected_vid) == self.root_var(found_vid)
69            }
70            _ => false,
71        }
72    }
73
74    pub(crate) fn obligations_for_self_ty_next(
75        &self,
76        self_ty: ty::TyVid,
77    ) -> PredicateObligations<'tcx> {
78        let obligations = self.fulfillment_cx.borrow().pending_obligations();
79        debug!(?obligations);
80        let mut obligations_for_self_ty = PredicateObligations::new();
81        for obligation in obligations {
82            let mut visitor = NestedObligationsForSelfTy {
83                fcx: self,
84                self_ty,
85                obligations_for_self_ty: &mut obligations_for_self_ty,
86                root_cause: &obligation.cause,
87            };
88
89            let goal = obligation.as_goal();
90            self.visit_proof_tree(goal, &mut visitor);
91        }
92
93        obligations_for_self_ty.retain_mut(|obligation| {
94            obligation.predicate = self.resolve_vars_if_possible(obligation.predicate);
95            !obligation.predicate.has_placeholders()
96        });
97        obligations_for_self_ty
98    }
99}
100
101struct NestedObligationsForSelfTy<'a, 'tcx> {
102    fcx: &'a FnCtxt<'a, 'tcx>,
103    self_ty: ty::TyVid,
104    root_cause: &'a ObligationCause<'tcx>,
105    obligations_for_self_ty: &'a mut PredicateObligations<'tcx>,
106}
107
108impl<'a, 'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'a, 'tcx> {
109    fn span(&self) -> Span {
110        self.root_cause.span
111    }
112
113    fn config(&self) -> InspectConfig {
114        // Using an intentionally low depth to minimize the chance of future
115        // breaking changes in case we adapt the approach later on. This also
116        // avoids any hangs for exponentially growing proof trees.
117        InspectConfig { max_depth: 5 }
118    }
119
120    fn visit_goal(&mut self, inspect_goal: &InspectGoal<'_, 'tcx>) {
121        // No need to walk into goal subtrees that certainly hold, since they
122        // wouldn't then be stalled on an infer var.
123        if inspect_goal.result() == Ok(Certainty::Yes) {
124            return;
125        }
126
127        let tcx = self.fcx.tcx;
128        let goal = inspect_goal.goal();
129        if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty) {
130            self.obligations_for_self_ty.push(traits::Obligation::new(
131                tcx,
132                self.root_cause.clone(),
133                goal.param_env,
134                goal.predicate,
135            ));
136        }
137
138        // If there's a unique way to prove a given goal, recurse into
139        // that candidate. This means that for `impl<F: FnOnce(u32)> Trait<F> for () {}`
140        // and a `(): Trait<?0>` goal we recurse into the impl and look at
141        // the nested `?0: FnOnce(u32)` goal.
142        if let Some(candidate) = inspect_goal.unique_applicable_candidate() {
143            candidate.visit_nested_no_probe(self)
144        }
145    }
146}