1//! The next-generation trait solver, currently still WIP.
2//!
3//! As a user of rust, you can use `-Znext-solver` to enable the new trait solver.
4//!
5//! As a developer of rustc, you shouldn't be using the new trait
6//! solver without asking the trait-system-refactor-initiative, but it can
7//! be enabled with `InferCtxtBuilder::with_next_trait_solver`. This will
8//! ensure that trait solving using that inference context will be routed
9//! to the new trait solver.
10//!
11//! For a high-level overview of how this solver works, check out the relevant
12//! section of the rustc-dev-guide.
1314mod alias_relate;
15mod assembly;
16mod effect_goals;
17mod eval_ctxt;
18pub mod inspect;
19mod normalizes_to;
20mod project_goals;
21mod search_graph;
22mod trait_goals;
2324use rustc_type_ir::inherent::*;
25pub use rustc_type_ir::solve::*;
26use rustc_type_ir::{selfas ty, Interner, TypingMode};
27use tracing::instrument;
2829pub use self::eval_ctxt::{EvalCtxt, GenerateProofTree, SolverDelegateEvalExt};
30use crate::delegate::SolverDelegate;
3132/// How many fixpoint iterations we should attempt inside of the solver before bailing
33/// with overflow.
34///
35/// We previously used `cx.recursion_limit().0.checked_ilog2().unwrap_or(0)` for this.
36/// However, it feels unlikely that uncreasing the recursion limit by a power of two
37/// to get one more itereation is every useful or desirable. We now instead used a constant
38/// here. If there ever ends up some use-cases where a bigger number of fixpoint iterations
39/// is required, we can add a new attribute for that or revert this to be dependant on the
40/// recursion limit again. However, this feels very unlikely.
41const FIXPOINT_STEP_LIMIT: usize = 8;
4243#[derive(Debug, Copy, Clone, PartialEq, Eq)]
44enum GoalEvaluationKind {
45 Root,
46 Nested,
47}
4849/// Whether evaluating this goal ended up changing the
50/// inference state.
51#[derive(PartialEq, Eq, Debug, Hash, Clone, Copy)]
52pub enum HasChanged {
53 Yes,
54 No,
55}
5657// FIXME(trait-system-refactor-initiative#117): we don't detect whether a response
58// ended up pulling down any universes.
59fn has_no_inference_or_external_constraints<I: Interner>(
60 response: ty::Canonical<I, Response<I>>,
61) -> bool {
62let ExternalConstraintsData {
63ref region_constraints,
64ref opaque_types,
65ref normalization_nested_goals,
66 } = *response.value.external_constraints;
67response.value.var_values.is_identity()
68 && region_constraints.is_empty()
69 && opaque_types.is_empty()
70 && normalization_nested_goals.is_empty()
71}
7273impl<'a, D, I> EvalCtxt<'a, D>
74where
75D: SolverDelegate<Interner = I>,
76 I: Interner,
77{
78#[instrument(level = "trace", skip(self))]
79fn compute_type_outlives_goal(
80&mut self,
81 goal: Goal<I, ty::OutlivesPredicate<I, I::Ty>>,
82 ) -> QueryResult<I> {
83let ty::OutlivesPredicate(ty, lt) = goal.predicate;
84self.register_ty_outlives(ty, lt);
85self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
86 }
8788#[instrument(level = "trace", skip(self))]
89fn compute_region_outlives_goal(
90&mut self,
91 goal: Goal<I, ty::OutlivesPredicate<I, I::Region>>,
92 ) -> QueryResult<I> {
93let ty::OutlivesPredicate(a, b) = goal.predicate;
94self.register_region_outlives(a, b);
95self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
96 }
9798#[instrument(level = "trace", skip(self))]
99fn compute_coerce_goal(&mut self, goal: Goal<I, ty::CoercePredicate<I>>) -> QueryResult<I> {
100self.compute_subtype_goal(Goal {
101 param_env: goal.param_env,
102 predicate: ty::SubtypePredicate {
103 a_is_expected: false,
104 a: goal.predicate.a,
105 b: goal.predicate.b,
106 },
107 })
108 }
109110#[instrument(level = "trace", skip(self))]
111fn compute_subtype_goal(&mut self, goal: Goal<I, ty::SubtypePredicate<I>>) -> QueryResult<I> {
112if goal.predicate.a.is_ty_var() && goal.predicate.b.is_ty_var() {
113self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
114 } else {
115self.sub(goal.param_env, goal.predicate.a, goal.predicate.b)?;
116self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
117 }
118 }
119120fn compute_dyn_compatible_goal(&mut self, trait_def_id: I::DefId) -> QueryResult<I> {
121if self.cx().trait_is_dyn_compatible(trait_def_id) {
122self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
123 } else {
124Err(NoSolution)
125 }
126 }
127128#[instrument(level = "trace", skip(self))]
129fn compute_well_formed_goal(&mut self, goal: Goal<I, I::GenericArg>) -> QueryResult<I> {
130match self.well_formed_goals(goal.param_env, goal.predicate) {
131Some(goals) => {
132self.add_goals(GoalSource::Misc, goals);
133self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
134 }
135None => self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS),
136 }
137 }
138139#[instrument(level = "trace", skip(self))]
140fn compute_const_evaluatable_goal(
141&mut self,
142Goal { param_env, predicate: ct }: Goal<I, I::Const>,
143 ) -> QueryResult<I> {
144match ct.kind() {
145 ty::ConstKind::Unevaluated(uv) => {
146// We never return `NoSolution` here as `evaluate_const` emits an
147 // error itself when failing to evaluate, so emitting an additional fulfillment
148 // error in that case is unnecessary noise. This may change in the future once
149 // evaluation failures are allowed to impact selection, e.g. generic const
150 // expressions in impl headers or `where`-clauses.
151152 // FIXME(generic_const_exprs): Implement handling for generic
153 // const expressions here.
154if let Some(_normalized) = self.evaluate_const(param_env, uv) {
155self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
156 } else {
157self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
158 }
159 }
160 ty::ConstKind::Infer(_) => {
161self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
162 }
163 ty::ConstKind::Placeholder(_) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => {
164self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
165 }
166// We can freely ICE here as:
167 // - `Param` gets replaced with a placeholder during canonicalization
168 // - `Bound` cannot exist as we don't have a binder around the self Type
169 // - `Expr` is part of `feature(generic_const_exprs)` and is not implemented yet
170ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Expr(_) => {
171panic!("unexpected const kind: {:?}", ct)
172 }
173 }
174 }
175176#[instrument(level = "trace", skip(self), ret)]
177fn compute_const_arg_has_type_goal(
178&mut self,
179 goal: Goal<I, (I::Const, I::Ty)>,
180 ) -> QueryResult<I> {
181let (ct, ty) = goal.predicate;
182183let ct_ty = match ct.kind() {
184 ty::ConstKind::Infer(_) => {
185return self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
186 }
187 ty::ConstKind::Error(_) => {
188return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
189 }
190 ty::ConstKind::Unevaluated(uv) => {
191self.cx().type_of(uv.def).instantiate(self.cx(), uv.args)
192 }
193 ty::ConstKind::Expr(_) => unimplemented!(
194"`feature(generic_const_exprs)` is not supported in the new trait solver"
195),
196 ty::ConstKind::Param(_) => {
197unreachable!("`ConstKind::Param` should have been canonicalized to `Placeholder`")
198 }
199 ty::ConstKind::Bound(_, _) => panic!("escaping bound vars in {:?}", ct),
200 ty::ConstKind::Value(cv) => cv.ty(),
201 ty::ConstKind::Placeholder(placeholder) => {
202self.cx().find_const_ty_from_env(goal.param_env, placeholder)
203 }
204 };
205206self.eq(goal.param_env, ct_ty, ty)?;
207self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
208 }
209}
210211impl<D, I> EvalCtxt<'_, D>
212where
213D: SolverDelegate<Interner = I>,
214 I: Interner,
215{
216/// Try to merge multiple possible ways to prove a goal, if that is not possible returns `None`.
217 ///
218 /// In this case we tend to flounder and return ambiguity by calling `[EvalCtxt::flounder]`.
219#[instrument(level = "trace", skip(self), ret)]
220fn try_merge_responses(
221&mut self,
222 responses: &[CanonicalResponse<I>],
223 ) -> Option<CanonicalResponse<I>> {
224if responses.is_empty() {
225return None;
226 }
227228// FIXME(-Znext-solver): We should instead try to find a `Certainty::Yes` response with
229 // a subset of the constraints that all the other responses have.
230let one = responses[0];
231if responses[1..].iter().all(|&resp| resp == one) {
232return Some(one);
233 }
234235 responses
236 .iter()
237 .find(|response| {
238 response.value.certainty == Certainty::Yes
239 && has_no_inference_or_external_constraints(**response)
240 })
241 .copied()
242 }
243244fn bail_with_ambiguity(&mut self, responses: &[CanonicalResponse<I>]) -> CanonicalResponse<I> {
245debug_assert!(!responses.is_empty());
246if let Certainty::Maybe(maybe_cause) =
247responses.iter().fold(Certainty::AMBIGUOUS, |certainty, response| {
248certainty.unify_with(response.value.certainty)
249 })
250 {
251self.make_ambiguous_response_no_constraints(maybe_cause)
252 } else {
253panic!("expected flounder response to be ambiguous")
254 }
255 }
256257/// If we fail to merge responses we flounder and return overflow or ambiguity.
258#[instrument(level = "trace", skip(self), ret)]
259fn flounder(&mut self, responses: &[CanonicalResponse<I>]) -> QueryResult<I> {
260if responses.is_empty() {
261return Err(NoSolution);
262 } else {
263Ok(self.bail_with_ambiguity(responses))
264 }
265 }
266267/// Normalize a type for when it is structurally matched on.
268 ///
269 /// This function is necessary in nearly all cases before matching on a type.
270 /// Not doing so is likely to be incomplete and therefore unsound during
271 /// coherence.
272#[instrument(level = "trace", skip(self, param_env), ret)]
273fn structurally_normalize_ty(
274&mut self,
275 param_env: I::ParamEnv,
276 ty: I::Ty,
277 ) -> Result<I::Ty, NoSolution> {
278self.structurally_normalize_term(param_env, ty.into()).map(|term| term.expect_ty())
279 }
280281/// Normalize a const for when it is structurally matched on, or more likely
282 /// when it needs `.try_to_*` called on it (e.g. to turn it into a usize).
283 ///
284 /// This function is necessary in nearly all cases before matching on a const.
285 /// Not doing so is likely to be incomplete and therefore unsound during
286 /// coherence.
287#[instrument(level = "trace", skip(self, param_env), ret)]
288fn structurally_normalize_const(
289&mut self,
290 param_env: I::ParamEnv,
291 ct: I::Const,
292 ) -> Result<I::Const, NoSolution> {
293self.structurally_normalize_term(param_env, ct.into()).map(|term| term.expect_const())
294 }
295296/// Normalize a term for when it is structurally matched on.
297 ///
298 /// This function is necessary in nearly all cases before matching on a ty/const.
299 /// Not doing so is likely to be incomplete and therefore unsound during coherence.
300fn structurally_normalize_term(
301&mut self,
302 param_env: I::ParamEnv,
303 term: I::Term,
304 ) -> Result<I::Term, NoSolution> {
305if let Some(_) = term.to_alias_term() {
306let normalized_term = self.next_term_infer_of_kind(term);
307let alias_relate_goal = Goal::new(
308self.cx(),
309param_env,
310ty::PredicateKind::AliasRelate(
311term,
312normalized_term,
313ty::AliasRelationDirection::Equate,
314 ),
315 );
316self.add_goal(GoalSource::Misc, alias_relate_goal);
317self.try_evaluate_added_goals()?;
318Ok(self.resolve_vars_if_possible(normalized_term))
319 } else {
320Ok(term)
321 }
322 }
323324fn opaque_type_is_rigid(&self, def_id: I::DefId) -> bool {
325match self.typing_mode() {
326// Opaques are never rigid outside of analysis mode.
327TypingMode::Coherence | TypingMode::PostAnalysis => false,
328// During analysis, opaques are rigid unless they may be defined by
329 // the current body.
330TypingMode::Analysis { defining_opaque_types: non_rigid_opaques }
331 | TypingMode::PostBorrowckAnalysis { defined_opaque_types: non_rigid_opaques } => {
332 !def_id.as_local().is_some_and(|def_id| non_rigid_opaques.contains(&def_id))
333 }
334 }
335 }
336}
337338fn response_no_constraints_raw<I: Interner>(
339 cx: I,
340 max_universe: ty::UniverseIndex,
341 variables: I::CanonicalVars,
342 certainty: Certainty,
343) -> CanonicalResponse<I> {
344ty::Canonical {
345max_universe,
346variables,
347 value: Response {
348 var_values: ty::CanonicalVarValues::make_identity(cx, variables),
349// FIXME: maybe we should store the "no response" version in cx, like
350 // we do for cx.types and stuff.
351external_constraints: cx.mk_external_constraints(ExternalConstraintsData::default()),
352certainty,
353 },
354 }
355}