rustc_trait_selection/solve/
fulfill.rs1use std::marker::PhantomData;
2use std::mem;
3use std::ops::ControlFlow;
4
5use rustc_data_structures::thinvec::ExtractIf;
6use rustc_hir::def_id::LocalDefId;
7use rustc_infer::infer::InferCtxt;
8use rustc_infer::traits::query::NoSolution;
9use rustc_infer::traits::{
10 FromSolverError, PredicateObligation, PredicateObligations, TraitEngine,
11};
12use rustc_middle::ty::{
13 self, DelayedSet, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
14 TypingMode,
15};
16use rustc_next_trait_solver::delegate::SolverDelegate as _;
17use rustc_next_trait_solver::solve::{
18 GoalEvaluation, GoalStalledOn, HasChanged, SolverDelegateEvalExt as _,
19};
20use rustc_span::Span;
21use thin_vec::ThinVec;
22use tracing::instrument;
23
24use self::derive_errors::*;
25use super::Certainty;
26use super::delegate::SolverDelegate;
27use super::inspect::{self, ProofTreeInferCtxtExt};
28use crate::traits::{FulfillmentError, ScrubbedTraitError};
29
30mod derive_errors;
31
32type PendingObligations<'tcx> =
34 ThinVec<(PredicateObligation<'tcx>, Option<GoalStalledOn<TyCtxt<'tcx>>>)>;
35
36pub struct FulfillmentCtxt<'tcx, E: 'tcx> {
48 obligations: ObligationStorage<'tcx>,
49
50 usable_in_snapshot: usize,
55 _errors: PhantomData<E>,
56}
57
58#[derive(Default, Debug)]
59struct ObligationStorage<'tcx> {
60 overflowed: Vec<PredicateObligation<'tcx>>,
66 pending: PendingObligations<'tcx>,
67}
68
69impl<'tcx> ObligationStorage<'tcx> {
70 fn register(
71 &mut self,
72 obligation: PredicateObligation<'tcx>,
73 stalled_on: Option<GoalStalledOn<TyCtxt<'tcx>>>,
74 ) {
75 self.pending.push((obligation, stalled_on));
76 }
77
78 fn has_pending_obligations(&self) -> bool {
79 !self.pending.is_empty() || !self.overflowed.is_empty()
80 }
81
82 fn clone_pending(&self) -> PredicateObligations<'tcx> {
83 let mut obligations: PredicateObligations<'tcx> =
84 self.pending.iter().map(|(o, _)| o.clone()).collect();
85 obligations.extend(self.overflowed.iter().cloned());
86 obligations
87 }
88
89 fn drain_pending(
90 &mut self,
91 cond: impl Fn(&PredicateObligation<'tcx>) -> bool,
92 ) -> PendingObligations<'tcx> {
93 let (unstalled, pending) =
94 mem::take(&mut self.pending).into_iter().partition(|(o, _)| cond(o));
95 self.pending = pending;
96 unstalled
97 }
98
99 fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'tcx>) {
100 infcx.probe(|_| {
101 self.overflowed.extend(
108 ExtractIf::new(&mut self.pending, |(o, stalled_on)| {
109 let goal = o.as_goal();
110 let result = <&SolverDelegate<'tcx>>::from(infcx).evaluate_root_goal(
111 goal,
112 o.cause.span,
113 stalled_on.take(),
114 );
115 matches!(result, Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. }))
116 })
117 .map(|(o, _)| o),
118 );
119 })
120 }
121}
122
123impl<'tcx, E: 'tcx> FulfillmentCtxt<'tcx, E> {
124 pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx, E> {
125 assert!(
126 infcx.next_trait_solver(),
127 "new trait solver fulfillment context created when \
128 infcx is set up for old trait solver"
129 );
130 FulfillmentCtxt {
131 obligations: Default::default(),
132 usable_in_snapshot: infcx.num_open_snapshots(),
133 _errors: PhantomData,
134 }
135 }
136
137 fn inspect_evaluated_obligation(
138 &self,
139 infcx: &InferCtxt<'tcx>,
140 obligation: &PredicateObligation<'tcx>,
141 result: &Result<GoalEvaluation<TyCtxt<'tcx>>, NoSolution>,
142 ) {
143 if let Some(inspector) = infcx.obligation_inspector.get() {
144 let result = match result {
145 Ok(GoalEvaluation { certainty, .. }) => Ok(*certainty),
146 Err(NoSolution) => Err(NoSolution),
147 };
148 (inspector)(infcx, &obligation, result);
149 }
150 }
151}
152
153impl<'tcx, E> TraitEngine<'tcx, E> for FulfillmentCtxt<'tcx, E>
154where
155 E: FromSolverError<'tcx, NextSolverError<'tcx>>,
156{
157 #[instrument(level = "trace", skip(self, infcx))]
158 fn register_predicate_obligation(
159 &mut self,
160 infcx: &InferCtxt<'tcx>,
161 obligation: PredicateObligation<'tcx>,
162 ) {
163 assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
164 self.obligations.register(obligation, None);
165 }
166
167 fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> {
168 self.obligations
169 .pending
170 .drain(..)
171 .map(|(obligation, _)| NextSolverError::Ambiguity(obligation))
172 .chain(
173 self.obligations
174 .overflowed
175 .drain(..)
176 .map(|obligation| NextSolverError::Overflow(obligation)),
177 )
178 .map(|e| E::from_solver_error(infcx, e))
179 .collect()
180 }
181
182 fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> {
183 assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
184 let mut errors = Vec::new();
185 loop {
186 let mut any_changed = false;
187 for (mut obligation, stalled_on) in self.obligations.drain_pending(|_| true) {
188 if !infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) {
189 self.obligations.on_fulfillment_overflow(infcx);
190 return errors;
192 }
193
194 let goal = obligation.as_goal();
195 let delegate = <&SolverDelegate<'tcx>>::from(infcx);
196 if let Some(certainty) =
197 delegate.compute_goal_fast_path(goal, obligation.cause.span)
198 {
199 match certainty {
200 Certainty::Yes => {}
207 Certainty::Maybe(_) => {
208 self.obligations.register(obligation, None);
209 }
210 }
211 continue;
212 }
213
214 let result = delegate.evaluate_root_goal(goal, obligation.cause.span, stalled_on);
215 self.inspect_evaluated_obligation(infcx, &obligation, &result);
216 let GoalEvaluation { goal, certainty, has_changed, stalled_on } = match result {
217 Ok(result) => result,
218 Err(NoSolution) => {
219 errors.push(E::from_solver_error(
220 infcx,
221 NextSolverError::TrueError(obligation),
222 ));
223 continue;
224 }
225 };
226
227 obligation.predicate = goal.predicate;
231 if has_changed == HasChanged::Yes {
232 obligation.recursion_depth += 1;
239 any_changed = true;
240 }
241
242 match certainty {
243 Certainty::Yes => {
244 if infcx.in_hir_typeck && obligation.has_non_region_infer() {
256 infcx.push_hir_typeck_potentially_region_dependent_goal(obligation);
257 }
258 }
259 Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on),
260 }
261 }
262
263 if !any_changed {
264 break;
265 }
266 }
267
268 errors
269 }
270
271 fn has_pending_obligations(&self) -> bool {
272 self.obligations.has_pending_obligations()
273 }
274
275 fn pending_obligations(&self) -> PredicateObligations<'tcx> {
276 self.obligations.clone_pending()
277 }
278
279 fn drain_stalled_obligations_for_coroutines(
280 &mut self,
281 infcx: &InferCtxt<'tcx>,
282 ) -> PredicateObligations<'tcx> {
283 let stalled_coroutines = match infcx.typing_mode() {
284 TypingMode::Analysis { defining_opaque_types_and_generators } => {
285 defining_opaque_types_and_generators
286 }
287 TypingMode::Coherence
288 | TypingMode::Borrowck { defining_opaque_types: _ }
289 | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ }
290 | TypingMode::PostAnalysis => return Default::default(),
291 };
292
293 if stalled_coroutines.is_empty() {
294 return Default::default();
295 }
296
297 self.obligations
298 .drain_pending(|obl| {
299 infcx.probe(|_| {
300 infcx
301 .visit_proof_tree(
302 obl.as_goal(),
303 &mut StalledOnCoroutines {
304 stalled_coroutines,
305 span: obl.cause.span,
306 cache: Default::default(),
307 },
308 )
309 .is_break()
310 })
311 })
312 .into_iter()
313 .map(|(o, _)| o)
314 .collect()
315 }
316}
317
318pub struct StalledOnCoroutines<'tcx> {
327 pub stalled_coroutines: &'tcx ty::List<LocalDefId>,
328 pub span: Span,
329 pub cache: DelayedSet<Ty<'tcx>>,
330}
331
332impl<'tcx> inspect::ProofTreeVisitor<'tcx> for StalledOnCoroutines<'tcx> {
333 type Result = ControlFlow<()>;
334
335 fn span(&self) -> rustc_span::Span {
336 self.span
337 }
338
339 fn visit_goal(&mut self, inspect_goal: &super::inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
340 inspect_goal.goal().predicate.visit_with(self)?;
341
342 if let Some(candidate) = inspect_goal.unique_applicable_candidate() {
343 candidate.visit_nested_no_probe(self)
344 } else {
345 ControlFlow::Continue(())
346 }
347 }
348}
349
350impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for StalledOnCoroutines<'tcx> {
351 type Result = ControlFlow<()>;
352
353 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
354 if !self.cache.insert(ty) {
355 return ControlFlow::Continue(());
356 }
357
358 if let ty::Coroutine(def_id, _) = *ty.kind()
359 && def_id.as_local().is_some_and(|def_id| self.stalled_coroutines.contains(&def_id))
360 {
361 ControlFlow::Break(())
362 } else if ty.has_coroutines() {
363 ty.super_visit_with(self)
364 } else {
365 ControlFlow::Continue(())
366 }
367 }
368}
369
370pub enum NextSolverError<'tcx> {
371 TrueError(PredicateObligation<'tcx>),
372 Ambiguity(PredicateObligation<'tcx>),
373 Overflow(PredicateObligation<'tcx>),
374}
375
376impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for FulfillmentError<'tcx> {
377 fn from_solver_error(infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
378 match error {
379 NextSolverError::TrueError(obligation) => {
380 fulfillment_error_for_no_solution(infcx, obligation)
381 }
382 NextSolverError::Ambiguity(obligation) => {
383 fulfillment_error_for_stalled(infcx, obligation)
384 }
385 NextSolverError::Overflow(obligation) => {
386 fulfillment_error_for_overflow(infcx, obligation)
387 }
388 }
389 }
390}
391
392impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<'tcx> {
393 fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
394 match error {
395 NextSolverError::TrueError(_) => ScrubbedTraitError::TrueError,
396 NextSolverError::Ambiguity(_) | NextSolverError::Overflow(_) => {
397 ScrubbedTraitError::Ambiguity
398 }
399 }
400 }
401}