Auto merge of #106092 - asquared31415:start_lang_item_checks, r=davidtwco
Add checks for the signature of the `start` lang item Closes #105963
This commit is contained in:
commit
4a04f252f9
@ -46,3 +46,14 @@ hir_typeck_add_missing_parentheses_in_range = you must surround the range in par
|
||||
|
||||
hir_typeck_op_trait_generic_params =
|
||||
`{$method_name}` must not have any generic parameters
|
||||
|
||||
hir_typeck_lang_start_incorrect_number_params = incorrect number of parameters for the `start` lang item
|
||||
hir_typeck_lang_start_incorrect_number_params_note_expected_count = the `start` lang item should have four parameters, but found {$found_param_count}
|
||||
|
||||
hir_typeck_lang_start_expected_sig_note = the `start` lang item should have the signature `fn(fn() -> T, isize, *const *const u8, u8) -> isize`
|
||||
|
||||
hir_typeck_lang_start_incorrect_param = parameter {$param_num} of the `start` lang item is incorrect
|
||||
.suggestion = change the type from `{$found_ty}` to `{$expected_ty}`
|
||||
|
||||
hir_typeck_lang_start_incorrect_ret_ty = the return type of the `start` lang item is incorrect
|
||||
.suggestion = change the type from `{$found_ty}` to `{$expected_ty}`
|
||||
|
@ -1,4 +1,7 @@
|
||||
use crate::coercion::CoerceMany;
|
||||
use crate::errors::{
|
||||
LangStartIncorrectNumberArgs, LangStartIncorrectParam, LangStartIncorrectRetTy,
|
||||
};
|
||||
use crate::gather_locals::GatherLocalsVisitor;
|
||||
use crate::FnCtxt;
|
||||
use crate::GeneratorTypes;
|
||||
@ -9,8 +12,9 @@ use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir_analysis::check::fn_maybe_err;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_infer::infer::RegionVariableOrigin;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, Binder, Ty, TyCtxt};
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_trait_selection::traits;
|
||||
use std::cell::RefCell;
|
||||
|
||||
@ -168,6 +172,10 @@ pub(super) fn check_fn<'a, 'tcx>(
|
||||
check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig, decl, declared_ret_ty);
|
||||
}
|
||||
|
||||
if let Some(lang_start_defid) = tcx.lang_items().start_fn() && lang_start_defid == hir.local_def_id(fn_id).to_def_id() {
|
||||
check_lang_start_fn(tcx, fn_sig, decl, fn_def_id);
|
||||
}
|
||||
|
||||
gen_ty
|
||||
}
|
||||
|
||||
@ -223,3 +231,126 @@ fn check_panic_info_fn(
|
||||
tcx.sess.span_err(span, "should have no const parameters");
|
||||
}
|
||||
}
|
||||
|
||||
fn check_lang_start_fn<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
fn_sig: ty::FnSig<'tcx>,
|
||||
decl: &'tcx hir::FnDecl<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
) {
|
||||
let inputs = fn_sig.inputs();
|
||||
|
||||
let arg_count = inputs.len();
|
||||
if arg_count != 4 {
|
||||
tcx.sess.emit_err(LangStartIncorrectNumberArgs {
|
||||
params_span: tcx.def_span(def_id),
|
||||
found_param_count: arg_count,
|
||||
});
|
||||
}
|
||||
|
||||
// only check args if they should exist by checking the count
|
||||
// note: this does not handle args being shifted or their order swapped very nicely
|
||||
// but it's a lang item, users shouldn't frequently encounter this
|
||||
|
||||
// first arg is `main: fn() -> T`
|
||||
if let Some(&main_arg) = inputs.get(0) {
|
||||
// make a Ty for the generic on the fn for diagnostics
|
||||
// FIXME: make the lang item generic checks check for the right generic *kind*
|
||||
// for example `start`'s generic should be a type parameter
|
||||
let generics = tcx.generics_of(def_id);
|
||||
let fn_generic = generics.param_at(0, tcx);
|
||||
let generic_tykind =
|
||||
ty::Param(ty::ParamTy { index: fn_generic.index, name: fn_generic.name });
|
||||
let generic_ty = tcx.mk_ty(generic_tykind);
|
||||
let expected_fn_sig =
|
||||
tcx.mk_fn_sig([].iter(), &generic_ty, false, hir::Unsafety::Normal, Abi::Rust);
|
||||
let expected_ty = tcx.mk_fn_ptr(Binder::dummy(expected_fn_sig));
|
||||
|
||||
// we emit the same error to suggest changing the arg no matter what's wrong with the arg
|
||||
let emit_main_fn_arg_err = || {
|
||||
tcx.sess.emit_err(LangStartIncorrectParam {
|
||||
param_span: decl.inputs[0].span,
|
||||
param_num: 1,
|
||||
expected_ty: expected_ty,
|
||||
found_ty: main_arg,
|
||||
});
|
||||
};
|
||||
|
||||
if let ty::FnPtr(main_fn_sig) = main_arg.kind() {
|
||||
let main_fn_inputs = main_fn_sig.inputs();
|
||||
if main_fn_inputs.iter().count() != 0 {
|
||||
emit_main_fn_arg_err();
|
||||
}
|
||||
|
||||
let output = main_fn_sig.output();
|
||||
output.map_bound(|ret_ty| {
|
||||
// if the output ty is a generic, it's probably the right one
|
||||
if !matches!(ret_ty.kind(), ty::Param(_)) {
|
||||
emit_main_fn_arg_err();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
emit_main_fn_arg_err();
|
||||
}
|
||||
}
|
||||
|
||||
// second arg is isize
|
||||
if let Some(&argc_arg) = inputs.get(1) {
|
||||
if argc_arg != tcx.types.isize {
|
||||
tcx.sess.emit_err(LangStartIncorrectParam {
|
||||
param_span: decl.inputs[1].span,
|
||||
param_num: 2,
|
||||
expected_ty: tcx.types.isize,
|
||||
found_ty: argc_arg,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// third arg is `*const *const u8`
|
||||
if let Some(&argv_arg) = inputs.get(2) {
|
||||
let mut argv_is_okay = false;
|
||||
if let ty::RawPtr(outer_ptr) = argv_arg.kind() {
|
||||
if outer_ptr.mutbl.is_not() {
|
||||
if let ty::RawPtr(inner_ptr) = outer_ptr.ty.kind() {
|
||||
if inner_ptr.mutbl.is_not() && inner_ptr.ty == tcx.types.u8 {
|
||||
argv_is_okay = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !argv_is_okay {
|
||||
let inner_ptr_ty =
|
||||
tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: tcx.types.u8 });
|
||||
let expected_ty =
|
||||
tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: inner_ptr_ty });
|
||||
tcx.sess.emit_err(LangStartIncorrectParam {
|
||||
param_span: decl.inputs[2].span,
|
||||
param_num: 3,
|
||||
expected_ty,
|
||||
found_ty: argv_arg,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// fourth arg is `sigpipe: u8`
|
||||
if let Some(&sigpipe_arg) = inputs.get(3) {
|
||||
if sigpipe_arg != tcx.types.u8 {
|
||||
tcx.sess.emit_err(LangStartIncorrectParam {
|
||||
param_span: decl.inputs[3].span,
|
||||
param_num: 4,
|
||||
expected_ty: tcx.types.u8,
|
||||
found_ty: sigpipe_arg,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// output type is isize
|
||||
if fn_sig.output() != tcx.types.isize {
|
||||
tcx.sess.emit_err(LangStartIncorrectRetTy {
|
||||
ret_span: decl.output.span(),
|
||||
expected_ty: tcx.types.isize,
|
||||
found_ty: fn_sig.output(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -172,3 +172,36 @@ impl AddToDiagnostic for TypeMismatchFruTypo {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_typeck_lang_start_incorrect_number_params)]
|
||||
#[note(hir_typeck_lang_start_incorrect_number_params_note_expected_count)]
|
||||
#[note(hir_typeck_lang_start_expected_sig_note)]
|
||||
pub struct LangStartIncorrectNumberArgs {
|
||||
#[primary_span]
|
||||
pub params_span: Span,
|
||||
pub found_param_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_typeck_lang_start_incorrect_param)]
|
||||
pub struct LangStartIncorrectParam<'tcx> {
|
||||
#[primary_span]
|
||||
#[suggestion(style = "short", code = "{expected_ty}", applicability = "machine-applicable")]
|
||||
pub param_span: Span,
|
||||
|
||||
pub param_num: usize,
|
||||
pub expected_ty: Ty<'tcx>,
|
||||
pub found_ty: Ty<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_typeck_lang_start_incorrect_ret_ty)]
|
||||
pub struct LangStartIncorrectRetTy<'tcx> {
|
||||
#[primary_span]
|
||||
#[suggestion(style = "short", code = "{expected_ty}", applicability = "machine-applicable")]
|
||||
pub ret_span: Span,
|
||||
|
||||
pub expected_ty: Ty<'tcx>,
|
||||
pub found_ty: Ty<'tcx>,
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ pub trait Copy {}
|
||||
pub unsafe trait Freeze {}
|
||||
|
||||
#[lang = "start"]
|
||||
fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
|
||||
fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
|
||||
0
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ trait Sized {}
|
||||
auto trait Freeze {}
|
||||
|
||||
#[lang = "start"]
|
||||
fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
|
||||
fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
|
||||
0
|
||||
}
|
||||
|
||||
|
8
tests/ui/lang-items/start_lang_item_args.argc.stderr
Normal file
8
tests/ui/lang-items/start_lang_item_args.argc.stderr
Normal file
@ -0,0 +1,8 @@
|
||||
error: parameter 2 of the `start` lang item is incorrect
|
||||
--> $DIR/start_lang_item_args.rs:75:38
|
||||
|
|
||||
LL | fn start<T>(_main: fn() -> T, _argc: i8, _argv: *const *const u8, _sigpipe: u8) -> isize {
|
||||
| ^^ help: change the type from `i8` to `isize`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
8
tests/ui/lang-items/start_lang_item_args.argv.stderr
Normal file
8
tests/ui/lang-items/start_lang_item_args.argv.stderr
Normal file
@ -0,0 +1,8 @@
|
||||
error: parameter 3 of the `start` lang item is incorrect
|
||||
--> $DIR/start_lang_item_args.rs:89:52
|
||||
|
|
||||
LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: u8, _sigpipe: u8) -> isize {
|
||||
| ^^ help: change the type from `u8` to `*const *const u8`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,13 @@
|
||||
error: parameter 3 of the `start` lang item is incorrect
|
||||
--> $DIR/start_lang_item_args.rs:82:52
|
||||
|
|
||||
LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const usize, _sigpipe: u8) -> isize {
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: change the type from `*const *const usize` to `*const *const u8`
|
||||
|
|
||||
LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
|
||||
| ~~~~~~~~~~~~~~~~
|
||||
|
||||
error: aborting due to previous error
|
||||
|
13
tests/ui/lang-items/start_lang_item_args.main_args.stderr
Normal file
13
tests/ui/lang-items/start_lang_item_args.main_args.stderr
Normal file
@ -0,0 +1,13 @@
|
||||
error: parameter 1 of the `start` lang item is incorrect
|
||||
--> $DIR/start_lang_item_args.rs:61:20
|
||||
|
|
||||
LL | fn start<T>(_main: fn(i32) -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
help: change the type from `fn(i32) -> T` to `fn() -> T`
|
||||
|
|
||||
LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
|
||||
| ~~~~~~~~~
|
||||
|
||||
error: aborting due to previous error
|
||||
|
13
tests/ui/lang-items/start_lang_item_args.main_ret.stderr
Normal file
13
tests/ui/lang-items/start_lang_item_args.main_ret.stderr
Normal file
@ -0,0 +1,13 @@
|
||||
error: parameter 1 of the `start` lang item is incorrect
|
||||
--> $DIR/start_lang_item_args.rs:68:20
|
||||
|
|
||||
LL | fn start<T>(_main: fn() -> u16, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
help: change the type from `fn() -> u16` to `fn() -> T`
|
||||
|
|
||||
LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
|
||||
| ~~~~~~~~~
|
||||
|
||||
error: aborting due to previous error
|
||||
|
8
tests/ui/lang-items/start_lang_item_args.main_ty.stderr
Normal file
8
tests/ui/lang-items/start_lang_item_args.main_ty.stderr
Normal file
@ -0,0 +1,8 @@
|
||||
error: parameter 1 of the `start` lang item is incorrect
|
||||
--> $DIR/start_lang_item_args.rs:54:20
|
||||
|
|
||||
LL | fn start<T>(_main: u64, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
|
||||
| ^^^ help: change the type from `u64` to `fn() -> T`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,11 @@
|
||||
error: incorrect number of parameters for the `start` lang item
|
||||
--> $DIR/start_lang_item_args.rs:15:1
|
||||
|
|
||||
LL | fn start<T>() -> isize {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the `start` lang item should have four parameters, but found 0
|
||||
= note: the `start` lang item should have the signature `fn(fn() -> T, isize, *const *const u8, u8) -> isize`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,8 @@
|
||||
error: the return type of the `start` lang item is incorrect
|
||||
--> $DIR/start_lang_item_args.rs:29:84
|
||||
|
|
||||
LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) {}
|
||||
| ^ help: change the type from `()` to `isize`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,11 @@
|
||||
error: incorrect number of parameters for the `start` lang item
|
||||
--> $DIR/start_lang_item_args.rs:22:1
|
||||
|
|
||||
LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the `start` lang item should have four parameters, but found 3
|
||||
= note: the `start` lang item should have the signature `fn(fn() -> T, isize, *const *const u8, u8) -> isize`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
101
tests/ui/lang-items/start_lang_item_args.rs
Normal file
101
tests/ui/lang-items/start_lang_item_args.rs
Normal file
@ -0,0 +1,101 @@
|
||||
// check-fail
|
||||
// revisions: missing_all_args missing_sigpipe_arg missing_ret start_ret too_many_args
|
||||
// revisions: main_ty main_args main_ret argc argv_inner_ptr argv sigpipe
|
||||
|
||||
#![feature(lang_items, no_core)]
|
||||
#![no_core]
|
||||
|
||||
#[lang = "copy"]
|
||||
pub trait Copy {}
|
||||
#[lang = "sized"]
|
||||
pub trait Sized {}
|
||||
|
||||
#[cfg(missing_all_args)]
|
||||
#[lang = "start"]
|
||||
fn start<T>() -> isize {
|
||||
//[missing_all_args]~^ ERROR incorrect number of parameters
|
||||
100
|
||||
}
|
||||
|
||||
#[cfg(missing_sigpipe_arg)]
|
||||
#[lang = "start"]
|
||||
fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
|
||||
//[missing_sigpipe_arg]~^ ERROR incorrect number of parameters
|
||||
100
|
||||
}
|
||||
|
||||
#[cfg(missing_ret)]
|
||||
#[lang = "start"]
|
||||
fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) {}
|
||||
//[missing_ret]~^ ERROR the return type of the `start` lang item is incorrect
|
||||
|
||||
#[cfg(start_ret)]
|
||||
#[lang = "start"]
|
||||
fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> u8 {
|
||||
//[start_ret]~^ ERROR the return type of the `start` lang item is incorrect
|
||||
100
|
||||
}
|
||||
|
||||
#[cfg(too_many_args)]
|
||||
#[lang = "start"]
|
||||
fn start<T>(
|
||||
//[too_many_args]~^ ERROR incorrect number of parameters
|
||||
_main: fn() -> T,
|
||||
_argc: isize,
|
||||
_argv: *const *const u8,
|
||||
_sigpipe: u8,
|
||||
_extra_arg: (),
|
||||
) -> isize {
|
||||
100
|
||||
}
|
||||
|
||||
#[cfg(main_ty)]
|
||||
#[lang = "start"]
|
||||
fn start<T>(_main: u64, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
|
||||
//[main_ty]~^ ERROR parameter 1 of the `start` lang item is incorrect
|
||||
100
|
||||
}
|
||||
|
||||
#[cfg(main_args)]
|
||||
#[lang = "start"]
|
||||
fn start<T>(_main: fn(i32) -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
|
||||
//[main_args]~^ ERROR parameter 1 of the `start` lang item is incorrect
|
||||
100
|
||||
}
|
||||
|
||||
#[cfg(main_ret)]
|
||||
#[lang = "start"]
|
||||
fn start<T>(_main: fn() -> u16, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
|
||||
//[main_ret]~^ ERROR parameter 1 of the `start` lang item is incorrect
|
||||
100
|
||||
}
|
||||
|
||||
#[cfg(argc)]
|
||||
#[lang = "start"]
|
||||
fn start<T>(_main: fn() -> T, _argc: i8, _argv: *const *const u8, _sigpipe: u8) -> isize {
|
||||
//[argc]~^ ERROR parameter 2 of the `start` lang item is incorrect
|
||||
100
|
||||
}
|
||||
|
||||
#[cfg(argv_inner_ptr)]
|
||||
#[lang = "start"]
|
||||
fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const usize, _sigpipe: u8) -> isize {
|
||||
//[argv_inner_ptr]~^ ERROR parameter 3 of the `start` lang item is incorrect
|
||||
100
|
||||
}
|
||||
|
||||
#[cfg(argv)]
|
||||
#[lang = "start"]
|
||||
fn start<T>(_main: fn() -> T, _argc: isize, _argv: u8, _sigpipe: u8) -> isize {
|
||||
//[argv]~^ ERROR parameter 3 of the `start` lang item is incorrect
|
||||
100
|
||||
}
|
||||
|
||||
#[cfg(sigpipe)]
|
||||
#[lang = "start"]
|
||||
fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: i64) -> isize {
|
||||
//[sigpipe]~^ ERROR parameter 4 of the `start` lang item is incorrect
|
||||
100
|
||||
}
|
||||
|
||||
fn main() {}
|
8
tests/ui/lang-items/start_lang_item_args.sigpipe.stderr
Normal file
8
tests/ui/lang-items/start_lang_item_args.sigpipe.stderr
Normal file
@ -0,0 +1,8 @@
|
||||
error: parameter 4 of the `start` lang item is incorrect
|
||||
--> $DIR/start_lang_item_args.rs:96:80
|
||||
|
|
||||
LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: i64) -> isize {
|
||||
| ^^^ help: change the type from `i64` to `u8`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,8 @@
|
||||
error: the return type of the `start` lang item is incorrect
|
||||
--> $DIR/start_lang_item_args.rs:34:87
|
||||
|
|
||||
LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> u8 {
|
||||
| ^^ help: change the type from `u8` to `isize`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,17 @@
|
||||
error: incorrect number of parameters for the `start` lang item
|
||||
--> $DIR/start_lang_item_args.rs:41:1
|
||||
|
|
||||
LL | / fn start<T>(
|
||||
LL | |
|
||||
LL | | _main: fn() -> T,
|
||||
LL | | _argc: isize,
|
||||
... |
|
||||
LL | | _extra_arg: (),
|
||||
LL | | ) -> isize {
|
||||
| |__________^
|
||||
|
|
||||
= note: the `start` lang item should have four parameters, but found 5
|
||||
= note: the `start` lang item should have the signature `fn(fn() -> T, isize, *const *const u8, u8) -> isize`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
Loading…
x
Reference in New Issue
Block a user