11use crate :: coercion:: CoerceMany ;
2+ use crate :: errors:: {
3+ LangStartIncorrectNumberArgs , LangStartIncorrectParam , LangStartIncorrectRetTy ,
4+ } ;
25use crate :: gather_locals:: GatherLocalsVisitor ;
36use crate :: FnCtxt ;
47use crate :: GeneratorTypes ;
@@ -9,8 +12,9 @@ use rustc_hir::lang_items::LangItem;
912use rustc_hir_analysis:: check:: fn_maybe_err;
1013use rustc_infer:: infer:: type_variable:: { TypeVariableOrigin , TypeVariableOriginKind } ;
1114use rustc_infer:: infer:: RegionVariableOrigin ;
12- use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
15+ use rustc_middle:: ty:: { self , Binder , Ty , TyCtxt } ;
1316use rustc_span:: def_id:: LocalDefId ;
17+ use rustc_target:: spec:: abi:: Abi ;
1418use rustc_trait_selection:: traits;
1519use std:: cell:: RefCell ;
1620
@@ -168,6 +172,10 @@ pub(super) fn check_fn<'a, 'tcx>(
168172 check_panic_info_fn ( tcx, panic_impl_did. expect_local ( ) , fn_sig, decl, declared_ret_ty) ;
169173 }
170174
175+ if let Some ( lang_start_defid) = tcx. lang_items ( ) . start_fn ( ) && lang_start_defid == hir. local_def_id ( fn_id) . to_def_id ( ) {
176+ check_lang_start_fn ( tcx, fn_sig, decl, fn_def_id) ;
177+ }
178+
171179 gen_ty
172180}
173181
@@ -223,3 +231,126 @@ fn check_panic_info_fn(
223231 tcx. sess . span_err ( span, "should have no const parameters" ) ;
224232 }
225233}
234+
235+ fn check_lang_start_fn < ' tcx > (
236+ tcx : TyCtxt < ' tcx > ,
237+ fn_sig : ty:: FnSig < ' tcx > ,
238+ decl : & ' tcx hir:: FnDecl < ' tcx > ,
239+ def_id : LocalDefId ,
240+ ) {
241+ let inputs = fn_sig. inputs ( ) ;
242+
243+ let arg_count = inputs. len ( ) ;
244+ if arg_count != 4 {
245+ tcx. sess . emit_err ( LangStartIncorrectNumberArgs {
246+ params_span : tcx. def_span ( def_id) ,
247+ found_param_count : arg_count,
248+ } ) ;
249+ }
250+
251+ // only check args if they should exist by checking the count
252+ // note: this does not handle args being shifted or their order swapped very nicely
253+ // but it's a lang item, users shouldn't frequently encounter this
254+
255+ // first arg is `main: fn() -> T`
256+ if let Some ( & main_arg) = inputs. get ( 0 ) {
257+ // make a Ty for the generic on the fn for diagnostics
258+ // FIXME: make the lang item generic checks check for the right generic *kind*
259+ // for example `start`'s generic should be a type parameter
260+ let generics = tcx. generics_of ( def_id) ;
261+ let fn_generic = generics. param_at ( 0 , tcx) ;
262+ let generic_tykind =
263+ ty:: Param ( ty:: ParamTy { index : fn_generic. index , name : fn_generic. name } ) ;
264+ let generic_ty = tcx. mk_ty ( generic_tykind) ;
265+ let expected_fn_sig =
266+ tcx. mk_fn_sig ( [ ] . iter ( ) , & generic_ty, false , hir:: Unsafety :: Normal , Abi :: Rust ) ;
267+ let expected_ty = tcx. mk_fn_ptr ( Binder :: dummy ( expected_fn_sig) ) ;
268+
269+ // we emit the same error to suggest changing the arg no matter what's wrong with the arg
270+ let emit_main_fn_arg_err = || {
271+ tcx. sess . emit_err ( LangStartIncorrectParam {
272+ param_span : decl. inputs [ 0 ] . span ,
273+ param_num : 1 ,
274+ expected_ty : expected_ty,
275+ found_ty : main_arg,
276+ } ) ;
277+ } ;
278+
279+ if let ty:: FnPtr ( main_fn_sig) = main_arg. kind ( ) {
280+ let main_fn_inputs = main_fn_sig. inputs ( ) ;
281+ if main_fn_inputs. iter ( ) . count ( ) != 0 {
282+ emit_main_fn_arg_err ( ) ;
283+ }
284+
285+ let output = main_fn_sig. output ( ) ;
286+ output. map_bound ( |ret_ty| {
287+ // if the output ty is a generic, it's probably the right one
288+ if !matches ! ( ret_ty. kind( ) , ty:: Param ( _) ) {
289+ emit_main_fn_arg_err ( ) ;
290+ }
291+ } ) ;
292+ } else {
293+ emit_main_fn_arg_err ( ) ;
294+ }
295+ }
296+
297+ // second arg is isize
298+ if let Some ( & argc_arg) = inputs. get ( 1 ) {
299+ if argc_arg != tcx. types . isize {
300+ tcx. sess . emit_err ( LangStartIncorrectParam {
301+ param_span : decl. inputs [ 1 ] . span ,
302+ param_num : 2 ,
303+ expected_ty : tcx. types . isize ,
304+ found_ty : argc_arg,
305+ } ) ;
306+ }
307+ }
308+
309+ // third arg is `*const *const u8`
310+ if let Some ( & argv_arg) = inputs. get ( 2 ) {
311+ let mut argv_is_okay = false ;
312+ if let ty:: RawPtr ( outer_ptr) = argv_arg. kind ( ) {
313+ if outer_ptr. mutbl . is_not ( ) {
314+ if let ty:: RawPtr ( inner_ptr) = outer_ptr. ty . kind ( ) {
315+ if inner_ptr. mutbl . is_not ( ) && inner_ptr. ty == tcx. types . u8 {
316+ argv_is_okay = true ;
317+ }
318+ }
319+ }
320+ }
321+
322+ if !argv_is_okay {
323+ let inner_ptr_ty =
324+ tcx. mk_ptr ( ty:: TypeAndMut { mutbl : hir:: Mutability :: Not , ty : tcx. types . u8 } ) ;
325+ let expected_ty =
326+ tcx. mk_ptr ( ty:: TypeAndMut { mutbl : hir:: Mutability :: Not , ty : inner_ptr_ty } ) ;
327+ tcx. sess . emit_err ( LangStartIncorrectParam {
328+ param_span : decl. inputs [ 2 ] . span ,
329+ param_num : 3 ,
330+ expected_ty,
331+ found_ty : argv_arg,
332+ } ) ;
333+ }
334+ }
335+
336+ // fourth arg is `sigpipe: u8`
337+ if let Some ( & sigpipe_arg) = inputs. get ( 3 ) {
338+ if sigpipe_arg != tcx. types . u8 {
339+ tcx. sess . emit_err ( LangStartIncorrectParam {
340+ param_span : decl. inputs [ 3 ] . span ,
341+ param_num : 4 ,
342+ expected_ty : tcx. types . u8 ,
343+ found_ty : sigpipe_arg,
344+ } ) ;
345+ }
346+ }
347+
348+ // output type is isize
349+ if fn_sig. output ( ) != tcx. types . isize {
350+ tcx. sess . emit_err ( LangStartIncorrectRetTy {
351+ ret_span : decl. output . span ( ) ,
352+ expected_ty : tcx. types . isize ,
353+ found_ty : fn_sig. output ( ) ,
354+ } ) ;
355+ }
356+ }
0 commit comments