rustc_hir_analysis/check/
entry.rs

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