• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 mod _impl;
2 mod adjust_fulfillment_errors;
3 mod arg_matrix;
4 mod checks;
5 mod suggestions;
6 
7 pub use _impl::*;
8 use rustc_errors::ErrorGuaranteed;
9 pub use suggestions::*;
10 
11 use crate::coercion::DynamicCoerceMany;
12 use crate::{Diverges, EnclosingBreakables, Inherited};
13 use rustc_hir as hir;
14 use rustc_hir::def_id::{DefId, LocalDefId};
15 use rustc_hir_analysis::astconv::AstConv;
16 use rustc_infer::infer;
17 use rustc_infer::infer::error_reporting::TypeErrCtxt;
18 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
19 use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
20 use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt};
21 use rustc_session::Session;
22 use rustc_span::symbol::Ident;
23 use rustc_span::{self, Span, DUMMY_SP};
24 use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt};
25 
26 use std::cell::{Cell, RefCell};
27 use std::ops::Deref;
28 
29 /// The `FnCtxt` stores type-checking context needed to type-check bodies of
30 /// functions, closures, and `const`s, including performing type inference
31 /// with [`InferCtxt`].
32 ///
33 /// This is in contrast to [`ItemCtxt`], which is used to type-check item *signatures*
34 /// and thus does not perform type inference.
35 ///
36 /// See [`ItemCtxt`]'s docs for more.
37 ///
38 /// [`ItemCtxt`]: rustc_hir_analysis::collect::ItemCtxt
39 /// [`InferCtxt`]: infer::InferCtxt
40 pub struct FnCtxt<'a, 'tcx> {
41     pub(super) body_id: LocalDefId,
42 
43     /// The parameter environment used for proving trait obligations
44     /// in this function. This can change when we descend into
45     /// closures (as they bring new things into scope), hence it is
46     /// not part of `Inherited` (as of the time of this writing,
47     /// closures do not yet change the environment, but they will
48     /// eventually).
49     pub(super) param_env: ty::ParamEnv<'tcx>,
50 
51     /// Number of errors that had been reported when we started
52     /// checking this function. On exit, if we find that *more* errors
53     /// have been reported, we will skip regionck and other work that
54     /// expects the types within the function to be consistent.
55     // FIXME(matthewjasper) This should not exist, and it's not correct
56     // if type checking is run in parallel.
57     err_count_on_creation: usize,
58 
59     /// If `Some`, this stores coercion information for returned
60     /// expressions. If `None`, this is in a context where return is
61     /// inappropriate, such as a const expression.
62     ///
63     /// This is a `RefCell<DynamicCoerceMany>`, which means that we
64     /// can track all the return expressions and then use them to
65     /// compute a useful coercion from the set, similar to a match
66     /// expression or other branching context. You can use methods
67     /// like `expected_ty` to access the declared return type (if
68     /// any).
69     pub(super) ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>,
70 
71     /// First span of a return site that we find. Used in error messages.
72     pub(super) ret_coercion_span: Cell<Option<Span>>,
73 
74     pub(super) resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>,
75 
76     /// Whether the last checked node generates a divergence (e.g.,
77     /// `return` will set this to `Always`). In general, when entering
78     /// an expression or other node in the tree, the initial value
79     /// indicates whether prior parts of the containing expression may
80     /// have diverged. It is then typically set to `Maybe` (and the
81     /// old value remembered) for processing the subparts of the
82     /// current expression. As each subpart is processed, they may set
83     /// the flag to `Always`, etc. Finally, at the end, we take the
84     /// result and "union" it with the original value, so that when we
85     /// return the flag indicates if any subpart of the parent
86     /// expression (up to and including this part) has diverged. So,
87     /// if you read it after evaluating a subexpression `X`, the value
88     /// you get indicates whether any subexpression that was
89     /// evaluating up to and including `X` diverged.
90     ///
91     /// We currently use this flag only for diagnostic purposes:
92     ///
93     /// - To warn about unreachable code: if, after processing a
94     ///   sub-expression but before we have applied the effects of the
95     ///   current node, we see that the flag is set to `Always`, we
96     ///   can issue a warning. This corresponds to something like
97     ///   `foo(return)`; we warn on the `foo()` expression. (We then
98     ///   update the flag to `WarnedAlways` to suppress duplicate
99     ///   reports.) Similarly, if we traverse to a fresh statement (or
100     ///   tail expression) from an `Always` setting, we will issue a
101     ///   warning. This corresponds to something like `{return;
102     ///   foo();}` or `{return; 22}`, where we would warn on the
103     ///   `foo()` or `22`.
104     ///
105     /// An expression represents dead code if, after checking it,
106     /// the diverges flag is set to something other than `Maybe`.
107     pub(super) diverges: Cell<Diverges>,
108 
109     pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>,
110 
111     pub(super) inh: &'a Inherited<'tcx>,
112 
113     pub(super) fallback_has_occurred: Cell<bool>,
114 }
115 
116 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
new( inh: &'a Inherited<'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, ) -> FnCtxt<'a, 'tcx>117     pub fn new(
118         inh: &'a Inherited<'tcx>,
119         param_env: ty::ParamEnv<'tcx>,
120         body_id: LocalDefId,
121     ) -> FnCtxt<'a, 'tcx> {
122         FnCtxt {
123             body_id,
124             param_env,
125             err_count_on_creation: inh.tcx.sess.err_count(),
126             ret_coercion: None,
127             ret_coercion_span: Cell::new(None),
128             resume_yield_tys: None,
129             diverges: Cell::new(Diverges::Maybe),
130             enclosing_breakables: RefCell::new(EnclosingBreakables {
131                 stack: Vec::new(),
132                 by_id: Default::default(),
133             }),
134             inh,
135             fallback_has_occurred: Cell::new(false),
136         }
137     }
138 
cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx>139     pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> {
140         ObligationCause::new(span, self.body_id, code)
141     }
142 
misc(&self, span: Span) -> ObligationCause<'tcx>143     pub fn misc(&self, span: Span) -> ObligationCause<'tcx> {
144         self.cause(span, ObligationCauseCode::MiscObligation)
145     }
146 
sess(&self) -> &Session147     pub fn sess(&self) -> &Session {
148         &self.tcx.sess
149     }
150 
151     /// Creates an `TypeErrCtxt` with a reference to the in-progress
152     /// `TypeckResults` which is used for diagnostics.
153     /// Use [`InferCtxt::err_ctxt`] to start one without a `TypeckResults`.
154     ///
155     /// [`InferCtxt::err_ctxt`]: infer::InferCtxt::err_ctxt
err_ctxt(&'a self) -> TypeErrCtxt<'a, 'tcx>156     pub fn err_ctxt(&'a self) -> TypeErrCtxt<'a, 'tcx> {
157         TypeErrCtxt {
158             infcx: &self.infcx,
159             typeck_results: Some(self.typeck_results.borrow()),
160             fallback_has_occurred: self.fallback_has_occurred.get(),
161             normalize_fn_sig: Box::new(|fn_sig| {
162                 if fn_sig.has_escaping_bound_vars() {
163                     return fn_sig;
164                 }
165                 self.probe(|_| {
166                     let ocx = ObligationCtxt::new(self);
167                     let normalized_fn_sig =
168                         ocx.normalize(&ObligationCause::dummy(), self.param_env, fn_sig);
169                     if ocx.select_all_or_error().is_empty() {
170                         let normalized_fn_sig = self.resolve_vars_if_possible(normalized_fn_sig);
171                         if !normalized_fn_sig.has_infer() {
172                             return normalized_fn_sig;
173                         }
174                     }
175                     fn_sig
176                 })
177             }),
178             autoderef_steps: Box::new(|ty| {
179                 let mut autoderef = self.autoderef(DUMMY_SP, ty).silence_errors();
180                 let mut steps = vec![];
181                 while let Some((ty, _)) = autoderef.next() {
182                     steps.push((ty, autoderef.current_obligations()));
183                 }
184                 steps
185             }),
186         }
187     }
188 
errors_reported_since_creation(&self) -> bool189     pub fn errors_reported_since_creation(&self) -> bool {
190         self.tcx.sess.err_count() > self.err_count_on_creation
191     }
192 
next_root_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx>193     pub fn next_root_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> {
194         Ty::new_var(self.tcx, self.next_ty_var_id_in_universe(origin, ty::UniverseIndex::ROOT))
195     }
196 }
197 
198 impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> {
199     type Target = Inherited<'tcx>;
deref(&self) -> &Self::Target200     fn deref(&self) -> &Self::Target {
201         &self.inh
202     }
203 }
204 
205 impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
tcx<'b>(&'b self) -> TyCtxt<'tcx>206     fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
207         self.tcx
208     }
209 
item_def_id(&self) -> DefId210     fn item_def_id(&self) -> DefId {
211         self.body_id.to_def_id()
212     }
213 
get_type_parameter_bounds( &self, _: Span, def_id: LocalDefId, _: Ident, ) -> ty::GenericPredicates<'tcx>214     fn get_type_parameter_bounds(
215         &self,
216         _: Span,
217         def_id: LocalDefId,
218         _: Ident,
219     ) -> ty::GenericPredicates<'tcx> {
220         let tcx = self.tcx;
221         let item_def_id = tcx.hir().ty_param_owner(def_id);
222         let generics = tcx.generics_of(item_def_id);
223         let index = generics.param_def_id_to_index[&def_id.to_def_id()];
224         ty::GenericPredicates {
225             parent: None,
226             predicates: tcx.arena.alloc_from_iter(
227                 self.param_env.caller_bounds().iter().filter_map(|predicate| {
228                     match predicate.kind().skip_binder() {
229                         ty::ClauseKind::Trait(data) if data.self_ty().is_param(index) => {
230                             // HACK(eddyb) should get the original `Span`.
231                             let span = tcx.def_span(def_id);
232                             Some((predicate, span))
233                         }
234                         _ => None,
235                     }
236                 }),
237             ),
238         }
239     }
240 
re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option<ty::Region<'tcx>>241     fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option<ty::Region<'tcx>> {
242         let v = match def {
243             Some(def) => infer::EarlyBoundRegion(span, def.name),
244             None => infer::MiscVariable(span),
245         };
246         Some(self.next_region_var(v))
247     }
248 
allow_ty_infer(&self) -> bool249     fn allow_ty_infer(&self) -> bool {
250         true
251     }
252 
ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx>253     fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> {
254         match param {
255             Some(param) => self.var_for_def(span, param).as_type().unwrap(),
256             None => self.next_ty_var(TypeVariableOrigin {
257                 kind: TypeVariableOriginKind::TypeInference,
258                 span,
259             }),
260         }
261     }
262 
ct_infer( &self, ty: Ty<'tcx>, param: Option<&ty::GenericParamDef>, span: Span, ) -> Const<'tcx>263     fn ct_infer(
264         &self,
265         ty: Ty<'tcx>,
266         param: Option<&ty::GenericParamDef>,
267         span: Span,
268     ) -> Const<'tcx> {
269         match param {
270             Some(param) => self.var_for_def(span, param).as_const().unwrap(),
271             None => self.next_const_var(
272                 ty,
273                 ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, span },
274             ),
275         }
276     }
277 
projected_ty_from_poly_trait_ref( &self, span: Span, item_def_id: DefId, item_segment: &hir::PathSegment<'_>, poly_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Ty<'tcx>278     fn projected_ty_from_poly_trait_ref(
279         &self,
280         span: Span,
281         item_def_id: DefId,
282         item_segment: &hir::PathSegment<'_>,
283         poly_trait_ref: ty::PolyTraitRef<'tcx>,
284     ) -> Ty<'tcx> {
285         let trait_ref = self.instantiate_binder_with_fresh_vars(
286             span,
287             infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id),
288             poly_trait_ref,
289         );
290 
291         let item_substs = self.astconv().create_substs_for_associated_item(
292             span,
293             item_def_id,
294             item_segment,
295             trait_ref.substs,
296         );
297 
298         Ty::new_projection(self.tcx(), item_def_id, item_substs)
299     }
300 
probe_adt(&self, span: Span, ty: Ty<'tcx>) -> Option<ty::AdtDef<'tcx>>301     fn probe_adt(&self, span: Span, ty: Ty<'tcx>) -> Option<ty::AdtDef<'tcx>> {
302         match ty.kind() {
303             ty::Adt(adt_def, _) => Some(*adt_def),
304             // FIXME(#104767): Should we handle bound regions here?
305             ty::Alias(ty::Projection | ty::Inherent, _) if !ty.has_escaping_bound_vars() => {
306                 self.normalize(span, ty).ty_adt_def()
307             }
308             _ => None,
309         }
310     }
311 
set_tainted_by_errors(&self, e: ErrorGuaranteed)312     fn set_tainted_by_errors(&self, e: ErrorGuaranteed) {
313         self.infcx.set_tainted_by_errors(e)
314     }
315 
record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span)316     fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span) {
317         // FIXME: normalization and escaping regions
318         let ty = if !ty.has_escaping_bound_vars() { self.normalize(span, ty) } else { ty };
319         self.write_ty(hir_id, ty)
320     }
321 
infcx(&self) -> Option<&infer::InferCtxt<'tcx>>322     fn infcx(&self) -> Option<&infer::InferCtxt<'tcx>> {
323         Some(&self.infcx)
324     }
325 }
326 
327 /// Represents a user-provided type in the raw form (never normalized).
328 ///
329 /// This is a bridge between the interface of `AstConv`, which outputs a raw `Ty`,
330 /// and the API in this module, which expect `Ty` to be fully normalized.
331 #[derive(Clone, Copy, Debug)]
332 pub struct RawTy<'tcx> {
333     pub raw: Ty<'tcx>,
334 
335     /// The normalized form of `raw`, stored here for efficiency.
336     pub normalized: Ty<'tcx>,
337 }
338