|  | 
|  | 1 | +use rustc_hir as hir; | 
|  | 2 | +use rustc_hir::Node; | 
|  | 3 | +use rustc_infer::infer::TyCtxtInferExt; | 
|  | 4 | +use rustc_middle::ty::{self, Ty, TyCtxt}; | 
|  | 5 | +use rustc_session::config::EntryFnType; | 
|  | 6 | +use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; | 
|  | 7 | +use rustc_span::{symbol::sym, Span}; | 
|  | 8 | +use rustc_target::spec::abi::Abi; | 
|  | 9 | +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; | 
|  | 10 | +use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; | 
|  | 11 | + | 
|  | 12 | +use std::ops::Not; | 
|  | 13 | + | 
|  | 14 | +use crate::errors; | 
|  | 15 | +use crate::require_same_types; | 
|  | 16 | + | 
|  | 17 | +pub(crate) fn check_for_entry_fn(tcx: TyCtxt<'_>) { | 
|  | 18 | +    match tcx.entry_fn(()) { | 
|  | 19 | +        Some((def_id, EntryFnType::Main { .. })) => check_main_fn_ty(tcx, def_id), | 
|  | 20 | +        Some((def_id, EntryFnType::Start)) => check_start_fn_ty(tcx, def_id), | 
|  | 21 | +        _ => {} | 
|  | 22 | +    } | 
|  | 23 | +} | 
|  | 24 | + | 
|  | 25 | +fn 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 | +        let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); | 
|  | 46 | +        match tcx.hir().find(hir_id) { | 
|  | 47 | +            Some(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 | +        let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); | 
|  | 61 | +        match tcx.hir().find(hir_id) { | 
|  | 62 | +            Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, generics, _), .. })) => { | 
|  | 63 | +                Some(generics.where_clause_span) | 
|  | 64 | +            } | 
|  | 65 | +            _ => { | 
|  | 66 | +                span_bug!(tcx.def_span(def_id), "main has a non-function type"); | 
|  | 67 | +            } | 
|  | 68 | +        } | 
|  | 69 | +    } | 
|  | 70 | + | 
|  | 71 | +    fn main_fn_asyncness_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> { | 
|  | 72 | +        if !def_id.is_local() { | 
|  | 73 | +            return None; | 
|  | 74 | +        } | 
|  | 75 | +        Some(tcx.def_span(def_id)) | 
|  | 76 | +    } | 
|  | 77 | + | 
|  | 78 | +    fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> { | 
|  | 79 | +        if !def_id.is_local() { | 
|  | 80 | +            return None; | 
|  | 81 | +        } | 
|  | 82 | +        let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); | 
|  | 83 | +        match tcx.hir().find(hir_id) { | 
|  | 84 | +            Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(fn_sig, _, _), .. })) => { | 
|  | 85 | +                Some(fn_sig.decl.output.span()) | 
|  | 86 | +            } | 
|  | 87 | +            _ => { | 
|  | 88 | +                span_bug!(tcx.def_span(def_id), "main has a non-function type"); | 
|  | 89 | +            } | 
|  | 90 | +        } | 
|  | 91 | +    } | 
|  | 92 | + | 
|  | 93 | +    let mut error = false; | 
|  | 94 | +    let main_diagnostics_def_id = main_fn_diagnostics_def_id(tcx, main_def_id, main_span); | 
|  | 95 | +    let main_fn_generics = tcx.generics_of(main_def_id); | 
|  | 96 | +    let main_fn_predicates = tcx.predicates_of(main_def_id); | 
|  | 97 | +    if main_fn_generics.count() != 0 || !main_fnsig.bound_vars().is_empty() { | 
|  | 98 | +        let generics_param_span = main_fn_generics_params_span(tcx, main_def_id); | 
|  | 99 | +        tcx.sess.emit_err(errors::MainFunctionGenericParameters { | 
|  | 100 | +            span: generics_param_span.unwrap_or(main_span), | 
|  | 101 | +            label_span: generics_param_span, | 
|  | 102 | +        }); | 
|  | 103 | +        error = true; | 
|  | 104 | +    } else if !main_fn_predicates.predicates.is_empty() { | 
|  | 105 | +        // generics may bring in implicit predicates, so we skip this check if generics is present. | 
|  | 106 | +        let generics_where_clauses_span = main_fn_where_clauses_span(tcx, main_def_id); | 
|  | 107 | +        tcx.sess.emit_err(errors::WhereClauseOnMain { | 
|  | 108 | +            span: generics_where_clauses_span.unwrap_or(main_span), | 
|  | 109 | +            generics_span: generics_where_clauses_span, | 
|  | 110 | +        }); | 
|  | 111 | +        error = true; | 
|  | 112 | +    } | 
|  | 113 | + | 
|  | 114 | +    let main_asyncness = tcx.asyncness(main_def_id); | 
|  | 115 | +    if let hir::IsAsync::Async = main_asyncness { | 
|  | 116 | +        let asyncness_span = main_fn_asyncness_span(tcx, main_def_id); | 
|  | 117 | +        tcx.sess.emit_err(errors::MainFunctionAsync { span: main_span, asyncness: asyncness_span }); | 
|  | 118 | +        error = true; | 
|  | 119 | +    } | 
|  | 120 | + | 
|  | 121 | +    for attr in tcx.get_attrs(main_def_id, sym::track_caller) { | 
|  | 122 | +        tcx.sess.emit_err(errors::TrackCallerOnMain { span: attr.span, annotated: main_span }); | 
|  | 123 | +        error = true; | 
|  | 124 | +    } | 
|  | 125 | + | 
|  | 126 | +    if !tcx.codegen_fn_attrs(main_def_id).target_features.is_empty() | 
|  | 127 | +        // Calling functions with `#[target_feature]` is not unsafe on WASM, see #84988 | 
|  | 128 | +        && !tcx.sess.target.is_like_wasm | 
|  | 129 | +        && !tcx.sess.opts.actually_rustdoc | 
|  | 130 | +    { | 
|  | 131 | +        tcx.sess.emit_err(errors::TargetFeatureOnMain { main: main_span }); | 
|  | 132 | +        error = true; | 
|  | 133 | +    } | 
|  | 134 | + | 
|  | 135 | +    if error { | 
|  | 136 | +        return; | 
|  | 137 | +    } | 
|  | 138 | + | 
|  | 139 | +    // Main should have no WC, so empty param env is OK here. | 
|  | 140 | +    let param_env = ty::ParamEnv::empty(); | 
|  | 141 | +    let expected_return_type; | 
|  | 142 | +    if let Some(term_did) = tcx.lang_items().termination() { | 
|  | 143 | +        let return_ty = main_fnsig.output(); | 
|  | 144 | +        let return_ty_span = main_fn_return_type_span(tcx, main_def_id).unwrap_or(main_span); | 
|  | 145 | +        if !return_ty.bound_vars().is_empty() { | 
|  | 146 | +            tcx.sess.emit_err(errors::MainFunctionReturnTypeGeneric { span: return_ty_span }); | 
|  | 147 | +            error = true; | 
|  | 148 | +        } | 
|  | 149 | +        let return_ty = return_ty.skip_binder(); | 
|  | 150 | +        let infcx = tcx.infer_ctxt().build(); | 
|  | 151 | +        let cause = traits::ObligationCause::new( | 
|  | 152 | +            return_ty_span, | 
|  | 153 | +            main_diagnostics_def_id, | 
|  | 154 | +            ObligationCauseCode::MainFunctionType, | 
|  | 155 | +        ); | 
|  | 156 | +        let ocx = traits::ObligationCtxt::new(&infcx); | 
|  | 157 | +        let norm_return_ty = ocx.normalize(&cause, param_env, return_ty); | 
|  | 158 | +        ocx.register_bound(cause, param_env, norm_return_ty, term_did); | 
|  | 159 | +        let errors = ocx.select_all_or_error(); | 
|  | 160 | +        if !errors.is_empty() { | 
|  | 161 | +            infcx.err_ctxt().report_fulfillment_errors(&errors); | 
|  | 162 | +            error = true; | 
|  | 163 | +        } | 
|  | 164 | +        // now we can take the return type of the given main function | 
|  | 165 | +        expected_return_type = main_fnsig.output(); | 
|  | 166 | +    } else { | 
|  | 167 | +        // standard () main return type | 
|  | 168 | +        expected_return_type = ty::Binder::dummy(Ty::new_unit(tcx)); | 
|  | 169 | +    } | 
|  | 170 | + | 
|  | 171 | +    if error { | 
|  | 172 | +        return; | 
|  | 173 | +    } | 
|  | 174 | + | 
|  | 175 | +    let se_ty = Ty::new_fn_ptr( | 
|  | 176 | +        tcx, | 
|  | 177 | +        expected_return_type.map_bound(|expected_return_type| { | 
|  | 178 | +            tcx.mk_fn_sig([], expected_return_type, false, hir::Unsafety::Normal, Abi::Rust) | 
|  | 179 | +        }), | 
|  | 180 | +    ); | 
|  | 181 | + | 
|  | 182 | +    require_same_types( | 
|  | 183 | +        tcx, | 
|  | 184 | +        &ObligationCause::new( | 
|  | 185 | +            main_span, | 
|  | 186 | +            main_diagnostics_def_id, | 
|  | 187 | +            ObligationCauseCode::MainFunctionType, | 
|  | 188 | +        ), | 
|  | 189 | +        param_env, | 
|  | 190 | +        se_ty, | 
|  | 191 | +        Ty::new_fn_ptr(tcx, main_fnsig), | 
|  | 192 | +    ); | 
|  | 193 | +} | 
|  | 194 | + | 
|  | 195 | +fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) { | 
|  | 196 | +    let start_def_id = start_def_id.expect_local(); | 
|  | 197 | +    let start_id = tcx.hir().local_def_id_to_hir_id(start_def_id); | 
|  | 198 | +    let start_span = tcx.def_span(start_def_id); | 
|  | 199 | +    let start_t = tcx.type_of(start_def_id).instantiate_identity(); | 
|  | 200 | +    match start_t.kind() { | 
|  | 201 | +        ty::FnDef(..) => { | 
|  | 202 | +            if let Some(Node::Item(it)) = tcx.hir().find(start_id) { | 
|  | 203 | +                if let hir::ItemKind::Fn(sig, generics, _) = &it.kind { | 
|  | 204 | +                    let mut error = false; | 
|  | 205 | +                    if !generics.params.is_empty() { | 
|  | 206 | +                        tcx.sess.emit_err(errors::StartFunctionParameters { span: generics.span }); | 
|  | 207 | +                        error = true; | 
|  | 208 | +                    } | 
|  | 209 | +                    if generics.has_where_clause_predicates { | 
|  | 210 | +                        tcx.sess.emit_err(errors::StartFunctionWhere { | 
|  | 211 | +                            span: generics.where_clause_span, | 
|  | 212 | +                        }); | 
|  | 213 | +                        error = true; | 
|  | 214 | +                    } | 
|  | 215 | +                    if let hir::IsAsync::Async = sig.header.asyncness { | 
|  | 216 | +                        let span = tcx.def_span(it.owner_id); | 
|  | 217 | +                        tcx.sess.emit_err(errors::StartAsync { span: span }); | 
|  | 218 | +                        error = true; | 
|  | 219 | +                    } | 
|  | 220 | + | 
|  | 221 | +                    let attrs = tcx.hir().attrs(start_id); | 
|  | 222 | +                    for attr in attrs { | 
|  | 223 | +                        if attr.has_name(sym::track_caller) { | 
|  | 224 | +                            tcx.sess.emit_err(errors::StartTrackCaller { | 
|  | 225 | +                                span: attr.span, | 
|  | 226 | +                                start: start_span, | 
|  | 227 | +                            }); | 
|  | 228 | +                            error = true; | 
|  | 229 | +                        } | 
|  | 230 | +                        if attr.has_name(sym::target_feature) | 
|  | 231 | +                            // Calling functions with `#[target_feature]` is | 
|  | 232 | +                            // not unsafe on WASM, see #84988 | 
|  | 233 | +                            && !tcx.sess.target.is_like_wasm | 
|  | 234 | +                            && !tcx.sess.opts.actually_rustdoc | 
|  | 235 | +                        { | 
|  | 236 | +                            tcx.sess.emit_err(errors::StartTargetFeature { | 
|  | 237 | +                                span: attr.span, | 
|  | 238 | +                                start: start_span, | 
|  | 239 | +                            }); | 
|  | 240 | +                            error = true; | 
|  | 241 | +                        } | 
|  | 242 | +                    } | 
|  | 243 | + | 
|  | 244 | +                    if error { | 
|  | 245 | +                        return; | 
|  | 246 | +                    } | 
|  | 247 | +                } | 
|  | 248 | +            } | 
|  | 249 | + | 
|  | 250 | +            let se_ty = Ty::new_fn_ptr( | 
|  | 251 | +                tcx, | 
|  | 252 | +                ty::Binder::dummy(tcx.mk_fn_sig( | 
|  | 253 | +                    [tcx.types.isize, Ty::new_imm_ptr(tcx, Ty::new_imm_ptr(tcx, tcx.types.u8))], | 
|  | 254 | +                    tcx.types.isize, | 
|  | 255 | +                    false, | 
|  | 256 | +                    hir::Unsafety::Normal, | 
|  | 257 | +                    Abi::Rust, | 
|  | 258 | +                )), | 
|  | 259 | +            ); | 
|  | 260 | + | 
|  | 261 | +            require_same_types( | 
|  | 262 | +                tcx, | 
|  | 263 | +                &ObligationCause::new( | 
|  | 264 | +                    start_span, | 
|  | 265 | +                    start_def_id, | 
|  | 266 | +                    ObligationCauseCode::StartFunctionType, | 
|  | 267 | +                ), | 
|  | 268 | +                ty::ParamEnv::empty(), // start should not have any where bounds. | 
|  | 269 | +                se_ty, | 
|  | 270 | +                Ty::new_fn_ptr(tcx, tcx.fn_sig(start_def_id).instantiate_identity()), | 
|  | 271 | +            ); | 
|  | 272 | +        } | 
|  | 273 | +        _ => { | 
|  | 274 | +            span_bug!(start_span, "start has a non-function type: found `{}`", start_t); | 
|  | 275 | +        } | 
|  | 276 | +    } | 
|  | 277 | +} | 
0 commit comments