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            .sub_raw(cause, param_env, DefineOpaqueTypes::Yes, expected, actual)
259            .map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
260    }
261
262    pub fn relate<T: ToTrace<'tcx>>(
263        &self,
264        cause: &ObligationCause<'tcx>,
265        param_env: ty::ParamEnv<'tcx>,
266        variance: Variance,
267        expected: T,
268        actual: T,
269    ) -> Result<(), TypeError<'tcx>> {
270        self.infcx
271            .at(cause, param_env)
272            .relate(DefineOpaqueTypes::Yes, expected, variance, actual)
273            .map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
274    }
275
276    /// Checks whether `expected` is a supertype of `actual`: `expected :> actual`.
277    pub fn sup<T: ToTrace<'tcx>>(
278        &self,
279        cause: &ObligationCause<'tcx>,
280        param_env: ty::ParamEnv<'tcx>,
281        expected: T,
282        actual: T,
283    ) -> Result<(), TypeError<'tcx>> {
284        self.infcx
285            .sup_raw(cause, param_env, DefineOpaqueTypes::Yes, expected, actual)
286            .map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
287    }
288
289    /// Computes the least-upper-bound, or mutual supertype, of two values.
290    pub fn lub<T: ToTrace<'tcx>>(
291        &self,
292        cause: &ObligationCause<'tcx>,
293        param_env: ty::ParamEnv<'tcx>,
294        expected: T,
295        actual: T,
296    ) -> Result<T, TypeError<'tcx>> {
297        self.infcx
298            .at(cause, param_env)
299            .lub(expected, actual)
300            .map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
301    }
302
303    /// Go over the list of pending obligations and try to evaluate them.
304    ///
305    /// For each result:
306    /// Ok: remove the obligation from the list
307    /// Ambiguous: leave the obligation in the list to be evaluated later
308    /// Err: remove the obligation from the list and return an error
309    ///
310    /// Returns a list of errors from obligations that evaluated to Err.
311    #[must_use]
312    pub fn try_evaluate_obligations(&self) -> TraitErrors<E> {
313        self.engine.borrow_mut().try_evaluate_obligations(self.infcx)
314    }
315
316    /// Evaluate all pending obligations, return error if they can't be evaluated.
317    ///
318    /// For each result:
319    /// Ok: remove the obligation from the list
320    /// Ambiguous: remove the obligation from the list and return an error
321    /// Err: remove the obligation from the list and return an error
322    ///
323    /// Returns a list of errors from obligations that evaluated to Ambiguous or Err.
324    #[must_use]
325    pub fn evaluate_obligations_error_on_ambiguity(&self) -> TraitErrors<E> {
326        self.engine.borrow_mut().evaluate_obligations_error_on_ambiguity(self.infcx)
327    }
328
329    /// Returns the not-yet-processed and stalled obligations from the
330    /// `ObligationCtxt`.
331    ///
332    /// Takes ownership of the context as doing operations such as
333    /// [`ObligationCtxt::eq`] afterwards will result in other obligations
334    /// getting ignored. You can make a new `ObligationCtxt` if this
335    /// needs to be done in a loop, for example.
336    #[must_use]
337    pub fn into_pending_obligations(self) -> PredicateObligations<'tcx> {
338        self.engine.borrow().pending_obligations()
339    }
340
341    /// Resolves regions and reports errors.
342    ///
343    /// Takes ownership of the context as doing trait solving afterwards
344    /// will result in region constraints getting ignored.
345    pub fn resolve_regions_and_report_errors(
346        self,
347        body_def_id: LocalDefId,
348        param_env: ty::ParamEnv<'tcx>,
349        assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>,
350    ) -> Result<(), ErrorGuaranteed> {
351        let errors = self.infcx.resolve_regions(body_def_id, param_env, assumed_wf_tys);
352        if errors.is_empty() {
353            Ok(())
354        } else {
355            Err(self.infcx.err_ctxt().report_region_errors(body_def_id, &errors))
356        }
357    }
358
359    /// Resolves regions and reports errors.
360    ///
361    /// Takes ownership of the context as doing trait solving afterwards
362    /// will result in region constraints getting ignored.
363    #[must_use]
364    pub fn resolve_regions(
365        self,
366        body_def_id: LocalDefId,
367        param_env: ty::ParamEnv<'tcx>,
368        assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>,
369    ) -> Vec<RegionResolutionError<'tcx>> {
370        self.infcx.resolve_regions(body_def_id, param_env, assumed_wf_tys)
371    }
372}
373
374impl<'tcx> ObligationCtxt<'_, 'tcx, FulfillmentError<'tcx>> {
375    pub fn assumed_wf_types_and_report_errors(
376        &self,
377        param_env: ty::ParamEnv<'tcx>,
378        def_id: LocalDefId,
379    ) -> Result<FxIndexSet<Ty<'tcx>>, ErrorGuaranteed> {
380        self.assumed_wf_types(param_env, def_id)
381            .map_err(|errors| self.infcx.err_ctxt().report_fulfillment_errors(errors))
382    }
383}
384
385impl<'tcx> ObligationCtxt<'_, 'tcx, ScrubbedTraitError<'tcx>> {
386    pub fn make_canonicalized_query_response<T>(
387        &self,
388        inference_vars: CanonicalVarValues<'tcx>,
389        answer: T,
390    ) -> Result<CanonicalQueryResponse<'tcx, T>, NoSolution>
391    where
392        T: Debug + TypeFoldable<TyCtxt<'tcx>>,
393        Canonical<'tcx, QueryResponse<'tcx, T>>: ArenaAllocatable<'tcx>,
394    {
395        self.infcx.make_canonicalized_query_response(
396            inference_vars,
397            answer,
398            &mut *self.engine.borrow_mut(),
399        )
400    }
401}
402
403impl<'tcx, E> ObligationCtxt<'_, 'tcx, E>
404where
405    E: FromSolverError<'tcx, NextSolverError<'tcx>> + FromSolverError<'tcx, OldSolverError<'tcx>>,
406{
407    pub fn assumed_wf_types(
408        &self,
409        param_env: ty::ParamEnv<'tcx>,
410        def_id: LocalDefId,
411    ) -> Result<FxIndexSet<Ty<'tcx>>, ThinVec<E>> {
412        let tcx = self.infcx.tcx;
413        let mut implied_bounds = FxIndexSet::default();
414        let mut errors = ThinVec::new();
415        for &(ty, span) in tcx.assumed_wf_types(def_id) {
416            // FIXME(@lcnr): rustc currently does not check wf for types
417            // pre-normalization, meaning that implied bounds are sometimes
418            // incorrect. See #100910 for more details.
419            //
420            // Not adding the unnormalized types here mostly fixes that, except
421            // that there are projections which are still ambiguous in the item definition
422            // but do normalize successfully when using the item, see #98543.
423            //
424            // Anyways, I will hopefully soon change implied bounds to make all of this
425            // sound and then uncomment this line again.
426
427            // implied_bounds.insert(ty);
428            let cause = ObligationCause::misc(span, def_id);
429            match self
430                .infcx
431                .at(&cause, param_env)
432                .deeply_normalize(Unnormalized::new_wip(ty), &mut *self.engine.borrow_mut())
433            {
434                // Insert well-formed types, ignoring duplicates.
435                Ok(normalized) => drop(implied_bounds.insert(normalized)),
436                Err(normalization_errors) => errors.extend(normalization_errors),
437            };
438        }
439
440        if errors.is_empty() { Ok(implied_bounds) } else { Err(errors) }
441    }
442
443    pub fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
444        &self,
445        cause: &ObligationCause<'tcx>,
446        param_env: ty::ParamEnv<'tcx>,
447        value: Unnormalized<'tcx, T>,
448    ) -> Result<T, ThinVec<E>> {
449        self.infcx.at(cause, param_env).deeply_normalize(value, &mut *self.engine.borrow_mut())
450    }
451
452    pub fn structurally_normalize_ty(
453        &self,
454        cause: &ObligationCause<'tcx>,
455        param_env: ty::ParamEnv<'tcx>,
456        value: Unnormalized<'tcx, Ty<'tcx>>,
457    ) -> Result<Ty<'tcx>, ThinVec<E>> {
458        self.infcx
459            .at(cause, param_env)
460            .structurally_normalize_ty(value, &mut *self.engine.borrow_mut())
461    }
462
463    pub fn structurally_normalize_const(
464        &self,
465        cause: &ObligationCause<'tcx>,
466        param_env: ty::ParamEnv<'tcx>,
467        value: Unnormalized<'tcx, ty::Const<'tcx>>,
468    ) -> Result<ty::Const<'tcx>, ThinVec<E>> {
469        self.infcx
470            .at(cause, param_env)
471            .structurally_normalize_const(value, &mut *self.engine.borrow_mut())
472    }
473
474    pub fn structurally_normalize_term(
475        &self,
476        cause: &ObligationCause<'tcx>,
477        param_env: ty::ParamEnv<'tcx>,
478        value: Unnormalized<'tcx, ty::Term<'tcx>>,
479    ) -> Result<ty::Term<'tcx>, ThinVec<E>> {
480        self.infcx
481            .at(cause, param_env)
482            .structurally_normalize_term(value, &mut *self.engine.borrow_mut())
483    }
484}