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: `CanonicalVarValues` should be interned and `Copy`.
18 // FIXME: uses of `infcx.at` need to enable deferred projection equality once that's implemented.
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,
33 use rustc_span::DUMMY_SP;
35 use crate::traits::ObligationCause;
44 pub use fulfill::FulfillmentCtxt;
46 /// A goal is a statement, i.e. `predicate`, we want to prove
47 /// given some assumptions, i.e. `param_env`.
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>,
57 impl<'tcx, P> Goal<'tcx, P> {
60 param_env: ty::ParamEnv<'tcx>,
61 predicate: impl ToPredicate<'tcx, P>,
63 Goal { param_env, predicate: predicate.to_predicate(tcx) }
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) }
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 }
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,
86 #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
93 pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity);
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 {
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)
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)
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.
122 /// We gave up due to an overflow, most often by hitting the recursion limit.
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.
131 opaque_types: Vec<(Ty<'tcx>, Ty<'tcx>)>,
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.
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>;
144 pub trait TyCtxtExt<'tcx> {
145 fn evaluate_goal(self, goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx>;
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)
155 struct EvalCtxt<'a, 'tcx> {
156 infcx: &'a InferCtxt<'tcx>,
157 var_values: CanonicalVarValues<'tcx>,
159 search_graph: &'a mut search_graph::SearchGraph<'tcx>,
162 impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
163 fn tcx(&self) -> TyCtxt<'tcx> {
167 /// Creates a new evaluation context outside of the trait solver.
169 /// With this solver making a canonical response doesn't make much sense.
170 /// The `search_graph` for this solver has to be completely empty.
171 fn new_outside_solver(
172 infcx: &'a InferCtxt<'tcx>,
173 search_graph: &'a mut search_graph::SearchGraph<'tcx>,
174 ) -> EvalCtxt<'a, 'tcx> {
175 assert!(search_graph.is_empty());
176 EvalCtxt { infcx, var_values: CanonicalVarValues::dummy(), search_graph }
179 #[instrument(level = "debug", skip(tcx, search_graph), ret)]
180 fn evaluate_canonical_goal(
182 search_graph: &'a mut search_graph::SearchGraph<'tcx>,
183 canonical_goal: CanonicalGoal<'tcx>,
184 ) -> QueryResult<'tcx> {
185 match search_graph.try_push_stack(tcx, canonical_goal) {
187 // Our goal is already on the stack, eager return.
188 Err(response) => return response,
191 // We may have to repeatedly recompute the goal in case of coinductive cycles,
192 // check out the `cache` module for more information.
194 // FIXME: Similar to `evaluate_all`, this has to check for overflow.
196 let (ref infcx, goal, var_values) =
197 tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal);
198 let mut ecx = EvalCtxt { infcx, var_values, search_graph };
199 let result = ecx.compute_goal(goal);
201 // FIXME: `Response` should be `Copy`
202 if search_graph.try_finalize_goal(tcx, canonical_goal, result.clone()) {
208 fn make_canonical_response(&self, certainty: Certainty) -> QueryResult<'tcx> {
209 let external_constraints = take_external_constraints(self.infcx)?;
211 Ok(self.infcx.canonicalize_response(Response {
212 var_values: self.var_values.clone(),
213 external_constraints,
218 /// Recursively evaluates `goal`, returning whether any inference vars have
219 /// been constrained and the certainty of the result.
222 goal: Goal<'tcx, ty::Predicate<'tcx>>,
223 ) -> Result<(bool, Certainty), NoSolution> {
224 let mut orig_values = OriginalQueryValues::default();
225 let canonical_goal = self.infcx.canonicalize_query(goal, &mut orig_values);
226 let canonical_response =
227 EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
229 !canonical_response.value.var_values.is_identity(),
230 instantiate_canonical_query_response(self.infcx, &orig_values, canonical_response),
234 fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> {
235 let Goal { param_env, predicate } = goal;
236 let kind = predicate.kind();
237 if let Some(kind) = kind.no_bound_vars() {
239 ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
240 self.compute_trait_goal(Goal { param_env, predicate })
242 ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => {
243 self.compute_projection_goal(Goal { param_env, predicate })
245 ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => {
246 self.compute_type_outlives_goal(Goal { param_env, predicate })
248 ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => {
249 self.compute_region_outlives_goal(Goal { param_env, predicate })
251 ty::PredicateKind::Subtype(predicate) => {
252 self.compute_subtype_goal(Goal { param_env, predicate })
254 ty::PredicateKind::Coerce(predicate) => {
255 self.compute_coerce_goal(Goal { param_env, predicate })
257 ty::PredicateKind::ClosureKind(def_id, substs, kind) => self
258 .compute_closure_kind_goal(Goal {
260 predicate: (def_id, substs, kind),
262 ty::PredicateKind::Ambiguous => self.make_canonical_response(Certainty::AMBIGUOUS),
263 // FIXME: implement these predicates :)
264 ty::PredicateKind::WellFormed(_)
265 | ty::PredicateKind::ObjectSafe(_)
266 | ty::PredicateKind::ConstEvaluatable(_)
267 | ty::PredicateKind::ConstEquate(_, _) => {
268 self.make_canonical_response(Certainty::Yes)
270 ty::PredicateKind::TypeWellFormedFromEnv(..) => {
271 bug!("TypeWellFormedFromEnv is only used for Chalk")
275 let kind = self.infcx.replace_bound_vars_with_placeholders(kind);
276 let goal = goal.with(self.tcx(), ty::Binder::dummy(kind));
277 let (_, certainty) = self.evaluate_goal(goal)?;
278 self.make_canonical_response(certainty)
282 fn compute_type_outlives_goal(
284 _goal: Goal<'tcx, TypeOutlivesPredicate<'tcx>>,
285 ) -> QueryResult<'tcx> {
286 self.make_canonical_response(Certainty::Yes)
289 fn compute_region_outlives_goal(
291 _goal: Goal<'tcx, RegionOutlivesPredicate<'tcx>>,
292 ) -> QueryResult<'tcx> {
293 self.make_canonical_response(Certainty::Yes)
296 fn compute_coerce_goal(
298 goal: Goal<'tcx, CoercePredicate<'tcx>>,
299 ) -> QueryResult<'tcx> {
300 self.compute_subtype_goal(Goal {
301 param_env: goal.param_env,
302 predicate: SubtypePredicate {
303 a_is_expected: false,
310 fn compute_subtype_goal(
312 goal: Goal<'tcx, SubtypePredicate<'tcx>>,
313 ) -> QueryResult<'tcx> {
314 if goal.predicate.a.is_ty_var() && goal.predicate.b.is_ty_var() {
315 // FIXME: Do we want to register a subtype relation between these vars?
316 // That won't actually reflect in the query response, so it seems moot.
317 self.make_canonical_response(Certainty::AMBIGUOUS)
319 self.infcx.probe(|_| {
320 let InferOk { value: (), obligations } = self
322 .at(&ObligationCause::dummy(), goal.param_env)
323 .sub(goal.predicate.a, goal.predicate.b)?;
324 self.evaluate_all_and_make_canonical_response(
325 obligations.into_iter().map(|pred| pred.into()).collect(),
331 fn compute_closure_kind_goal(
333 goal: Goal<'tcx, (DefId, ty::SubstsRef<'tcx>, ty::ClosureKind)>,
334 ) -> QueryResult<'tcx> {
335 let (_, substs, expected_kind) = goal.predicate;
336 let found_kind = substs.as_closure().kind_ty().to_opt_closure_kind();
338 let Some(found_kind) = found_kind else {
339 return self.make_canonical_response(Certainty::AMBIGUOUS);
341 if found_kind.extends(expected_kind) {
342 self.make_canonical_response(Certainty::Yes)
349 impl<'tcx> EvalCtxt<'_, 'tcx> {
352 mut goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
353 ) -> Result<Certainty, NoSolution> {
354 let mut new_goals = Vec::new();
355 self.repeat_while_none(|this| {
356 let mut has_changed = Err(Certainty::Yes);
357 for goal in goals.drain(..) {
358 let (changed, certainty) = match this.evaluate_goal(goal) {
359 Ok(result) => result,
360 Err(NoSolution) => return Some(Err(NoSolution)),
364 has_changed = Ok(());
369 Certainty::Maybe(_) => {
370 new_goals.push(goal);
371 has_changed = has_changed.map_err(|c| c.unify_and(certainty));
378 mem::swap(&mut new_goals, &mut goals);
381 Err(certainty) => Some(Ok(certainty)),
386 fn evaluate_all_and_make_canonical_response(
388 goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
389 ) -> QueryResult<'tcx> {
390 self.evaluate_all(goals).and_then(|certainty| self.make_canonical_response(certainty))
394 #[instrument(level = "debug", skip(infcx), ret)]
395 fn take_external_constraints<'tcx>(
396 infcx: &InferCtxt<'tcx>,
397 ) -> Result<ExternalConstraints<'tcx>, NoSolution> {
398 let region_obligations = infcx.take_registered_region_obligations();
399 let opaque_types = infcx.take_opaque_types_for_query_response();
400 Ok(ExternalConstraints {
401 // FIXME: Now that's definitely wrong :)
403 // Should also do the leak check here I think
404 regions: drop(region_obligations),
409 fn instantiate_canonical_query_response<'tcx>(
410 infcx: &InferCtxt<'tcx>,
411 original_values: &OriginalQueryValues<'tcx>,
412 response: CanonicalResponse<'tcx>,
414 let Ok(InferOk { value, obligations }) = infcx
415 .instantiate_query_response_and_region_obligations(
416 &ObligationCause::dummy(),
417 ty::ParamEnv::empty(),
419 &response.unchecked_map(|resp| QueryResponse {
420 var_values: resp.var_values,
421 region_constraints: QueryRegionConstraints::default(),
422 certainty: match resp.certainty {
423 Certainty::Yes => OldCertainty::Proven,
424 Certainty::Maybe(_) => OldCertainty::Ambiguous,
426 opaque_types: resp.external_constraints.opaque_types,
427 value: resp.certainty,
430 assert!(obligations.is_empty());
434 pub(super) fn response_no_constraints<'tcx>(
436 goal: Canonical<'tcx, impl Sized>,
437 certainty: Certainty,
438 ) -> QueryResult<'tcx> {
439 let var_values = goal
443 .map(|(i, info)| match info.kind {
444 CanonicalVarKind::Ty(_) | CanonicalVarKind::PlaceholderTy(_) => {
445 tcx.mk_ty(ty::Bound(ty::INNERMOST, ty::BoundVar::from_usize(i).into())).into()
447 CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => {
448 let br = ty::BoundRegion {
449 var: ty::BoundVar::from_usize(i),
450 kind: ty::BrAnon(i as u32, None),
452 tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)).into()
454 CanonicalVarKind::Const(_, ty) | CanonicalVarKind::PlaceholderConst(_, ty) => tcx
455 .mk_const(ty::ConstKind::Bound(ty::INNERMOST, ty::BoundVar::from_usize(i)), ty)
461 max_universe: goal.max_universe,
462 variables: goal.variables,
464 var_values: CanonicalVarValues { var_values },
465 external_constraints: Default::default(),