Skip to content

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

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

Merged
merged 1 commit into from
Jan 13, 2023
Merged
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
@@ -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;
@@ -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(),
});
}
}
33 changes: 33 additions & 0 deletions compiler/rustc_hir_typeck/src/errors.rs
Original file line number Diff line number Diff line change
@@ -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
@@ -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
}

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

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

8 changes: 8 additions & 0 deletions tests/ui/lang-items/start_lang_item_args.start_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: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

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