diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 2fd875c344767..e22e2b5570394 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -1764,12 +1764,12 @@ The `main` function was incorrectly declared. Erroneous code example: ```compile_fail,E0580 -fn main() -> i32 { // error: main function has wrong type - 0 +fn main(x: i32) { // error: main function has wrong type + println!("{}", x); } ``` -The `main` function prototype should never take arguments or return type. +The `main` function prototype should never take arguments. Example: ``` diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index ab3c619dcdcd0..3381a640b9768 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -585,20 +585,23 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { trait_ref.to_predicate(), post_message) })); + let explanation = + if obligation.cause.code == ObligationCauseCode::MainFunctionType { + "consider using `()`, or a `Result`".to_owned() + } else { + format!("{}the trait `{}` is not implemented for `{}`", + pre_message, + trait_ref, + trait_ref.self_ty()) + }; + if let Some(ref s) = label { // If it has a custom "#[rustc_on_unimplemented]" // error message, let's display it as the label! err.span_label(span, s.as_str()); - err.help(&format!("{}the trait `{}` is not implemented for `{}`", - pre_message, - trait_ref, - trait_ref.self_ty())); + err.help(&explanation); } else { - err.span_label(span, - &*format!("{}the trait `{}` is not implemented for `{}`", - pre_message, - trait_ref, - trait_ref.self_ty())); + err.span_label(span, explanation); } if let Some(ref s) = note { // If it has a custom "#[rustc_on_unimplemented]" note, let's display it diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 69879bbe85d6e..15d526e4153df 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1121,25 +1121,23 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, } fcx.demand_suptype(span, ret_ty, actual_return_ty); - if fcx.tcx.features().termination_trait { - // If the termination trait language item is activated, check that the main return type - // implements the termination trait. - if let Some(term_id) = fcx.tcx.lang_items().termination() { - if let Some((id, _)) = *fcx.tcx.sess.entry_fn.borrow() { - if id == fn_id { - match fcx.sess().entry_type.get() { - Some(config::EntryMain) => { - let substs = fcx.tcx.mk_substs(iter::once(Kind::from(ret_ty))); - let trait_ref = ty::TraitRef::new(term_id, substs); - let cause = traits::ObligationCause::new( - span, fn_id, ObligationCauseCode::MainFunctionType); - - inherited.register_predicate( - traits::Obligation::new( - cause, param_env, trait_ref.to_predicate())); - }, - _ => {}, - } + // Check that the main return type implements the termination trait. + if let Some(term_id) = fcx.tcx.lang_items().termination() { + if let Some((id, _)) = *fcx.tcx.sess.entry_fn.borrow() { + if id == fn_id { + match fcx.sess().entry_type.get() { + Some(config::EntryMain) => { + let substs = fcx.tcx.mk_substs(iter::once(Kind::from(ret_ty))); + let trait_ref = ty::TraitRef::new(term_id, substs); + let return_ty_span = decl.output.span(); + let cause = traits::ObligationCause::new( + return_ty_span, fn_id, ObligationCauseCode::MainFunctionType); + + inherited.register_predicate( + traits::Obligation::new( + cause, param_env, trait_ref.to_predicate())); + }, + _ => {}, } } } diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 9f98932f24b52..cd21d53b837b2 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -208,8 +208,7 @@ fn check_main_fn_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } let actual = tcx.fn_sig(main_def_id); - let expected_return_type = if tcx.lang_items().termination().is_some() - && tcx.features().termination_trait { + let expected_return_type = if tcx.lang_items().termination().is_some() { // we take the return type of the given main function, the real check is done // in `check_fn` actual.output().skip_binder() diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 36eb729182210..0b06c5d4d656e 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -308,7 +308,6 @@ #![feature(str_char)] #![feature(str_internals)] #![feature(str_utf16)] -#![feature(termination_trait)] #![feature(test, rustc_private)] #![feature(thread_local)] #![feature(toowned_clone_into)] @@ -325,6 +324,7 @@ #![cfg_attr(test, feature(update_panic_count))] #![cfg_attr(windows, feature(used))] #![cfg_attr(stage0, feature(never_type))] +#![cfg_attr(stage0, feature(termination_trait))] #![default_lib_allocator] diff --git a/src/libstd/process.rs b/src/libstd/process.rs index d5ac2d19e831f..c877bf6aa35cd 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -1442,8 +1442,9 @@ pub fn id() -> u32 { /// a successful execution. In case of a failure, `libc::EXIT_FAILURE` is returned. #[cfg_attr(not(test), lang = "termination")] #[unstable(feature = "termination_trait_lib", issue = "43301")] -#[rustc_on_unimplemented = - "`main` can only return types that implement {Termination}, not `{Self}`"] +#[rustc_on_unimplemented( + message="`main` has invalid return type `{Self}`", + label="`main` can only return types that implement {Termination}")] pub trait Termination { /// Is called to get the representation of the value as status code. /// This status code is returned to the operating system. diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 8feb8d6a4020d..b1d7237fc5362 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -428,8 +428,8 @@ declare_features! ( // `foo.rs` as an alternative to `foo/mod.rs` (active, non_modrs_mods, "1.24.0", Some(44660), None), - // Termination trait in main (RFC 1937) - (active, termination_trait, "1.24.0", Some(43301), None), + // Termination trait in tests (RFC 1937) + (active, termination_trait_test, "1.24.0", Some(48854), None), // Allows use of the :lifetime macro fragment specifier (active, macro_lifetime_matcher, "1.24.0", Some(46895), None), @@ -556,6 +556,8 @@ declare_features! ( (accepted, inclusive_range_syntax, "1.26.0", Some(28237), None), // allow `..=` in patterns (RFC 1192) (accepted, dotdoteq_in_patterns, "1.26.0", Some(28237), None), + // Termination trait in main (RFC 1937) + (accepted, termination_trait, "1.26.0", Some(43301), None), ); // If you change this, please modify src/doc/unstable-book as well. You must diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 5264b627e9613..6c01171d1003e 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -332,7 +332,7 @@ fn is_test_fn(cx: &TestCtxt, i: &ast::Item) -> bool { ast::ItemKind::Fn(ref decl, _, _, _, ref generics, _) => { // If the termination trait is active, the compiler will check that the output // type implements the `Termination` trait as `libtest` enforces that. - let output_matches = if cx.features.termination_trait { + let output_matches = if cx.features.termination_trait_test { true } else { let no_output = match decl.output { @@ -359,7 +359,7 @@ fn is_test_fn(cx: &TestCtxt, i: &ast::Item) -> bool { match has_test_signature(cx, i) { Yes => true, No => { - if cx.features.termination_trait { + if cx.features.termination_trait_test { diag.span_err(i.span, "functions used as tests can not have any arguments"); } else { diag.span_err(i.span, "functions used as tests must have signature fn() -> ()"); @@ -388,7 +388,7 @@ fn is_bench_fn(cx: &TestCtxt, i: &ast::Item) -> bool { // If the termination trait is active, the compiler will check that the output // type implements the `Termination` trait as `libtest` enforces that. - let output_matches = if cx.features.termination_trait { + let output_matches = if cx.features.termination_trait_test { true } else { let no_output = match decl.output { @@ -416,7 +416,7 @@ fn is_bench_fn(cx: &TestCtxt, i: &ast::Item) -> bool { if has_bench_attr && !has_bench_signature { let diag = cx.span_diagnostic; - if cx.features.termination_trait { + if cx.features.termination_trait_test { diag.span_err(i.span, "functions used as benches must have signature \ `fn(&mut Bencher) -> impl Termination`"); } else { diff --git a/src/test/compile-fail/feature-gate-termination_trait_test.rs b/src/test/compile-fail/feature-gate-termination_trait_test.rs new file mode 100644 index 0000000000000..4af7e94671627 --- /dev/null +++ b/src/test/compile-fail/feature-gate-termination_trait_test.rs @@ -0,0 +1,22 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: --test + +fn main() {} + +#[cfg(test)] +mod tests { + #[test] + fn it_works() -> Result<(), ()> { + //~^ ERROR functions used as tests must have signature fn() -> () + Ok(()) + } +} diff --git a/src/test/compile-fail/feature-gate-termination_trait.rs b/src/test/compile-fail/rfc-1937-termination-trait/termination-trait-main-i32.rs similarity index 71% rename from src/test/compile-fail/feature-gate-termination_trait.rs rename to src/test/compile-fail/rfc-1937-termination-trait/termination-trait-main-i32.rs index 5a56445b64e57..0e6ddf7c92f1a 100644 --- a/src/test/compile-fail/feature-gate-termination_trait.rs +++ b/src/test/compile-fail/rfc-1937-termination-trait/termination-trait-main-i32.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn main() -> i32 { //~ ERROR main function has wrong type [E0580] +fn main() -> i32 { +//~^ ERROR `main` has invalid return type `i32` +//~| NOTE `main` can only return types that implement std::process::Termination +//~| HELP consider using `()`, or a `Result` 0 } diff --git a/src/test/compile-fail/rfc-1937-termination-trait/termination-trait-not-satisfied.rs b/src/test/compile-fail/rfc-1937-termination-trait/termination-trait-not-satisfied.rs index e87e0ceebf1b1..b5f5472b49290 100644 --- a/src/test/compile-fail/rfc-1937-termination-trait/termination-trait-not-satisfied.rs +++ b/src/test/compile-fail/rfc-1937-termination-trait/termination-trait-not-satisfied.rs @@ -8,10 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(termination_trait)] - struct ReturnType {} -fn main() -> ReturnType { //~ ERROR `ReturnType: std::process::Termination` is not satisfied +fn main() -> ReturnType { //~ ERROR `main` has invalid return type `ReturnType` ReturnType {} } diff --git a/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-never.rs b/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-never.rs index c1dd44a91765e..863de85af88fb 100644 --- a/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-never.rs +++ b/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-never.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(termination_trait)] - // error-pattern:oh, dear fn main() -> ! { diff --git a/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs b/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs index 8ce27c0a06250..0c6cb4de9567d 100644 --- a/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs +++ b/src/test/run-fail/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs @@ -11,8 +11,6 @@ // must-compile-successfully // failure-status: 1 -#![feature(termination_trait)] - use std::io::{Error, ErrorKind}; fn main() -> Result<(), Box> { diff --git a/src/test/run-pass/rfc-1937-termination-trait/termination-trait-for-empty.rs b/src/test/run-pass/rfc-1937-termination-trait/termination-trait-for-empty.rs index 5e534da012875..046d27a9f0fe5 100644 --- a/src/test/run-pass/rfc-1937-termination-trait/termination-trait-for-empty.rs +++ b/src/test/run-pass/rfc-1937-termination-trait/termination-trait-for-empty.rs @@ -8,6 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(termination_trait)] - fn main() {} diff --git a/src/test/run-pass/rfc-1937-termination-trait/termination-trait-for-exitcode.rs b/src/test/run-pass/rfc-1937-termination-trait/termination-trait-for-exitcode.rs index 80fa4d17b6116..4aa7d8c3a77d2 100644 --- a/src/test/run-pass/rfc-1937-termination-trait/termination-trait-for-exitcode.rs +++ b/src/test/run-pass/rfc-1937-termination-trait/termination-trait-for-exitcode.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(termination_trait)] #![feature(process_exitcode_placeholder)] use std::process::ExitCode; diff --git a/src/test/run-pass/rfc-1937-termination-trait/termination-trait-for-result-box-error_ok.rs b/src/test/run-pass/rfc-1937-termination-trait/termination-trait-for-result-box-error_ok.rs index 269ac451cf4d8..33686ed0b8fa2 100644 --- a/src/test/run-pass/rfc-1937-termination-trait/termination-trait-for-result-box-error_ok.rs +++ b/src/test/run-pass/rfc-1937-termination-trait/termination-trait-for-result-box-error_ok.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(termination_trait)] - use std::io::Error; fn main() -> Result<(), Box> { diff --git a/src/test/run-pass/rfc-1937-termination-trait/termination-trait-for-result.rs b/src/test/run-pass/rfc-1937-termination-trait/termination-trait-for-result.rs index 751db0fb50082..1c87e31e763e9 100644 --- a/src/test/run-pass/rfc-1937-termination-trait/termination-trait-for-result.rs +++ b/src/test/run-pass/rfc-1937-termination-trait/termination-trait-for-result.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(termination_trait)] - use std::io::Error; fn main() -> Result<(), Error> { diff --git a/src/test/run-pass/rfc-1937-termination-trait/termination-trait-in-test.rs b/src/test/run-pass/rfc-1937-termination-trait/termination-trait-in-test.rs index 494500d522abe..11997eb691728 100644 --- a/src/test/run-pass/rfc-1937-termination-trait/termination-trait-in-test.rs +++ b/src/test/run-pass/rfc-1937-termination-trait/termination-trait-in-test.rs @@ -10,7 +10,7 @@ // compile-flags: --test -#![feature(termination_trait)] +#![feature(termination_trait_test)] #![feature(test)] extern crate test; diff --git a/src/test/compile-fail/rfc-1937-termination-trait/termination-trait-main-wrong-type.rs b/src/test/ui/rfc-1937-termination-trait/termination-trait-main-wrong-type.rs similarity index 78% rename from src/test/compile-fail/rfc-1937-termination-trait/termination-trait-main-wrong-type.rs rename to src/test/ui/rfc-1937-termination-trait/termination-trait-main-wrong-type.rs index 93e2561adf753..425f51ca2fb54 100644 --- a/src/test/compile-fail/rfc-1937-termination-trait/termination-trait-main-wrong-type.rs +++ b/src/test/ui/rfc-1937-termination-trait/termination-trait-main-wrong-type.rs @@ -7,9 +7,7 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(termination_trait)] -fn main() -> char { -//~^ ERROR: the trait bound `char: std::process::Termination` is not satisfied +fn main() -> char { //~ ERROR ' ' } diff --git a/src/test/ui/rfc-1937-termination-trait/termination-trait-main-wrong-type.stderr b/src/test/ui/rfc-1937-termination-trait/termination-trait-main-wrong-type.stderr new file mode 100644 index 0000000000000..5109d9275c58b --- /dev/null +++ b/src/test/ui/rfc-1937-termination-trait/termination-trait-main-wrong-type.stderr @@ -0,0 +1,11 @@ +error[E0277]: `main` has invalid return type `char` + --> $DIR/termination-trait-main-wrong-type.rs:11:14 + | +LL | fn main() -> char { //~ ERROR + | ^^^^ `main` can only return types that implement std::process::Termination + | + = help: consider using `()`, or a `Result` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`.