rustc_hir_analysis/check/
entry.rs

1use std::ops::Not;
2
3use rustc_abi::ExternAbi;
4use rustc_hir as hir;
5use rustc_hir::Node;
6use rustc_infer::infer::TyCtxtInferExt;
7use rustc_middle::span_bug;
8use rustc_middle::ty::{self, TyCtxt, TypingMode};
9use rustc_session::config::EntryFnType;
10use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
11use rustc_span::{Span, sym};
12use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
13use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
14
15use super::check_function_signature;
16use crate::errors;
17
18pub(crate) fn check_for_entry_fn(tcx: TyCtxt<'_>) {
19    match tcx.entry_fn(()) {
20        Some((def_id, EntryFnType::Main { .. })) => check_main_fn_ty(tcx, def_id),
21        _ => {}
22    }
23}
24
25fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
26    let main_fnsig = tcx.fn_sig(main_def_id).instantiate_identity();
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();
32            if !matches!(hir_type.kind(), ty::FnDef(..)) {
33                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                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                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                span_bug!(tcx.def_span(def_id), "main has a non-function type");
86            }
87        }
88    }
89
90    let mut error = false;
91    let main_diagnostics_def_id = main_fn_diagnostics_def_id(tcx, main_def_id, main_span);
92
93    let main_asyncness = tcx.asyncness(main_def_id);
94    if main_asyncness.is_async() {
95        let asyncness_span = main_fn_asyncness_span(tcx, main_def_id);
96        tcx.dcx()
97            .emit_err(errors::MainFunctionAsync { span: main_span, asyncness: asyncness_span });
98        error = true;
99    }
100
101    for attr in tcx.get_attrs(main_def_id, sym::track_caller) {
102        tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr.span, annotated: main_span });
103        error = true;
104    }
105
106    if !tcx.codegen_fn_attrs(main_def_id).target_features.is_empty()
107        // Calling functions with `#[target_feature]` is not unsafe on WASM, see #84988
108        && !tcx.sess.target.is_like_wasm
109        && !tcx.sess.opts.actually_rustdoc
110    {
111        tcx.dcx().emit_err(errors::TargetFeatureOnMain { main: main_span });
112        error = true;
113    }
114
115    if error {
116        return;
117    }
118
119    // Main should have no WC, so empty param env is OK here.
120    let param_env = ty::ParamEnv::empty();
121    let expected_return_type;
122    if let Some(term_did) = tcx.lang_items().termination() {
123        let return_ty = main_fnsig.output();
124        let return_ty_span = main_fn_return_type_span(tcx, main_def_id).unwrap_or(main_span);
125        let Some(return_ty) = return_ty.no_bound_vars() else {
126            tcx.dcx().emit_err(errors::MainFunctionReturnTypeGeneric { span: return_ty_span });
127            return;
128        };
129        let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
130        let cause = traits::ObligationCause::new(
131            return_ty_span,
132            main_diagnostics_def_id,
133            ObligationCauseCode::MainFunctionType,
134        );
135        let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx);
136        let norm_return_ty = ocx.normalize(&cause, param_env, return_ty);
137        ocx.register_bound(cause, param_env, norm_return_ty, term_did);
138        let errors = ocx.select_all_or_error();
139        if !errors.is_empty() {
140            infcx.err_ctxt().report_fulfillment_errors(errors);
141            error = true;
142        }
143        // now we can take the return type of the given main function
144        expected_return_type = norm_return_ty;
145    } else {
146        // standard () main return type
147        expected_return_type = tcx.types.unit;
148    }
149
150    if error {
151        return;
152    }
153
154    let expected_sig = ty::Binder::dummy(tcx.mk_fn_sig(
155        [],
156        expected_return_type,
157        false,
158        hir::Safety::Safe,
159        ExternAbi::Rust,
160    ));
161
162    if check_function_signature(
163        tcx,
164        ObligationCause::new(
165            main_span,
166            main_diagnostics_def_id,
167            ObligationCauseCode::MainFunctionType,
168        ),
169        main_def_id,
170        expected_sig,
171    )
172    .is_err()
173    {
174        return;
175    }
176
177    let main_fn_generics = tcx.generics_of(main_def_id);
178    let main_fn_predicates = tcx.predicates_of(main_def_id);
179    if main_fn_generics.count() != 0 || !main_fnsig.bound_vars().is_empty() {
180        let generics_param_span = main_fn_generics_params_span(tcx, main_def_id);
181        tcx.dcx().emit_err(errors::MainFunctionGenericParameters {
182            span: generics_param_span.unwrap_or(main_span),
183            label_span: generics_param_span,
184        });
185    } else if !main_fn_predicates.predicates.is_empty() {
186        // generics may bring in implicit predicates, so we skip this check if generics is present.
187        let generics_where_clauses_span = main_fn_where_clauses_span(tcx, main_def_id);
188        tcx.dcx().emit_err(errors::WhereClauseOnMain {
189            span: generics_where_clauses_span.unwrap_or(main_span),
190            generics_span: generics_where_clauses_span,
191        });
192    }
193}