]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_trait_selection/src/solve/mod.rs
Rollup merge of #107431 - notriddle:notriddle/colon, r=thomcc
[rust.git] / compiler / rustc_trait_selection / src / solve / mod.rs
1 //! The new trait solver, currently still WIP.
2 //!
3 //! As a user of the trait system, you can use `TyCtxt::evaluate_goal` to
4 //! interact with this solver.
5 //!
6 //! For a high-level overview of how this solver works, check out the relevant
7 //! section of the rustc-dev-guide.
8 //!
9 //! FIXME(@lcnr): Write that section. If you read this before then ask me
10 //! about it on zulip.
11
12 // FIXME: Instead of using `infcx.canonicalize_query` we have to add a new routine which
13 // preserves universes and creates a unique var (in the highest universe) for each
14 // appearance of a region.
15
16 // FIXME: uses of `infcx.at` need to enable deferred projection equality once that's implemented.
17
18 use std::mem;
19
20 use rustc_hir::def_id::DefId;
21 use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues};
22 use rustc_infer::infer::canonical::{OriginalQueryValues, QueryRegionConstraints, QueryResponse};
23 use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt};
24 use rustc_infer::traits::query::NoSolution;
25 use rustc_infer::traits::Obligation;
26 use rustc_middle::infer::canonical::Certainty as OldCertainty;
27 use rustc_middle::ty::{self, Ty, TyCtxt};
28 use rustc_middle::ty::{
29     CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, ToPredicate, TypeOutlivesPredicate,
30 };
31 use rustc_span::DUMMY_SP;
32
33 use crate::traits::ObligationCause;
34
35 mod assembly;
36 mod fulfill;
37 mod infcx_ext;
38 mod project_goals;
39 mod search_graph;
40 mod trait_goals;
41
42 pub use fulfill::FulfillmentCtxt;
43
44 /// A goal is a statement, i.e. `predicate`, we want to prove
45 /// given some assumptions, i.e. `param_env`.
46 ///
47 /// Most of the time the `param_env` contains the `where`-bounds of the function
48 /// we're currently typechecking while the `predicate` is some trait bound.
49 #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
50 pub struct Goal<'tcx, P> {
51     param_env: ty::ParamEnv<'tcx>,
52     predicate: P,
53 }
54
55 impl<'tcx, P> Goal<'tcx, P> {
56     pub fn new(
57         tcx: TyCtxt<'tcx>,
58         param_env: ty::ParamEnv<'tcx>,
59         predicate: impl ToPredicate<'tcx, P>,
60     ) -> Goal<'tcx, P> {
61         Goal { param_env, predicate: predicate.to_predicate(tcx) }
62     }
63
64     /// Updates the goal to one with a different `predicate` but the same `param_env`.
65     fn with<Q>(self, tcx: TyCtxt<'tcx>, predicate: impl ToPredicate<'tcx, Q>) -> Goal<'tcx, Q> {
66         Goal { param_env: self.param_env, predicate: predicate.to_predicate(tcx) }
67     }
68 }
69
70 impl<'tcx, P> From<Obligation<'tcx, P>> for Goal<'tcx, P> {
71     fn from(obligation: Obligation<'tcx, P>) -> Goal<'tcx, P> {
72         Goal { param_env: obligation.param_env, predicate: obligation.predicate }
73     }
74 }
75
76 #[derive(Debug, PartialEq, Eq, Clone, Hash, TypeFoldable, TypeVisitable)]
77 pub struct Response<'tcx> {
78     pub var_values: CanonicalVarValues<'tcx>,
79     /// Additional constraints returned by this query.
80     pub external_constraints: ExternalConstraints<'tcx>,
81     pub certainty: Certainty,
82 }
83
84 #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
85 pub enum Certainty {
86     Yes,
87     Maybe(MaybeCause),
88 }
89
90 impl Certainty {
91     pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity);
92
93     /// When proving multiple goals using **AND**, e.g. nested obligations for an impl,
94     /// use this function to unify the certainty of these goals
95     pub fn unify_and(self, other: Certainty) -> Certainty {
96         match (self, other) {
97             (Certainty::Yes, Certainty::Yes) => Certainty::Yes,
98             (Certainty::Yes, Certainty::Maybe(_)) => other,
99             (Certainty::Maybe(_), Certainty::Yes) => self,
100             (Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Overflow)) => {
101                 Certainty::Maybe(MaybeCause::Overflow)
102             }
103             // If at least one of the goals is ambiguous, hide the overflow as the ambiguous goal
104             // may still result in failure.
105             (Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(_))
106             | (Certainty::Maybe(_), Certainty::Maybe(MaybeCause::Ambiguity)) => {
107                 Certainty::Maybe(MaybeCause::Ambiguity)
108             }
109         }
110     }
111 }
112
113 /// Why we failed to evaluate a goal.
114 #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
115 pub enum MaybeCause {
116     /// We failed due to ambiguity. This ambiguity can either
117     /// be a true ambiguity, i.e. there are multiple different answers,
118     /// or we hit a case where we just don't bother, e.g. `?x: Trait` goals.
119     Ambiguity,
120     /// We gave up due to an overflow, most often by hitting the recursion limit.
121     Overflow,
122 }
123
124 /// Additional constraints returned on success.
125 #[derive(Debug, PartialEq, Eq, Clone, Hash, TypeFoldable, TypeVisitable, Default)]
126 pub struct ExternalConstraints<'tcx> {
127     // FIXME: implement this.
128     regions: (),
129     opaque_types: Vec<(Ty<'tcx>, Ty<'tcx>)>,
130 }
131
132 type CanonicalGoal<'tcx, T = ty::Predicate<'tcx>> = Canonical<'tcx, Goal<'tcx, T>>;
133 type CanonicalResponse<'tcx> = Canonical<'tcx, Response<'tcx>>;
134 /// The result of evaluating a canonical query.
135 ///
136 /// FIXME: We use a different type than the existing canonical queries. This is because
137 /// we need to add a `Certainty` for `overflow` and may want to restructure this code without
138 /// having to worry about changes to currently used code. Once we've made progress on this
139 /// solver, merge the two responses again.
140 pub type QueryResult<'tcx> = Result<CanonicalResponse<'tcx>, NoSolution>;
141
142 pub trait InferCtxtEvalExt<'tcx> {
143     /// Evaluates a goal from **outside** of the trait solver.
144     ///
145     /// Using this while inside of the solver is wrong as it uses a new
146     /// search graph which would break cycle detection.
147     fn evaluate_root_goal(
148         &self,
149         goal: Goal<'tcx, ty::Predicate<'tcx>>,
150     ) -> Result<(bool, Certainty), NoSolution>;
151 }
152
153 impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
154     fn evaluate_root_goal(
155         &self,
156         goal: Goal<'tcx, ty::Predicate<'tcx>>,
157     ) -> Result<(bool, Certainty), NoSolution> {
158         let mut search_graph = search_graph::SearchGraph::new(self.tcx);
159
160         let result = EvalCtxt {
161             search_graph: &mut search_graph,
162             infcx: self,
163             var_values: CanonicalVarValues::dummy(),
164         }
165         .evaluate_goal(goal);
166
167         assert!(search_graph.is_empty());
168         result
169     }
170 }
171
172 struct EvalCtxt<'a, 'tcx> {
173     infcx: &'a InferCtxt<'tcx>,
174     var_values: CanonicalVarValues<'tcx>,
175
176     search_graph: &'a mut search_graph::SearchGraph<'tcx>,
177 }
178
179 impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
180     fn tcx(&self) -> TyCtxt<'tcx> {
181         self.infcx.tcx
182     }
183
184     /// The entry point of the solver.
185     ///
186     /// This function deals with (coinductive) cycles, overflow, and caching
187     /// and then calls [`EvalCtxt::compute_goal`] which contains the actual
188     /// logic of the solver.
189     ///
190     /// Instead of calling this function directly, use either [EvalCtxt::evaluate_goal]
191     /// if you're inside of the solver or [InferCtxtEvalExt::evaluate_root_goal] if you're
192     /// outside of it.
193     #[instrument(level = "debug", skip(tcx, search_graph), ret)]
194     fn evaluate_canonical_goal(
195         tcx: TyCtxt<'tcx>,
196         search_graph: &'a mut search_graph::SearchGraph<'tcx>,
197         canonical_goal: CanonicalGoal<'tcx>,
198     ) -> QueryResult<'tcx> {
199         match search_graph.try_push_stack(tcx, canonical_goal) {
200             Ok(()) => {}
201             // Our goal is already on the stack, eager return.
202             Err(response) => return response,
203         }
204
205         // We may have to repeatedly recompute the goal in case of coinductive cycles,
206         // check out the `cache` module for more information.
207         //
208         // FIXME: Similar to `evaluate_all`, this has to check for overflow.
209         loop {
210             let (ref infcx, goal, var_values) =
211                 tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal);
212             let mut ecx = EvalCtxt { infcx, var_values, search_graph };
213             let result = ecx.compute_goal(goal);
214
215             // FIXME: `Response` should be `Copy`
216             if search_graph.try_finalize_goal(tcx, canonical_goal, result.clone()) {
217                 return result;
218             }
219         }
220     }
221
222     fn make_canonical_response(&self, certainty: Certainty) -> QueryResult<'tcx> {
223         let external_constraints = take_external_constraints(self.infcx)?;
224
225         Ok(self.infcx.canonicalize_response(Response {
226             var_values: self.var_values,
227             external_constraints,
228             certainty,
229         }))
230     }
231
232     /// Recursively evaluates `goal`, returning whether any inference vars have
233     /// been constrained and the certainty of the result.
234     fn evaluate_goal(
235         &mut self,
236         goal: Goal<'tcx, ty::Predicate<'tcx>>,
237     ) -> Result<(bool, Certainty), NoSolution> {
238         let mut orig_values = OriginalQueryValues::default();
239         let canonical_goal = self.infcx.canonicalize_query(goal, &mut orig_values);
240         let canonical_response =
241             EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
242         Ok((
243             !canonical_response.value.var_values.is_identity(),
244             instantiate_canonical_query_response(self.infcx, &orig_values, canonical_response),
245         ))
246     }
247
248     fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> {
249         let Goal { param_env, predicate } = goal;
250         let kind = predicate.kind();
251         if let Some(kind) = kind.no_bound_vars() {
252             match kind {
253                 ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
254                     self.compute_trait_goal(Goal { param_env, predicate })
255                 }
256                 ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => {
257                     self.compute_projection_goal(Goal { param_env, predicate })
258                 }
259                 ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => {
260                     self.compute_type_outlives_goal(Goal { param_env, predicate })
261                 }
262                 ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => {
263                     self.compute_region_outlives_goal(Goal { param_env, predicate })
264                 }
265                 ty::PredicateKind::Subtype(predicate) => {
266                     self.compute_subtype_goal(Goal { param_env, predicate })
267                 }
268                 ty::PredicateKind::Coerce(predicate) => {
269                     self.compute_coerce_goal(Goal { param_env, predicate })
270                 }
271                 ty::PredicateKind::ClosureKind(def_id, substs, kind) => self
272                     .compute_closure_kind_goal(Goal {
273                         param_env,
274                         predicate: (def_id, substs, kind),
275                     }),
276                 ty::PredicateKind::ObjectSafe(trait_def_id) => {
277                     self.compute_object_safe_goal(trait_def_id)
278                 }
279                 ty::PredicateKind::WellFormed(arg) => {
280                     self.compute_well_formed_goal(Goal { param_env, predicate: arg })
281                 }
282                 ty::PredicateKind::Ambiguous => self.make_canonical_response(Certainty::AMBIGUOUS),
283                 // FIXME: implement these predicates :)
284                 ty::PredicateKind::ConstEvaluatable(_) | ty::PredicateKind::ConstEquate(_, _) => {
285                     self.make_canonical_response(Certainty::Yes)
286                 }
287                 ty::PredicateKind::TypeWellFormedFromEnv(..) => {
288                     bug!("TypeWellFormedFromEnv is only used for Chalk")
289                 }
290             }
291         } else {
292             let kind = self.infcx.replace_bound_vars_with_placeholders(kind);
293             let goal = goal.with(self.tcx(), ty::Binder::dummy(kind));
294             let (_, certainty) = self.evaluate_goal(goal)?;
295             self.make_canonical_response(certainty)
296         }
297     }
298
299     fn compute_type_outlives_goal(
300         &mut self,
301         _goal: Goal<'tcx, TypeOutlivesPredicate<'tcx>>,
302     ) -> QueryResult<'tcx> {
303         self.make_canonical_response(Certainty::Yes)
304     }
305
306     fn compute_region_outlives_goal(
307         &mut self,
308         _goal: Goal<'tcx, RegionOutlivesPredicate<'tcx>>,
309     ) -> QueryResult<'tcx> {
310         self.make_canonical_response(Certainty::Yes)
311     }
312
313     fn compute_coerce_goal(
314         &mut self,
315         goal: Goal<'tcx, CoercePredicate<'tcx>>,
316     ) -> QueryResult<'tcx> {
317         self.compute_subtype_goal(Goal {
318             param_env: goal.param_env,
319             predicate: SubtypePredicate {
320                 a_is_expected: false,
321                 a: goal.predicate.a,
322                 b: goal.predicate.b,
323             },
324         })
325     }
326
327     fn compute_subtype_goal(
328         &mut self,
329         goal: Goal<'tcx, SubtypePredicate<'tcx>>,
330     ) -> QueryResult<'tcx> {
331         if goal.predicate.a.is_ty_var() && goal.predicate.b.is_ty_var() {
332             // FIXME: Do we want to register a subtype relation between these vars?
333             // That won't actually reflect in the query response, so it seems moot.
334             self.make_canonical_response(Certainty::AMBIGUOUS)
335         } else {
336             let InferOk { value: (), obligations } = self
337                 .infcx
338                 .at(&ObligationCause::dummy(), goal.param_env)
339                 .sub(goal.predicate.a, goal.predicate.b)?;
340             self.evaluate_all_and_make_canonical_response(
341                 obligations.into_iter().map(|pred| pred.into()).collect(),
342             )
343         }
344     }
345
346     fn compute_closure_kind_goal(
347         &mut self,
348         goal: Goal<'tcx, (DefId, ty::SubstsRef<'tcx>, ty::ClosureKind)>,
349     ) -> QueryResult<'tcx> {
350         let (_, substs, expected_kind) = goal.predicate;
351         let found_kind = substs.as_closure().kind_ty().to_opt_closure_kind();
352
353         let Some(found_kind) = found_kind else {
354             return self.make_canonical_response(Certainty::AMBIGUOUS);
355         };
356         if found_kind.extends(expected_kind) {
357             self.make_canonical_response(Certainty::Yes)
358         } else {
359             Err(NoSolution)
360         }
361     }
362
363     fn compute_object_safe_goal(&mut self, trait_def_id: DefId) -> QueryResult<'tcx> {
364         if self.tcx().check_is_object_safe(trait_def_id) {
365             self.make_canonical_response(Certainty::Yes)
366         } else {
367             Err(NoSolution)
368         }
369     }
370
371     fn compute_well_formed_goal(
372         &mut self,
373         goal: Goal<'tcx, ty::GenericArg<'tcx>>,
374     ) -> QueryResult<'tcx> {
375         match crate::traits::wf::unnormalized_obligations(
376             self.infcx,
377             goal.param_env,
378             goal.predicate,
379         ) {
380             Some(obligations) => self.evaluate_all_and_make_canonical_response(
381                 obligations.into_iter().map(|o| o.into()).collect(),
382             ),
383             None => self.make_canonical_response(Certainty::AMBIGUOUS),
384         }
385     }
386 }
387
388 impl<'tcx> EvalCtxt<'_, 'tcx> {
389     // Recursively evaluates a list of goals to completion, returning the certainty
390     // of all of the goals.
391     fn evaluate_all(
392         &mut self,
393         mut goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
394     ) -> Result<Certainty, NoSolution> {
395         let mut new_goals = Vec::new();
396         self.repeat_while_none(|this| {
397             let mut has_changed = Err(Certainty::Yes);
398             for goal in goals.drain(..) {
399                 let (changed, certainty) = match this.evaluate_goal(goal) {
400                     Ok(result) => result,
401                     Err(NoSolution) => return Some(Err(NoSolution)),
402                 };
403
404                 if changed {
405                     has_changed = Ok(());
406                 }
407
408                 match certainty {
409                     Certainty::Yes => {}
410                     Certainty::Maybe(_) => {
411                         new_goals.push(goal);
412                         has_changed = has_changed.map_err(|c| c.unify_and(certainty));
413                     }
414                 }
415             }
416
417             match has_changed {
418                 Ok(()) => {
419                     mem::swap(&mut new_goals, &mut goals);
420                     None
421                 }
422                 Err(certainty) => Some(Ok(certainty)),
423             }
424         })
425     }
426
427     // Recursively evaluates a list of goals to completion, making a query response.
428     //
429     // This is just a convenient way of calling [`EvalCtxt::evaluate_all`],
430     // then [`EvalCtxt::make_canonical_response`].
431     fn evaluate_all_and_make_canonical_response(
432         &mut self,
433         goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
434     ) -> QueryResult<'tcx> {
435         self.evaluate_all(goals).and_then(|certainty| self.make_canonical_response(certainty))
436     }
437 }
438
439 #[instrument(level = "debug", skip(infcx), ret)]
440 fn take_external_constraints<'tcx>(
441     infcx: &InferCtxt<'tcx>,
442 ) -> Result<ExternalConstraints<'tcx>, NoSolution> {
443     let region_obligations = infcx.take_registered_region_obligations();
444     let opaque_types = infcx.take_opaque_types_for_query_response();
445     Ok(ExternalConstraints {
446         // FIXME: Now that's definitely wrong :)
447         //
448         // Should also do the leak check here I think
449         regions: drop(region_obligations),
450         opaque_types,
451     })
452 }
453
454 fn instantiate_canonical_query_response<'tcx>(
455     infcx: &InferCtxt<'tcx>,
456     original_values: &OriginalQueryValues<'tcx>,
457     response: CanonicalResponse<'tcx>,
458 ) -> Certainty {
459     let Ok(InferOk { value, obligations }) = infcx
460         .instantiate_query_response_and_region_obligations(
461             &ObligationCause::dummy(),
462             ty::ParamEnv::empty(),
463             original_values,
464             &response.unchecked_map(|resp| QueryResponse {
465                 var_values: resp.var_values,
466                 region_constraints: QueryRegionConstraints::default(),
467                 certainty: match resp.certainty {
468                     Certainty::Yes => OldCertainty::Proven,
469                     Certainty::Maybe(_) => OldCertainty::Ambiguous,
470                 },
471                 opaque_types: resp.external_constraints.opaque_types,
472                 value: resp.certainty,
473             }),
474         ) else { bug!(); };
475     assert!(obligations.is_empty());
476     value
477 }
478
479 pub(super) fn response_no_constraints<'tcx>(
480     tcx: TyCtxt<'tcx>,
481     goal: Canonical<'tcx, impl Sized>,
482     certainty: Certainty,
483 ) -> QueryResult<'tcx> {
484     Ok(Canonical {
485         max_universe: goal.max_universe,
486         variables: goal.variables,
487         value: Response {
488             var_values: CanonicalVarValues::make_identity(tcx, goal.variables),
489             external_constraints: Default::default(),
490             certainty,
491         },
492     })
493 }