rustc_trait_selection/solve/
fulfill.rs

1use std::marker::PhantomData;
2use std::mem;
3
4use rustc_data_structures::thinvec::ExtractIf;
5use rustc_infer::infer::InferCtxt;
6use rustc_infer::traits::query::NoSolution;
7use rustc_infer::traits::{
8    FromSolverError, PredicateObligation, PredicateObligations, TraitEngine,
9};
10use rustc_next_trait_solver::solve::{GenerateProofTree, HasChanged, SolverDelegateEvalExt as _};
11use tracing::instrument;
12
13use self::derive_errors::*;
14use super::Certainty;
15use super::delegate::SolverDelegate;
16use crate::traits::{FulfillmentError, ScrubbedTraitError};
17
18mod derive_errors;
19
20/// A trait engine using the new trait solver.
21///
22/// This is mostly identical to how `evaluate_all` works inside of the
23/// solver, except that the requirements are slightly different.
24///
25/// Unlike `evaluate_all` it is possible to add new obligations later on
26/// and we also have to track diagnostics information by using `Obligation`
27/// instead of `Goal`.
28///
29/// It is also likely that we want to use slightly different datastructures
30/// here as this will have to deal with far more root goals than `evaluate_all`.
31pub struct FulfillmentCtxt<'tcx, E: 'tcx> {
32    obligations: ObligationStorage<'tcx>,
33
34    /// The snapshot in which this context was created. Using the context
35    /// outside of this snapshot leads to subtle bugs if the snapshot
36    /// gets rolled back. Because of this we explicitly check that we only
37    /// use the context in exactly this snapshot.
38    usable_in_snapshot: usize,
39    _errors: PhantomData<E>,
40}
41
42#[derive(Default)]
43struct ObligationStorage<'tcx> {
44    /// Obligations which resulted in an overflow in fulfillment itself.
45    ///
46    /// We cannot eagerly return these as error so we instead store them here
47    /// to avoid recomputing them each time `select_where_possible` is called.
48    /// This also allows us to return the correct `FulfillmentError` for them.
49    overflowed: PredicateObligations<'tcx>,
50    pending: PredicateObligations<'tcx>,
51}
52
53impl<'tcx> ObligationStorage<'tcx> {
54    fn register(&mut self, obligation: PredicateObligation<'tcx>) {
55        self.pending.push(obligation);
56    }
57
58    fn clone_pending(&self) -> PredicateObligations<'tcx> {
59        let mut obligations = self.pending.clone();
60        obligations.extend(self.overflowed.iter().cloned());
61        obligations
62    }
63
64    fn take_pending(&mut self) -> PredicateObligations<'tcx> {
65        let mut obligations = mem::take(&mut self.pending);
66        obligations.append(&mut self.overflowed);
67        obligations
68    }
69
70    fn unstalled_for_select(&mut self) -> impl Iterator<Item = PredicateObligation<'tcx>> {
71        mem::take(&mut self.pending).into_iter()
72    }
73
74    fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'tcx>) {
75        infcx.probe(|_| {
76            // IMPORTANT: we must not use solve any inference variables in the obligations
77            // as this is all happening inside of a probe. We use a probe to make sure
78            // we get all obligations involved in the overflow. We pretty much check: if
79            // we were to do another step of `select_where_possible`, which goals would
80            // change.
81            // FIXME: <https://github.com/Gankra/thin-vec/pull/66> is merged, this can be removed.
82            self.overflowed.extend(ExtractIf::new(&mut self.pending, |o| {
83                let goal = o.clone().into();
84                let result = <&SolverDelegate<'tcx>>::from(infcx)
85                    .evaluate_root_goal(goal, GenerateProofTree::No, o.cause.span)
86                    .0;
87                matches!(result, Ok((HasChanged::Yes, _)))
88            }));
89        })
90    }
91}
92
93impl<'tcx, E: 'tcx> FulfillmentCtxt<'tcx, E> {
94    pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx, E> {
95        assert!(
96            infcx.next_trait_solver(),
97            "new trait solver fulfillment context created when \
98            infcx is set up for old trait solver"
99        );
100        FulfillmentCtxt {
101            obligations: Default::default(),
102            usable_in_snapshot: infcx.num_open_snapshots(),
103            _errors: PhantomData,
104        }
105    }
106
107    fn inspect_evaluated_obligation(
108        &self,
109        infcx: &InferCtxt<'tcx>,
110        obligation: &PredicateObligation<'tcx>,
111        result: &Result<(HasChanged, Certainty), NoSolution>,
112    ) {
113        if let Some(inspector) = infcx.obligation_inspector.get() {
114            let result = match result {
115                Ok((_, c)) => Ok(*c),
116                Err(NoSolution) => Err(NoSolution),
117            };
118            (inspector)(infcx, &obligation, result);
119        }
120    }
121}
122
123impl<'tcx, E> TraitEngine<'tcx, E> for FulfillmentCtxt<'tcx, E>
124where
125    E: FromSolverError<'tcx, NextSolverError<'tcx>>,
126{
127    #[instrument(level = "trace", skip(self, infcx))]
128    fn register_predicate_obligation(
129        &mut self,
130        infcx: &InferCtxt<'tcx>,
131        obligation: PredicateObligation<'tcx>,
132    ) {
133        assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
134        self.obligations.register(obligation);
135    }
136
137    fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> {
138        self.obligations
139            .pending
140            .drain(..)
141            .map(|obligation| NextSolverError::Ambiguity(obligation))
142            .chain(
143                self.obligations
144                    .overflowed
145                    .drain(..)
146                    .map(|obligation| NextSolverError::Overflow(obligation)),
147            )
148            .map(|e| E::from_solver_error(infcx, e))
149            .collect()
150    }
151
152    fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> {
153        assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
154        let mut errors = Vec::new();
155        for i in 0.. {
156            if !infcx.tcx.recursion_limit().value_within_limit(i) {
157                self.obligations.on_fulfillment_overflow(infcx);
158                // Only return true errors that we have accumulated while processing.
159                return errors;
160            }
161
162            let mut has_changed = false;
163            for obligation in self.obligations.unstalled_for_select() {
164                let goal = obligation.clone().into();
165                let result = <&SolverDelegate<'tcx>>::from(infcx)
166                    .evaluate_root_goal(goal, GenerateProofTree::No, obligation.cause.span)
167                    .0;
168                self.inspect_evaluated_obligation(infcx, &obligation, &result);
169                let (changed, certainty) = match result {
170                    Ok(result) => result,
171                    Err(NoSolution) => {
172                        errors.push(E::from_solver_error(
173                            infcx,
174                            NextSolverError::TrueError(obligation),
175                        ));
176                        continue;
177                    }
178                };
179
180                if changed == HasChanged::Yes {
181                    has_changed = true;
182                }
183
184                match certainty {
185                    Certainty::Yes => {}
186                    Certainty::Maybe(_) => self.obligations.register(obligation),
187                }
188            }
189
190            if !has_changed {
191                break;
192            }
193        }
194
195        errors
196    }
197
198    fn has_pending_obligations(&self) -> bool {
199        !self.obligations.pending.is_empty() || !self.obligations.overflowed.is_empty()
200    }
201
202    fn pending_obligations(&self) -> PredicateObligations<'tcx> {
203        self.obligations.clone_pending()
204    }
205
206    fn drain_unstalled_obligations(&mut self, _: &InferCtxt<'tcx>) -> PredicateObligations<'tcx> {
207        self.obligations.take_pending()
208    }
209}
210
211pub enum NextSolverError<'tcx> {
212    TrueError(PredicateObligation<'tcx>),
213    Ambiguity(PredicateObligation<'tcx>),
214    Overflow(PredicateObligation<'tcx>),
215}
216
217impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for FulfillmentError<'tcx> {
218    fn from_solver_error(infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
219        match error {
220            NextSolverError::TrueError(obligation) => {
221                fulfillment_error_for_no_solution(infcx, obligation)
222            }
223            NextSolverError::Ambiguity(obligation) => {
224                fulfillment_error_for_stalled(infcx, obligation)
225            }
226            NextSolverError::Overflow(obligation) => {
227                fulfillment_error_for_overflow(infcx, obligation)
228            }
229        }
230    }
231}
232
233impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<'tcx> {
234    fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
235        match error {
236            NextSolverError::TrueError(_) => ScrubbedTraitError::TrueError,
237            NextSolverError::Ambiguity(_) | NextSolverError::Overflow(_) => {
238                ScrubbedTraitError::Ambiguity
239            }
240        }
241    }
242}