rustc_hir_typeck/fn_ctxt/
mod.rs

1mod _impl;
2mod adjust_fulfillment_errors;
3mod arg_matrix;
4mod checks;
5mod inspect_obligations;
6mod suggestions;
7
8use std::cell::{Cell, RefCell};
9use std::ops::Deref;
10
11use hir::def_id::CRATE_DEF_ID;
12use rustc_errors::DiagCtxtHandle;
13use rustc_hir::def_id::{DefId, LocalDefId};
14use rustc_hir::{self as hir, HirId, ItemLocalMap};
15use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, RegionInferReason};
16use rustc_infer::infer;
17use rustc_infer::traits::{DynCompatibilityViolation, Obligation};
18use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt};
19use rustc_session::Session;
20use rustc_span::{self, DUMMY_SP, ErrorGuaranteed, Ident, Span, sym};
21use rustc_trait_selection::error_reporting::TypeErrCtxt;
22use rustc_trait_selection::error_reporting::infer::sub_relations::SubRelations;
23use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt};
24
25use crate::coercion::DynamicCoerceMany;
26use crate::fallback::DivergingFallbackBehavior;
27use crate::fn_ctxt::checks::DivergingBlockBehavior;
28use crate::{CoroutineTypes, Diverges, EnclosingBreakables, TypeckRootCtxt};
29
30/// The `FnCtxt` stores type-checking context needed to type-check bodies of
31/// functions, closures, and `const`s, including performing type inference
32/// with [`InferCtxt`].
33///
34/// This is in contrast to `rustc_hir_analysis::collect::ItemCtxt`, which is
35/// used to type-check item *signatures* and thus does not perform type
36/// inference.
37///
38/// See `ItemCtxt`'s docs for more.
39///
40/// [`InferCtxt`]: infer::InferCtxt
41pub(crate) struct FnCtxt<'a, 'tcx> {
42    pub(super) body_id: LocalDefId,
43
44    /// The parameter environment used for proving trait obligations
45    /// in this function. This can change when we descend into
46    /// closures (as they bring new things into scope), hence it is
47    /// not part of `Inherited` (as of the time of this writing,
48    /// closures do not yet change the environment, but they will
49    /// eventually).
50    pub(super) param_env: ty::ParamEnv<'tcx>,
51
52    /// If `Some`, this stores coercion information for returned
53    /// expressions. If `None`, this is in a context where return is
54    /// inappropriate, such as a const expression.
55    ///
56    /// This is a `RefCell<DynamicCoerceMany>`, which means that we
57    /// can track all the return expressions and then use them to
58    /// compute a useful coercion from the set, similar to a match
59    /// expression or other branching context. You can use methods
60    /// like `expected_ty` to access the declared return type (if
61    /// any).
62    pub(super) ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>,
63
64    /// First span of a return site that we find. Used in error messages.
65    pub(super) ret_coercion_span: Cell<Option<Span>>,
66
67    pub(super) coroutine_types: Option<CoroutineTypes<'tcx>>,
68
69    /// Whether the last checked node generates a divergence (e.g.,
70    /// `return` will set this to `Always`). In general, when entering
71    /// an expression or other node in the tree, the initial value
72    /// indicates whether prior parts of the containing expression may
73    /// have diverged. It is then typically set to `Maybe` (and the
74    /// old value remembered) for processing the subparts of the
75    /// current expression. As each subpart is processed, they may set
76    /// the flag to `Always`, etc. Finally, at the end, we take the
77    /// result and "union" it with the original value, so that when we
78    /// return the flag indicates if any subpart of the parent
79    /// expression (up to and including this part) has diverged. So,
80    /// if you read it after evaluating a subexpression `X`, the value
81    /// you get indicates whether any subexpression that was
82    /// evaluating up to and including `X` diverged.
83    ///
84    /// We currently use this flag for the following purposes:
85    ///
86    /// - To warn about unreachable code: if, after processing a
87    ///   sub-expression but before we have applied the effects of the
88    ///   current node, we see that the flag is set to `Always`, we
89    ///   can issue a warning. This corresponds to something like
90    ///   `foo(return)`; we warn on the `foo()` expression. (We then
91    ///   update the flag to `WarnedAlways` to suppress duplicate
92    ///   reports.) Similarly, if we traverse to a fresh statement (or
93    ///   tail expression) from an `Always` setting, we will issue a
94    ///   warning. This corresponds to something like `{return;
95    ///   foo();}` or `{return; 22}`, where we would warn on the
96    ///   `foo()` or `22`.
97    /// - To assign the `!` type to block expressions with diverging
98    ///   statements.
99    ///
100    /// An expression represents dead code if, after checking it,
101    /// the diverges flag is set to something other than `Maybe`.
102    pub(super) diverges: Cell<Diverges>,
103
104    /// If one of the function arguments is a never pattern, this counts as diverging code. This
105    /// affect typechecking of the function body.
106    pub(super) function_diverges_because_of_empty_arguments: Cell<Diverges>,
107
108    /// Whether the currently checked node is the whole body of the function.
109    pub(super) is_whole_body: Cell<bool>,
110
111    pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>,
112
113    pub(super) root_ctxt: &'a TypeckRootCtxt<'tcx>,
114
115    pub(super) fallback_has_occurred: Cell<bool>,
116
117    pub(super) diverging_fallback_behavior: DivergingFallbackBehavior,
118    pub(super) diverging_block_behavior: DivergingBlockBehavior,
119
120    /// Clauses that we lowered as part of the `impl_trait_in_bindings` feature.
121    ///
122    /// These are stored here so we may collect them when canonicalizing user
123    /// type ascriptions later.
124    pub(super) trait_ascriptions: RefCell<ItemLocalMap<Vec<ty::Clause<'tcx>>>>,
125}
126
127impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
128    pub(crate) fn new(
129        root_ctxt: &'a TypeckRootCtxt<'tcx>,
130        param_env: ty::ParamEnv<'tcx>,
131        body_id: LocalDefId,
132    ) -> FnCtxt<'a, 'tcx> {
133        let (diverging_fallback_behavior, diverging_block_behavior) =
134            never_type_behavior(root_ctxt.tcx);
135        FnCtxt {
136            body_id,
137            param_env,
138            ret_coercion: None,
139            ret_coercion_span: Cell::new(None),
140            coroutine_types: None,
141            diverges: Cell::new(Diverges::Maybe),
142            function_diverges_because_of_empty_arguments: Cell::new(Diverges::Maybe),
143            is_whole_body: Cell::new(false),
144            enclosing_breakables: RefCell::new(EnclosingBreakables {
145                stack: Vec::new(),
146                by_id: Default::default(),
147            }),
148            root_ctxt,
149            fallback_has_occurred: Cell::new(false),
150            diverging_fallback_behavior,
151            diverging_block_behavior,
152            trait_ascriptions: Default::default(),
153        }
154    }
155
156    pub(crate) fn dcx(&self) -> DiagCtxtHandle<'a> {
157        self.root_ctxt.infcx.dcx()
158    }
159
160    pub(crate) fn cause(
161        &self,
162        span: Span,
163        code: ObligationCauseCode<'tcx>,
164    ) -> ObligationCause<'tcx> {
165        ObligationCause::new(span, self.body_id, code)
166    }
167
168    pub(crate) fn misc(&self, span: Span) -> ObligationCause<'tcx> {
169        self.cause(span, ObligationCauseCode::Misc)
170    }
171
172    pub(crate) fn sess(&self) -> &Session {
173        self.tcx.sess
174    }
175
176    /// Creates an `TypeErrCtxt` with a reference to the in-progress
177    /// `TypeckResults` which is used for diagnostics.
178    /// Use [`InferCtxtErrorExt::err_ctxt`] to start one without a `TypeckResults`.
179    ///
180    /// [`InferCtxtErrorExt::err_ctxt`]: rustc_trait_selection::error_reporting::InferCtxtErrorExt::err_ctxt
181    pub(crate) fn err_ctxt(&'a self) -> TypeErrCtxt<'a, 'tcx> {
182        let mut sub_relations = SubRelations::default();
183        sub_relations.add_constraints(
184            self,
185            self.fulfillment_cx.borrow_mut().pending_obligations().iter().map(|o| o.predicate),
186        );
187        TypeErrCtxt {
188            infcx: &self.infcx,
189            sub_relations: RefCell::new(sub_relations),
190            typeck_results: Some(self.typeck_results.borrow()),
191            fallback_has_occurred: self.fallback_has_occurred.get(),
192            normalize_fn_sig: Box::new(|fn_sig| {
193                if fn_sig.has_escaping_bound_vars() {
194                    return fn_sig;
195                }
196                self.probe(|_| {
197                    let ocx = ObligationCtxt::new(self);
198                    let normalized_fn_sig =
199                        ocx.normalize(&ObligationCause::dummy(), self.param_env, fn_sig);
200                    if ocx.select_all_or_error().is_empty() {
201                        let normalized_fn_sig = self.resolve_vars_if_possible(normalized_fn_sig);
202                        if !normalized_fn_sig.has_infer() {
203                            return normalized_fn_sig;
204                        }
205                    }
206                    fn_sig
207                })
208            }),
209            autoderef_steps: Box::new(|ty| {
210                let mut autoderef = self.autoderef(DUMMY_SP, ty).silence_errors();
211                let mut steps = vec![];
212                while let Some((ty, _)) = autoderef.next() {
213                    steps.push((ty, autoderef.current_obligations()));
214                }
215                steps
216            }),
217        }
218    }
219}
220
221impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> {
222    type Target = TypeckRootCtxt<'tcx>;
223    fn deref(&self) -> &Self::Target {
224        self.root_ctxt
225    }
226}
227
228impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
229    fn tcx(&self) -> TyCtxt<'tcx> {
230        self.tcx
231    }
232
233    fn dcx(&self) -> DiagCtxtHandle<'_> {
234        self.root_ctxt.dcx()
235    }
236
237    fn item_def_id(&self) -> LocalDefId {
238        self.body_id
239    }
240
241    fn re_infer(&self, span: Span, reason: RegionInferReason<'_>) -> ty::Region<'tcx> {
242        let v = match reason {
243            RegionInferReason::Param(def) => infer::RegionParameterDefinition(span, def.name),
244            _ => infer::MiscVariable(span),
245        };
246        self.next_region_var(v)
247    }
248
249    fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> {
250        match param {
251            Some(param) => self.var_for_def(span, param).as_type().unwrap(),
252            None => self.next_ty_var(span),
253        }
254    }
255
256    fn ct_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Const<'tcx> {
257        // FIXME ideally this shouldn't use unwrap
258        match param {
259            Some(param) => self.var_for_def(span, param).as_const().unwrap(),
260            None => self.next_const_var(span),
261        }
262    }
263
264    fn register_trait_ascription_bounds(
265        &self,
266        bounds: Vec<(ty::Clause<'tcx>, Span)>,
267        hir_id: HirId,
268        _span: Span,
269    ) {
270        for (clause, span) in bounds {
271            if clause.has_escaping_bound_vars() {
272                self.dcx().span_delayed_bug(span, "clause should have no escaping bound vars");
273                continue;
274            }
275
276            self.trait_ascriptions.borrow_mut().entry(hir_id.local_id).or_default().push(clause);
277
278            let clause = self.normalize(span, clause);
279            self.register_predicate(Obligation::new(
280                self.tcx,
281                self.misc(span),
282                self.param_env,
283                clause,
284            ));
285        }
286    }
287
288    fn probe_ty_param_bounds(
289        &self,
290        _: Span,
291        def_id: LocalDefId,
292        _: Ident,
293    ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
294        let tcx = self.tcx;
295        let item_def_id = tcx.hir_ty_param_owner(def_id);
296        let generics = tcx.generics_of(item_def_id);
297        let index = generics.param_def_id_to_index[&def_id.to_def_id()];
298        // HACK(eddyb) should get the original `Span`.
299        let span = tcx.def_span(def_id);
300
301        ty::EarlyBinder::bind(tcx.arena.alloc_from_iter(
302            self.param_env.caller_bounds().iter().filter_map(|predicate| {
303                match predicate.kind().skip_binder() {
304                    ty::ClauseKind::Trait(data) if data.self_ty().is_param(index) => {
305                        Some((predicate, span))
306                    }
307                    _ => None,
308                }
309            }),
310        ))
311    }
312
313    fn lower_assoc_item_path(
314        &self,
315        span: Span,
316        item_def_id: DefId,
317        item_segment: &rustc_hir::PathSegment<'tcx>,
318        poly_trait_ref: ty::PolyTraitRef<'tcx>,
319    ) -> Result<(DefId, ty::GenericArgsRef<'tcx>), ErrorGuaranteed> {
320        let trait_ref = self.instantiate_binder_with_fresh_vars(
321            span,
322            // FIXME(mgca): `item_def_id` can be an AssocConst; rename this variant.
323            infer::BoundRegionConversionTime::AssocTypeProjection(item_def_id),
324            poly_trait_ref,
325        );
326
327        let item_args = self.lowerer().lower_generic_args_of_assoc_item(
328            span,
329            item_def_id,
330            item_segment,
331            trait_ref.args,
332        );
333
334        Ok((item_def_id, item_args))
335    }
336
337    fn probe_adt(&self, span: Span, ty: Ty<'tcx>) -> Option<ty::AdtDef<'tcx>> {
338        match ty.kind() {
339            ty::Adt(adt_def, _) => Some(*adt_def),
340            // FIXME(#104767): Should we handle bound regions here?
341            ty::Alias(ty::Projection | ty::Inherent | ty::Free, _)
342                if !ty.has_escaping_bound_vars() =>
343            {
344                if self.next_trait_solver() {
345                    self.try_structurally_resolve_type(span, ty).ty_adt_def()
346                } else {
347                    self.normalize(span, ty).ty_adt_def()
348                }
349            }
350            _ => None,
351        }
352    }
353
354    fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span) {
355        // FIXME: normalization and escaping regions
356        let ty = if !ty.has_escaping_bound_vars() {
357            // NOTE: These obligations are 100% redundant and are implied by
358            // WF obligations that are registered elsewhere, but they have a
359            // better cause code assigned to them in `add_required_obligations_for_hir`.
360            // This means that they should shadow obligations with worse spans.
361            if let ty::Alias(ty::Projection | ty::Free, ty::AliasTy { args, def_id, .. }) =
362                ty.kind()
363            {
364                self.add_required_obligations_for_hir(span, *def_id, args, hir_id);
365            }
366
367            self.normalize(span, ty)
368        } else {
369            ty
370        };
371        self.write_ty(hir_id, ty)
372    }
373
374    fn infcx(&self) -> Option<&infer::InferCtxt<'tcx>> {
375        Some(&self.infcx)
376    }
377
378    fn lower_fn_sig(
379        &self,
380        decl: &rustc_hir::FnDecl<'tcx>,
381        _generics: Option<&rustc_hir::Generics<'_>>,
382        _hir_id: rustc_hir::HirId,
383        _hir_ty: Option<&hir::Ty<'_>>,
384    ) -> (Vec<Ty<'tcx>>, Ty<'tcx>) {
385        let input_tys = decl.inputs.iter().map(|a| self.lowerer().lower_arg_ty(a, None)).collect();
386
387        let output_ty = match decl.output {
388            hir::FnRetTy::Return(output) => self.lowerer().lower_ty(output),
389            hir::FnRetTy::DefaultReturn(..) => self.tcx().types.unit,
390        };
391        (input_tys, output_ty)
392    }
393
394    fn dyn_compatibility_violations(&self, trait_def_id: DefId) -> Vec<DynCompatibilityViolation> {
395        self.tcx.dyn_compatibility_violations(trait_def_id).to_vec()
396    }
397}
398
399/// The `ty` representation of a user-provided type. Depending on the use-site
400/// we want to either use the unnormalized or the normalized form of this type.
401///
402/// This is a bridge between the interface of HIR ty lowering, which outputs a raw
403/// `Ty`, and the API in this module, which expect `Ty` to be fully normalized.
404#[derive(Clone, Copy, Debug)]
405pub(crate) struct LoweredTy<'tcx> {
406    /// The unnormalized type provided by the user.
407    pub raw: Ty<'tcx>,
408
409    /// The normalized form of `raw`, stored here for efficiency.
410    pub normalized: Ty<'tcx>,
411}
412
413impl<'tcx> LoweredTy<'tcx> {
414    fn from_raw(fcx: &FnCtxt<'_, 'tcx>, span: Span, raw: Ty<'tcx>) -> LoweredTy<'tcx> {
415        // FIXME(-Znext-solver): We're still figuring out how to best handle
416        // normalization and this doesn't feel too great. We should look at this
417        // code again before stabilizing it.
418        let normalized = if fcx.next_trait_solver() {
419            fcx.try_structurally_resolve_type(span, raw)
420        } else {
421            fcx.normalize(span, raw)
422        };
423        LoweredTy { raw, normalized }
424    }
425}
426
427fn never_type_behavior(tcx: TyCtxt<'_>) -> (DivergingFallbackBehavior, DivergingBlockBehavior) {
428    let (fallback, block) = parse_never_type_options_attr(tcx);
429    let fallback = fallback.unwrap_or_else(|| default_fallback(tcx));
430    let block = block.unwrap_or_default();
431
432    (fallback, block)
433}
434
435/// Returns the default fallback which is used when there is no explicit override via `#![never_type_options(...)]`.
436fn default_fallback(tcx: TyCtxt<'_>) -> DivergingFallbackBehavior {
437    // Edition 2024: fallback to `!`
438    if tcx.sess.edition().at_least_rust_2024() {
439        return DivergingFallbackBehavior::ToNever;
440    }
441
442    // `feature(never_type_fallback)`: fallback to `!` or `()` trying to not break stuff
443    if tcx.features().never_type_fallback() {
444        return DivergingFallbackBehavior::ContextDependent;
445    }
446
447    // Otherwise: fallback to `()`
448    DivergingFallbackBehavior::ToUnit
449}
450
451fn parse_never_type_options_attr(
452    tcx: TyCtxt<'_>,
453) -> (Option<DivergingFallbackBehavior>, Option<DivergingBlockBehavior>) {
454    // Error handling is dubious here (unwraps), but that's probably fine for an internal attribute.
455    // Just don't write incorrect attributes <3
456
457    let mut fallback = None;
458    let mut block = None;
459
460    let items = tcx
461        .get_attr(CRATE_DEF_ID, sym::rustc_never_type_options)
462        .map(|attr| attr.meta_item_list().unwrap())
463        .unwrap_or_default();
464
465    for item in items {
466        if item.has_name(sym::fallback) && fallback.is_none() {
467            let mode = item.value_str().unwrap();
468            match mode {
469                sym::unit => fallback = Some(DivergingFallbackBehavior::ToUnit),
470                sym::niko => fallback = Some(DivergingFallbackBehavior::ContextDependent),
471                sym::never => fallback = Some(DivergingFallbackBehavior::ToNever),
472                sym::no => fallback = Some(DivergingFallbackBehavior::NoFallback),
473                _ => {
474                    tcx.dcx().span_err(item.span(), format!("unknown never type fallback mode: `{mode}` (supported: `unit`, `niko`, `never` and `no`)"));
475                }
476            };
477            continue;
478        }
479
480        if item.has_name(sym::diverging_block_default) && block.is_none() {
481            let default = item.value_str().unwrap();
482            match default {
483                sym::unit => block = Some(DivergingBlockBehavior::Unit),
484                sym::never => block = Some(DivergingBlockBehavior::Never),
485                _ => {
486                    tcx.dcx().span_err(item.span(), format!("unknown diverging block default: `{default}` (supported: `unit` and `never`)"));
487                }
488            };
489            continue;
490        }
491
492        tcx.dcx().span_err(
493            item.span(),
494            format!(
495                "unknown or duplicate never type option: `{}` (supported: `fallback`, `diverging_block_default`)",
496                item.name().unwrap()
497            ),
498        );
499    }
500
501    (fallback, block)
502}