diff --git a/compiler/rustc_error_messages/locales/en-US/hir_typeck.ftl b/compiler/rustc_error_messages/locales/en-US/hir_typeck.ftl index 0612dbae0b630..ca72b7faa9289 100644 --- a/compiler/rustc_error_messages/locales/en-US/hir_typeck.ftl +++ b/compiler/rustc_error_messages/locales/en-US/hir_typeck.ftl @@ -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}` diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 32f86b8042c11..57feefbcab6c8 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -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(), + }); + } +} diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 507272fdec5d7..5b4fd5e4a5283 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -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>, +} diff --git a/src/tools/clippy/tests/ui/def_id_nocore.rs b/src/tools/clippy/tests/ui/def_id_nocore.rs index a7da8f89aa3d4..1af77d1a25b2d 100644 --- a/src/tools/clippy/tests/ui/def_id_nocore.rs +++ b/src/tools/clippy/tests/ui/def_id_nocore.rs @@ -15,7 +15,7 @@ pub trait Copy {} pub unsafe trait Freeze {} #[lang = "start"] -fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize { +fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { 0 } diff --git a/tests/run-make-fulldeps/target-specs/foo.rs b/tests/run-make-fulldeps/target-specs/foo.rs index d576a1dd28192..22939e87912c1 100644 --- a/tests/run-make-fulldeps/target-specs/foo.rs +++ b/tests/run-make-fulldeps/target-specs/foo.rs @@ -11,7 +11,7 @@ trait Sized {} auto trait Freeze {} #[lang = "start"] -fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize { +fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { 0 } diff --git a/tests/ui/lang-items/start_lang_item_args.argc.stderr b/tests/ui/lang-items/start_lang_item_args.argc.stderr new file mode 100644 index 0000000000000..65c99a93c7511 --- /dev/null +++ b/tests/ui/lang-items/start_lang_item_args.argc.stderr @@ -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(_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 + diff --git a/tests/ui/lang-items/start_lang_item_args.argv.stderr b/tests/ui/lang-items/start_lang_item_args.argv.stderr new file mode 100644 index 0000000000000..f0947a9b3e933 --- /dev/null +++ b/tests/ui/lang-items/start_lang_item_args.argv.stderr @@ -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(_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 + diff --git a/tests/ui/lang-items/start_lang_item_args.argv_inner_ptr.stderr b/tests/ui/lang-items/start_lang_item_args.argv_inner_ptr.stderr new file mode 100644 index 0000000000000..08efd5088f99b --- /dev/null +++ b/tests/ui/lang-items/start_lang_item_args.argv_inner_ptr.stderr @@ -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(_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(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { + | ~~~~~~~~~~~~~~~~ + +error: aborting due to previous error + diff --git a/tests/ui/lang-items/start_lang_item_args.main_args.stderr b/tests/ui/lang-items/start_lang_item_args.main_args.stderr new file mode 100644 index 0000000000000..c20a744661d49 --- /dev/null +++ b/tests/ui/lang-items/start_lang_item_args.main_args.stderr @@ -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(_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(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { + | ~~~~~~~~~ + +error: aborting due to previous error + diff --git a/tests/ui/lang-items/start_lang_item_args.main_ret.stderr b/tests/ui/lang-items/start_lang_item_args.main_ret.stderr new file mode 100644 index 0000000000000..8f967252f49be --- /dev/null +++ b/tests/ui/lang-items/start_lang_item_args.main_ret.stderr @@ -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(_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(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { + | ~~~~~~~~~ + +error: aborting due to previous error + diff --git a/tests/ui/lang-items/start_lang_item_args.main_ty.stderr b/tests/ui/lang-items/start_lang_item_args.main_ty.stderr new file mode 100644 index 0000000000000..deb37b868ea89 --- /dev/null +++ b/tests/ui/lang-items/start_lang_item_args.main_ty.stderr @@ -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(_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 + diff --git a/tests/ui/lang-items/start_lang_item_args.missing_all_args.stderr b/tests/ui/lang-items/start_lang_item_args.missing_all_args.stderr new file mode 100644 index 0000000000000..004c2a67f62f1 --- /dev/null +++ b/tests/ui/lang-items/start_lang_item_args.missing_all_args.stderr @@ -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() -> 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 + diff --git a/tests/ui/lang-items/start_lang_item_args.missing_ret.stderr b/tests/ui/lang-items/start_lang_item_args.missing_ret.stderr new file mode 100644 index 0000000000000..1d8285b59000a --- /dev/null +++ b/tests/ui/lang-items/start_lang_item_args.missing_ret.stderr @@ -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(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) {} + | ^ help: change the type from `()` to `isize` + +error: aborting due to previous error + diff --git a/tests/ui/lang-items/start_lang_item_args.missing_sigpipe_arg.stderr b/tests/ui/lang-items/start_lang_item_args.missing_sigpipe_arg.stderr new file mode 100644 index 0000000000000..e545a750f24a9 --- /dev/null +++ b/tests/ui/lang-items/start_lang_item_args.missing_sigpipe_arg.stderr @@ -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(_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 + diff --git a/tests/ui/lang-items/start_lang_item_args.rs b/tests/ui/lang-items/start_lang_item_args.rs new file mode 100644 index 0000000000000..0dbfba39cb605 --- /dev/null +++ b/tests/ui/lang-items/start_lang_item_args.rs @@ -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() -> isize { + //[missing_all_args]~^ ERROR incorrect number of parameters + 100 +} + +#[cfg(missing_sigpipe_arg)] +#[lang = "start"] +fn start(_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(_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(_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( + //[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(_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(_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(_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(_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(_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(_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(_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() {} diff --git a/tests/ui/lang-items/start_lang_item_args.sigpipe.stderr b/tests/ui/lang-items/start_lang_item_args.sigpipe.stderr new file mode 100644 index 0000000000000..b20ae31280133 --- /dev/null +++ b/tests/ui/lang-items/start_lang_item_args.sigpipe.stderr @@ -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(_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 + diff --git a/tests/ui/lang-items/start_lang_item_args.start_ret.stderr b/tests/ui/lang-items/start_lang_item_args.start_ret.stderr new file mode 100644 index 0000000000000..935d5f3c8b426 --- /dev/null +++ b/tests/ui/lang-items/start_lang_item_args.start_ret.stderr @@ -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(_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 + diff --git a/tests/ui/lang-items/start_lang_item_args.too_many_args.stderr b/tests/ui/lang-items/start_lang_item_args.too_many_args.stderr new file mode 100644 index 0000000000000..30a7ed18a3d23 --- /dev/null +++ b/tests/ui/lang-items/start_lang_item_args.too_many_args.stderr @@ -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( +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 +