| //! A utility module to inspect currently ambiguous obligations in the current context. |
| use crate::FnCtxt; |
| use rustc_infer::traits::{self, ObligationCause}; |
| use rustc_middle::traits::solve::Goal; |
| use rustc_middle::ty::{self, Ty, TypeVisitableExt}; |
| use rustc_span::Span; |
| use rustc_trait_selection::solve::inspect::ProofTreeInferCtxtExt; |
| use rustc_trait_selection::solve::inspect::{InspectConfig, InspectGoal, ProofTreeVisitor}; |
| |
| impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |
| /// Returns a list of all obligations whose self type has been unified |
| /// with the unconstrained type `self_ty`. |
| #[instrument(skip(self), level = "debug")] |
| pub(crate) fn obligations_for_self_ty( |
| &self, |
| self_ty: ty::TyVid, |
| ) -> Vec<traits::PredicateObligation<'tcx>> { |
| if self.next_trait_solver() { |
| self.obligations_for_self_ty_next(self_ty) |
| } else { |
| let ty_var_root = self.root_var(self_ty); |
| let mut obligations = self.fulfillment_cx.borrow().pending_obligations(); |
| trace!("pending_obligations = {:#?}", obligations); |
| obligations |
| .retain(|obligation| self.predicate_has_self_ty(obligation.predicate, ty_var_root)); |
| obligations |
| } |
| } |
| |
| #[instrument(level = "debug", skip(self), ret)] |
| fn predicate_has_self_ty( |
| &self, |
| predicate: ty::Predicate<'tcx>, |
| expected_vid: ty::TyVid, |
| ) -> bool { |
| match predicate.kind().skip_binder() { |
| ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { |
| self.type_matches_expected_vid(expected_vid, data.self_ty()) |
| } |
| ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { |
| self.type_matches_expected_vid(expected_vid, data.projection_term.self_ty()) |
| } |
| ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) |
| | ty::PredicateKind::Subtype(..) |
| | ty::PredicateKind::Coerce(..) |
| | ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..)) |
| | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..)) |
| | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..)) |
| | ty::PredicateKind::ObjectSafe(..) |
| | ty::PredicateKind::NormalizesTo(..) |
| | ty::PredicateKind::AliasRelate(..) |
| | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) |
| | ty::PredicateKind::ConstEquate(..) |
| | ty::PredicateKind::Ambiguous => false, |
| } |
| } |
| |
| #[instrument(level = "debug", skip(self), ret)] |
| fn type_matches_expected_vid(&self, expected_vid: ty::TyVid, ty: Ty<'tcx>) -> bool { |
| let ty = self.shallow_resolve(ty); |
| debug!(?ty); |
| |
| match *ty.kind() { |
| ty::Infer(ty::TyVar(found_vid)) => { |
| self.root_var(expected_vid) == self.root_var(found_vid) |
| } |
| _ => false, |
| } |
| } |
| |
| pub(crate) fn obligations_for_self_ty_next( |
| &self, |
| self_ty: ty::TyVid, |
| ) -> Vec<traits::PredicateObligation<'tcx>> { |
| let obligations = self.fulfillment_cx.borrow().pending_obligations(); |
| debug!(?obligations); |
| let mut obligations_for_self_ty = vec![]; |
| for obligation in obligations { |
| let mut visitor = NestedObligationsForSelfTy { |
| fcx: self, |
| self_ty, |
| obligations_for_self_ty: &mut obligations_for_self_ty, |
| root_cause: &obligation.cause, |
| }; |
| |
| let goal = Goal::new(self.tcx, obligation.param_env, obligation.predicate); |
| self.visit_proof_tree(goal, &mut visitor); |
| } |
| |
| obligations_for_self_ty.retain_mut(|obligation| { |
| obligation.predicate = self.resolve_vars_if_possible(obligation.predicate); |
| !obligation.predicate.has_placeholders() |
| }); |
| obligations_for_self_ty |
| } |
| } |
| |
| struct NestedObligationsForSelfTy<'a, 'tcx> { |
| fcx: &'a FnCtxt<'a, 'tcx>, |
| self_ty: ty::TyVid, |
| root_cause: &'a ObligationCause<'tcx>, |
| obligations_for_self_ty: &'a mut Vec<traits::PredicateObligation<'tcx>>, |
| } |
| |
| impl<'a, 'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'a, 'tcx> { |
| type Result = (); |
| |
| fn span(&self) -> Span { |
| self.root_cause.span |
| } |
| |
| fn config(&self) -> InspectConfig { |
| // Using an intentionally low depth to minimize the chance of future |
| // breaking changes in case we adapt the approach later on. This also |
| // avoids any hangs for exponentially growing proof trees. |
| InspectConfig { max_depth: 5 } |
| } |
| |
| fn visit_goal(&mut self, inspect_goal: &InspectGoal<'_, 'tcx>) { |
| let tcx = self.fcx.tcx; |
| let goal = inspect_goal.goal(); |
| if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty) { |
| self.obligations_for_self_ty.push(traits::Obligation::new( |
| tcx, |
| self.root_cause.clone(), |
| goal.param_env, |
| goal.predicate, |
| )); |
| } |
| |
| // If there's a unique way to prove a given goal, recurse into |
| // that candidate. This means that for `impl<F: FnOnce(u32)> Trait<F> for () {}` |
| // and a `(): Trait<?0>` goal we recurse into the impl and look at |
| // the nested `?0: FnOnce(u32)` goal. |
| if let Some(candidate) = inspect_goal.unique_applicable_candidate() { |
| candidate.visit_nested_no_probe(self) |
| } |
| } |
| } |