Skip to content

Commit

Permalink
Rollup merge of rust-lang#49162 - tmandry:stabilize-termination-trait…
Browse files Browse the repository at this point in the history
…, r=nikomatsakis

Stabilize termination_trait, split out termination_trait_test

For rust-lang#48453.

First time contribution, so I'd really appreciate any feedback on how this PR can be better.

Not sure exactly what kind of documentation update is needed. If there is no PR to update the reference, I can try doing that this week as I have time.
  • Loading branch information
kennytm committed Mar 24, 2018
2 parents c892e68 + 2b13d95 commit 8d57071
Show file tree
Hide file tree
Showing 20 changed files with 85 additions and 61 deletions.
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 @@ -595,20 +595,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 @@ -1130,25 +1130,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 @@ -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),
Expand Down Expand Up @@ -565,6 +565,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`.

0 comments on commit 8d57071

Please sign in to comment.