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, SolverDelegateEvalExt as _, StalledOnCoroutines,
13};
14use thin_vec::ThinVec;
15use tracing::instrument;
16
17use self::derive_errors::*;
18use super::Certainty;
19use super::delegate::SolverDelegate;
20use crate::error_reporting::InferCtxtErrorExt;
21use crate::traits::{FulfillmentError, FulfillmentErrorCode, ScrubbedTraitError};
22
23mod derive_errors;
24
25// `ThinVec` is important for performance, but not for the usual memory layout reasons.
26// `try_evaluate_obligations` is extremely hot and uses `retain_mut`. `ThinVec::retain_mut` is
27// simple and sub-optimal in terms of how it moves elements, but it can be inlined.
28// `Vec::retain_mut` is more sophisticated and minimizes element moves, but also contains more code
29// and doesn't get inlined in `try_evaluate_obligations`, giving worse performance overall.
30type PendingObligations<'tcx> =
31    ThinVec<(PredicateObligation<'tcx>, Option<GoalStalledOn<TyCtxt<'tcx>>>)>;
32
33/// A trait engine using the new trait solver.
34///
35/// This is mostly identical to how `evaluate_all` works inside of the
36/// solver, except that the requirements are slightly different.
37///
38/// Unlike `evaluate_all` it is possible to add new obligations later on
39/// and we also have to track diagnostics information by using `Obligation`
40/// instead of `Goal`.
41///
42/// It is also likely that we want to use slightly different datastructures
43/// here as this will have to deal with far more root goals than `evaluate_all`.
44pub struct FulfillmentCtxt<'tcx, E: 'tcx> {
45    obligations: ObligationStorage<'tcx>,
46
47    /// The snapshot in which this context was created. Using the context
48    /// outside of this snapshot leads to subtle bugs if the snapshot
49    /// gets rolled back. Because of this we explicitly check that we only
50    /// use the context in exactly this snapshot.
51    usable_in_snapshot: usize,
52    _errors: PhantomData<E>,
53}
54
55#[derive(#[automatically_derived]
impl<'tcx> ::core::default::Default for ObligationStorage<'tcx> {
    #[inline]
    fn default() -> Self {
        Self { 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_field1_finish(f,
            "ObligationStorage", "pending", &&self.pending)
    }
}Debug)]
56struct ObligationStorage<'tcx> {
57    pending: PendingObligations<'tcx>,
58}
59
60impl<'tcx> ObligationStorage<'tcx> {
61    fn register(
62        &mut self,
63        obligation: PredicateObligation<'tcx>,
64        stalled_on: Option<GoalStalledOn<TyCtxt<'tcx>>>,
65    ) {
66        self.pending.push((obligation, stalled_on));
67    }
68
69    fn has_pending_obligations(&self) -> bool {
70        !self.pending.is_empty()
71    }
72
73    fn clone_pending(&self) -> PredicateObligations<'tcx> {
74        self.pending.iter().map(|(o, _)| o.clone()).collect()
75    }
76
77    fn clone_pending_filtered<F>(&self, f: F) -> PredicateObligations<'tcx>
78    where
79        F: FnMut(&&(PredicateObligation<'tcx>, Option<GoalStalledOn<TyCtxt<'tcx>>>)) -> bool,
80    {
81        self.pending.iter().filter(f).map(|(o, _)| o.clone()).collect()
82    }
83
84    fn drain_pending(
85        &mut self,
86        cond: impl Fn(&PredicateObligation<'tcx>, &Option<GoalStalledOn<TyCtxt<'tcx>>>) -> bool,
87    ) -> PendingObligations<'tcx> {
88        let (unstalled, pending) =
89            mem::take(&mut self.pending).into_iter().partition(|(o, s)| cond(o, s));
90        self.pending = pending;
91        unstalled
92    }
93}
94
95impl<'tcx, E: 'tcx> FulfillmentCtxt<'tcx, E> {
96    pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx, E> {
97        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!(
98            infcx.next_trait_solver(),
99            "new trait solver fulfillment context created when \
100            infcx is set up for old trait solver"
101        );
102        FulfillmentCtxt {
103            obligations: Default::default(),
104            usable_in_snapshot: infcx.num_open_snapshots(),
105            _errors: PhantomData,
106        }
107    }
108
109    fn inspect_evaluated_obligation(
110        infcx: &InferCtxt<'tcx>,
111        obligation: &PredicateObligation<'tcx>,
112        result: &Result<GoalEvaluation<TyCtxt<'tcx>>, NoSolution>,
113    ) {
114        if let Some(inspector) = infcx.obligation_inspector.get() {
115            let result = match result {
116                Ok(GoalEvaluation { certainty, .. }) => Ok(*certainty),
117                Err(NoSolution) => Err(NoSolution),
118            };
119            (inspector)(infcx, &obligation, result);
120        }
121    }
122}
123
124impl<'tcx, E> TraitEngine<'tcx, E> for FulfillmentCtxt<'tcx, E>
125where
126    E: FromSolverError<'tcx, NextSolverError<'tcx>>,
127{
128    {}
#[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("/rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_trait_selection/src/solve/fulfill.rs"),
                                    ::tracing_core::__macro_support::Option::Some(128u32),
                                    ::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))]
