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_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::infer::canonical::{Canonical, CanonicalVarValues};
28 use rustc_middle::ty::{self, Ty, TyCtxt};
29 use rustc_middle::ty::{RegionOutlivesPredicate, ToPredicate, TypeOutlivesPredicate};
30 use rustc_span::DUMMY_SP;
32 use crate::traits::ObligationCause;
34 use self::cache::response_no_constraints;
35 use self::infcx_ext::InferCtxtExt;
45 pub use fulfill::FulfillmentCtxt;
47 /// A goal is a statement, i.e. `predicate`, we want to prove
48 /// given some assumptions, i.e. `param_env`.
50 /// Most of the time the `param_env` contains the `where`-bounds of the function
51 /// we're currently typechecking while the `predicate` is some trait bound.
52 #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
53 pub struct Goal<'tcx, P> {
54 param_env: ty::ParamEnv<'tcx>,
58 impl<'tcx, P> Goal<'tcx, P> {
61 param_env: ty::ParamEnv<'tcx>,
62 predicate: impl ToPredicate<'tcx, P>,
64 Goal { param_env, predicate: predicate.to_predicate(tcx) }
67 /// Updates the goal to one with a different `predicate` but the same `param_env`.
68 fn with<Q>(self, tcx: TyCtxt<'tcx>, predicate: impl ToPredicate<'tcx, Q>) -> Goal<'tcx, Q> {
69 Goal { param_env: self.param_env, predicate: predicate.to_predicate(tcx) }
73 impl<'tcx, P> From<Obligation<'tcx, P>> for Goal<'tcx, P> {
74 fn from(obligation: Obligation<'tcx, P>) -> Goal<'tcx, P> {
75 Goal { param_env: obligation.param_env, predicate: obligation.predicate }
79 #[derive(Debug, PartialEq, Eq, Clone, Hash, TypeFoldable, TypeVisitable)]
80 pub struct Response<'tcx> {
81 pub var_values: CanonicalVarValues<'tcx>,
82 /// Additional constraints returned by this query.
83 pub external_constraints: ExternalConstraints<'tcx>,
84 pub certainty: Certainty,
87 #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
94 /// When proving multiple goals using **AND**, e.g. nested obligations for an impl,
95 /// use this function to unify the certainty of these goals
96 pub fn unify_and(self, other: Certainty) -> Certainty {
98 (Certainty::Yes, Certainty::Yes) => Certainty::Yes,
99 (Certainty::Yes, Certainty::Maybe(_)) => other,
100 (Certainty::Maybe(_), Certainty::Yes) => self,
101 (Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Overflow)) => {
102 Certainty::Maybe(MaybeCause::Overflow)
104 // If at least one of the goals is ambiguous, hide the overflow as the ambiguous goal
105 // may still result in failure.
106 (Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(_))
107 | (Certainty::Maybe(_), Certainty::Maybe(MaybeCause::Ambiguity)) => {
108 Certainty::Maybe(MaybeCause::Ambiguity)
114 /// Why we failed to evaluate a goal.
115 #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
116 pub enum MaybeCause {
117 /// We failed due to ambiguity. This ambiguity can either
118 /// be a true ambiguity, i.e. there are multiple different answers,
119 /// or we hit a case where we just don't bother, e.g. `?x: Trait` goals.
121 /// We gave up due to an overflow, most often by hitting the recursion limit.
125 /// Additional constraints returned on success.
126 #[derive(Debug, PartialEq, Eq, Clone, Hash, TypeFoldable, TypeVisitable, Default)]
127 pub struct ExternalConstraints<'tcx> {
128 // FIXME: implement this.
130 opaque_types: Vec<(Ty<'tcx>, Ty<'tcx>)>,
133 type CanonicalGoal<'tcx, T = ty::Predicate<'tcx>> = Canonical<'tcx, Goal<'tcx, T>>;
134 type CanonicalResponse<'tcx> = Canonical<'tcx, Response<'tcx>>;
135 /// The result of evaluating a canonical query.
137 /// FIXME: We use a different type than the existing canonical queries. This is because
138 /// we need to add a `Certainty` for `overflow` and may want to restructure this code without
139 /// having to worry about changes to currently used code. Once we've made progress on this
140 /// solver, merge the two responses again.
141 pub type QueryResult<'tcx> = Result<CanonicalResponse<'tcx>, NoSolution>;
143 pub trait TyCtxtExt<'tcx> {
144 fn evaluate_goal(self, goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx>;
147 impl<'tcx> TyCtxtExt<'tcx> for TyCtxt<'tcx> {
148 fn evaluate_goal(self, goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx> {
149 let mut cx = EvalCtxt::new(self);
150 cx.evaluate_canonical_goal(goal)
154 struct EvalCtxt<'tcx> {
157 provisional_cache: cache::ProvisionalCache<'tcx>,
158 overflow_data: overflow::OverflowData,
161 impl<'tcx> EvalCtxt<'tcx> {
162 fn new(tcx: TyCtxt<'tcx>) -> EvalCtxt<'tcx> {
165 provisional_cache: cache::ProvisionalCache::empty(),
166 overflow_data: overflow::OverflowData::new(tcx),
170 /// Recursively evaluates `goal`, returning whether any inference vars have
171 /// been constrained and the certainty of the result.
174 infcx: &InferCtxt<'tcx>,
175 goal: Goal<'tcx, ty::Predicate<'tcx>>,
176 ) -> Result<(bool, Certainty), NoSolution> {
177 let mut orig_values = OriginalQueryValues::default();
178 let canonical_goal = infcx.canonicalize_query(goal, &mut orig_values);
179 let canonical_response = self.evaluate_canonical_goal(canonical_goal)?;
181 true, // FIXME: check whether `var_values` are an identity substitution.
182 instantiate_canonical_query_response(infcx, &orig_values, canonical_response),
186 fn evaluate_canonical_goal(&mut self, goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx> {
187 match self.try_push_stack(goal) {
189 // Our goal is already on the stack, eager return.
190 Err(response) => return response,
193 // We may have to repeatedly recompute the goal in case of coinductive cycles,
194 // check out the `cache` module for more information.
196 // FIXME: Similar to `evaluate_all`, this has to check for overflow.
198 let result = self.compute_goal(goal);
200 // FIXME: `Response` should be `Copy`
201 if self.try_finalize_goal(goal, result.clone()) {
207 fn compute_goal(&mut self, canonical_goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx> {
208 // WARNING: We're looking at a canonical value without instantiating it here.
210 // We have to be incredibly careful to not change the order of bound variables or
211 // remove any. As we go from `Goal<'tcx, Predicate>` to `Goal` with the variants
212 // of `PredicateKind` this is the case and it is and faster than instantiating and
214 let Goal { param_env, predicate } = canonical_goal.value;
216 if let Some(kind) = predicate.kind().no_bound_vars_ignoring_escaping(self.tcx) {
218 ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => self.compute_trait_goal(
219 canonical_goal.unchecked_rebind(Goal { param_env, predicate }),
221 ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => self
222 .compute_projection_goal(
223 canonical_goal.unchecked_rebind(Goal { param_env, predicate }),
225 ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => self
226 .compute_type_outlives_goal(
227 canonical_goal.unchecked_rebind(Goal { param_env, predicate }),
229 ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => self
230 .compute_region_outlives_goal(
231 canonical_goal.unchecked_rebind(Goal { param_env, predicate }),
233 // FIXME: implement these predicates :)
234 ty::PredicateKind::WellFormed(_)
235 | ty::PredicateKind::ObjectSafe(_)
236 | ty::PredicateKind::ClosureKind(_, _, _)
237 | ty::PredicateKind::Subtype(_)
238 | ty::PredicateKind::Coerce(_)
239 | ty::PredicateKind::ConstEvaluatable(_)
240 | ty::PredicateKind::ConstEquate(_, _)
241 | ty::PredicateKind::TypeWellFormedFromEnv(_)
242 | ty::PredicateKind::Ambiguous => {
244 response_no_constraints(self.tcx, canonical_goal, Certainty::Yes)
248 let (infcx, goal, var_values) =
249 self.tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal);
250 let kind = infcx.replace_bound_vars_with_placeholders(goal.predicate.kind());
251 let goal = goal.with(self.tcx, ty::Binder::dummy(kind));
252 let (_, certainty) = self.evaluate_goal(&infcx, goal)?;
253 infcx.make_canonical_response(var_values, certainty)
257 fn compute_type_outlives_goal(
259 goal: CanonicalGoal<'tcx, TypeOutlivesPredicate<'tcx>>,
260 ) -> QueryResult<'tcx> {
262 response_no_constraints(self.tcx, goal, Certainty::Yes)
265 fn compute_region_outlives_goal(
267 goal: CanonicalGoal<'tcx, RegionOutlivesPredicate<'tcx>>,
268 ) -> QueryResult<'tcx> {
270 response_no_constraints(self.tcx, goal, Certainty::Yes)
274 impl<'tcx> EvalCtxt<'tcx> {
277 infcx: &InferCtxt<'tcx>,
278 mut goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
279 ) -> Result<Certainty, NoSolution> {
280 let mut new_goals = Vec::new();
281 self.repeat_while_none(|this| {
282 let mut has_changed = Err(Certainty::Yes);
283 for goal in goals.drain(..) {
284 let (changed, certainty) = match this.evaluate_goal(infcx, goal) {
285 Ok(result) => result,
286 Err(NoSolution) => return Some(Err(NoSolution)),
290 has_changed = Ok(());
295 Certainty::Maybe(_) => {
296 new_goals.push(goal);
297 has_changed = has_changed.map_err(|c| c.unify_and(certainty));
304 mem::swap(&mut new_goals, &mut goals);
307 Err(certainty) => Some(Ok(certainty)),
313 fn instantiate_canonical_query_response<'tcx>(
314 infcx: &InferCtxt<'tcx>,
315 original_values: &OriginalQueryValues<'tcx>,
316 response: CanonicalResponse<'tcx>,
318 let Ok(InferOk { value, obligations }) = infcx
319 .instantiate_query_response_and_region_obligations(
320 &ObligationCause::dummy(),
321 ty::ParamEnv::empty(),
323 &response.unchecked_map(|resp| QueryResponse {
324 var_values: resp.var_values,
325 region_constraints: QueryRegionConstraints::default(),
326 certainty: match resp.certainty {
327 Certainty::Yes => OldCertainty::Proven,
328 Certainty::Maybe(_) => OldCertainty::Ambiguous,
330 opaque_types: resp.external_constraints.opaque_types,
331 value: resp.certainty,
334 assert!(obligations.is_empty());