rustc_trait_selection/solve/
fulfill.rs

1use 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
32// FIXME: Do we need to use a `ThinVec` here?
33type PendingObligations<'tcx> =
34    ThinVec<(PredicateObligation<'tcx>, Option<GoalStalledOn<TyCtxt<'tcx>>>)>;
35
36/// A trait engine using the new trait solver.
37///
38/// This is mostly identical to how `evaluate_all` works inside of the
39/// solver, except that the requirements are slightly different.
40///
41/// Unlike `evaluate_all` it is possible to add new obligations later on
42/// and we also have to track diagnostics information by using `Obligation`
43/// instead of `Goal`.
44///
45/// It is also likely that we want to use slightly different datastructures
46/// here as this will have to deal with far more root goals than `evaluate_all`.
47pub struct FulfillmentCtxt<'tcx, E: 'tcx> {
48    obligations: ObligationStorage<'tcx>,
49
50    /// The snapshot in which this context was created. Using the context
51    /// outside of this snapshot leads to subtle bugs if the snapshot
52    /// gets rolled back. Because of this we explicitly check that we only
53    /// use the context in exactly this snapshot.
54    usable_in_snapshot: usize,
55    _errors: PhantomData<E>,
56}
57
58#[derive(Default, Debug)]
59struct ObligationStorage<'tcx> {
60    /// Obligations which resulted in an overflow in fulfillment itself.
61    ///
62    /// We cannot eagerly return these as error so we instead store them here
63    /// to avoid recomputing them each time `select_where_possible` is called.
64    /// This also allows us to return the correct `FulfillmentError` for them.
65    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            // IMPORTANT: we must not use solve any inference variables in the obligations
102            // as this is all happening inside of a probe. We use a probe to make sure
103            // we get all obligations involved in the overflow. We pretty much check: if
104            // we were to do another step of `select_where_possible`, which goals would
105            // change.
106            // FIXME: <https://github.com/Gankra/thin-vec/pull/66> is merged, this can be removed.
107            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                    // Only return true errors that we have accumulated while processing.
191                    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                        // This fast path doesn't depend on region identity so it doesn't
201                        // matter if the goal contains inference variables or not, so we
202                        // don't need to call `push_hir_typeck_potentially_region_dependent_goal`
203                        // here.
204                        //
205                        // Only goals proven via the trait solver should be region dependent.
206                        Certainty::Yes => {}
207                        Certainty::Maybe(_) => {
208                            self.obligations.register(obligation, None);
209                        }
210                    }
211                    continue;
212                }
213
214                let result = delegate.evaluate_root_goal(goal, obligation.cause.span, stalled_on);
215                self.inspect_evaluated_obligation(infcx, &obligation, &result);
216                let GoalEvaluation { goal, certainty, has_changed, stalled_on } = match result {
217                    Ok(result) => result,
218                    Err(NoSolution) => {
219                        errors.push(E::from_solver_error(
220                            infcx,
221                            NextSolverError::TrueError(obligation),
222                        ));
223                        continue;
224                    }
225                };
226
227                // We've resolved the goal in `evaluate_root_goal`, avoid redoing this work
228                // in the next iteration. This does not resolve the inference variables
229                // constrained by evaluating the goal.
230                obligation.predicate = goal.predicate;
231                if has_changed == HasChanged::Yes {
232                    // We increment the recursion depth here to track the number of times
233                    // this goal has resulted in inference progress. This doesn't precisely
234                    // model the way that we track recursion depth in the old solver due
235                    // to the fact that we only process root obligations, but it is a good
236                    // approximation and should only result in fulfillment overflow in
237                    // pathological cases.
238                    obligation.recursion_depth += 1;
239                    any_changed = true;
240                }
241
242                match certainty {
243                    Certainty::Yes => {
244                        // Goals may depend on structural identity. Region uniquification at the
245                        // start of MIR borrowck may cause things to no longer be so, potentially
246                        // causing an ICE.
247                        //
248                        // While we uniquify root goals in HIR this does not handle cases where
249                        // regions are hidden inside of a type or const inference variable.
250                        //
251                        // FIXME(-Znext-solver): This does not handle inference variables hidden
252                        // inside of an opaque type, e.g. if there's `Opaque = (?x, ?x)` in the
253                        // storage, we can also rely on structural identity of `?x` even if we
254                        // later uniquify it in MIR borrowck.
255                        if infcx.in_hir_typeck
256                            && (obligation.has_non_region_infer() || obligation.has_free_regions())
257                        {
258                            infcx.push_hir_typeck_potentially_region_dependent_goal(obligation);
259                        }
260                    }
261                    Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on),
262                }
263            }
264
265            if !any_changed {
266                break;
267            }
268        }
269
270        errors
271    }
272
273    fn has_pending_obligations(&self) -> bool {
274        self.obligations.has_pending_obligations()
275    }
276
277    fn pending_obligations(&self) -> PredicateObligations<'tcx> {
278        self.obligations.clone_pending()
279    }
280
281    fn drain_stalled_obligations_for_coroutines(
282        &mut self,
283        infcx: &InferCtxt<'tcx>,
284    ) -> PredicateObligations<'tcx> {
285        let stalled_coroutines = match infcx.typing_mode() {
286            TypingMode::Analysis { defining_opaque_types_and_generators } => {
287                defining_opaque_types_and_generators
288            }
289            TypingMode::Coherence
290            | TypingMode::Borrowck { defining_opaque_types: _ }
291            | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ }
292            | TypingMode::PostAnalysis => return Default::default(),
293        };
294
295        if stalled_coroutines.is_empty() {
296            return Default::default();
297        }
298
299        self.obligations
300            .drain_pending(|obl| {
301                infcx.probe(|_| {
302                    infcx
303                        .visit_proof_tree(
304                            obl.as_goal(),
305                            &mut StalledOnCoroutines {
306                                stalled_coroutines,
307                                span: obl.cause.span,
308                                cache: Default::default(),
309                            },
310                        )
311                        .is_break()
312                })
313            })
314            .into_iter()
315            .map(|(o, _)| o)
316            .collect()
317    }
318}
319
320/// Detect if a goal is stalled on a coroutine that is owned by the current typeck root.
321///
322/// This function can (erroneously) fail to detect a predicate, i.e. it doesn't need to
323/// be complete. However, this will lead to ambiguity errors, so we want to make it
324/// accurate.
325///
326/// This function can be also return false positives, which will lead to poor diagnostics
327/// so we want to keep this visitor *precise* too.
328pub struct StalledOnCoroutines<'tcx> {
329    pub stalled_coroutines: &'tcx ty::List<LocalDefId>,
330    pub span: Span,
331    pub cache: DelayedSet<Ty<'tcx>>,
332}
333
334impl<'tcx> inspect::ProofTreeVisitor<'tcx> for StalledOnCoroutines<'tcx> {
335    type Result = ControlFlow<()>;
336
337    fn span(&self) -> rustc_span::Span {
338        self.span
339    }
340
341    fn visit_goal(&mut self, inspect_goal: &super::inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
342        inspect_goal.goal().predicate.visit_with(self)?;
343
344        if let Some(candidate) = inspect_goal.unique_applicable_candidate() {
345            candidate.visit_nested_no_probe(self)
346        } else {
347            ControlFlow::Continue(())
348        }
349    }
350}
351
352impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for StalledOnCoroutines<'tcx> {
353    type Result = ControlFlow<()>;
354
355    fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
356        if !self.cache.insert(ty) {
357            return ControlFlow::Continue(());
358        }
359
360        if let ty::Coroutine(def_id, _) = *ty.kind()
361            && def_id.as_local().is_some_and(|def_id| self.stalled_coroutines.contains(&def_id))
362        {
363            ControlFlow::Break(())
364        } else if ty.has_coroutines() {
365            ty.super_visit_with(self)
366        } else {
367            ControlFlow::Continue(())
368        }
369    }
370}
371
372pub enum NextSolverError<'tcx> {
373    TrueError(PredicateObligation<'tcx>),
374    Ambiguity(PredicateObligation<'tcx>),
375    Overflow(PredicateObligation<'tcx>),
376}
377
378impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for FulfillmentError<'tcx> {
379    fn from_solver_error(infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
380        match error {
381            NextSolverError::TrueError(obligation) => {
382                fulfillment_error_for_no_solution(infcx, obligation)
383            }
384            NextSolverError::Ambiguity(obligation) => {
385                fulfillment_error_for_stalled(infcx, obligation)
386            }
387            NextSolverError::Overflow(obligation) => {
388                fulfillment_error_for_overflow(infcx, obligation)
389            }
390        }
391    }
392}
393
394impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<'tcx> {
395    fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
396        match error {
397            NextSolverError::TrueError(_) => ScrubbedTraitError::TrueError,
398            NextSolverError::Ambiguity(_) | NextSolverError::Overflow(_) => {
399                ScrubbedTraitError::Ambiguity
400            }
401        }
402    }
403}