]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_trait_selection/src/solve/assembly.rs
Rollup merge of #107114 - Erk-:add-absolute-note-to-path-join, r=m-ou-se
[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::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult};
5 use rustc_hir::def_id::DefId;
6 use rustc_infer::traits::query::NoSolution;
7 use rustc_infer::traits::util::elaborate_predicates;
8 use rustc_middle::ty::TypeFoldable;
9 use rustc_middle::ty::{self, Ty, TyCtxt};
10 use std::fmt::Debug;
11
12 /// A candidate is a possible way to prove a goal.
13 ///
14 /// It consists of both the `source`, which describes how that goal would be proven,
15 /// and the `result` when using the given `source`.
16 #[derive(Debug, Clone)]
17 pub(super) struct Candidate<'tcx> {
18     pub(super) source: CandidateSource,
19     pub(super) result: CanonicalResponse<'tcx>,
20 }
21
22 /// Possible ways the given goal can be proven.
23 #[derive(Debug, Clone, Copy)]
24 pub(super) enum CandidateSource {
25     /// A user written impl.
26     ///
27     /// ## Examples
28     ///
29     /// ```rust
30     /// fn main() {
31     ///     let x: Vec<u32> = Vec::new();
32     ///     // This uses the impl from the standard library to prove `Vec<T>: Clone`.
33     ///     let y = x.clone();
34     /// }
35     /// ```
36     Impl(DefId),
37     /// A builtin impl generated by the compiler. When adding a new special
38     /// trait, try to use actual impls whenever possible. Builtin impls should
39     /// only be used in cases where the impl cannot be manually be written.
40     ///
41     /// Notable examples are auto traits, `Sized`, and `DiscriminantKind`.
42     /// For a list of all traits with builtin impls, check out the
43     /// [`EvalCtxt::assemble_builtin_impl_candidates`] method. Not
44     BuiltinImpl,
45     /// An assumption from the environment.
46     ///
47     /// More precicely we've used the `n-th` assumption in the `param_env`.
48     ///
49     /// ## Examples
50     ///
51     /// ```rust
52     /// fn is_clone<T: Clone>(x: T) -> (T, T) {
53     ///     // This uses the assumption `T: Clone` from the `where`-bounds
54     ///     // to prove `T: Clone`.
55     ///     (x.clone(), x)
56     /// }
57     /// ```
58     ParamEnv(usize),
59     /// If the self type is an alias type, e.g. an opaque type or a projection,
60     /// we know the bounds on that alias to hold even without knowing its concrete
61     /// underlying type.
62     ///
63     /// More precisely this candidate is using the `n-th` bound in the `item_bounds` of
64     /// the self type.
65     ///
66     /// ## Examples
67     ///
68     /// ```rust
69     /// trait Trait {
70     ///     type Assoc: Clone;
71     /// }
72     ///
73     /// fn foo<T: Trait>(x: <T as Trait>::Assoc) {
74     ///     // We prove `<T as Trait>::Assoc` by looking at the bounds on `Assoc` in
75     ///     // in the trait definition.
76     ///     let _y = x.clone();
77     /// }
78     /// ```
79     AliasBound(usize),
80 }
81
82 pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq {
83     fn self_ty(self) -> Ty<'tcx>;
84
85     fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self;
86
87     fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId;
88
89     fn consider_impl_candidate(
90         ecx: &mut EvalCtxt<'_, 'tcx>,
91         goal: Goal<'tcx, Self>,
92         impl_def_id: DefId,
93     ) -> QueryResult<'tcx>;
94
95     fn consider_assumption(
96         ecx: &mut EvalCtxt<'_, 'tcx>,
97         goal: Goal<'tcx, Self>,
98         assumption: ty::Predicate<'tcx>,
99     ) -> QueryResult<'tcx>;
100
101     fn consider_auto_trait_candidate(
102         ecx: &mut EvalCtxt<'_, 'tcx>,
103         goal: Goal<'tcx, Self>,
104     ) -> QueryResult<'tcx>;
105
106     fn consider_trait_alias_candidate(
107         ecx: &mut EvalCtxt<'_, 'tcx>,
108         goal: Goal<'tcx, Self>,
109     ) -> QueryResult<'tcx>;
110
111     fn consider_builtin_sized_candidate(
112         ecx: &mut EvalCtxt<'_, 'tcx>,
113         goal: Goal<'tcx, Self>,
114     ) -> QueryResult<'tcx>;
115
116     fn consider_builtin_copy_clone_candidate(
117         ecx: &mut EvalCtxt<'_, 'tcx>,
118         goal: Goal<'tcx, Self>,
119     ) -> QueryResult<'tcx>;
120
121     fn consider_builtin_pointer_sized_candidate(
122         ecx: &mut EvalCtxt<'_, 'tcx>,
123         goal: Goal<'tcx, Self>,
124     ) -> QueryResult<'tcx>;
125
126     fn consider_builtin_fn_trait_candidates(
127         ecx: &mut EvalCtxt<'_, 'tcx>,
128         goal: Goal<'tcx, Self>,
129         kind: ty::ClosureKind,
130     ) -> QueryResult<'tcx>;
131
132     fn consider_builtin_tuple_candidate(
133         ecx: &mut EvalCtxt<'_, 'tcx>,
134         goal: Goal<'tcx, Self>,
135     ) -> QueryResult<'tcx>;
136 }
137
138 impl<'tcx> EvalCtxt<'_, 'tcx> {
139     pub(super) fn assemble_and_evaluate_candidates<G: GoalKind<'tcx>>(
140         &mut self,
141         goal: Goal<'tcx, G>,
142     ) -> Vec<Candidate<'tcx>> {
143         debug_assert_eq!(goal, self.infcx.resolve_vars_if_possible(goal));
144
145         // HACK: `_: Trait` is ambiguous, because it may be satisfied via a builtin rule,
146         // object bound, alias bound, etc. We are unable to determine this until we can at
147         // least structually resolve the type one layer.
148         if goal.predicate.self_ty().is_ty_var() {
149             return vec![Candidate {
150                 source: CandidateSource::BuiltinImpl,
151                 result: self.make_canonical_response(Certainty::AMBIGUOUS).unwrap(),
152             }];
153         }
154
155         let mut candidates = Vec::new();
156
157         self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates);
158
159         self.assemble_impl_candidates(goal, &mut candidates);
160
161         self.assemble_builtin_impl_candidates(goal, &mut candidates);
162
163         self.assemble_param_env_candidates(goal, &mut candidates);
164
165         self.assemble_alias_bound_candidates(goal, &mut candidates);
166
167         self.assemble_object_bound_candidates(goal, &mut candidates);
168
169         candidates
170     }
171
172     /// If the self type of a goal is a projection, computing the relevant candidates is difficult.
173     ///
174     /// To deal with this, we first try to normalize the self type and add the candidates for the normalized
175     /// self type to the list of candidates in case that succeeds. Note that we can't just eagerly return in
176     /// this case as projections as self types add `
177     fn assemble_candidates_after_normalizing_self_ty<G: GoalKind<'tcx>>(
178         &mut self,
179         goal: Goal<'tcx, G>,
180         candidates: &mut Vec<Candidate<'tcx>>,
181     ) {
182         let tcx = self.tcx();
183         // FIXME: We also have to normalize opaque types, not sure where to best fit that in.
184         let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.self_ty().kind() else {
185             return
186         };
187         self.infcx.probe(|_| {
188             let normalized_ty = self.infcx.next_ty_infer();
189             let normalizes_to_goal = goal.with(
190                 tcx,
191                 ty::Binder::dummy(ty::ProjectionPredicate {
192                     projection_ty,
193                     term: normalized_ty.into(),
194                 }),
195             );
196             let normalization_certainty = match self.evaluate_goal(normalizes_to_goal) {
197                 Ok((_, certainty)) => certainty,
198                 Err(NoSolution) => return,
199             };
200             let normalized_ty = self.infcx.resolve_vars_if_possible(normalized_ty);
201
202             // NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate.
203             // This doesn't work as long as we use `CandidateSource` in winnowing.
204             let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
205             // FIXME: This is broken if we care about the `usize` of `AliasBound` because the self type
206             // could be normalized to yet another projection with different item bounds.
207             let normalized_candidates = self.assemble_and_evaluate_candidates(goal);
208             for mut normalized_candidate in normalized_candidates {
209                 normalized_candidate.result =
210                     normalized_candidate.result.unchecked_map(|mut response| {
211                         // FIXME: This currently hides overflow in the normalization step of the self type
212                         // which is probably wrong. Maybe `unify_and` should actually keep overflow as
213                         // we treat it as non-fatal anyways.
214                         response.certainty = response.certainty.unify_and(normalization_certainty);
215                         response
216                     });
217                 candidates.push(normalized_candidate);
218             }
219         })
220     }
221
222     fn assemble_impl_candidates<G: GoalKind<'tcx>>(
223         &mut self,
224         goal: Goal<'tcx, G>,
225         candidates: &mut Vec<Candidate<'tcx>>,
226     ) {
227         let tcx = self.tcx();
228         tcx.for_each_relevant_impl(
229             goal.predicate.trait_def_id(tcx),
230             goal.predicate.self_ty(),
231             |impl_def_id| match G::consider_impl_candidate(self, goal, impl_def_id) {
232                 Ok(result) => candidates
233                     .push(Candidate { source: CandidateSource::Impl(impl_def_id), result }),
234                 Err(NoSolution) => (),
235             },
236         );
237     }
238
239     fn assemble_builtin_impl_candidates<G: GoalKind<'tcx>>(
240         &mut self,
241         goal: Goal<'tcx, G>,
242         candidates: &mut Vec<Candidate<'tcx>>,
243     ) {
244         let lang_items = self.tcx().lang_items();
245         let trait_def_id = goal.predicate.trait_def_id(self.tcx());
246         let result = if self.tcx().trait_is_auto(trait_def_id) {
247             G::consider_auto_trait_candidate(self, goal)
248         } else if self.tcx().trait_is_alias(trait_def_id) {
249             G::consider_trait_alias_candidate(self, goal)
250         } else if lang_items.sized_trait() == Some(trait_def_id) {
251             G::consider_builtin_sized_candidate(self, goal)
252         } else if lang_items.copy_trait() == Some(trait_def_id)
253             || lang_items.clone_trait() == Some(trait_def_id)
254         {
255             G::consider_builtin_copy_clone_candidate(self, goal)
256         } else if lang_items.pointer_sized() == Some(trait_def_id) {
257             G::consider_builtin_pointer_sized_candidate(self, goal)
258         } else if let Some(kind) = self.tcx().fn_trait_kind_from_def_id(trait_def_id) {
259             G::consider_builtin_fn_trait_candidates(self, goal, kind)
260         } else if lang_items.tuple_trait() == Some(trait_def_id) {
261             G::consider_builtin_tuple_candidate(self, goal)
262         } else {
263             Err(NoSolution)
264         };
265
266         match result {
267             Ok(result) => {
268                 candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
269             }
270             Err(NoSolution) => (),
271         }
272     }
273
274     fn assemble_param_env_candidates<G: GoalKind<'tcx>>(
275         &mut self,
276         goal: Goal<'tcx, G>,
277         candidates: &mut Vec<Candidate<'tcx>>,
278     ) {
279         for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() {
280             match G::consider_assumption(self, goal, assumption) {
281                 Ok(result) => {
282                     candidates.push(Candidate { source: CandidateSource::ParamEnv(i), result })
283                 }
284                 Err(NoSolution) => (),
285             }
286         }
287     }
288
289     fn assemble_alias_bound_candidates<G: GoalKind<'tcx>>(
290         &mut self,
291         goal: Goal<'tcx, G>,
292         candidates: &mut Vec<Candidate<'tcx>>,
293     ) {
294         let alias_ty = match goal.predicate.self_ty().kind() {
295             ty::Bool
296             | ty::Char
297             | ty::Int(_)
298             | ty::Uint(_)
299             | ty::Float(_)
300             | ty::Adt(_, _)
301             | ty::Foreign(_)
302             | ty::Str
303             | ty::Array(_, _)
304             | ty::Slice(_)
305             | ty::RawPtr(_)
306             | ty::Ref(_, _, _)
307             | ty::FnDef(_, _)
308             | ty::FnPtr(_)
309             | ty::Dynamic(..)
310             | ty::Closure(..)
311             | ty::Generator(..)
312             | ty::GeneratorWitness(_)
313             | ty::Never
314             | ty::Tuple(_)
315             | ty::Param(_)
316             | ty::Placeholder(..)
317             | ty::Infer(_)
318             | ty::Error(_) => return,
319             ty::Bound(..) => bug!("unexpected bound type: {goal:?}"),
320             ty::Alias(_, alias_ty) => alias_ty,
321         };
322
323         for (i, (assumption, _)) in self
324             .tcx()
325             .bound_explicit_item_bounds(alias_ty.def_id)
326             .subst_iter_copied(self.tcx(), alias_ty.substs)
327             .enumerate()
328         {
329             match G::consider_assumption(self, goal, assumption) {
330                 Ok(result) => {
331                     candidates.push(Candidate { source: CandidateSource::AliasBound(i), result })
332                 }
333                 Err(NoSolution) => (),
334             }
335         }
336     }
337
338     fn assemble_object_bound_candidates<G: GoalKind<'tcx>>(
339         &mut self,
340         goal: Goal<'tcx, G>,
341         candidates: &mut Vec<Candidate<'tcx>>,
342     ) {
343         let self_ty = goal.predicate.self_ty();
344         let bounds = match *self_ty.kind() {
345             ty::Bool
346             | ty::Char
347             | ty::Int(_)
348             | ty::Uint(_)
349             | ty::Float(_)
350             | ty::Adt(_, _)
351             | ty::Foreign(_)
352             | ty::Str
353             | ty::Array(_, _)
354             | ty::Slice(_)
355             | ty::RawPtr(_)
356             | ty::Ref(_, _, _)
357             | ty::FnDef(_, _)
358             | ty::FnPtr(_)
359             | ty::Alias(..)
360             | ty::Closure(..)
361             | ty::Generator(..)
362             | ty::GeneratorWitness(_)
363             | ty::Never
364             | ty::Tuple(_)
365             | ty::Param(_)
366             | ty::Placeholder(..)
367             | ty::Infer(_)
368             | ty::Error(_) => return,
369             ty::Bound(..) => bug!("unexpected bound type: {goal:?}"),
370             ty::Dynamic(bounds, ..) => bounds,
371         };
372
373         let tcx = self.tcx();
374         for assumption in
375             elaborate_predicates(tcx, bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty)))
376         {
377             match G::consider_assumption(self, goal, assumption.predicate) {
378                 Ok(result) => {
379                     candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
380                 }
381                 Err(NoSolution) => (),
382             }
383         }
384     }
385 }