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