Skip to main content

rustc_trait_selection/traits/
engine.rs

1use std::cell::RefCell;
2use std::fmt::Debug;
3
4use rustc_data_structures::fx::FxIndexSet;
5use rustc_errors::ErrorGuaranteed;
6use rustc_hir::def_id::{DefId, LocalDefId};
7use rustc_infer::infer::at::ToTrace;
8use rustc_infer::infer::canonical::{
9    Canonical, CanonicalQueryResponse, CanonicalVarValues, QueryResponse,
10};
11use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError, TypeTrace};
12use rustc_infer::traits::{PredicateObligations, TraitErrors};
13use rustc_middle::arena::ArenaAllocatable;
14use rustc_middle::traits::query::NoSolution;
15use rustc_middle::ty::error::TypeError;
16use rustc_middle::ty::relate::Relate;
17use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Unnormalized, Upcast, Variance};
18use thin_vec::ThinVec;
19
20use super::{FromSolverError, FulfillmentContext, ScrubbedTraitError, TraitEngine};
21use crate::error_reporting::InferCtxtErrorExt;
22use crate::regions::InferCtxtRegionExt;
23use crate::solve::{FulfillmentCtxt as NextFulfillmentCtxt, NextSolverError};
24use crate::traits::fulfill::OldSolverError;
25use crate::traits::{
26    FulfillmentError, NormalizeExt, Obligation, ObligationCause, PredicateObligation,
27    StructurallyNormalizeExt,
28};
29
30/// A fulfillment engine, stored inline rather than boxed as a
31/// `dyn TraitEngine` because some of its holders (e.g. [`ObligationCtxt`])
32/// are created very often (once per candidate probe during method
33/// resolution), so the heap allocation would be expensive.
34pub enum FulfillmentEngine<'tcx, E> {
35    Old(FulfillmentContext<'tcx, E>),
36    Next(NextFulfillmentCtxt<'tcx, E>),
37}
38
39impl<'tcx, E> FulfillmentEngine<'tcx, E>
40where
41    E: FromSolverError<'tcx, NextSolverError<'tcx>> + FromSolverError<'tcx, OldSolverError<'tcx>>,
42{
43    pub fn new(infcx: &InferCtxt<'tcx>) -> Self {
44        if infcx.next_trait_solver() {
45            FulfillmentEngine::Next(NextFulfillmentCtxt::new(infcx))
46        } else {
47            if !!infcx.tcx.next_trait_solver_globally() {
    {
        ::core::panicking::panic_fmt(format_args!("using old solver even though new solver is enabled globally"));
    }
};assert!(
48                !infcx.tcx.next_trait_solver_globally(),
49                "using old solver even though new solver is enabled globally"
50            );
51            FulfillmentEngine::Old(FulfillmentContext::new(infcx))
52        }
53    }
54}
55
56impl<'tcx, E> TraitEngine<'tcx, E> for FulfillmentEngine<'tcx, E>
57where
58    E: FromSolverError<'tcx, NextSolverError<'tcx>> + FromSolverError<'tcx, OldSolverError<'tcx>>,
59{
60    fn register_predicate_obligation(
61        &mut self,
62        infcx: &InferCtxt<'tcx>,
63        obligation: PredicateObligation<'tcx>,
64    ) {
65        match self {
66            FulfillmentEngine::Old(engine) => {
67                engine.register_predicate_obligation(infcx, obligation)
68            }
69            FulfillmentEngine::Next(engine) => {
70                engine.register_predicate_obligation(infcx, obligation)
71            }
72        }
73    }
74
75    fn register_predicate_obligations(
76        &mut self,
77        infcx: &InferCtxt<'tcx>,
78        obligations: PredicateObligations<'tcx>,
79    ) {
80        match self {
81            FulfillmentEngine::Old(engine) => {
82                engine.register_predicate_obligations(infcx, obligations)
83            }
84            FulfillmentEngine::Next(engine) => {
85                engine.register_predicate_obligations(infcx, obligations)
86            }
87        }
88    }
89
90    fn try_evaluate_obligations(&mut self, infcx: &InferCtxt<'tcx>) -> TraitErrors<E> {
91        match self {
92            FulfillmentEngine::Old(engine) => engine.try_evaluate_obligations(infcx),
93            FulfillmentEngine::Next(engine) => engine.try_evaluate_obligations(infcx),
94        }
95    }
96
97    fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> TraitErrors<E> {
98        match self {
99            FulfillmentEngine::Old(engine) => engine.collect_remaining_errors(infcx),
100            FulfillmentEngine::Next(engine) => engine.collect_remaining_errors(infcx),
101        }
102    }
103
104    fn has_pending_obligations(&self) -> bool {
105        match self {
106            FulfillmentEngine::Old(engine) => engine.has_pending_obligations(),
107            FulfillmentEngine::Next(engine) => engine.has_pending_obligations(),
108        }
109    }
110
111    fn pending_obligations(&self) -> PredicateObligations<'tcx> {
112        match self {
113            FulfillmentEngine::Old(engine) => engine.pending_obligations(),
114            FulfillmentEngine::Next(engine) => engine.pending_obligations(),
115        }
116    }
117
118    fn pending_obligations_potentially_referencing_sub_root(
119        &self,
120        infcx: &InferCtxt<'tcx>,
121        sub_root: ty::TyVid,
122    ) -> PredicateObligations<'tcx> {
123        match self {
124            FulfillmentEngine::Old(engine) => {
125                engine.pending_obligations_potentially_referencing_sub_root(infcx, sub_root)
126            }
127            FulfillmentEngine::Next(engine) => {
128                engine.pending_obligations_potentially_referencing_sub_root(infcx, sub_root)
129            }
130        }
131    }
132
133    fn drain_stalled_obligations_for_coroutines(
134        &mut self,
135        infcx: &InferCtxt<'tcx>,
136    ) -> PredicateObligations<'tcx> {
137        match self {
138            FulfillmentEngine::Old(engine) => {
139                engine.drain_stalled_obligations_for_coroutines(infcx)
140            }
141            FulfillmentEngine::Next(engine) => {
142                engine.drain_stalled_obligations_for_coroutines(infcx)
143            }
144        }
145    }
146}
147
148/// Used if you want to have pleasant experience when dealing
149/// with obligations outside of hir or mir typeck.
150pub struct ObligationCtxt<'a, 'tcx, E = ScrubbedTraitError<'tcx>> {
151    pub infcx: &'a InferCtxt<'tcx>,
152    engine: RefCell<FulfillmentEngine<'tcx, E>>,
153}
154
155impl<'a, 'tcx> ObligationCtxt<'a, 'tcx, FulfillmentError<'tcx>> {
156    pub fn new_with_diagnostics(infcx: &'a InferCtxt<'tcx>) -> Self {
157        Self { infcx, engine: RefCell::new(FulfillmentEngine::new(infcx)) }
158    }
159}
160
161impl<'a, 'tcx> ObligationCtxt<'a, 'tcx, ScrubbedTraitError<'tcx>> {
162    pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self {
163        Self { infcx, engine: RefCell::new(FulfillmentEngine::new(infcx)) }
164    }
165}
166
167impl<'a, 'tcx, E> ObligationCtxt<'a, 'tcx, E>
168where
169    E: FromSolverError<'tcx, NextSolverError<'tcx>> + FromSolverError<'tcx, OldSolverError<'tcx>>,
170{
171    pub fn register_obligation(&self, obligation: PredicateObligation<'tcx>) {
172        self.engine.borrow_mut().register_predicate_obligation(self.infcx, obligation);
173    }
174
175    pub fn register_obligations(
176        &self,
177        obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>,
178    ) {
179        // Can't use `register_predicate_obligations` because the iterator
180        // may also use this `ObligationCtxt`.
181        for obligation in obligations {
182            self.engine.borrow_mut().register_predicate_obligation(self.infcx, obligation)
183        }
184    }
185
186    pub fn register_infer_ok_obligations<T>(&self, infer_ok: InferOk<'tcx, T>) -> T {
187        let InferOk { value, obligations } = infer_ok;
188        self.engine.borrow_mut().register_predicate_obligations(self.infcx, obligations);
189        value
190    }
191
192    /// Requires that `ty` must implement the trait with `def_id` in
193    /// the given environment. This trait must not have any type
194    /// parameters (except for `Self`).
195    pub fn register_bound(
196        &self,
197        cause: ObligationCause<'tcx>,
198        param_env: ty::ParamEnv<'tcx>,
199        ty: Ty<'tcx>,
200        def_id: DefId,
201    ) {
202        let tcx = self.infcx.tcx;
203        let trait_ref = ty::TraitRef::new(tcx, def_id, [ty]);
204        self.register_obligation(Obligation {
205            cause,
206            recursion_depth: 0,
207            param_env,
208            predicate: trait_ref.upcast(tcx),
209        });
210    }
211
212    pub fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
213        &self,
214        cause: &ObligationCause<'tcx>,
215        param_env: ty::ParamEnv<'tcx>,
216        value: Unnormalized<'tcx, T>,
217    ) -> T {
218        let infer_ok = self.infcx.at(cause, param_env).normalize(value);
219        self.register_infer_ok_obligations(infer_ok)
220    }
221
222    pub fn eq<T: ToTrace<'tcx>>(
223        &self,
224        cause: &ObligationCause<'tcx>,
225        param_env: ty::ParamEnv<'tcx>,
226        expected: T,
227        actual: T,
228    ) -> Result<(), TypeError<'tcx>> {
229        self.infcx
230            .at(cause, param_env)
231            .eq(DefineOpaqueTypes::Yes, expected, actual)
232            .map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
233    }
234
235    pub fn eq_trace<T: Relate<TyCtxt<'tcx>>>(
236        &self,
237        cause: &ObligationCause<'tcx>,
238        param_env: ty::ParamEnv<'tcx>,
239        trace: TypeTrace<'tcx>,
240        expected: T,
241        actual: T,
242    ) -> Result<(), TypeError<'tcx>> {
243        self.infcx
244            .at(cause, param_env)
245            .eq_trace(DefineOpaqueTypes::Yes, trace, expected, actual)
246            .map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
247    }
248
249    /// Checks whether `expected` is a subtype of `actual`: `expected <: actual`.
250    pub fn sub<T: ToTrace<'tcx>>(
251        &self,
252        cause: &ObligationCause<'tcx>,
253        param_env: ty::ParamEnv<'tcx>,
254        expected: T,
255        actual: T,
256    ) -> Result<(), TypeError<'tcx>> {
257        self.infcx
258            .at(cause, param_env)
259            .sub(DefineOpaqueTypes::Yes, expected, actual)
260            .map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
261    }
262
263    pub fn relate<T: ToTrace<'tcx>>(
264        &self,
265        cause: &ObligationCause<'tcx>,
266        param_env: ty::ParamEnv<'tcx>,
267        variance: Variance,
268        expected: T,
269        actual: T,
270    ) -> Result<(), TypeError<'tcx>> {
271        self.infcx
272            .at(cause, param_env)
273            .relate(DefineOpaqueTypes::Yes, expected, variance, actual)
274            .map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
275    }
276
277    /// Checks whether `expected` is a supertype of `actual`: `expected :> actual`.
278    pub fn sup<T: ToTrace<'tcx>>(
279        &self,
280        cause: &ObligationCause<'tcx>,
281        param_env: ty::ParamEnv<'tcx>,
282        expected: T,
283        actual: T,
284    ) -> Result<(), TypeError<'tcx>> {
285        self.infcx
286            .at(cause, param_env)
287            .sup(DefineOpaqueTypes::Yes, expected, actual)
288            .map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
289    }
290
291    /// Computes the least-upper-bound, or mutual supertype, of two values.
292    pub fn lub<T: ToTrace<'tcx>>(
293        &self,
294        cause: &ObligationCause<'tcx>,
295        param_env: ty::ParamEnv<'tcx>,
296        expected: T,
297        actual: T,
298    ) -> Result<T, TypeError<'tcx>> {
299        self.infcx
300            .at(cause, param_env)
301            .lub(expected, actual)
302            .map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
303    }
304
305    /// Go over the list of pending obligations and try to evaluate them.
306    ///
307    /// For each result:
308    /// Ok: remove the obligation from the list
309    /// Ambiguous: leave the obligation in the list to be evaluated later
310    /// Err: remove the obligation from the list and return an error
311    ///
312    /// Returns a list of errors from obligations that evaluated to Err.
313    #[must_use]
314    pub fn try_evaluate_obligations(&self) -> TraitErrors<E> {
315        self.engine.borrow_mut().try_evaluate_obligations(self.infcx)
316    }
317
318    /// Evaluate all pending obligations, return error if they can't be evaluated.
319    ///
320    /// For each result:
321    /// Ok: remove the obligation from the list
322    /// Ambiguous: remove the obligation from the list and return an error
323    /// Err: remove the obligation from the list and return an error
324    ///
325    /// Returns a list of errors from obligations that evaluated to Ambiguous or Err.
326    #[must_use]
327    pub fn evaluate_obligations_error_on_ambiguity(&self) -> TraitErrors<E> {
328        self.engine.borrow_mut().evaluate_obligations_error_on_ambiguity(self.infcx)
329    }
330
331    /// Returns the not-yet-processed and stalled obligations from the
332    /// `ObligationCtxt`.
333    ///
334    /// Takes ownership of the context as doing operations such as
335    /// [`ObligationCtxt::eq`] afterwards will result in other obligations
336    /// getting ignored. You can make a new `ObligationCtxt` if this
337    /// needs to be done in a loop, for example.
338    #[must_use]
339    pub fn into_pending_obligations(self) -> PredicateObligations<'tcx> {
340        self.engine.borrow().pending_obligations()
341    }
342
343    /// Resolves regions and reports errors.
344    ///
345    /// Takes ownership of the context as doing trait solving afterwards
346    /// will result in region constraints getting ignored.
347    pub fn resolve_regions_and_report_errors(
348        self,
349        body_def_id: LocalDefId,
350        param_env: ty::ParamEnv<'tcx>,
351        assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>,
352    ) -> Result<(), ErrorGuaranteed> {
353        let errors = self.infcx.resolve_regions(body_def_id, param_env, assumed_wf_tys);
354        if errors.is_empty() {
355            Ok(())
356        } else {
357            Err(self.infcx.err_ctxt().report_region_errors(body_def_id, &errors))
358        }
359    }
360
361    /// Resolves regions and reports errors.
362    ///
363    /// Takes ownership of the context as doing trait solving afterwards
364    /// will result in region constraints getting ignored.
365    #[must_use]
366    pub fn resolve_regions(
367        self,
368        body_def_id: LocalDefId,
369        param_env: ty::ParamEnv<'tcx>,
370        assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>,
371    ) -> Vec<RegionResolutionError<'tcx>> {
372        self.infcx.resolve_regions(body_def_id, param_env, assumed_wf_tys)
373    }
374}
375
376impl<'tcx> ObligationCtxt<'_, 'tcx, FulfillmentError<'tcx>> {
377    pub fn assumed_wf_types_and_report_errors(
378        &self,
379        param_env: ty::ParamEnv<'tcx>,
380        def_id: LocalDefId,
381    ) -> Result<FxIndexSet<Ty<'tcx>>, ErrorGuaranteed> {
382        self.assumed_wf_types(param_env, def_id)
383            .map_err(|errors| self.infcx.err_ctxt().report_fulfillment_errors(errors))
384    }
385}
386
387impl<'tcx> ObligationCtxt<'_, 'tcx, ScrubbedTraitError<'tcx>> {
388    pub fn make_canonicalized_query_response<T>(
389        &self,
390        inference_vars: CanonicalVarValues<'tcx>,
391        answer: T,
392    ) -> Result<CanonicalQueryResponse<'tcx, T>, NoSolution>
393    where
394        T: Debug + TypeFoldable<TyCtxt<'tcx>>,
395        Canonical<'tcx, QueryResponse<'tcx, T>>: ArenaAllocatable<'tcx>,
396    {
397        self.infcx.make_canonicalized_query_response(
398            inference_vars,
399            answer,
400            &mut *self.engine.borrow_mut(),
401        )
402    }
403}
404
405impl<'tcx, E> ObligationCtxt<'_, 'tcx, E>
406where
407    E: FromSolverError<'tcx, NextSolverError<'tcx>> + FromSolverError<'tcx, OldSolverError<'tcx>>,
408{
409    pub fn assumed_wf_types(
410        &self,
411        param_env: ty::ParamEnv<'tcx>,
412        def_id: LocalDefId,
413    ) -> Result<FxIndexSet<Ty<'tcx>>, ThinVec<E>> {
414        let tcx = self.infcx.tcx;
415        let mut implied_bounds = FxIndexSet::default();
416        let mut errors = ThinVec::new();
417        for &(ty, span) in tcx.assumed_wf_types(def_id) {
418            // FIXME(@lcnr): rustc currently does not check wf for types
419            // pre-normalization, meaning that implied bounds are sometimes
420            // incorrect. See #100910 for more details.
421            //
422            // Not adding the unnormalized types here mostly fixes that, except
423            // that there are projections which are still ambiguous in the item definition
424            // but do normalize successfully when using the item, see #98543.
425            //
426            // Anyways, I will hopefully soon change implied bounds to make all of this
427            // sound and then uncomment this line again.
428
429            // implied_bounds.insert(ty);
430            let cause = ObligationCause::misc(span, def_id);
431            match self
432                .infcx
433                .at(&cause, param_env)
434                .deeply_normalize(Unnormalized::new_wip(ty), &mut *self.engine.borrow_mut())
435            {
436                // Insert well-formed types, ignoring duplicates.
437                Ok(normalized) => drop(implied_bounds.insert(normalized)),
438                Err(normalization_errors) => errors.extend(normalization_errors),
439            };
440        }
441
442        if errors.is_empty() { Ok(implied_bounds) } else { Err(errors) }
443    }
444
445    pub fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
446        &self,
447        cause: &ObligationCause<'tcx>,
448        param_env: ty::ParamEnv<'tcx>,
449        value: Unnormalized<'tcx, T>,
450    ) -> Result<T, ThinVec<E>> {
451        self.infcx.at(cause, param_env).deeply_normalize(value, &mut *self.engine.borrow_mut())
452    }
453
454    pub fn structurally_normalize_ty(
455        &self,
456        cause: &ObligationCause<'tcx>,
457        param_env: ty::ParamEnv<'tcx>,
458        value: Unnormalized<'tcx, Ty<'tcx>>,
459    ) -> Result<Ty<'tcx>, ThinVec<E>> {
460        self.infcx
461            .at(cause, param_env)
462            .structurally_normalize_ty(value, &mut *self.engine.borrow_mut())
463    }
464
465    pub fn structurally_normalize_const(
466        &self,
467        cause: &ObligationCause<'tcx>,
468        param_env: ty::ParamEnv<'tcx>,
469        value: Unnormalized<'tcx, ty::Const<'tcx>>,
470    ) -> Result<ty::Const<'tcx>, ThinVec<E>> {
471        self.infcx
472            .at(cause, param_env)
473            .structurally_normalize_const(value, &mut *self.engine.borrow_mut())
474    }
475
476    pub fn structurally_normalize_term(
477        &self,
478        cause: &ObligationCause<'tcx>,
479        param_env: ty::ParamEnv<'tcx>,
480        value: Unnormalized<'tcx, ty::Term<'tcx>>,
481    ) -> Result<ty::Term<'tcx>, ThinVec<E>> {
482        self.infcx
483            .at(cause, param_env)
484            .structurally_normalize_term(value, &mut *self.engine.borrow_mut())
485    }
486}