129    fn register_predicate_obligation(
130        &mut self,
131        infcx: &InferCtxt<'tcx>,
132        obligation: PredicateObligation<'tcx>,
133    ) {
134        assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
135
136        let delegate = <&SolverDelegate<'tcx>>::from(infcx);
137        if let Some(GoalEvaluation { goal: _, certainty, has_changed: _, stalled_on }) =
138            compute_goal_fast_path(delegate, obligation.as_goal(), obligation.cause.span)
139        {
140            // If we can take the fast path, don't even bother adding the goal to obligations,
141            // or if `Certainty::Maybe`, add it with precise stalled_on information.
142            match certainty {
143                Certainty::Yes => {}
144                Certainty::Maybe(_) => {
145                    self.obligations.register(obligation, stalled_on);
146                }
147            }
148        } else {
149            self.obligations.register(obligation, None);
150        }
151    }
152
153    #[inline]
154    fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> TraitErrors<E> {
155        if self.obligations.pending.is_empty() {
156            // Typically in more than 99.9% of cases this condition is true, therefore we outline
157            // the other case.
158            TraitErrors::NoErrors
159        } else {
160            let errors = collect_remaining_errors_impl(self, infcx);
161            TraitErrors::from_iter(errors.into_iter())
162        }
163    }
164
165    fn try_evaluate_obligations(&mut self, infcx: &InferCtxt<'tcx>) -> TraitErrors<E> {
166        {
    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());
167        let mut errors = TraitErrors::NoErrors;
168        let delegate = <&SolverDelegate<'tcx>>::from(infcx);
169        loop {
170            let mut any_changed = false;
171
172            self.obligations.pending.retain_mut(|(obligation, opt_stalled_on)| {
173                // Common case: still stalled; keep the obligation. This path is extremely hot in
174                // some cases; there can be thousands of pending obligations.
175                if let Some(stalled_on) = opt_stalled_on
176                    && delegate.goal_remains_stalled(stalled_on)
177                {
178                    return true;
179                }
180
181                let result = delegate.evaluate_root_goal(
182                    obligation.as_goal(),
183                    obligation.cause.span,
184                    opt_stalled_on.take(),
185                );
186                Self::inspect_evaluated_obligation(infcx, &obligation, &result);
187                let GoalEvaluation { goal, certainty, has_changed, stalled_on } = match result {
188                    Ok(result) => result,
189                    Err(NoSolution) => {
190                        errors.push(E::from_solver_error(
191                            infcx,
192                            NextSolverError::TrueError(obligation.clone()),
193                        ));
194                        return false;
195                    }
196                };
197
198                // We've resolved the goal in `evaluate_root_goal`, avoid redoing this work
199                // in the next iteration. This does not resolve the inference variables
200                // constrained by evaluating the goal.
201                obligation.predicate = goal.predicate;
202                if has_changed == HasChanged::Yes {
203                    if !infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) {
204                        // We limit the total count of inference progress to avoid hang so we don't
205                        // try to recover from this.
206                        // It's more complicated to collect all overflows thus we stopped doing that.
207                        // Eager aborting is also what the old solver does.
208                        //
209                        // Note: it's incredibly rare to actually encounter fulfillment overflow
210                        // as a single obligation would have to result in different inference progress
211                        // a `recursion_depth` number of times. This mostly happens in bugs or with
212                        // `Subtype` obligations because we no longer use the `sub_unification_table`
213                        // in generalization.
214                        infcx.err_ctxt().report_overflow_obligation(obligation, true);
215                    } else {
216                        // We increment the recursion depth here to track the number of times
217                        // this goal has resulted in inference progress. This doesn't precisely
218                        // model the way that we track recursion depth in the old solver due
219                        // to the fact that we only process root obligations, but it is a good
220                        // approximation and should only result in fulfillment overflow in
221                        // pathological cases.
222                        obligation.recursion_depth += 1;
223                        any_changed = true;
224                    }
225                }
226
227                match certainty {
228                    Certainty::Yes => {
229                        // Goals may depend on structural identity. Region uniquification at the
230                        // start of MIR borrowck may cause things to no longer be so, potentially
231                        // causing an ICE.
232                        //
233                        // While we uniquify root goals in HIR this does not handle cases where
234                        // regions are hidden inside of a type or const inference variable.
235                        //
236                        // FIXME(-Znext-solver): This does not handle inference variables hidden
237                        // inside of an opaque type, e.g. if there's `Opaque = (?x, ?x)` in the
238                        // storage, we can also rely on structural identity of `?x` even if we
239                        // later uniquify it in MIR borrowck.
240                        if infcx.in_hir_typeck
241                            && (obligation.has_non_region_infer() || obligation.has_free_regions())
242                        {
243                            infcx.push_hir_typeck_potentially_region_dependent_goal(
244                                obligation.clone(),
245                            );
246                        }
247                        false
248                    }
249                    Certainty::Maybe(_) => {
250                        // Update `opt_stalled_on` goal, for the next retain_mut, because we are
251                        // running until a fixpoint.
252                        *opt_stalled_on = stalled_on;
253                        true
254                    }
255                }
256            });
257
258            if !any_changed {
259                break;
260            }
261        }
262
263        errors
264    }
265
266    fn has_pending_obligations(&self) -> bool {
267        self.obligations.has_pending_obligations()
268    }
269
270    fn pending_obligations(&self) -> PredicateObligations<'tcx> {
271        self.obligations.clone_pending()
272    }
273
274    fn pending_obligations_potentially_referencing_sub_root(
275        &self,
276        infcx: &InferCtxt<'tcx>,
277        vid: ty::TyVid,
278    ) -> PredicateObligations<'tcx> {
279        // `-Zdisable-fast-paths`: same gate as the other new-solver fast paths.
280        if infcx.tcx.disable_trait_solver_fast_paths() {
281            return self.obligations.clone_pending();
282        }
283        self.obligations.clone_pending_filtered(|(_, stalled_on)| {
284            let Some(stalled_on) = stalled_on else { return true };
285            // Don't reuse the sub-unification roots cached on `stalled_on`:
286            // a later sub-unification merge can have changed which root
287            // each stalled var belongs to, so the cached info can be stale.
288            // Walk `stalled_vars` and recompute the current root instead.
289            //
290            // Conservative here: if a stalled var no longer resolves to an
291            // infer var, some unification happened, so the goal is no longer
292            // stalled. Include it to be re-evaluated downstream.
293            stalled_on.stalled_vars.iter().filter_map(|arg| arg.as_type(infcx.tcx)).any(|ty| {
294                match *infcx.shallow_resolve(ty).kind() {
295                    ty::Infer(ty::TyVar(tv)) => infcx.sub_unification_table_root_var(tv) == vid,
296                    _ => true,
297                }
298            })
299        })
300    }
301
302    fn pending_obligations_potentially_referencing_float_infer(
303        &self,
304        infcx: &InferCtxt<'tcx>,
305    ) -> PredicateObligations<'tcx> {
306        // `-Zdisable-fast-paths`: same gate as the other new-solver fast paths.
307        if infcx.tcx.disable_trait_solver_fast_paths() {
308            return self.obligations.clone_pending();
309        }
310
311        self.obligations.clone_pending_filtered(|(_, stalled_on)| {
312            let Some(stalled_on) = stalled_on else { return true };
313            // If the stalled vars don't have float infers, the nested goals won't
314            // have them either. We only create float infers for user written literals.
315            stalled_on
316                .stalled_vars
317                .iter()
318                .filter_map(|arg| arg.as_type(infcx.tcx))
319                .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(_))))
320        })
321    }
322
323    fn drain_stalled_obligations_for_coroutines(
324        &mut self,
325        infcx: &InferCtxt<'tcx>,
326    ) -> PredicateObligations<'tcx> {
327        let stalled_coroutines = match infcx.typing_mode_raw().assert_not_erased() {
328            TypingMode::Typeck { defining_opaque_types_and_generators } => {
329                defining_opaque_types_and_generators
330            }
331            TypingMode::Coherence
332            | TypingMode::PostTypeckUntilBorrowck { defining_opaque_types: _ }
333            | TypingMode::PostBorrowck { defined_opaque_types: _ }
334            | TypingMode::Reflection
335            | TypingMode::PostAnalysis
336            | TypingMode::Codegen => return Default::default(),
337        };
338
339        if stalled_coroutines.is_empty() {
340            return Default::default();
341        }
342
343        self.obligations
344            .drain_pending(|_, stalled_on| {
345                stalled_on.as_ref().is_some_and(|s| {
346                    match s.stalled_maybe_info.stalled_on_coroutines {
347                        StalledOnCoroutines::Yes => true,
348                        StalledOnCoroutines::No => false,
349                    }
350                })
351            })
352            .into_iter()
353            .map(|(o, _)| o)
354            .collect()
355    }
356}
357
358#[cold]
359#[inline(never)]
360fn collect_remaining_errors_impl<'tcx, E>(
361    cx: &mut FulfillmentCtxt<'tcx, E>,
362    infcx: &InferCtxt<'tcx>,
363) -> ThinVec<E>
364where
365    E: FromSolverError<'tcx, NextSolverError<'tcx>>,
366{
367    cx.obligations
368        .pending
369        .drain(..)
370        .filter_map(|(obligation, _)| {
371            try_ambiguity_error_for_stalled(infcx, obligation).map(NextSolverError::Ambiguity)
372        })
373        .map(|e| E::from_solver_error(infcx, e))
374        .collect()
375}
376
377// We evaluate stalled obligations while collecting remaining errors because a
378// previously ambiguous goal may have become successful. In that case we emit a
379// delayed bug instead of producing a fulfillment error. Store the diagnostic
380// information here so error conversion does not reevaluate the goal.
381pub struct NextSolverAmbiguityError<'tcx> {
382    root_obligation: PredicateObligation<'tcx>,
383    code: FulfillmentErrorCode<'tcx>,
384    refine_obligation: bool,
385}
386
387pub enum NextSolverError<'tcx> {
388    TrueError(PredicateObligation<'tcx>),
389    Ambiguity(NextSolverAmbiguityError<'tcx>),
390}
391
392impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for FulfillmentError<'tcx> {
393    fn from_solver_error(infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
394        match error {
395            NextSolverError::TrueError(obligation) => {
396                fulfillment_error_for_no_solution(infcx, obligation)
397            }
398            NextSolverError::Ambiguity(ambiguity) => {
399                fulfillment_error_for_stalled(infcx, ambiguity)
400            }
401        }
402    }
403}
404
405impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<'tcx> {
406    fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
407        match error {
408            NextSolverError::TrueError(_) => ScrubbedTraitError::TrueError,
409            NextSolverError::Ambiguity(_) => ScrubbedTraitError::Ambiguity,
410        }
411    }
412}
413
414// Some types are used a lot. Make sure they don't unintentionally get bigger.
415#[cfg(target_pointer_width = "64")]
416mod size_asserts {
417    use rustc_data_structures::static_assert_size;
418
419    use super::*;
420    // tidy-alphabetical-start
421    // Before #160005 this pair was greater than 128 bytes, which triggered the use of (slow)
422    // `memcpy` for moving elements of `PendingObligations`. Then #160479 greatly reduced the
423    // number of `memcpy` operations in `try_evaluate_obligations`. So the size of this pair is
424    // much less important than it was, but still shouldn't be changed without some thought.
425    const _: [(); 104] =
    [();
            ::std::mem::size_of::<(PredicateObligation<'_>,
                    Option<GoalStalledOn<TyCtxt<'_>>>)>()];static_assert_size!((PredicateObligation<'_>, Option<GoalStalledOn<TyCtxt<'_>>>), 104);
426    // tidy-alphabetical-end
427}