]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_trait_selection/src/solve/assembly.rs
Auto merge of #106742 - compiler-errors:new-solver-make-it-not-ice, r=lcnr
[rust.git] / compiler / rustc_trait_selection / src / solve / assembly.rs
1 //! Code shared by trait and projection goals for candidate assembly.
2
3 use super::infcx_ext::InferCtxtExt;
4 use super::{
5     instantiate_canonical_query_response, CanonicalGoal, CanonicalResponse, Certainty, EvalCtxt,
6     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 =
125                         instantiate_canonical_query_response(&self.infcx, &orig_values, result);
126
127                     // FIXME: This is a bit scary if the `normalizes_to_goal` overflows.
128                     //
129                     // If we have an ambiguous candidate it hides that normalization
130                     // caused an overflow which may cause issues.
131                     self.try_insert_candidate(
132                         source,
133                         normalization_certainty.unify_and(candidate_certainty),
134                     )
135                 })
136             }
137         })
138     }
139
140     fn assemble_impl_candidates(&mut self, goal: Goal<'tcx, G>) {
141         self.cx.tcx.for_each_relevant_impl(
142             goal.predicate.trait_def_id(self.cx.tcx),
143             goal.predicate.self_ty(),
144             |impl_def_id| G::consider_impl_candidate(self, goal, impl_def_id),
145         );
146     }
147 }