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(
98 self.pending
99 .extract_if(.., |(o, stalled_on)| {
100 let goal = o.as_goal();
101 let result = <&SolverContext<'db>>::from(infcx).evaluate_root_goal(
102 goal,
103 o.cause.span(),
104 stalled_on.take(),
105 );
106 matches!(result, Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. }))
107 })
108 .map(|(o, _)| o),
109 );
110 })
111 }
112}
113
114impl<'db> FulfillmentCtxt<'db> {
115 pub fn new(infcx: &InferCtxt<'db>) -> FulfillmentCtxt<'db> {
116 FulfillmentCtxt {
117 obligations: Default::default(),
118 usable_in_snapshot: infcx.num_open_snapshots(),
119 try_evaluate_obligations_scratch: Default::default(),
120 }
121 }
122}
123
124impl<'db> FulfillmentCtxt<'db> {
125 #[tracing::instrument(level = "trace", skip(self, infcx))]
126 pub(crate) fn register_predicate_obligation(
127 &mut self,
128 infcx: &InferCtxt<'db>,
129 obligation: PredicateObligation<'db>,
130 ) {
131 assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
132 self.obligations.register(obligation, None);
133 }
134
135 pub(crate) fn register_predicate_obligations(
136 &mut self,
137 infcx: &InferCtxt<'db>,
138 obligations: impl IntoIterator<Item = PredicateObligation<'db>>,
139 ) {
140 assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
141 obligations.into_iter().for_each(|obligation| self.obligations.register(obligation, None));
142 }
143
144 pub(crate) fn collect_remaining_errors(
145 &mut self,
146 _infcx: &InferCtxt<'db>,
147 ) -> Vec<NextSolverError<'db>> {
148 self.obligations
149 .pending
150 .drain(..)
151 .map(|(obligation, _)| NextSolverError::Ambiguity(obligation))
152 .chain(self.obligations.overflowed.drain(..).map(NextSolverError::Overflow))
153 .collect()
154 }
155
156 pub(crate) fn try_evaluate_obligations(
157 &mut self,
158 infcx: &InferCtxt<'db>,
159 ) -> Vec<NextSolverError<'db>> {
160 assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
161 self.try_evaluate_obligations_scratch.clear();
162 let mut errors = Vec::new();
163 loop {
164 let mut any_changed = false;
165 self.try_evaluate_obligations_scratch.extend(self.obligations.drain_pending(|_| true));
166 for (mut obligation, stalled_on) in self.try_evaluate_obligations_scratch.drain(..) {
167 if obligation.recursion_depth >= infcx.interner.recursion_limit() {
168 self.obligations.on_fulfillment_overflow(infcx);
169 return errors;
171 }
172
173 let goal = obligation.as_goal();
174 let delegate = <&SolverContext<'db>>::from(infcx);
175 if let Some(certainty) =
176 delegate.compute_goal_fast_path(goal, obligation.cause.span())
177 {
178 match certainty {
179 Certainty::Yes => {}
180 Certainty::Maybe { .. } => {
181 self.obligations.register(obligation, None);
182 }
183 }
184 continue;
185 }
186
187 let result = delegate.evaluate_root_goal(goal, obligation.cause.span(), stalled_on);
188 infcx.inspect_evaluated_obligation(&obligation, &result, || {
189 Some(
190 delegate.evaluate_root_goal_for_proof_tree(goal, obligation.cause.span()).1,
191 )
192 });
193 let GoalEvaluation { goal: _, certainty, has_changed, stalled_on } = match result {
194 Ok(result) => result,
195 Err(NoSolution) => {
196 errors.push(NextSolverError::TrueError(obligation));
197 continue;
198 }
199 };
200
201 if has_changed == HasChanged::Yes {
202 obligation.recursion_depth += 1;
209 any_changed = true;
210 }
211
212 match certainty {
213 Certainty::Yes => {}
214 Certainty::Maybe { .. } => self.obligations.register(obligation, stalled_on),
215 }
216 }
217
218 if !any_changed {
219 break;
220 }
221 }
222
223 errors
224 }
225
226 pub(crate) fn evaluate_obligations_error_on_ambiguity(
227 &mut self,
228 infcx: &InferCtxt<'db>,
229 ) -> Vec<NextSolverError<'db>> {
230 let errors = self.try_evaluate_obligations(infcx);
231 if !errors.is_empty() {
232 return errors;
233 }
234
235 self.collect_remaining_errors(infcx)
236 }
237
238 pub(crate) fn pending_obligations(&self) -> PredicateObligations<'db> {
239 self.obligations.clone_pending()
240 }
241
242 pub(crate) fn drain_stalled_obligations_for_coroutines(
243 &mut self,
244 infcx: &InferCtxt<'db>,
245 ) -> PredicateObligations<'db> {
246 let stalled_coroutines = match infcx.typing_mode_raw().assert_not_erased() {
247 TypingMode::Analysis { defining_opaque_types_and_generators } => {
248 defining_opaque_types_and_generators
249 }
250 TypingMode::Coherence
251 | TypingMode::Borrowck { defining_opaque_types: _ }
252 | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ }
253 | TypingMode::PostAnalysis => return Default::default(),
254 };
255 let stalled_coroutines = stalled_coroutines.as_slice();
256
257 if stalled_coroutines.is_empty() {
258 return Default::default();
259 }
260
261 self.obligations
262 .drain_pending(|obl| {
263 infcx.probe(|_| {
264 infcx
265 .visit_proof_tree(
266 obl.as_goal(),
267 &mut StalledOnCoroutines {
268 stalled_coroutines,
269 span: obl.cause.span(),
270 cache: Default::default(),
271 },
272 )
273 .is_break()
274 })
275 })
276 .map(|(o, _)| o)
277 .collect()
278 }
279}
280
281pub struct StalledOnCoroutines<'a, 'db> {
290 pub stalled_coroutines: &'a [SolverDefId<'db>],
291 pub span: Span,
292 pub cache: FxHashSet<Ty<'db>>,
293}
294
295impl<'db> ProofTreeVisitor<'db> for StalledOnCoroutines<'_, 'db> {
296 type Result = ControlFlow<()>;
297
298 fn span(&self) -> Span {
299 self.span
300 }
301
302 fn visit_goal(&mut self, inspect_goal: &super::inspect::InspectGoal<'_, 'db>) -> Self::Result {
303 inspect_goal.goal().predicate.visit_with(self)?;
304
305 if let Some(candidate) = inspect_goal.unique_applicable_candidate() {
306 candidate.visit_nested_no_probe(self)
307 } else {
308 ControlFlow::Continue(())
309 }
310 }
311}
312
313impl<'db> TypeVisitor<DbInterner<'db>> for StalledOnCoroutines<'_, 'db> {
314 type Result = ControlFlow<()>;
315
316 fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result {
317 if !self.cache.insert(ty) {
318 return ControlFlow::Continue(());
319 }
320
321 if let TyKind::Coroutine(def_id, _) = ty.kind()
322 && self.stalled_coroutines.contains(&def_id.into())
323 {
324 ControlFlow::Break(())
325 } else if ty.has_coroutines() {
326 ty.super_visit_with(self)
327 } else {
328 ControlFlow::Continue(())
329 }
330 }
331}
332
333#[derive(Debug, Clone)]
334pub enum NextSolverError<'db> {
335 TrueError(PredicateObligation<'db>),
336 Ambiguity(PredicateObligation<'db>),
337 Overflow(PredicateObligation<'db>),
338}
339
340impl NextSolverError<'_> {
341 #[inline]
342 pub fn is_true_error(&self) -> bool {
343 matches!(self, NextSolverError::TrueError(_))
344 }
345}