Skip to main content

rustc_hir_analysis/check/
entry.rs

1use std::ops::Not;
2
3use rustc_hir as hir;
4use rustc_hir::{Node, find_attr};
5use rustc_infer::infer::TyCtxtInferExt;
6use rustc_middle::span_bug;
7use rustc_middle::ty::{self, TyCtxt, TypingMode, Unnormalized};
8use rustc_session::config::EntryFnType;
9use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
10use rustc_span::{ErrorGuaranteed, Span};
11use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
12use rustc_trait_selection::regions::InferCtxtRegionExt;
13use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
14
15use super::check_function_signature;
16use crate::diagnostics;
17
18pub(crate) fn check_for_entry_fn(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
19    match tcx.entry_fn(()) {
20        Some((def_id, EntryFnType::Main { .. })) => check_main_fn_ty(tcx, def_id),
21        _ => Ok(()),
22    }
23}
24
25fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) -> Result<(), ErrorGuaranteed> {
26    let main_fnsig = tcx.fn_sig(main_def_id).instantiate_identity().skip_norm_wip();
27    let main_span = tcx.def_span(main_def_id);
28
29    fn main_fn_diagnostics_def_id(tcx: TyCtxt<'_>, def_id: DefId, sp: Span) -> LocalDefId {
30        if let Some(local_def_id) = def_id.as_local() {
31            let hir_type = tcx.type_of(local_def_id).instantiate_identity().skip_norm_wip();
32            if !#[allow(non_exhaustive_omitted_patterns)] match hir_type.kind() {
    ty::FnDef(..) => true,
    _ => false,
}matches!(hir_type.kind(), ty::FnDef(..)) {
33                ::rustc_middle::util::bug::span_bug_fmt(sp,
    format_args!("main has a non-function type: found `{0}`", hir_type));span_bug!(sp, "main has a non-function type: found `{}`", hir_type);
34            }
35            local_def_id
36        } else {
37            CRATE_DEF_ID
38        }
39    }
40
41    fn main_fn_generics_params_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
42        if !def_id.is_local() {
43            return None;
44        }
45        match tcx.hir_node_by_def_id(def_id.expect_local()) {
46            Node::Item(hir::Item { kind: hir::ItemKind::Fn { generics, .. }, .. }) => {
47                generics.params.is_empty().not().then_some(generics.span)
48            }
49            _ => {
50                ::rustc_middle::util::bug::span_bug_fmt(tcx.def_span(def_id),
    format_args!("main has a non-function type"));span_bug!(tcx.def_span(def_id), "main has a non-function type");
51            }
52        }
53    }
54
55    fn main_fn_where_clauses_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
56        if !def_id.is_local() {
57            return None;
58        }
59        match tcx.hir_node_by_def_id(def_id.expect_local()) {
60            Node::Item(hir::Item { kind: hir::ItemKind::Fn { generics, .. }, .. }) => {
61                Some(generics.where_clause_span)
62            }
63            _ => {
64                ::rustc_middle::util::bug::span_bug_fmt(tcx.def_span(def_id),
    format_args!("main has a non-function type"));span_bug!(tcx.def_span(def_id), "main has a non-function type");
65            }
66        }
67    }
68
69    fn main_fn_asyncness_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
70        if !def_id.is_local() {
71            return None;
72        }
73        Some(tcx.def_span(def_id))
74    }
75
76    fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
77        if !def_id.is_local() {
78            return None;
79        }
80        match tcx.hir_node_by_def_id(def_id.expect_local()) {
81            Node::Item(hir::Item { kind: hir::ItemKind::Fn { sig: fn_sig, .. }, .. }) => {
82                Some(fn_sig.decl.output.span())
83            }
84            _ => {
85                ::rustc_middle::util::bug::span_bug_fmt(tcx.def_span(def_id),
    format_args!("main has a non-function type"));span_bug!(tcx.def_span(def_id), "main has a non-function type");
86            }
87        }
88    }
89
90    let main_diagnostics_def_id = main_fn_diagnostics_def_id(tcx, main_def_id, main_span);
91
92    let main_asyncness = tcx.asyncness(main_def_id);
93    if main_asyncness.is_async() {
94        let asyncness_span = main_fn_asyncness_span(tcx, main_def_id);
95        return Err(tcx.dcx().emit_err(diagnostics::MainFunctionAsync {
96            span: main_span,
97            asyncness: asyncness_span,
98        }));
99    }
100
101    if let Some(attr_span) = {
    {
        'done:
            {
            for i in
                ::rustc_hir::attrs::HasAttrs::get_attrs(main_def_id, &tcx) {
                #[allow(unused_imports)]
                use rustc_hir::attrs::AttributeKind::*;
                let i: &rustc_hir::Attribute = i;
                match i {
                    rustc_hir::Attribute::Parsed(TrackCaller(span)) => {
                        break 'done Some(*span);
                    }
                    rustc_hir::Attribute::Unparsed(..) =>
                        {}
                        #[deny(unreachable_patterns)]
                        _ => {}
                }
            }
            None
        }
    }
}find_attr!(tcx, main_def_id, TrackCaller(span) => *span) {
102        return Err(tcx
103            .dcx()
104            .emit_err(diagnostics::TrackCallerOnMain { span: attr_span, annotated: main_span }));
105    }
106
107    if !tcx.codegen_fn_attrs(main_def_id).target_features.is_empty()
108        // Calling functions with `#[target_feature]` is not unsafe on WASM, see #84988
109        && !tcx.sess.target.is_like_wasm
110        && !tcx.sess.opts.actually_rustdoc
111    {
112        return Err(tcx.dcx().emit_err(diagnostics::TargetFeatureOnMain { main: main_span }));
113    }
114
115    // Main should have no WC, so empty param env is OK here.
116    let param_env = ty::ParamEnv::empty();
117    let expected_return_type;
118    if let Some(term_did) = tcx.lang_items().termination() {
119        let return_ty = main_fnsig.output();
120        let return_ty_span = main_fn_return_type_span(tcx, main_def_id).unwrap_or(main_span);
121        let Some(return_ty) = return_ty.no_bound_vars() else {
122            return Err(tcx
123                .dcx()
124                .emit_err(diagnostics::MainFunctionReturnTypeGeneric { span: return_ty_span }));
125        };
126        let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
127        let cause = traits::ObligationCause::new(
128            return_ty_span,
129            main_diagnostics_def_id,
130            ObligationCauseCode::MainFunctionType,
131        );
132        let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx);
133        let norm_return_ty = ocx.normalize(&cause, param_env, Unnormalized::new_wip(return_ty));
134        ocx.register_bound(cause, param_env, norm_return_ty, term_did);
135        let errors = ocx.evaluate_obligations_error_on_ambiguity();
136        if !errors.is_empty() {
137            return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
138        }
139
140        let region_errors =
141            infcx.resolve_regions(main_diagnostics_def_id, param_env, ty::List::empty());
142
143        if !region_errors.is_empty() {
144            return Err(infcx
145                .err_ctxt()
146                .report_region_errors(main_diagnostics_def_id, &region_errors));
147        }
148        // now we can take the return type of the given main function
149        expected_return_type = norm_return_ty;
150    } else {
151        // standard () main return type
152        expected_return_type = tcx.types.unit;
153    }
154
155    let expected_sig = ty::Binder::dummy(tcx.mk_fn_sig_safe_rust_abi([], expected_return_type));
156
157    check_function_signature(
158        tcx,
159        ObligationCause::new(
160            main_span,
161            main_diagnostics_def_id,
162            ObligationCauseCode::MainFunctionType,
163        ),
164        main_def_id,
165        expected_sig,
166    )?;
167
168    let main_fn_generics = tcx.generics_of(main_def_id);
169    let main_fn_predicates = tcx.predicates_of(main_def_id);
170    if main_fn_generics.count() != 0 || !main_fnsig.bound_vars().is_empty() {
171        let generics_param_span = main_fn_generics_params_span(tcx, main_def_id);
172        return Err(tcx.dcx().emit_err(diagnostics::MainFunctionGenericParameters {
173            span: generics_param_span.unwrap_or(main_span),
174            label_span: generics_param_span,
175        }));
176    } else if !main_fn_predicates.predicates.is_empty() {
177        // generics may bring in implicit predicates, so we skip this check if generics is present.
178        let generics_where_clauses_span = main_fn_where_clauses_span(tcx, main_def_id);
179        return Err(tcx.dcx().emit_err(diagnostics::WhereClauseOnMain {
180            span: generics_where_clauses_span.unwrap_or(main_span),
181            generics_span: generics_where_clauses_span,
182        }));
183    }
184
185    Ok(())
186}