1use std::ops::ControlFlow;
4
5use rustc_hash::FxHashSet;
6use rustc_next_trait_solver::{
7 delegate::SolverDelegate,
8 solve::{GoalEvaluation, GoalStalledOn, HasChanged, SolverDelegateEvalExt},
9};
10use rustc_type_ir::{
11 Interner, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
12 inherent::IntoKind,
13 solve::{Certainty, NoSolution},
14};
15
16use crate::{
17 Span,
18 next_solver::{
19 DbInterner, SolverContext, SolverDefId, Ty, TyKind, TypingMode,
20 infer::{
21 InferCtxt,
22 traits::{PredicateObligation, PredicateObligations},
23 },
24 inspect::ProofTreeVisitor,
25 },
26};
27
28type PendingObligations<'db> =
29 Vec<(PredicateObligation<'db>, Option<GoalStalledOn<DbInterner<'db>>>)>;
30
31#[derive(Debug)]
43pub struct FulfillmentCtxt<'db> {
44 obligations: ObligationStorage<'db>,
45
46 usable_in_snapshot: usize,
51 try_evaluate_obligations_scratch: PendingObligations<'db>,
52}
53
54#[derive(Default, Debug, Clone)]
55struct ObligationStorage<'db> {
56 overflowed: Vec<PredicateObligation<'db>>,
62 pending: PendingObligations<'db>,
63}
64
65impl<'db> ObligationStorage<'db> {
66 fn register(
67 &mut self,
68 obligation: PredicateObligation<'db>,
69 stalled_on: Option<GoalStalledOn<DbInterner<'db>>>,
70 ) {
71 self.pending.push((obligation, stalled_on));
72 }
73
74 fn clone_pending(&self) -> PredicateObligations<'db> {
75 let mut obligations: PredicateObligations<'db> =
76 self.pending.iter().map(|(o, _)| o.clone()).collect();
77 obligations.extend(self.overflowed.iter().cloned());
78 obligations
79 }
80
81 fn drain_pending<'this, 'cond>(
82 &'this mut self,
83 cond: impl 'cond + Fn(&PredicateObligation<'db>) -> bool,
84 ) -> impl Iterator<Item = (PredicateObligation<'db>, Option<GoalStalledOn<DbInterner<'db>>>)>
85 {
86 self.pending.extract_if(.., move |(o, _)| cond(o))
87 }
88
89 fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'db>) {
90 infcx.probe(|_| {
91 self.overflowed.extend(
97 self.pending
98 .extract_if(.., |(o, stalled_on)| {
99 let goal = o.as_goal();
100 let result = <&SolverContext<'db>>::from(infcx).evaluate_root_goal(
101 goal,
102 o.cause.span(),
103 stalled_on.take(),
104 );
105 matches!(result, Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. }))
106 })
107 .map(|(o, _)| o),
108 );
109 })
110 }
111}
112
113impl<'db> FulfillmentCtxt<'db> {
114 pub fn new(infcx: &InferCtxt<'db>) -> FulfillmentCtxt<'db> {
115 FulfillmentCtxt {
116 obligations: Default::default(),
117 usable_in_snapshot: infcx.num_open_snapshots(),
118 try_evaluate_obligations_scratch: Default::default(),
119 }
120 }
121}
122
123impl<'db> FulfillmentCtxt<'db> {
124 #[tracing::instrument(level = "trace", skip(self, infcx))]
125 pub(crate) fn register_predicate_obligation(
126 &mut self,
127 infcx: &InferCtxt<'db>,
128 obligation: PredicateObligation<'db>,
129 ) {
130 assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
131 self.obligations.register(obligation, None);
132 }
133
134 pub(crate) fn register_predicate_obligations(
135 &mut self,
136 infcx: &InferCtxt<'db>,
137 obligations: impl IntoIterator<Item = PredicateObligation<'db>>,
138 ) {
139 assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
140 obligations.into_iter().for_each(|obligation| self.obligations.register(obligation, None));
141 }
142
143 pub(crate) fn collect_remaining_errors(
144 &mut self,
145 _infcx: &InferCtxt<'db>,
146 ) -> Vec<NextSolverError<'db>> {
147 self.obligations
148 .pending
149 .drain(..)
150 .map(|(obligation, _)| NextSolverError::Ambiguity(obligation))
151 .chain(self.obligations.overflowed.drain(..).map(NextSolverError::Overflow))
152 .collect()
153 }
154
155 pub(crate) fn try_evaluate_obligations(
156 &mut self,
157 infcx: &InferCtxt<'db>,
158 ) -> Vec<NextSolverError<'db>> {
159 assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
160 self.try_evaluate_obligations_scratch.clear();
161 let mut errors = Vec::new();
162 loop {
163 let mut any_changed = false;
164 self.try_evaluate_obligations_scratch.extend(self.obligations.drain_pending(|_| true));
165 for (mut obligation, stalled_on) in self.try_evaluate_obligations_scratch.drain(..) {
166 if obligation.recursion_depth >= infcx.interner.recursion_limit() {
167 self.obligations.on_fulfillment_overflow(infcx);
168 return errors;
170 }
171
172 let goal = obligation.as_goal();
173 let delegate = <&SolverContext<'db>>::from(infcx);
174 if let Some(certainty) =
175 delegate.compute_goal_fast_path(goal, obligation.cause.span())
176 {
177 match certainty {
178 Certainty::Yes => {}
179 Certainty::Maybe { .. } => {
180 self.obligations.register(obligation, None);
181 }
182 }
183 continue;
184 }
185
186 let result = delegate.evaluate_root_goal(goal, obligation.cause.span(), stalled_on);
187 infcx.inspect_evaluated_obligation(&obligation, &result, || {
188 Some(
189 delegate.evaluate_root_goal_for_proof_tree(goal, obligation.cause.span()).1,
190 )
191 });
192 let GoalEvaluation { goal: _, certainty, has_changed, stalled_on } = match result {
193 Ok(result) => result,
194 Err(NoSolution) => {
195 errors.push(NextSolverError::TrueError(obligation));
196 continue;
197 }
198 };
199
200 if has_changed == HasChanged::Yes {
201 obligation.recursion_depth += 1;
208 any_changed = true;
209 }
210
211 match certainty {
212 Certainty::Yes => {}
213 Certainty::Maybe { .. } => self.obligations.register(obligation, stalled_on),
214 }
215 }
216
217 if !any_changed {
218 break;
219 }
220 }
221
222 errors
223 }
224
225 pub(crate) fn evaluate_obligations_error_on_ambiguity(
226 &mut self,
227 infcx: &InferCtxt<'db>,
228 ) -> Vec<NextSolverError<'db>> {
229 let errors = self.try_evaluate_obligations(infcx);
230 if !errors.is_empty() {
231 return errors;
232 }
233
234 self.collect_remaining_errors(infcx)
235 }
236
237 pub(crate) fn pending_obligations(&self) -> PredicateObligations<'db> {
238 self.obligations.clone_pending()
239 }
240
241 pub(crate) fn drain_stalled_obligations_for_coroutines(
242 &mut self,
243 infcx: &InferCtxt<'db>,
244 ) -> PredicateObligations<'db> {
245 let stalled_coroutines = match infcx.typing_mode_raw().assert_not_erased() {
246 TypingMode::Analysis { defining_opaque_types_and_generators } => {
247 defining_opaque_types_and_generators
248 }
249 TypingMode::Coherence
250 | TypingMode::Borrowck { defining_opaque_types: _ }
251 | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ }
252 | TypingMode::PostAnalysis => return Default::default(),
253 };
254 let stalled_coroutines = stalled_coroutines.as_slice();
255
256 if stalled_coroutines.is_empty() {
257 return Default::default();
258 }
259
260 self.obligations
261 .drain_pending(|obl| {
262 infcx.probe(|_| {
263 infcx
264 .visit_proof_tree(
265 obl.as_goal(),
266 &mut StalledOnCoroutines {
267 stalled_coroutines,
268 span: obl.cause.span(),
269 cache: Default::default(),
270 },
271 )
272 .is_break()
273 })
274 })
275 .map(|(o, _)| o)
276 .collect()
277 }
278}
279
280pub struct StalledOnCoroutines<'a, 'db> {
289 pub stalled_coroutines: &'a [SolverDefId<'db>],
290 pub span: Span,
291 pub cache: FxHashSet<Ty<'db>>,
292}
293
294impl<'db> ProofTreeVisitor<'db> for StalledOnCoroutines<'_, 'db> {
295 type Result = ControlFlow<()>;
296
297 fn span(&self) -> Span {
298 self.span
299 }
300
301 fn visit_goal(&mut self, inspect_goal: &super::inspect::InspectGoal<'_, 'db>) -> Self::Result {
302 inspect_goal.goal().predicate.visit_with(self)?;
303
304 if let Some(candidate) = inspect_goal.unique_applicable_candidate() {
305 candidate.visit_nested_no_probe(self)
306 } else {
307 ControlFlow::Continue(())
308 }
309 }
310}
311
312impl<'db> TypeVisitor<DbInterner<'db>> for StalledOnCoroutines<'_, 'db> {
313 type Result = ControlFlow<()>;
314
315 fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result {
316 if !self.cache.insert(ty) {
317 return ControlFlow::Continue(());
318 }
319
320 if let TyKind::Coroutine(def_id, _) = ty.kind()
321 && self.stalled_coroutines.contains(&def_id.into())
322 {
323 ControlFlow::Break(())
324 } else if ty.has_coroutines() {
325 ty.super_visit_with(self)
326 } else {
327 ControlFlow::Continue(())
328 }
329 }
330}
331
332#[derive(Debug, Clone)]
333pub enum NextSolverError<'db> {
334 TrueError(PredicateObligation<'db>),
335 Ambiguity(PredicateObligation<'db>),
336 Overflow(PredicateObligation<'db>),
337}
338
339impl NextSolverError<'_> {
340 #[inline]
341 pub fn is_true_error(&self) -> bool {
342 matches!(self, NextSolverError::TrueError(_))
343 }
344}