1 //! The new trait solver, currently still WIP.
3 //! As a user of the trait system, you can use `TyCtxt::evaluate_goal` to
4 //! interact with this solver.
6 //! For a high-level overview of how this solver works, check out the relevant
7 //! section of the rustc-dev-guide.
9 //! FIXME(@lcnr): Write that section. If you read this before then ask me
10 //! about it on zulip.
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.
16 // FIXME: uses of `infcx.at` need to enable deferred projection equality once that's implemented.
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,
31 use rustc_span::DUMMY_SP;
33 use crate::traits::ObligationCause;
42 pub use fulfill::FulfillmentCtxt;
44 /// A goal is a statement, i.e. `predicate`, we want to prove
45 /// given some assumptions, i.e. `param_env`.
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>,
55 impl<'tcx, P> Goal<'tcx, P> {
58 param_env: ty::ParamEnv<'tcx>,
59 predicate: impl ToPredicate<'tcx, P>,
61 Goal { param_env, predicate: predicate.to_predicate(tcx) }
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) }
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 }
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,
84 #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
91 pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity);
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 {
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)
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)
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.
120 /// We gave up due to an overflow, most often by hitting the recursion limit.
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.
129 opaque_types: Vec<(Ty<'tcx>, Ty<'tcx>)>,
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.
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>;
142 pub trait InferCtxtEvalExt<'tcx> {
143 /// Evaluates a goal from **outside** of the trait solver.
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(
149 goal: Goal<'tcx, ty::Predicate<'tcx>>,
150 ) -> Result<(bool, Certainty), NoSolution>;
153 impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
154 fn evaluate_root_goal(
156 goal: Goal<'tcx, ty::Predicate<'tcx>>,
157 ) -> Result<(bool, Certainty), NoSolution> {
158 let mut search_graph = search_graph::SearchGraph::new(self.tcx);
160 let result = EvalCtxt {
161 search_graph: &mut search_graph,
163 var_values: CanonicalVarValues::dummy(),
164 in_projection_eq_hack: false,
166 .evaluate_goal(goal);
168 assert!(search_graph.is_empty());
173 struct EvalCtxt<'a, 'tcx> {
174 infcx: &'a InferCtxt<'tcx>,
175 var_values: CanonicalVarValues<'tcx>,
177 search_graph: &'a mut search_graph::SearchGraph<'tcx>,
179 /// This field is used by a debug assertion in [`EvalCtxt::evaluate_goal`],
180 /// see the comment in that method for more details.
181 in_projection_eq_hack: bool,
184 impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
185 fn tcx(&self) -> TyCtxt<'tcx> {
189 /// The entry point of the solver.
191 /// This function deals with (coinductive) cycles, overflow, and caching
192 /// and then calls [`EvalCtxt::compute_goal`] which contains the actual
193 /// logic of the solver.
195 /// Instead of calling this function directly, use either [EvalCtxt::evaluate_goal]
196 /// if you're inside of the solver or [InferCtxtEvalExt::evaluate_root_goal] if you're
198 #[instrument(level = "debug", skip(tcx, search_graph), ret)]
199 fn evaluate_canonical_goal(
201 search_graph: &'a mut search_graph::SearchGraph<'tcx>,
202 canonical_goal: CanonicalGoal<'tcx>,
203 ) -> QueryResult<'tcx> {
204 match search_graph.try_push_stack(tcx, canonical_goal) {
206 // Our goal is already on the stack, eager return.
207 Err(response) => return response,
210 // We may have to repeatedly recompute the goal in case of coinductive cycles,
211 // check out the `cache` module for more information.
213 // FIXME: Similar to `evaluate_all`, this has to check for overflow.
215 let (ref infcx, goal, var_values) =
216 tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal);
218 EvalCtxt { infcx, var_values, search_graph, in_projection_eq_hack: false };
219 let result = ecx.compute_goal(goal);
221 // FIXME: `Response` should be `Copy`
222 if search_graph.try_finalize_goal(tcx, canonical_goal, result.clone()) {
228 fn make_canonical_response(&self, certainty: Certainty) -> QueryResult<'tcx> {
229 let external_constraints = take_external_constraints(self.infcx)?;
231 Ok(self.infcx.canonicalize_response(Response {
232 var_values: self.var_values,
233 external_constraints,
238 /// Recursively evaluates `goal`, returning whether any inference vars have
239 /// been constrained and the certainty of the result.
242 goal: Goal<'tcx, ty::Predicate<'tcx>>,
243 ) -> Result<(bool, Certainty), NoSolution> {
244 let mut orig_values = OriginalQueryValues::default();
245 let canonical_goal = self.infcx.canonicalize_query(goal, &mut orig_values);
246 let canonical_response =
247 EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
249 let has_changed = !canonical_response.value.var_values.is_identity();
251 instantiate_canonical_query_response(self.infcx, &orig_values, canonical_response);
253 // Check that rerunning this query with its inference constraints applied
254 // doesn't result in new inference constraints and has the same result.
256 // If we have projection goals like `<T as Trait>::Assoc == u32` we recursively
257 // call `exists<U> <T as Trait>::Assoc == U` to enable better caching. This goal
258 // could constrain `U` to `u32` which would cause this check to result in a
260 if cfg!(debug_assertions) && has_changed && !self.in_projection_eq_hack {
261 let mut orig_values = OriginalQueryValues::default();
262 let canonical_goal = self.infcx.canonicalize_query(goal, &mut orig_values);
263 let canonical_response =
264 EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
265 assert!(canonical_response.value.var_values.is_identity());
266 assert_eq!(certainty, canonical_response.value.certainty);
269 Ok((has_changed, certainty))
272 fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> {
273 let Goal { param_env, predicate } = goal;
274 let kind = predicate.kind();
275 if let Some(kind) = kind.no_bound_vars() {
277 ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
278 self.compute_trait_goal(Goal { param_env, predicate })
280 ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => {
281 self.compute_projection_goal(Goal { param_env, predicate })
283 ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => {
284 self.compute_type_outlives_goal(Goal { param_env, predicate })
286 ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => {
287 self.compute_region_outlives_goal(Goal { param_env, predicate })
289 ty::PredicateKind::Subtype(predicate) => {
290 self.compute_subtype_goal(Goal { param_env, predicate })
292 ty::PredicateKind::Coerce(predicate) => {
293 self.compute_coerce_goal(Goal { param_env, predicate })
295 ty::PredicateKind::ClosureKind(def_id, substs, kind) => self
296 .compute_closure_kind_goal(Goal {
298 predicate: (def_id, substs, kind),
300 ty::PredicateKind::ObjectSafe(trait_def_id) => {
301 self.compute_object_safe_goal(trait_def_id)
303 ty::PredicateKind::WellFormed(arg) => {
304 self.compute_well_formed_goal(Goal { param_env, predicate: arg })
306 ty::PredicateKind::Ambiguous => self.make_canonical_response(Certainty::AMBIGUOUS),
307 // FIXME: implement these predicates :)
308 ty::PredicateKind::ConstEvaluatable(_) | ty::PredicateKind::ConstEquate(_, _) => {
309 self.make_canonical_response(Certainty::Yes)
311 ty::PredicateKind::TypeWellFormedFromEnv(..) => {
312 bug!("TypeWellFormedFromEnv is only used for Chalk")
316 let kind = self.infcx.replace_bound_vars_with_placeholders(kind);
317 let goal = goal.with(self.tcx(), ty::Binder::dummy(kind));
318 let (_, certainty) = self.evaluate_goal(goal)?;
319 self.make_canonical_response(certainty)
323 fn compute_type_outlives_goal(
325 _goal: Goal<'tcx, TypeOutlivesPredicate<'tcx>>,
326 ) -> QueryResult<'tcx> {
327 self.make_canonical_response(Certainty::Yes)
330 fn compute_region_outlives_goal(
332 _goal: Goal<'tcx, RegionOutlivesPredicate<'tcx>>,
333 ) -> QueryResult<'tcx> {
334 self.make_canonical_response(Certainty::Yes)
337 fn compute_coerce_goal(
339 goal: Goal<'tcx, CoercePredicate<'tcx>>,
340 ) -> QueryResult<'tcx> {
341 self.compute_subtype_goal(Goal {
342 param_env: goal.param_env,
343 predicate: SubtypePredicate {
344 a_is_expected: false,
351 fn compute_subtype_goal(
353 goal: Goal<'tcx, SubtypePredicate<'tcx>>,
354 ) -> QueryResult<'tcx> {
355 if goal.predicate.a.is_ty_var() && goal.predicate.b.is_ty_var() {
356 // FIXME: Do we want to register a subtype relation between these vars?
357 // That won't actually reflect in the query response, so it seems moot.
358 self.make_canonical_response(Certainty::AMBIGUOUS)
360 let InferOk { value: (), obligations } = self
362 .at(&ObligationCause::dummy(), goal.param_env)
363 .sub(goal.predicate.a, goal.predicate.b)?;
364 self.evaluate_all_and_make_canonical_response(
365 obligations.into_iter().map(|pred| pred.into()).collect(),
370 fn compute_closure_kind_goal(
372 goal: Goal<'tcx, (DefId, ty::SubstsRef<'tcx>, ty::ClosureKind)>,
373 ) -> QueryResult<'tcx> {
374 let (_, substs, expected_kind) = goal.predicate;
375 let found_kind = substs.as_closure().kind_ty().to_opt_closure_kind();
377 let Some(found_kind) = found_kind else {
378 return self.make_canonical_response(Certainty::AMBIGUOUS);
380 if found_kind.extends(expected_kind) {
381 self.make_canonical_response(Certainty::Yes)
387 fn compute_object_safe_goal(&mut self, trait_def_id: DefId) -> QueryResult<'tcx> {
388 if self.tcx().check_is_object_safe(trait_def_id) {
389 self.make_canonical_response(Certainty::Yes)
395 fn compute_well_formed_goal(
397 goal: Goal<'tcx, ty::GenericArg<'tcx>>,
398 ) -> QueryResult<'tcx> {
399 match crate::traits::wf::unnormalized_obligations(
404 Some(obligations) => self.evaluate_all_and_make_canonical_response(
405 obligations.into_iter().map(|o| o.into()).collect(),
407 None => self.make_canonical_response(Certainty::AMBIGUOUS),
412 impl<'tcx> EvalCtxt<'_, 'tcx> {
413 // Recursively evaluates a list of goals to completion, returning the certainty
414 // of all of the goals.
417 mut goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
418 ) -> Result<Certainty, NoSolution> {
419 let mut new_goals = Vec::new();
420 self.repeat_while_none(|this| {
421 let mut has_changed = Err(Certainty::Yes);
422 for goal in goals.drain(..) {
423 let (changed, certainty) = match this.evaluate_goal(goal) {
424 Ok(result) => result,
425 Err(NoSolution) => return Some(Err(NoSolution)),
429 has_changed = Ok(());
434 Certainty::Maybe(_) => {
435 new_goals.push(goal);
436 has_changed = has_changed.map_err(|c| c.unify_and(certainty));
443 mem::swap(&mut new_goals, &mut goals);
446 Err(certainty) => Some(Ok(certainty)),
451 // Recursively evaluates a list of goals to completion, making a query response.
453 // This is just a convenient way of calling [`EvalCtxt::evaluate_all`],
454 // then [`EvalCtxt::make_canonical_response`].
455 fn evaluate_all_and_make_canonical_response(
457 goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
458 ) -> QueryResult<'tcx> {
459 self.evaluate_all(goals).and_then(|certainty| self.make_canonical_response(certainty))
463 #[instrument(level = "debug", skip(infcx), ret)]
464 fn take_external_constraints<'tcx>(
465 infcx: &InferCtxt<'tcx>,
466 ) -> Result<ExternalConstraints<'tcx>, NoSolution> {
467 let region_obligations = infcx.take_registered_region_obligations();
468 let opaque_types = infcx.take_opaque_types_for_query_response();
469 Ok(ExternalConstraints {
470 // FIXME: Now that's definitely wrong :)
472 // Should also do the leak check here I think
473 regions: drop(region_obligations),
478 fn instantiate_canonical_query_response<'tcx>(
479 infcx: &InferCtxt<'tcx>,
480 original_values: &OriginalQueryValues<'tcx>,
481 response: CanonicalResponse<'tcx>,
483 let Ok(InferOk { value, obligations }) = infcx
484 .instantiate_query_response_and_region_obligations(
485 &ObligationCause::dummy(),
486 ty::ParamEnv::empty(),
488 &response.unchecked_map(|resp| QueryResponse {
489 var_values: resp.var_values,
490 region_constraints: QueryRegionConstraints::default(),
491 certainty: match resp.certainty {
492 Certainty::Yes => OldCertainty::Proven,
493 Certainty::Maybe(_) => OldCertainty::Ambiguous,
495 opaque_types: resp.external_constraints.opaque_types,
496 value: resp.certainty,
499 assert!(obligations.is_empty());
503 pub(super) fn response_no_constraints<'tcx>(
505 goal: Canonical<'tcx, impl Sized>,
506 certainty: Certainty,
507 ) -> QueryResult<'tcx> {
509 max_universe: goal.max_universe,
510 variables: goal.variables,
512 var_values: CanonicalVarValues::make_identity(tcx, goal.variables),
513 external_constraints: Default::default(),