Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add checks for the signature of the start lang item #106092

Merged
merged 1 commit into from
Jan 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions compiler/rustc_error_messages/locales/en-US/hir_typeck.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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}`
133 changes: 132 additions & 1 deletion compiler/rustc_hir_typeck/src/check.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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(),
});
}
}
33 changes: 33 additions & 0 deletions compiler/rustc_hir_typeck/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
}
2 changes: 1 addition & 1 deletion src/tools/clippy/tests/ui/def_id_nocore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion tests/run-make-fulldeps/target-specs/foo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
8 changes: 8 additions & 0 deletions tests/ui/lang-items/start_lang_item_args.argc.stderr
Original file line number Diff line number Diff line change
@@ -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 changes: 8 additions & 0 deletions tests/ui/lang-items/start_lang_item_args.argv.stderr
Original file line number Diff line number Diff line change
@@ -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

13 changes: 13 additions & 0 deletions tests/ui/lang-items/start_lang_item_args.argv_inner_ptr.stderr
Original file line number Diff line number Diff line change
@@ -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 changes: 13 additions & 0 deletions tests/ui/lang-items/start_lang_item_args.main_args.stderr
Original file line number Diff line number Diff line change
@@ -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 changes: 13 additions & 0 deletions tests/ui/lang-items/start_lang_item_args.main_ret.stderr
Original file line number Diff line number Diff line change
@@ -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 changes: 8 additions & 0 deletions tests/ui/lang-items/start_lang_item_args.main_ty.stderr
Original file line number Diff line number Diff line change
@@ -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

11 changes: 11 additions & 0 deletions tests/ui/lang-items/start_lang_item_args.missing_all_args.stderr
Original file line number Diff line number Diff line change
@@ -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

8 changes: 8 additions & 0 deletions tests/ui/lang-items/start_lang_item_args.missing_ret.stderr
Original file line number Diff line number Diff line change
@@ -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

Original file line number Diff line number Diff line change
@@ -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

Loading