Skip to main content

rustc_trait_selection/solve/
fulfill.rs

1use std::marker::PhantomData;
2use std::mem;
3
4use rustc_infer::infer::InferCtxt;
5use rustc_infer::traits::query::NoSolution;
6use rustc_infer::traits::{
7    FromSolverError, PredicateObligation, PredicateObligations, TraitEngine, TraitErrors,
8};
9use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt, TypingMode};
10use rustc_next_trait_solver::solve::fast_path::compute_goal_fast_path;
11use rustc_next_trait_solver::solve::{
12    GoalEvaluation, GoalStalledOn, HasChanged, MaybeInfo, SolverDelegateEvalExt as _,
13    StalledOnCoroutines,
14};
15use thin_vec::ThinVec;
16use tracing::instrument;
17
18use self::derive_errors::*;
19use super::Certainty;
20use super::delegate::SolverDelegate;
21use crate::traits::{FulfillmentError, ScrubbedTraitError};
22
23mod derive_errors;
24
25// FIXME: Do we need to use a `ThinVec` here?
26type PendingObligations<'tcx> =
27    ThinVec<(PredicateObligation<'tcx>, Option<GoalStalledOn<TyCtxt<'tcx>>>)>;
28
29/// A trait engine using the new trait solver.
30///
31/// This is mostly identical to how `evaluate_all` works inside of the
32/// solver, except that the requirements are slightly different.
33///
34/// Unlike `evaluate_all` it is possible to add new obligations later on
35/// and we also have to track diagnostics information by using `Obligation`
36/// instead of `Goal`.
37///
38/// It is also likely that we want to use slightly different datastructures
39/// here as this will have to deal with far more root goals than `evaluate_all`.
40pub struct FulfillmentCtxt<'tcx, E: 'tcx> {
41    obligations: ObligationStorage<'tcx>,
42
43    /// The snapshot in which this context was created. Using the context
44    /// outside of this snapshot leads to subtle bugs if the snapshot
45    /// gets rolled back. Because of this we explicitly check that we only
46    /// use the context in exactly this snapshot.
47    usable_in_snapshot: usize,
48    _errors: PhantomData<E>,
49}
50
51#[derive(#[automatically_derived]
impl<'tcx> ::core::default::Default for ObligationStorage<'tcx> {
    #[inline]
    fn default() -> ObligationStorage<'tcx> {
        ObligationStorage {
            overflowed: ::core::default::Default::default(),
            pending: ::core::default::Default::default(),
        }
    }
}Default, #[automatically_derived]
impl<'tcx> ::core::fmt::Debug for ObligationStorage<'tcx> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f,
            "ObligationStorage", "overflowed", &self.overflowed, "pending",
            &&self.pending)
    }
}Debug)]
52struct ObligationStorage<'tcx> {
53    /// Obligations which resulted in an overflow in fulfillment itself.
54    ///
55    /// We cannot eagerly return these as error so we instead store them here
56    /// to avoid recomputing them each time `try_evaluate_obligations` is called.
57    /// This also allows us to return the correct `FulfillmentError` for them.
58    overflowed: Vec<PredicateObligation<'tcx>>,
59    pending: PendingObligations<'tcx>,
60}
61
62impl<'tcx> ObligationStorage<'tcx> {
63    fn register(
64        &mut self,
65        obligation: PredicateObligation<'tcx>,
66        stalled_on: Option<GoalStalledOn<TyCtxt<'tcx>>>,
67    ) {
68        self.pending.push((obligation, stalled_on));
69    }
70
71    fn has_pending_obligations(&self) -> bool {
72        !self.pending.is_empty() || !self.overflowed.is_empty()
73    }
74
75    fn clone_pending(&self) -> PredicateObligations<'tcx> {
76        let mut obligations: PredicateObligations<'tcx> =
77            self.pending.iter().map(|(o, _)| o.clone()).collect();
78        obligations.extend(self.overflowed.iter().cloned());
79        obligations
80    }
81
82    fn clone_pending_filtered<F>(&self, f: F) -> PredicateObligations<'tcx>
83    where
84        F: FnMut(&&(PredicateObligation<'tcx>, Option<GoalStalledOn<TyCtxt<'tcx>>>)) -> bool,
85    {
86        let mut obligations: PredicateObligations<'tcx> =
87            self.pending.iter().filter(f).map(|(o, _)| o.clone()).collect();
88        obligations.extend(self.overflowed.iter().cloned());
89        obligations
90    }
91
92    fn drain_pending(
93        &mut self,
94        cond: impl Fn(&PredicateObligation<'tcx>, &Option<GoalStalledOn<TyCtxt<'tcx>>>) -> bool,
95    ) -> PendingObligations<'tcx> {
96        let (unstalled, pending) =
97            mem::take(&mut self.pending).into_iter().partition(|(o, s)| cond(o, s));
98        self.pending = pending;
99        unstalled
100    }
101
102    fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'tcx>) {
103        infcx.probe(|_| {
104            // IMPORTANT: we must not use solve any inference variables in the obligations
105            // as this is all happening inside of a probe. We use a probe to make sure
106            // we get all obligations involved in the overflow. We pretty much check: if
107            // we were to do another step of `try_evaluate_obligations`, which goals would
108            // change.
109            self.overflowed.extend(
110                self.pending
111                    .extract_if(.., |(o, stalled_on)| {
112                        let goal = o.as_goal();
113                        let result = <&SolverDelegate<'tcx>>::from(infcx).evaluate_root_goal(
114                            goal,
115                            o.cause.span,
116                            stalled_on.take(),
117                        );
118                        #[allow(non_exhaustive_omitted_patterns)] match result {
    Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. }) => true,
    _ => false,
}matches!(result, Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. }))
119                    })
120                    .map(|(o, _)| o),
121            );
122        })
123    }
124}
125
126impl<'tcx, E: 'tcx> FulfillmentCtxt<'tcx, E> {
127    pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx, E> {
128        if !infcx.next_trait_solver() {
    {
        ::core::panicking::panic_fmt(format_args!("new trait solver fulfillment context created when infcx is set up for old trait solver"));
    }
};assert!(
129            infcx.next_trait_solver(),
130            "new trait solver fulfillment context created when \
131            infcx is set up for old trait solver"
132        );
133        FulfillmentCtxt {
134            obligations: Default::default(),
135            usable_in_snapshot: infcx.num_open_snapshots(),
136            _errors: PhantomData,
137        }
138    }
139
140    fn inspect_evaluated_obligation(
141        infcx: &InferCtxt<'tcx>,
142        obligation: &PredicateObligation<'tcx>,
143        result: &Result<GoalEvaluation<TyCtxt<'tcx>>, NoSolution>,
144    ) {
145        if let Some(inspector) = infcx.obligation_inspector.get() {
146            let result = match result {
147                Ok(GoalEvaluation { certainty, .. }) => Ok(*certainty),
148                Err(NoSolution) => Err(NoSolution),
149            };
150            (inspector)(infcx, &obligation, result);
151        }
152    }
153}
154
155impl<'tcx, E> TraitEngine<'tcx, E> for FulfillmentCtxt<'tcx, E>
156where
157    E: FromSolverError<'tcx, NextSolverError<'tcx>>,
158{
159    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("register_predicate_obligation",
                                    "rustc_trait_selection::solve::fulfill",
                                    ::tracing::Level::TRACE,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_trait_selection/src/solve/fulfill.rs"),
                                    ::tracing_core::__macro_support::Option::Some(159u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_trait_selection::solve::fulfill"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("obligation")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("obligation");
                                                        NAME.as_str()
                                                    }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&obligation)
                                                            as &dyn ::tracing::field::Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: () = loop {};
            return __tracing_attr_fake_return;
        }
        {
            {
                match (&self.usable_in_snapshot, &infcx.num_open_snapshots())
                    {
                    (left_val, right_val) => {
                        if !(*left_val == *right_val) {
                            let kind = ::core::panicking::AssertKind::Eq;
                            ::core::panicking::assert_failed(kind, &*left_val,
                                &*right_val, ::core::option::Option::None);
                        }
                    }
                }
            };
            let delegate = <&SolverDelegate<'tcx>>::from(infcx);
            if let Some(GoalEvaluation {
                    goal: _, certainty, has_changed: _, stalled_on }) =
                    compute_goal_fast_path(delegate, obligation.as_goal(),
                        obligation.cause.span) {
                match certainty {
                    Certainty::Yes => {}
                    Certainty::Maybe(_) => {
                        self.obligations.register(obligation, stalled_on);
                    }
                }
            } else { self.obligations.register(obligation, None); }
        }
    }
}#[instrument(level = "trace", skip(self, infcx))]
160    fn register_predicate_obligation(
161        &mut self,
162        infcx: &InferCtxt<'tcx>,
163        obligation: PredicateObligation<'tcx>,
164    ) {
165        assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
166
167        let delegate = <&SolverDelegate<'tcx>>::from(infcx);
168        if let Some(GoalEvaluation { goal: _, certainty, has_changed: _, stalled_on }) =
169            compute_goal_fast_path(delegate, obligation.as_goal(), obligation.cause.span)
170        {
171            // If we can take the fast path, don't even bother adding the goal to obligations,
172            // or if `Certainty::Maybe`, add it with precise stalled_on information.
173            match certainty {
174                Certainty::Yes => {}
175                Certainty::Maybe(_) => {
176                    self.obligations.register(obligation, stalled_on);
177                }
178            }
179        } else {
180            self.obligations.register(obligation, None);
181        }
182    }
183
184    #[inline]
185    fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> TraitErrors<E> {
186        if self.obligations.pending.is_empty() && self.obligations.overflowed.is_empty() {
187            // Typically in more than 99.9% of cases this condition is true, therefore we outline
188            // the other case.
189            TraitErrors::NoErrors
190        } else {
191            TraitErrors::HasErrors(collect_remaining_errors_impl(self, infcx))
192        }
193    }
194
195    fn try_evaluate_obligations(&mut self, infcx: &InferCtxt<'tcx>) -> TraitErrors<E> {
196        {
    match (&self.usable_in_snapshot, &infcx.num_open_snapshots()) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
197        let mut errors = TraitErrors::NoErrors;
198        let delegate = <&SolverDelegate<'tcx>>::from(infcx);
199        loop {
200            let mut any_changed = false;
201            let mut overflowed = false;
202
203            self.obligations.pending.retain_mut(|(obligation, opt_stalled_on)| {
204                if overflowed {
205                    return false;
206                }
207
208                // Common case: still stalled; keep the obligation. This path is extremely hot in
209                // some cases; there can be thousands of pending obligations.
210                if let Some(stalled_on) = opt_stalled_on
211                    && let Some(certainty) = delegate.goal_remains_stalled(stalled_on)
212                    && #[allow(non_exhaustive_omitted_patterns)] match certainty {
    Certainty::Maybe(_) => true,
    _ => false,
}matches!(certainty, Certainty::Maybe(_))
213                {
214                    return true;
215                }
216
217                let result = delegate.evaluate_root_goal(
218                    obligation.as_goal(),
219                    obligation.cause.span,
220                    opt_stalled_on.take(),
221                );
222                Self::inspect_evaluated_obligation(infcx, &obligation, &result);
223                let GoalEvaluation { goal, certainty, has_changed, stalled_on } = match result {
224                    Ok(result) => result,
225                    Err(NoSolution) => {
226                        errors.push(E::from_solver_error(
227                            infcx,
228                            NextSolverError::TrueError(obligation.clone()),
229                        ));
230                        return false;
231                    }
232                };
233
234                // We've resolved the goal in `evaluate_root_goal`, avoid redoing this work
235                // in the next iteration. This does not resolve the inference variables
236                // constrained by evaluating the goal.
237                obligation.predicate = goal.predicate;
238                if has_changed == HasChanged::Yes {
239                    // We increment the recursion depth here to track the number of times
240                    // this goal has resulted in inference progress. This doesn't precisely
241                    // model the way that we track recursion depth in the old solver due
242                    // to the fact that we only process root obligations, but it is a good
243                    // approximation and should only result in fulfillment overflow in
244                    // pathological cases.
245                    obligation.recursion_depth += 1;
246
247                    if !infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) {
248                        // At this point we want to stop evaluating goals. We can't break out of
249                        // `retain_mut`, so instead we set this flag which causes all other
250                        // elements to be skipped.
251                        overflowed = true;
252                        return false;
253                    } else {
254                        any_changed = true;
255                    }
256                }
257
258                match certainty {
259                    Certainty::Yes => {
260                        // Goals may depend on structural identity. Region uniquification at the
261                        // start of MIR borrowck may cause things to no longer be so, potentially
262                        // causing an ICE.
263                        //
264                        // While we uniquify root goals in HIR this does not handle cases where
265                        // regions are hidden inside of a type or const inference variable.
266                        //
267                        // FIXME(-Znext-solver): This does not handle inference variables hidden
268                        // inside of an opaque type, e.g. if there's `Opaque = (?x, ?x)` in the
269                        // storage, we can also rely on structural identity of `?x` even if we
270                        // later uniquify it in MIR borrowck.
271                        if infcx.in_hir_typeck
272                            && (obligation.has_non_region_infer() || obligation.has_free_regions())
273                        {
274                            infcx.push_hir_typeck_potentially_region_dependent_goal(
275                                obligation.clone(),
276                            );
277                        }
278                        false
279                    }
280                    Certainty::Maybe(_) => {
281                        // Update `opt_stalled_on` goal, for the next retain_mut, because we are
282                        // running until a fixpoint.
283                        *opt_stalled_on = stalled_on;
284                        true
285                    }
286                }
287            });
288            if overflowed {
289                self.obligations.on_fulfillment_overflow(infcx);
290                // Only return true errors that we have accumulated while processing.
291                return errors;
292            }
293
294            if !any_changed {
295                break;
296            }
297        }
298
299        errors
300    }
301
302    fn has_pending_obligations(&self) -> bool {
303        self.obligations.has_pending_obligations()
304    }
305
306    fn pending_obligations(&self) -> PredicateObligations<'tcx> {
307        self.obligations.clone_pending()
308    }
309
310    fn pending_obligations_potentially_referencing_sub_root(
311        &self,
312        infcx: &InferCtxt<'tcx>,
313        vid: ty::TyVid,
314    ) -> PredicateObligations<'tcx> {
315        // `-Zdisable-fast-paths`: same gate as the other new-solver fast paths.
316        if infcx.tcx.disable_trait_solver_fast_paths() {
317            return self.obligations.clone_pending();
318        }
319        self.obligations.clone_pending_filtered(|(_, stalled_on)| {
320            let Some(stalled_on) = stalled_on else { return true };
321            // Don't reuse the sub-unification roots cached on `stalled_on`:
322            // a later sub-unification merge can have changed which root
323            // each stalled var belongs to, so the cached info can be stale.
324            // Walk `stalled_vars` and recompute the current root instead.
325            //
326            // Conservative here: if a stalled var no longer resolves to an
327            // infer var, some unification happened, so the goal is no longer
328            // stalled. Include it to be re-evaluated downstream.
329            stalled_on.stalled_vars.iter().filter_map(|arg| arg.as_type()).any(|ty| {
330                match *infcx.shallow_resolve(ty).kind() {
331                    ty::Infer(ty::TyVar(tv)) => infcx.sub_unification_table_root_var(tv) == vid,
332                    _ => true,
333                }
334            })
335        })
336    }
337
338    fn pending_obligations_potentially_referencing_float_infer(
339        &self,
340        infcx: &InferCtxt<'tcx>,
341    ) -> PredicateObligations<'tcx> {
342        // `-Zdisable-fast-paths`: same gate as the other new-solver fast paths.
343        if infcx.tcx.disable_trait_solver_fast_paths() {
344            return self.obligations.clone_pending();
345        }
346
347        self.obligations.clone_pending_filtered(|(_, stalled_on)| {
348            let Some(stalled_on) = stalled_on else { return true };
349            // If the stalled vars don't have float infers, the nested goals won't
350            // have them either. We only create float infers for user written literals.
351            stalled_on
352                .stalled_vars
353                .iter()
354                .filter_map(|arg| arg.as_type())
355                .any(|ty| #[allow(non_exhaustive_omitted_patterns)] match infcx.shallow_resolve(ty).kind()
    {
    ty::Infer(ty::FloatVar(_)) => true,
    _ => false,
}matches!(infcx.shallow_resolve(ty).kind(), ty::Infer(ty::FloatVar(_))))
356        })
357    }
358
359    fn drain_stalled_obligations_for_coroutines(
360        &mut self,
361        infcx: &InferCtxt<'tcx>,
362    ) -> PredicateObligations<'tcx> {
363        let stalled_coroutines = match infcx.typing_mode_raw().assert_not_erased() {
364            TypingMode::Typeck { defining_opaque_types_and_generators } => {
365                defining_opaque_types_and_generators
366            }
367            TypingMode::Coherence
368            | TypingMode::PostTypeckUntilBorrowck { defining_opaque_types: _ }
369            | TypingMode::PostBorrowck { defined_opaque_types: _ }
370            | TypingMode::Reflection
371            | TypingMode::PostAnalysis
372            | TypingMode::Codegen => return Default::default(),
373        };
374
375        if stalled_coroutines.is_empty() {
376            return Default::default();
377        }
378
379        self.obligations
380            .drain_pending(|_, stalled_on| {
381                stalled_on.as_ref().is_some_and(|s| match s.stalled_certainty {
382                    Certainty::Maybe(MaybeInfo {
383                        cause: _,
384                        opaque_types_jank: _,
385                        stalled_on_coroutines: StalledOnCoroutines::Yes,
386                    }) => true,
387                    Certainty::Maybe(_) | Certainty::Yes => false,
388                })
389            })
390            .into_iter()
391            .map(|(o, _)| o)
392            .collect()
393    }
394}
395
396#[cold]
397#[inline(never)]
398fn collect_remaining_errors_impl<'tcx, E>(
399    cx: &mut FulfillmentCtxt<'tcx, E>,
400    infcx: &InferCtxt<'tcx>,
401) -> ThinVec<E>
402where
403    E: FromSolverError<'tcx, NextSolverError<'tcx>>,
404{
405    cx.obligations
406        .pending
407        .drain(..)
408        .map(|(obligation, _)| NextSolverError::Ambiguity(obligation))
409        .chain(
410            cx.obligations
411                .overflowed
412                .drain(..)
413                .map(|obligation| NextSolverError::Overflow(obligation)),
414        )
415        .map(|e| E::from_solver_error(infcx, e))
416        .collect()
417}
418
419pub enum NextSolverError<'tcx> {
420    TrueError(PredicateObligation<'tcx>),
421    Ambiguity(PredicateObligation<'tcx>),
422    Overflow(PredicateObligation<'tcx>),
423}
424
425impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for FulfillmentError<'tcx> {
426    fn from_solver_error(infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
427        match error {
428            NextSolverError::TrueError(obligation) => {
429                fulfillment_error_for_no_solution(infcx, obligation)
430            }
431            NextSolverError::Ambiguity(obligation) => {
432                fulfillment_error_for_stalled(infcx, obligation)
433            }
434            NextSolverError::Overflow(obligation) => {
435                fulfillment_error_for_overflow(infcx, obligation)
436            }
437        }
438    }
439}
440
441impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<'tcx> {
442    fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
443        match error {
444            NextSolverError::TrueError(_) => ScrubbedTraitError::TrueError,
445            NextSolverError::Ambiguity(_) | NextSolverError::Overflow(_) => {
446                ScrubbedTraitError::Ambiguity
447            }
448        }
449    }
450}
451
452// Some types are used a lot. Make sure they don't unintentionally get bigger.
453#[cfg(target_pointer_width = "64")]
454mod size_asserts {
455    use rustc_data_structures::static_assert_size;
456
457    use super::*;
458    // tidy-alphabetical-start
459    // Before #160005 this pair was greater than 128 bytes, which triggered the use of (slow)
460    // `memcpy` for moving elements of `PendingObligations`.
461    const _: [(); 104] =
    [();
            ::std::mem::size_of::<(PredicateObligation<'_>,
                    Option<GoalStalledOn<TyCtxt<'_>>>)>()];static_assert_size!((PredicateObligation<'_>, Option<GoalStalledOn<TyCtxt<'_>>>), 104);
462    // tidy-alphabetical-end
463}