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
256 && (obligation.has_non_region_infer() || obligation.has_free_regions())
257 {
258 infcx.push_hir_typeck_potentially_region_dependent_goal(obligation);
259 }
260 }
261 Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on),
262 }
263 }
264
265 if !any_changed {
266 break;
267 }
268 }
269
270 errors
271 }
272
273 fn has_pending_obligations(&self) -> bool {
274 self.obligations.has_pending_obligations()
275 }
276
277 fn pending_obligations(&self) -> PredicateObligations<'tcx> {
278 self.obligations.clone_pending()
279 }
280
281 fn drain_stalled_obligations_for_coroutines(
282 &mut self,
283 infcx: &InferCtxt<'tcx>,
284 ) -> PredicateObligations<'tcx> {
285 let stalled_coroutines = match infcx.typing_mode() {
286 TypingMode::Analysis { defining_opaque_types_and_generators } => {
287 defining_opaque_types_and_generators
288 }
289 TypingMode::Coherence
290 | TypingMode::Borrowck { defining_opaque_types: _ }
291 | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ }
292 | TypingMode::PostAnalysis => return Default::default(),
293 };
294
295 if stalled_coroutines.is_empty() {
296 return Default::default();
297 }
298
299 self.obligations
300 .drain_pending(|obl| {
301 infcx.probe(|_| {
302 infcx
303 .visit_proof_tree(
304 obl.as_goal(),
305 &mut StalledOnCoroutines {
306 stalled_coroutines,
307 span: obl.cause.span,
308 cache: Default::default(),
309 },
310 )
311 .is_break()
312 })
313 })
314 .into_iter()
315 .map(|(o, _)| o)
316 .collect()
317 }
318}
319
320pub struct StalledOnCoroutines<'tcx> {
329 pub stalled_coroutines: &'tcx ty::List<LocalDefId>,
330 pub span: Span,
331 pub cache: DelayedSet<Ty<'tcx>>,
332}
333
334impl<'tcx> inspect::ProofTreeVisitor<'tcx> for StalledOnCoroutines<'tcx> {
335 type Result = ControlFlow<()>;
336
337 fn span(&self) -> rustc_span::Span {
338 self.span
339 }
340
341 fn visit_goal(&mut self, inspect_goal: &super::inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
342 inspect_goal.goal().predicate.visit_with(self)?;
343
344 if let Some(candidate) = inspect_goal.unique_applicable_candidate() {
345 candidate.visit_nested_no_probe(self)
346 } else {
347 ControlFlow::Continue(())
348 }
349 }
350}
351
352impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for StalledOnCoroutines<'tcx> {
353 type Result = ControlFlow<()>;
354
355 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
356 if !self.cache.insert(ty) {
357 return ControlFlow::Continue(());
358 }
359
360 if let ty::Coroutine(def_id, _) = *ty.kind()
361 && def_id.as_local().is_some_and(|def_id| self.stalled_coroutines.contains(&def_id))
362 {
363 ControlFlow::Break(())
364 } else if ty.has_coroutines() {
365 ty.super_visit_with(self)
366 } else {
367 ControlFlow::Continue(())
368 }
369 }
370}
371
372pub enum NextSolverError<'tcx> {
373 TrueError(PredicateObligation<'tcx>),
374 Ambiguity(PredicateObligation<'tcx>),
375 Overflow(PredicateObligation<'tcx>),
376}
377
378impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for FulfillmentError<'tcx> {
379 fn from_solver_error(infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
380 match error {
381 NextSolverError::TrueError(obligation) => {
382 fulfillment_error_for_no_solution(infcx, obligation)
383 }
384 NextSolverError::Ambiguity(obligation) => {
385 fulfillment_error_for_stalled(infcx, obligation)
386 }
387 NextSolverError::Overflow(obligation) => {
388 fulfillment_error_for_overflow(infcx, obligation)
389 }
390 }
391 }
392}
393
394impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<'tcx> {
395 fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
396 match error {
397 NextSolverError::TrueError(_) => ScrubbedTraitError::TrueError,
398 NextSolverError::Ambiguity(_) | NextSolverError::Overflow(_) => {
399 ScrubbedTraitError::Ambiguity
400 }
401 }
402 }
403}