Skip to main content

rustc_hir_typeck/
check.rs

1use std::cell::RefCell;
2
3use rustc_hir as hir;
4use rustc_hir::def::DefKind;
5use rustc_hir::lang_items::LangItem;
6use rustc_hir_analysis::check::check_function_signature;
7use rustc_infer::infer::RegionVariableOrigin;
8use rustc_infer::traits::WellFormedLoc;
9use rustc_middle::ty::{self, Binder, Ty, TyCtxt};
10use rustc_span::def_id::LocalDefId;
11use rustc_span::sym;
12use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};
13use tracing::{debug, instrument};
14
15use crate::coercion::CoerceMany;
16use crate::gather_locals::GatherLocalsVisitor;
17use crate::{CoroutineTypes, Diverges, FnCtxt};
18
19/// Helper used for fns and closures. Does the grungy work of checking a function
20/// body and returns the function context used for that purpose, since in the case of a fn item
21/// there is still a bit more to do.
22///
23/// * ...
24/// * inherited: other fields inherited from the enclosing fn (if any)
25#[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("check_fn",
                                    "rustc_hir_typeck::check", ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/check.rs"),
                                    ::tracing_core::__macro_support::Option::Some(25u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::check"),
                                    ::tracing_core::field::FieldSet::new(&["fn_sig",
                                                    "coroutine_types", "decl", "fn_def_id",
                                                    "params_can_be_unsized"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                let mut iter = meta.fields().iter();
                                meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&fn_sig)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&coroutine_types)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&decl)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&fn_def_id)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&params_can_be_unsized
                                                            as &dyn Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: Option<CoroutineTypes<'tcx>> =
                loop {};
            return __tracing_attr_fake_return;
        }
        {
            let fn_id = fcx.tcx.local_def_id_to_hir_id(fn_def_id);
            let tcx = fcx.tcx;
            let declared_ret_ty = fn_sig.output();
            let ret_ty =
                fcx.register_infer_ok_obligations(fcx.infcx.replace_opaque_types_with_inference_vars(declared_ret_ty,
                        fn_def_id, decl.output.span(), fcx.param_env));
            fcx.coroutine_types = coroutine_types;
            fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty)));
            let span = body.value.span;
            for param in body.params {
                GatherLocalsVisitor::gather_from_param(fcx, param);
            }
            let maybe_va_list =
                fn_sig.c_variadic().then(||
                        {
                            let span = body.params.last().unwrap().span;
                            let va_list_did =
                                tcx.require_lang_item(LangItem::VaList, span);
                            let region =
                                fcx.next_region_var(RegionVariableOrigin::Misc(span));
                            tcx.type_of(va_list_did).instantiate(tcx,
                                    &[region.into()]).skip_norm_wip()
                        });
            let inputs_hir =
                tcx.hir_fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs);
            let inputs_fn = fn_sig.inputs().iter().copied();
            for (idx, (param_ty, param)) in
                inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() {
                if fcx.tcx.is_typeck_child(fn_def_id.to_def_id()) {
                    fcx.register_wf_obligation(param_ty.into(), param.span,
                        ObligationCauseCode::WellFormed(Some(WellFormedLoc::Param {
                                    function: fn_def_id,
                                    param_idx: idx,
                                })));
                }
                let ty: Option<&hir::Ty<'_>> =
                    inputs_hir.and_then(|h| h.get(idx));
                let ty_span = ty.map(|ty| ty.span);
                fcx.check_pat_top(param.pat, param_ty, ty_span, None, None);
                if param.pat.is_never_pattern() {
                    fcx.function_diverges_because_of_empty_arguments.set(Diverges::Always {
                            span: param.pat.span,
                            custom_note: Some("any code following a never pattern is unreachable"),
                        });
                }
                if !params_can_be_unsized {
                    fcx.require_type_is_sized(param_ty, param.ty_span,
                        ObligationCauseCode::SizedArgumentType(if ty_span ==
                                        Some(param.span) && tcx.is_closure_like(fn_def_id.into()) {
                                None
                            } else { ty.map(|ty| ty.hir_id) }));
                }
                fcx.write_ty(param.hir_id, param_ty);
            }
            fcx.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id,
                fn_sig);
            if fcx.tcx.is_typeck_child(fn_def_id.to_def_id()) {
                let return_or_body_span =
                    match decl.output {
                        hir::FnRetTy::DefaultReturn(_) => body.value.span,
                        hir::FnRetTy::Return(ty) => ty.span,
                    };
                fcx.require_type_is_sized(declared_ret_ty,
                    return_or_body_span, ObligationCauseCode::SizedReturnType);
            }
            fcx.is_whole_body.set(true);
            fcx.check_return_or_body_tail(body.value, false);
            let coercion = fcx.ret_coercion.take().unwrap().into_inner();
            let mut actual_return_ty = coercion.complete(fcx);
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("event compiler/rustc_hir_typeck/src/check.rs:137",
                                    "rustc_hir_typeck::check", ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/check.rs"),
                                    ::tracing_core::__macro_support::Option::Some(137u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::check"),
                                    ::tracing_core::field::FieldSet::new(&["message"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::EVENT)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let enabled =
                    ::tracing::Level::DEBUG <=
                                ::tracing::level_filters::STATIC_MAX_LEVEL &&
                            ::tracing::Level::DEBUG <=
                                ::tracing::level_filters::LevelFilter::current() &&
                        {
                            let interest = __CALLSITE.interest();
                            !interest.is_never() &&
                                ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                                    interest)
                        };
                if enabled {
                    (|value_set: ::tracing::field::ValueSet|
                                {
                                    let meta = __CALLSITE.metadata();
                                    ::tracing::Event::dispatch(meta, &value_set);
                                    ;
                                })({
                            #[allow(unused_imports)]
                            use ::tracing::field::{debug, display, Value};
                            let mut iter = __CALLSITE.metadata().fields().iter();
                            __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                ::tracing::__macro_support::Option::Some(&format_args!("actual_return_ty = {0:?}",
                                                                actual_return_ty) as &dyn Value))])
                        });
                } else { ; }
            };
            if let ty::Dynamic(..) = declared_ret_ty.kind() {
                actual_return_ty = fcx.next_ty_var(span);
                {
                    use ::tracing::__macro_support::Callsite as _;
                    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                        {
                            static META: ::tracing::Metadata<'static> =
                                {
                                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_hir_typeck/src/check.rs:143",
                                        "rustc_hir_typeck::check", ::tracing::Level::DEBUG,
                                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/check.rs"),
                                        ::tracing_core::__macro_support::Option::Some(143u32),
                                        ::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::check"),
                                        ::tracing_core::field::FieldSet::new(&["message"],
                                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                        ::tracing::metadata::Kind::EVENT)
                                };
                            ::tracing::callsite::DefaultCallsite::new(&META)
                        };
                    let enabled =
                        ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            {
                                let interest = __CALLSITE.interest();
                                !interest.is_never() &&
                                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                                        interest)
                            };
                    if enabled {
                        (|value_set: ::tracing::field::ValueSet|
                                    {
                                        let meta = __CALLSITE.metadata();
                                        ::tracing::Event::dispatch(meta, &value_set);
                                        ;
                                    })({
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                let mut iter = __CALLSITE.metadata().fields().iter();
                                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&format_args!("actual_return_ty replaced with {0:?}",
                                                                    actual_return_ty) as &dyn Value))])
                            });
                    } else { ; }
                };
            }
            fcx.demand_suptype(span, ret_ty, actual_return_ty);
            if tcx.is_lang_item(fn_def_id.to_def_id(), LangItem::PanicImpl) {
                check_panic_info_fn(tcx, fn_def_id, fn_sig);
            }
            if tcx.is_lang_item(fn_def_id.to_def_id(), LangItem::Start) {
                check_lang_start_fn(tcx, fn_sig, fn_def_id);
            }
            fcx.coroutine_types
        }
    }
}#[instrument(skip(fcx, body), level = "debug")]
26pub(super) fn check_fn<'a, 'tcx>(
27    fcx: &mut FnCtxt<'a, 'tcx>,
28    fn_sig: ty::FnSig<'tcx>,
29    coroutine_types: Option<CoroutineTypes<'tcx>>,
30    decl: &'tcx hir::FnDecl<'tcx>,
31    fn_def_id: LocalDefId,
32    body: &'tcx hir::Body<'tcx>,
33    params_can_be_unsized: bool,
34) -> Option<CoroutineTypes<'tcx>> {
35    let fn_id = fcx.tcx.local_def_id_to_hir_id(fn_def_id);
36    let tcx = fcx.tcx;
37    let declared_ret_ty = fn_sig.output();
38    let ret_ty =
39        fcx.register_infer_ok_obligations(fcx.infcx.replace_opaque_types_with_inference_vars(
40            declared_ret_ty,
41            fn_def_id,
42            decl.output.span(),
43            fcx.param_env,
44        ));
45
46    fcx.coroutine_types = coroutine_types;
47    fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty)));
48
49    let span = body.value.span;
50
51    for param in body.params {
52        GatherLocalsVisitor::gather_from_param(fcx, param);
53    }
54
55    // C-variadic fns also have a `VaList` input that's not listed in `fn_sig`
56    // (as it's created inside the body itself, not passed in from outside).
57    let maybe_va_list = fn_sig.c_variadic().then(|| {
58        let span = body.params.last().unwrap().span;
59        let va_list_did = tcx.require_lang_item(LangItem::VaList, span);
60        let region = fcx.next_region_var(RegionVariableOrigin::Misc(span));
61
62        tcx.type_of(va_list_did).instantiate(tcx, &[region.into()]).skip_norm_wip()
63    });
64
65    // Add formal parameters.
66    let inputs_hir = tcx.hir_fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs);
67    let inputs_fn = fn_sig.inputs().iter().copied();
68    for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() {
69        // We checked the root's signature during wfcheck, but not the child.
70        if fcx.tcx.is_typeck_child(fn_def_id.to_def_id()) {
71            fcx.register_wf_obligation(
72                param_ty.into(),
73                param.span,
74                ObligationCauseCode::WellFormed(Some(WellFormedLoc::Param {
75                    function: fn_def_id,
76                    param_idx: idx,
77                })),
78            );
79        }
80
81        // Check the pattern.
82        let ty: Option<&hir::Ty<'_>> = inputs_hir.and_then(|h| h.get(idx));
83        let ty_span = ty.map(|ty| ty.span);
84        fcx.check_pat_top(param.pat, param_ty, ty_span, None, None);
85        if param.pat.is_never_pattern() {
86            fcx.function_diverges_because_of_empty_arguments.set(Diverges::Always {
87                span: param.pat.span,
88                custom_note: Some("any code following a never pattern is unreachable"),
89            });
90        }
91
92        // Check that argument is Sized.
93        if !params_can_be_unsized {
94            fcx.require_type_is_sized(
95                param_ty,
96                param.ty_span,
97                // ty.span == binding_span iff this is a closure parameter with no type ascription,
98                // or if it's an implicit `self` parameter
99                ObligationCauseCode::SizedArgumentType(
100                    if ty_span == Some(param.span) && tcx.is_closure_like(fn_def_id.into()) {
101                        None
102                    } else {
103                        ty.map(|ty| ty.hir_id)
104                    },
105                ),
106            );
107        }
108
109        fcx.write_ty(param.hir_id, param_ty);
110    }
111
112    fcx.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig);
113
114    // We checked the root's ret ty during wfcheck, but not the child.
115    if fcx.tcx.is_typeck_child(fn_def_id.to_def_id()) {
116        let return_or_body_span = match decl.output {
117            hir::FnRetTy::DefaultReturn(_) => body.value.span,
118            hir::FnRetTy::Return(ty) => ty.span,
119        };
120
121        fcx.require_type_is_sized(
122            declared_ret_ty,
123            return_or_body_span,
124            ObligationCauseCode::SizedReturnType,
125        );
126    }
127
128    fcx.is_whole_body.set(true);
129    fcx.check_return_or_body_tail(body.value, false);
130
131    // Finalize the return check by taking the LUB of the return types
132    // we saw and assigning it to the expected return type. This isn't
133    // really expected to fail, since the coercions would have failed
134    // earlier when trying to find a LUB.
135    let coercion = fcx.ret_coercion.take().unwrap().into_inner();
136    let mut actual_return_ty = coercion.complete(fcx);
137    debug!("actual_return_ty = {:?}", actual_return_ty);
138    if let ty::Dynamic(..) = declared_ret_ty.kind() {
139        // We have special-cased the case where the function is declared
140        // `-> dyn Foo` and we don't actually relate it to the
141        // `fcx.ret_coercion`, so just instantiate a type variable.
142        actual_return_ty = fcx.next_ty_var(span);
143        debug!("actual_return_ty replaced with {:?}", actual_return_ty);
144    }
145
146    // HACK(oli-obk, compiler-errors): We should be comparing this against
147    // `declared_ret_ty`, but then anything uninferred would be inferred to
148    // the opaque type itself. That again would cause writeback to assume
149    // we have a recursive call site and do the sadly stabilized fallback to `()`.
150    fcx.demand_suptype(span, ret_ty, actual_return_ty);
151
152    // Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !`
153    if tcx.is_lang_item(fn_def_id.to_def_id(), LangItem::PanicImpl) {
154        check_panic_info_fn(tcx, fn_def_id, fn_sig);
155    }
156
157    if tcx.is_lang_item(fn_def_id.to_def_id(), LangItem::Start) {
158        check_lang_start_fn(tcx, fn_sig, fn_def_id);
159    }
160
161    fcx.coroutine_types
162}
163
164fn check_panic_info_fn(tcx: TyCtxt<'_>, fn_id: LocalDefId, fn_sig: ty::FnSig<'_>) {
165    let span = tcx.def_span(fn_id);
166
167    let DefKind::Fn = tcx.def_kind(fn_id) else {
168        tcx.dcx().span_err(span, "should be a function");
169        return;
170    };
171
172    let generic_counts = tcx.generics_of(fn_id).own_counts();
173    if generic_counts.types != 0 {
174        tcx.dcx().span_err(span, "should have no type parameters");
175    }
176    if generic_counts.consts != 0 {
177        tcx.dcx().span_err(span, "should have no const parameters");
178    }
179
180    let panic_info_did = tcx.require_lang_item(hir::LangItem::PanicInfo, span);
181
182    // build type `for<'a, 'b> fn(&'a PanicInfo<'b>) -> !`
183    let panic_info_ty = tcx
184        .type_of(panic_info_did)
185        .instantiate(
186            tcx,
187            &[ty::GenericArg::from(ty::Region::new_bound(
188                tcx,
189                ty::INNERMOST,
190                ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BoundRegionKind::Anon },
191            ))],
192        )
193        .skip_norm_wip();
194    let panic_info_ref_ty = Ty::new_imm_ref(
195        tcx,
196        ty::Region::new_bound(
197            tcx,
198            ty::INNERMOST,
199            ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::Anon },
200        ),
201        panic_info_ty,
202    );
203
204    let bounds = tcx.mk_bound_variable_kinds(&[
205        ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon),
206        ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon),
207    ]);
208    let expected_sig = ty::Binder::bind_with_vars(
209        tcx.mk_fn_sig_rust_abi([panic_info_ref_ty], tcx.types.never, fn_sig.safety()),
210        bounds,
211    );
212
213    let _ = check_function_signature(
214        tcx,
215        ObligationCause::new(span, fn_id, ObligationCauseCode::LangFunctionType(sym::panic_impl)),
216        fn_id.into(),
217        expected_sig,
218    );
219}
220
221fn check_lang_start_fn<'tcx>(tcx: TyCtxt<'tcx>, fn_sig: ty::FnSig<'tcx>, def_id: LocalDefId) {
222    // build type `fn(main: fn() -> T, argc: isize, argv: *const *const u8, sigpipe: u8)`
223
224    // make a Ty for the generic on the fn for diagnostics
225    // FIXME: make the lang item generic checks check for the right generic *kind*
226    // for example `start`'s generic should be a type parameter
227    let generics = tcx.generics_of(def_id);
228    let fn_generic = generics.param_at(0, tcx);
229    let generic_ty = Ty::new_param(tcx, fn_generic.index, fn_generic.name);
230    let main_fn_ty =
231        Ty::new_fn_ptr(tcx, Binder::dummy(tcx.mk_fn_sig_safe_rust_abi([], generic_ty)));
232
233    let expected_sig = ty::Binder::dummy(tcx.mk_fn_sig_rust_abi(
234        [
235            main_fn_ty,
236            tcx.types.isize,
237            Ty::new_imm_ptr(tcx, Ty::new_imm_ptr(tcx, tcx.types.u8)),
238            tcx.types.u8,
239        ],
240        tcx.types.isize,
241        fn_sig.safety(),
242    ));
243
244    let _ = check_function_signature(
245        tcx,
246        ObligationCause::new(
247            tcx.def_span(def_id),
248            def_id,
249            ObligationCauseCode::LangFunctionType(sym::start),
250        ),
251        def_id.into(),
252        expected_sig,
253    );
254}