1
1
use crate :: coercion:: CoerceMany ;
2
+ use crate :: errors:: {
3
+ LangStartIncorrectNumberArgs , LangStartIncorrectParam , LangStartIncorrectRetTy ,
4
+ } ;
2
5
use crate :: gather_locals:: GatherLocalsVisitor ;
3
6
use crate :: FnCtxt ;
4
7
use crate :: GeneratorTypes ;
@@ -9,8 +12,9 @@ use rustc_hir::lang_items::LangItem;
9
12
use rustc_hir_analysis:: check:: fn_maybe_err;
10
13
use rustc_infer:: infer:: type_variable:: { TypeVariableOrigin , TypeVariableOriginKind } ;
11
14
use rustc_infer:: infer:: RegionVariableOrigin ;
12
- use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
15
+ use rustc_middle:: ty:: { self , Binder , Ty , TyCtxt } ;
13
16
use rustc_span:: def_id:: LocalDefId ;
17
+ use rustc_target:: spec:: abi:: Abi ;
14
18
use rustc_trait_selection:: traits;
15
19
use std:: cell:: RefCell ;
16
20
@@ -168,6 +172,10 @@ pub(super) fn check_fn<'a, 'tcx>(
168
172
check_panic_info_fn ( tcx, panic_impl_did. expect_local ( ) , fn_sig, decl, declared_ret_ty) ;
169
173
}
170
174
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
+
171
179
gen_ty
172
180
}
173
181
@@ -223,3 +231,126 @@ fn check_panic_info_fn(
223
231
tcx. sess . span_err ( span, "should have no const parameters" ) ;
224
232
}
225
233
}
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