|
| 1 | +//! Code shared by trait and projection goals for candidate assembly. |
| 2 | +
|
| 3 | +use super::infcx_ext::InferCtxtExt; |
| 4 | +use super::{ |
| 5 | + fixme_instantiate_canonical_query_response, CanonicalGoal, CanonicalResponse, Certainty, |
| 6 | + EvalCtxt, Goal, |
| 7 | +}; |
| 8 | +use rustc_hir::def_id::DefId; |
| 9 | +use rustc_infer::infer::TyCtxtInferExt; |
| 10 | +use rustc_infer::infer::{ |
| 11 | + canonical::{CanonicalVarValues, OriginalQueryValues}, |
| 12 | + InferCtxt, |
| 13 | +}; |
| 14 | +use rustc_infer::traits::query::NoSolution; |
| 15 | +use rustc_middle::ty::TypeFoldable; |
| 16 | +use rustc_middle::ty::{self, Ty, TyCtxt}; |
| 17 | +use rustc_span::DUMMY_SP; |
| 18 | +use std::fmt::Debug; |
| 19 | + |
| 20 | +/// A candidate is a possible way to prove a goal. |
| 21 | +/// |
| 22 | +/// It consists of both the `source`, which describes how that goal would be proven, |
| 23 | +/// and the `result` when using the given `source`. |
| 24 | +/// |
| 25 | +/// For the list of possible candidates, please look at the documentation of |
| 26 | +/// [super::trait_goals::CandidateSource] and [super::project_goals::CandidateSource]. |
| 27 | +#[derive(Debug, Clone)] |
| 28 | +pub(super) struct Candidate<'tcx, G: GoalKind<'tcx>> { |
| 29 | + pub(super) source: G::CandidateSource, |
| 30 | + pub(super) result: CanonicalResponse<'tcx>, |
| 31 | +} |
| 32 | + |
| 33 | +pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy { |
| 34 | + type CandidateSource: Debug + Copy; |
| 35 | + |
| 36 | + fn self_ty(self) -> Ty<'tcx>; |
| 37 | + |
| 38 | + fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self; |
| 39 | + |
| 40 | + fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId; |
| 41 | + |
| 42 | + fn consider_impl_candidate( |
| 43 | + acx: &mut AssemblyCtxt<'_, 'tcx, Self>, |
| 44 | + goal: Goal<'tcx, Self>, |
| 45 | + impl_def_id: DefId, |
| 46 | + ); |
| 47 | +} |
| 48 | + |
| 49 | +/// An abstraction which correctly deals with the canonical results for candidates. |
| 50 | +/// |
| 51 | +/// It also deduplicates the behavior between trait and projection predicates. |
| 52 | +pub(super) struct AssemblyCtxt<'a, 'tcx, G: GoalKind<'tcx>> { |
| 53 | + pub(super) cx: &'a mut EvalCtxt<'tcx>, |
| 54 | + pub(super) infcx: &'a InferCtxt<'tcx>, |
| 55 | + var_values: CanonicalVarValues<'tcx>, |
| 56 | + candidates: Vec<Candidate<'tcx, G>>, |
| 57 | +} |
| 58 | + |
| 59 | +impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> { |
| 60 | + pub(super) fn assemble_and_evaluate_candidates( |
| 61 | + cx: &'a mut EvalCtxt<'tcx>, |
| 62 | + goal: CanonicalGoal<'tcx, G>, |
| 63 | + ) -> Vec<Candidate<'tcx, G>> { |
| 64 | + let (ref infcx, goal, var_values) = |
| 65 | + cx.tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal); |
| 66 | + let mut acx = AssemblyCtxt { cx, infcx, var_values, candidates: Vec::new() }; |
| 67 | + |
| 68 | + acx.assemble_candidates_after_normalizing_self_ty(goal); |
| 69 | + |
| 70 | + acx.assemble_impl_candidates(goal); |
| 71 | + |
| 72 | + acx.candidates |
| 73 | + } |
| 74 | + |
| 75 | + pub(super) fn try_insert_candidate( |
| 76 | + &mut self, |
| 77 | + source: G::CandidateSource, |
| 78 | + certainty: Certainty, |
| 79 | + ) { |
| 80 | + match self.infcx.make_canonical_response(self.var_values.clone(), certainty) { |
| 81 | + Ok(result) => self.candidates.push(Candidate { source, result }), |
| 82 | + Err(NoSolution) => debug!(?source, ?certainty, "failed leakcheck"), |
| 83 | + } |
| 84 | + } |
| 85 | + |
| 86 | + /// If the self type of a goal is a projection, computing the relevant candidates is difficult. |
| 87 | + /// |
| 88 | + /// To deal with this, we first try to normalize the self type and add the candidates for the normalized |
| 89 | + /// self type to the list of candidates in case that succeeds. Note that we can't just eagerly return in |
| 90 | + /// this case as projections as self types add ` |
| 91 | + fn assemble_candidates_after_normalizing_self_ty(&mut self, goal: Goal<'tcx, G>) { |
| 92 | + let tcx = self.cx.tcx; |
| 93 | + // FIXME: We also have to normalize opaque types, not sure where to best fit that in. |
| 94 | + let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.self_ty().kind() else { |
| 95 | + return |
| 96 | + }; |
| 97 | + self.infcx.probe(|_| { |
| 98 | + let normalized_ty = self.infcx.next_ty_infer(); |
| 99 | + let normalizes_to_goal = goal.with( |
| 100 | + tcx, |
| 101 | + ty::Binder::dummy(ty::ProjectionPredicate { |
| 102 | + projection_ty, |
| 103 | + term: normalized_ty.into(), |
| 104 | + }), |
| 105 | + ); |
| 106 | + let normalization_certainty = |
| 107 | + match self.cx.evaluate_goal(&self.infcx, normalizes_to_goal) { |
| 108 | + Ok((_, certainty)) => certainty, |
| 109 | + Err(NoSolution) => return, |
| 110 | + }; |
| 111 | + |
| 112 | + // NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate. |
| 113 | + // This doesn't work as long as we use `CandidateSource` in both winnowing and to resolve associated items. |
| 114 | + let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty)); |
| 115 | + let mut orig_values = OriginalQueryValues::default(); |
| 116 | + let goal = self.infcx.canonicalize_query(goal, &mut orig_values); |
| 117 | + let normalized_candidates = |
| 118 | + AssemblyCtxt::assemble_and_evaluate_candidates(self.cx, goal); |
| 119 | + |
| 120 | + // Map each candidate from being canonical wrt the current inference context to being |
| 121 | + // canonical wrt the caller. |
| 122 | + for Candidate { source, result } in normalized_candidates { |
| 123 | + self.infcx.probe(|_| { |
| 124 | + let candidate_certainty = fixme_instantiate_canonical_query_response( |
| 125 | + &self.infcx, |
| 126 | + &orig_values, |
| 127 | + result, |
| 128 | + ); |
| 129 | + |
| 130 | + // FIXME: This is a bit scary if the `normalizes_to_goal` overflows. |
| 131 | + // |
| 132 | + // If we have an ambiguous candidate it hides that normalization |
| 133 | + // caused an overflow which may cause issues. |
| 134 | + self.try_insert_candidate( |
| 135 | + source, |
| 136 | + normalization_certainty.unify_and(candidate_certainty), |
| 137 | + ) |
| 138 | + }) |
| 139 | + } |
| 140 | + }) |
| 141 | + } |
| 142 | + |
| 143 | + fn assemble_impl_candidates(&mut self, goal: Goal<'tcx, G>) { |
| 144 | + self.cx.tcx.for_each_relevant_impl( |
| 145 | + goal.predicate.trait_def_id(self.cx.tcx), |
| 146 | + goal.predicate.self_ty(), |
| 147 | + |impl_def_id| G::consider_impl_candidate(self, goal, impl_def_id), |
| 148 | + ); |
| 149 | + } |
| 150 | +} |
0 commit comments