rustc_hir_typeck/
typeck_root_ctxt.rs

1use std::cell::RefCell;
2use std::ops::Deref;
3
4use rustc_data_structures::unord::{UnordMap, UnordSet};
5use rustc_hir as hir;
6use rustc_hir::def_id::LocalDefId;
7use rustc_hir::{HirId, HirIdMap};
8use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt};
9use rustc_middle::span_bug;
10use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypingMode};
11use rustc_span::Span;
12use rustc_span::def_id::LocalDefIdMap;
13use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
14use rustc_trait_selection::traits::{
15    self, FulfillmentError, PredicateObligation, TraitEngine, TraitEngineExt as _,
16};
17use tracing::{debug, instrument};
18
19use super::callee::DeferredCallResolution;
20
21/// Data shared between a "typeck root" and its nested bodies,
22/// e.g. closures defined within the function. For example:
23/// ```ignore (illustrative)
24/// fn foo() {
25///     bar(move || { ... })
26/// }
27/// ```
28/// Here, the function `foo()` and the closure passed to
29/// `bar()` will each have their own `FnCtxt`, but they will
30/// share the inference context, will process obligations together,
31/// can access each other's local types (scoping permitted), etc.
32pub(crate) struct TypeckRootCtxt<'tcx> {
33    pub(super) infcx: InferCtxt<'tcx>,
34
35    pub(super) typeck_results: RefCell<ty::TypeckResults<'tcx>>,
36
37    pub(super) locals: RefCell<HirIdMap<Ty<'tcx>>>,
38
39    pub(super) fulfillment_cx: RefCell<Box<dyn TraitEngine<'tcx, FulfillmentError<'tcx>>>>,
40
41    /// Some additional `Sized` obligations badly affect type inference.
42    /// These obligations are added in a later stage of typeck.
43    /// Removing these may also cause additional complications, see #101066.
44    pub(super) deferred_sized_obligations:
45        RefCell<Vec<(Ty<'tcx>, Span, traits::ObligationCauseCode<'tcx>)>>,
46
47    /// When we process a call like `c()` where `c` is a closure type,
48    /// we may not have decided yet whether `c` is a `Fn`, `FnMut`, or
49    /// `FnOnce` closure. In that case, we defer full resolution of the
50    /// call until upvar inference can kick in and make the
51    /// decision. We keep these deferred resolutions grouped by the
52    /// def-id of the closure, so that once we decide, we can easily go
53    /// back and process them.
54    pub(super) deferred_call_resolutions: RefCell<LocalDefIdMap<Vec<DeferredCallResolution<'tcx>>>>,
55
56    pub(super) deferred_cast_checks: RefCell<Vec<super::cast::CastCheck<'tcx>>>,
57
58    pub(super) deferred_transmute_checks: RefCell<Vec<(Ty<'tcx>, Ty<'tcx>, HirId)>>,
59
60    pub(super) deferred_asm_checks: RefCell<Vec<(&'tcx hir::InlineAsm<'tcx>, HirId)>>,
61
62    pub(super) deferred_coroutine_interiors: RefCell<Vec<(LocalDefId, Ty<'tcx>)>>,
63
64    pub(super) deferred_repeat_expr_checks:
65        RefCell<Vec<(&'tcx hir::Expr<'tcx>, Ty<'tcx>, ty::Const<'tcx>)>>,
66
67    /// Whenever we introduce an adjustment from `!` into a type variable,
68    /// we record that type variable here. This is later used to inform
69    /// fallback. See the `fallback` module for details.
70    pub(super) diverging_type_vars: RefCell<UnordSet<Ty<'tcx>>>,
71
72    pub(super) infer_var_info: RefCell<UnordMap<ty::TyVid, ty::InferVarInfo>>,
73}
74
75impl<'tcx> Deref for TypeckRootCtxt<'tcx> {
76    type Target = InferCtxt<'tcx>;
77    fn deref(&self) -> &Self::Target {
78        &self.infcx
79    }
80}
81
82impl<'tcx> TypeckRootCtxt<'tcx> {
83    pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
84        let hir_owner = tcx.local_def_id_to_hir_id(def_id).owner;
85
86        let infcx =
87            tcx.infer_ctxt().ignoring_regions().build(TypingMode::analysis_in_body(tcx, def_id));
88        let typeck_results = RefCell::new(ty::TypeckResults::new(hir_owner));
89
90        TypeckRootCtxt {
91            typeck_results,
92            fulfillment_cx: RefCell::new(<dyn TraitEngine<'_, _>>::new(&infcx)),
93            infcx,
94            locals: RefCell::new(Default::default()),
95            deferred_sized_obligations: RefCell::new(Vec::new()),
96            deferred_call_resolutions: RefCell::new(Default::default()),
97            deferred_cast_checks: RefCell::new(Vec::new()),
98            deferred_transmute_checks: RefCell::new(Vec::new()),
99            deferred_asm_checks: RefCell::new(Vec::new()),
100            deferred_coroutine_interiors: RefCell::new(Vec::new()),
101            deferred_repeat_expr_checks: RefCell::new(Vec::new()),
102            diverging_type_vars: RefCell::new(Default::default()),
103            infer_var_info: RefCell::new(Default::default()),
104        }
105    }
106
107    #[instrument(level = "debug", skip(self))]
108    pub(super) fn register_predicate(&self, obligation: traits::PredicateObligation<'tcx>) {
109        if obligation.has_escaping_bound_vars() {
110            span_bug!(obligation.cause.span, "escaping bound vars in predicate {:?}", obligation);
111        }
112
113        self.update_infer_var_info(&obligation);
114
115        self.fulfillment_cx.borrow_mut().register_predicate_obligation(self, obligation);
116    }
117
118    pub(super) fn register_predicates<I>(&self, obligations: I)
119    where
120        I: IntoIterator<Item = traits::PredicateObligation<'tcx>>,
121    {
122        for obligation in obligations {
123            self.register_predicate(obligation);
124        }
125    }
126
127    pub(super) fn register_infer_ok_obligations<T>(&self, infer_ok: InferOk<'tcx, T>) -> T {
128        self.register_predicates(infer_ok.obligations);
129        infer_ok.value
130    }
131
132    fn update_infer_var_info(&self, obligation: &PredicateObligation<'tcx>) {
133        let infer_var_info = &mut self.infer_var_info.borrow_mut();
134
135        // (*) binder skipped
136        if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(tpred)) =
137            obligation.predicate.kind().skip_binder()
138            && let Some(ty) =
139                self.shallow_resolve(tpred.self_ty()).ty_vid().map(|t| self.root_var(t))
140            && self.tcx.lang_items().sized_trait().is_some_and(|st| st != tpred.trait_ref.def_id)
141        {
142            let new_self_ty = self.tcx.types.unit;
143
144            // Then construct a new obligation with Self = () added
145            // to the ParamEnv, and see if it holds.
146            let o = obligation.with(
147                self.tcx,
148                obligation.predicate.kind().rebind(
149                    // (*) binder moved here
150                    ty::PredicateKind::Clause(ty::ClauseKind::Trait(
151                        tpred.with_self_ty(self.tcx, new_self_ty),
152                    )),
153                ),
154            );
155            // Don't report overflow errors. Otherwise equivalent to may_hold.
156            if let Ok(result) = self.probe(|_| self.evaluate_obligation(&o))
157                && result.may_apply()
158            {
159                infer_var_info.entry(ty).or_default().self_in_trait = true;
160            }
161        }
162
163        if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) =
164            obligation.predicate.kind().skip_binder()
165        {
166            // If the projection predicate (Foo::Bar == X) has X as a non-TyVid,
167            // we need to make it into one.
168            if let Some(vid) = predicate.term.as_type().and_then(|ty| ty.ty_vid()) {
169                debug!("infer_var_info: {:?}.output = true", vid);
170                infer_var_info.entry(vid).or_default().output = true;
171            }
172        }
173    }
174}