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