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 => {}
201 Certainty::Maybe(_) => {
202 self.obligations.register(obligation, None);
203 }
204 }
205 continue;
206 }
207
208 let result = delegate.evaluate_root_goal(goal, obligation.cause.span, stalled_on);
209 self.inspect_evaluated_obligation(infcx, &obligation, &result);
210 let GoalEvaluation { certainty, has_changed, stalled_on } = match result {
211 Ok(result) => result,
212 Err(NoSolution) => {
213 errors.push(E::from_solver_error(
214 infcx,
215 NextSolverError::TrueError(obligation),
216 ));
217 continue;
218 }
219 };
220
221 if has_changed == HasChanged::Yes {
222 obligation.recursion_depth += 1;
229 any_changed = true;
230 }
231
232 match certainty {
233 Certainty::Yes => {}
234 Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on),
235 }
236 }
237
238 if !any_changed {
239 break;
240 }
241 }
242
243 errors
244 }
245
246 fn has_pending_obligations(&self) -> bool {
247 self.obligations.has_pending_obligations()
248 }
249
250 fn pending_obligations(&self) -> PredicateObligations<'tcx> {
251 self.obligations.clone_pending()
252 }
253
254 fn drain_stalled_obligations_for_coroutines(
255 &mut self,
256 infcx: &InferCtxt<'tcx>,
257 ) -> PredicateObligations<'tcx> {
258 let stalled_coroutines = match infcx.typing_mode() {
259 TypingMode::Analysis { defining_opaque_types_and_generators } => {
260 defining_opaque_types_and_generators
261 }
262 TypingMode::Coherence
263 | TypingMode::Borrowck { defining_opaque_types: _ }
264 | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ }
265 | TypingMode::PostAnalysis => return Default::default(),
266 };
267
268 if stalled_coroutines.is_empty() {
269 return Default::default();
270 }
271
272 self.obligations
273 .drain_pending(|obl| {
274 infcx.probe(|_| {
275 infcx
276 .visit_proof_tree(
277 obl.as_goal(),
278 &mut StalledOnCoroutines {
279 stalled_coroutines,
280 span: obl.cause.span,
281 cache: Default::default(),
282 },
283 )
284 .is_break()
285 })
286 })
287 .into_iter()
288 .map(|(o, _)| o)
289 .collect()
290 }
291}
292
293pub struct StalledOnCoroutines<'tcx> {
302 pub stalled_coroutines: &'tcx ty::List<LocalDefId>,
303 pub span: Span,
304 pub cache: DelayedSet<Ty<'tcx>>,
305}
306
307impl<'tcx> inspect::ProofTreeVisitor<'tcx> for StalledOnCoroutines<'tcx> {
308 type Result = ControlFlow<()>;
309
310 fn span(&self) -> rustc_span::Span {
311 self.span
312 }
313
314 fn visit_goal(&mut self, inspect_goal: &super::inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
315 inspect_goal.goal().predicate.visit_with(self)?;
316
317 if let Some(candidate) = inspect_goal.unique_applicable_candidate() {
318 candidate.visit_nested_no_probe(self)
319 } else {
320 ControlFlow::Continue(())
321 }
322 }
323}
324
325impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for StalledOnCoroutines<'tcx> {
326 type Result = ControlFlow<()>;
327
328 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
329 if !self.cache.insert(ty) {
330 return ControlFlow::Continue(());
331 }
332
333 if let ty::CoroutineWitness(def_id, _) = *ty.kind()
334 && def_id.as_local().is_some_and(|def_id| self.stalled_coroutines.contains(&def_id))
335 {
336 ControlFlow::Break(())
337 } else if ty.has_coroutines() {
338 ty.super_visit_with(self)
339 } else {
340 ControlFlow::Continue(())
341 }
342 }
343}
344
345pub enum NextSolverError<'tcx> {
346 TrueError(PredicateObligation<'tcx>),
347 Ambiguity(PredicateObligation<'tcx>),
348 Overflow(PredicateObligation<'tcx>),
349}
350
351impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for FulfillmentError<'tcx> {
352 fn from_solver_error(infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
353 match error {
354 NextSolverError::TrueError(obligation) => {
355 fulfillment_error_for_no_solution(infcx, obligation)
356 }
357 NextSolverError::Ambiguity(obligation) => {
358 fulfillment_error_for_stalled(infcx, obligation)
359 }
360 NextSolverError::Overflow(obligation) => {
361 fulfillment_error_for_overflow(infcx, obligation)
362 }
363 }
364 }
365}
366
367impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<'tcx> {
368 fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
369 match error {
370 NextSolverError::TrueError(_) => ScrubbedTraitError::TrueError,
371 NextSolverError::Ambiguity(_) | NextSolverError::Overflow(_) => {
372 ScrubbedTraitError::Ambiguity
373 }
374 }
375 }
376}