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