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

Stabilize termination_trait, split out termination_trait_test #49162

Merged
merged 11 commits into from
Mar 24, 2018
6 changes: 3 additions & 3 deletions src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:

```
Expand Down
21 changes: 12 additions & 9 deletions src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
36 changes: 17 additions & 19 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1106,25 +1106,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()));
},
_ => {},
}
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/librustc_typeck/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion src/libstd/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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]

Expand Down
5 changes: 3 additions & 2 deletions src/libstd/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 4 additions & 2 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,8 +429,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),
Expand Down Expand Up @@ -555,6 +555,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
Expand Down
8 changes: 4 additions & 4 deletions src/libsyntax/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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() -> ()");
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
22 changes: 22 additions & 0 deletions src/test/compile-fail/feature-gate-termination_trait_test.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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(())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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() -> ! {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
// must-compile-successfully
// failure-status: 1

#![feature(termination_trait)]

use std::io::{Error, ErrorKind};

fn main() -> Result<(), Box<Error>> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,4 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(termination_trait)]

fn main() {}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Error>> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

// compile-flags: --test

#![feature(termination_trait)]
#![feature(termination_trait_test)]
#![feature(test)]

extern crate test;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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
' '
}
Original file line number Diff line number Diff line change
@@ -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`.