about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBastian Köcher <git@kchr.de>2017-12-03 22:16:24 +0100
committerBastian Köcher <git@kchr.de>2017-12-26 12:26:39 +0100
commitd7918fb2e889c5ccb58ab8b97d2581cc763f2306 (patch)
treea25c4ba1da52089945ce84e9ec8e1b51f45cfd8f
parent8cdde6db7138cf2365dd9ceb5b8814e92a922ed4 (diff)
downloadrust-d7918fb2e889c5ccb58ab8b97d2581cc763f2306.tar.gz
rust-d7918fb2e889c5ccb58ab8b97d2581cc763f2306.zip
Implements RFC 1937: `?` in `main`
This is the first part of the RFC 1937 that supports new
`Termination` trait in the rust `main` function.
-rw-r--r--src/librustc/middle/lang_items.rs2
-rw-r--r--src/librustc_mir/monomorphize/collector.rs84
-rw-r--r--src/librustc_mir/monomorphize/partitioning.rs11
-rw-r--r--src/librustc_trans/base.rs34
-rw-r--r--src/librustc_typeck/check/mod.rs24
-rw-r--r--src/librustc_typeck/lib.rs16
-rw-r--r--src/libstd/lib.rs6
-rw-r--r--src/libstd/rt.rs45
-rw-r--r--src/libstd/termination.rs74
-rw-r--r--src/test/compile-fail/termination-trait-not-satisfied.rs15
-rw-r--r--src/test/run-pass/termination-trait-for-empty.rs13
-rw-r--r--src/test/run-pass/termination-trait-for-i32.rs15
-rw-r--r--src/test/run-pass/termination-trait-for-result.rs17
13 files changed, 304 insertions, 52 deletions
diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs
index f8933d06360..dca676130b9 100644
--- a/src/librustc/middle/lang_items.rs
+++ b/src/librustc/middle/lang_items.rs
@@ -338,6 +338,8 @@ language_item_table! {
     U128ShloFnLangItem,              "u128_shlo",               u128_shlo_fn;
     I128ShroFnLangItem,              "i128_shro",               i128_shro_fn;
     U128ShroFnLangItem,              "u128_shro",               u128_shro_fn;
+
+    TerminationTraitLangItem,        "termination",             termination;
 }
 
 impl<'a, 'tcx, 'gcx> TyCtxt<'a, 'tcx, 'gcx> {
diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs
index 0b8666800a5..056c6989aba 100644
--- a/src/librustc_mir/monomorphize/collector.rs
+++ b/src/librustc_mir/monomorphize/collector.rs
@@ -194,11 +194,13 @@ use rustc::hir::itemlikevisit::ItemLikeVisitor;
 use rustc::hir::map as hir_map;
 use rustc::hir::def_id::DefId;
 use rustc::middle::const_val::ConstVal;
-use rustc::middle::lang_items::{ExchangeMallocFnLangItem};
+use rustc::middle::lang_items::{ExchangeMallocFnLangItem,StartFnLangItem};
+use rustc::middle::trans::TransItem;
 use rustc::traits;
-use rustc::ty::subst::Substs;
+use rustc::ty::subst::{Substs, Kind};
 use rustc::ty::{self, TypeFoldable, Ty, TyCtxt};
 use rustc::ty::adjustment::CustomCoerceUnsized;
+use rustc::session::config;
 use rustc::mir::{self, Location};
 use rustc::mir::visit::Visitor as MirVisitor;
 use rustc::mir::mono::MonoItem;
@@ -212,6 +214,8 @@ use rustc_data_structures::bitvec::BitVector;
 
 use syntax::attr;
 
+use std::iter;
+
 #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
 pub enum MonoItemCollectionMode {
     Eager,
@@ -329,6 +333,8 @@ fn collect_roots<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             tcx.hir.local_def_id(node_id)
         });
 
+        debug!("collect_roots: entry_fn = {:?}", entry_fn);
+
         let mut visitor = RootCollector {
             tcx,
             mode,
@@ -951,16 +957,8 @@ impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> {
                 // actually used somewhere. Just declaring them is insufficient.
             }
             hir::ItemFn(..) => {
-                let tcx = self.tcx;
-                let def_id = tcx.hir.local_def_id(item.id);
-
-                if self.is_root(def_id) {
-                    debug!("RootCollector: ItemFn({})",
-                           def_id_to_string(tcx, def_id));
-
-                    let instance = Instance::mono(tcx, def_id);
-                    self.output.push(MonoItem::Fn(instance));
-                }
+                let def_id = self.tcx.hir.local_def_id(item.id);
+                self.push_if_root(def_id);
             }
         }
     }
@@ -973,16 +971,8 @@ impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> {
     fn visit_impl_item(&mut self, ii: &'v hir::ImplItem) {
         match ii.node {
             hir::ImplItemKind::Method(hir::MethodSig { .. }, _) => {
-                let tcx = self.tcx;
-                let def_id = tcx.hir.local_def_id(ii.id);
-
-                if self.is_root(def_id) {
-                    debug!("RootCollector: MethodImplItem({})",
-                           def_id_to_string(tcx, def_id));
-
-                    let instance = Instance::mono(tcx, def_id);
-                    self.output.push(MonoItem::Fn(instance));
-                }
+                let def_id = self.tcx.hir.local_def_id(ii.id);
+                self.push_if_root(def_id);
             }
             _ => { /* Nothing to do here */ }
         }
@@ -1003,6 +993,56 @@ impl<'b, 'a, 'v> RootCollector<'b, 'a, 'v> {
             }
         }
     }
+
+    /// If `def_id` represents a root, then push it onto the list of
+    /// outputs. (Note that all roots must be monomorphic.)
+    fn push_if_root(&mut self, def_id: DefId) {
+        if self.is_root(def_id) {
+            debug!("RootCollector::push_if_root: found root def_id={:?}", def_id);
+
+            let instance = Instance::mono(self.tcx, def_id);
+            self.output.push(create_fn_trans_item(instance));
+
+            self.push_extra_entry_roots(def_id);
+        }
+    }
+
+    /// As a special case, when/if we encounter the
+    /// `main()` function, we also have to generate a
+    /// monomorphized copy of the start lang item based on
+    /// the return type of `main`. This is not needed when
+    /// the user writes their own `start` manually.
+    fn push_extra_entry_roots(&mut self, def_id: DefId) {
+        if self.entry_fn != Some(def_id) {
+            return;
+        }
+
+        if self.tcx.sess.entry_type.get() != Some(config::EntryMain) {
+            return;
+        }
+
+        let start_def_id = match self.tcx.lang_items().require(StartFnLangItem) {
+            Ok(s) => s,
+            Err(err) => self.tcx.sess.fatal(&err),
+        };
+        let main_ret_ty = self.tcx.fn_sig(def_id).output();
+
+        // Given that `main()` has no arguments,
+        // then its return type cannot have
+        // late-bound regions, since late-bound
+        // regions must appear in the argument
+        // listing.
+        let main_ret_ty = self.tcx.no_late_bound_regions(&main_ret_ty).unwrap();
+
+        let start_instance = Instance::resolve(
+            self.tcx,
+            ty::ParamEnv::empty(traits::Reveal::All),
+            start_def_id,
+            self.tcx.mk_substs(iter::once(Kind::from(main_ret_ty)))
+        ).unwrap();
+
+        self.output.push(create_fn_trans_item(start_instance));
+    }
 }
 
 fn item_has_type_parameters<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs
index b1fef274cba..e34698da4da 100644
--- a/src/librustc_mir/monomorphize/partitioning.rs
+++ b/src/librustc_mir/monomorphize/partitioning.rs
@@ -106,7 +106,8 @@ use monomorphize::collector::InliningMap;
 use rustc::dep_graph::WorkProductId;
 use rustc::hir::def_id::DefId;
 use rustc::hir::map::DefPathData;
-use rustc::mir::mono::{Linkage, Visibility};
+use rustc::middle::lang_items::StartFnLangItem;
+use rustc::middle::trans::{Linkage, Visibility};
 use rustc::ty::{self, TyCtxt, InstanceDef};
 use rustc::ty::item_path::characteristic_def_id_of_type;
 use rustc::util::nodemap::{FxHashMap, FxHashSet};
@@ -312,7 +313,13 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                     MonoItem::Fn(ref instance) => {
                         let visibility = match instance.def {
                             InstanceDef::Item(def_id) => {
-                                if def_id.is_local() {
+                                let start_def_id = tcx.lang_items().require(StartFnLangItem);
+
+                                // If we encounter the lang start item, we set the visibility to
+                                // default.
+                                if start_def_id == Ok(def_id) {
+                                    Visibility::Default
+                                } else if def_id.is_local() {
                                     if tcx.is_exported_symbol(def_id) {
                                         Visibility::Default
                                     } else {
diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs
index 0efe5f9a5cb..b2d516e7868 100644
--- a/src/librustc_trans/base.rs
+++ b/src/librustc_trans/base.rs
@@ -44,6 +44,7 @@ use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::layout::{self, Align, TyLayout, LayoutOf};
 use rustc::ty::maps::Providers;
 use rustc::dep_graph::{DepNode, DepConstructor};
+use rustc::ty::subst::Kind;
 use rustc::middle::cstore::{self, LinkMeta, LinkagePreference};
 use rustc::util::common::{time, print_time_passes_entry};
 use rustc::session::config::{self, NoDebugInfo};
@@ -79,6 +80,7 @@ use std::str;
 use std::sync::Arc;
 use std::time::{Instant, Duration};
 use std::i32;
+use std::iter;
 use std::sync::mpsc;
 use syntax_pos::Span;
 use syntax_pos::symbol::InternedString;
@@ -540,17 +542,28 @@ fn maybe_create_entry_wrapper(ccx: &CrateContext) {
 
     let et = ccx.sess().entry_type.get().unwrap();
     match et {
-        config::EntryMain => create_entry_fn(ccx, span, main_llfn, true),
-        config::EntryStart => create_entry_fn(ccx, span, main_llfn, false),
+        config::EntryMain => create_entry_fn(ccx, span, main_llfn, main_def_id, true),
+        config::EntryStart => create_entry_fn(ccx, span, main_llfn, main_def_id, false),
         config::EntryNone => {}    // Do nothing.
     }
 
-    fn create_entry_fn(ccx: &CrateContext,
+    fn create_entry_fn<'ccx>(ccx: &'ccx CrateContext,
                        sp: Span,
                        rust_main: ValueRef,
+                       rust_main_def_id: DefId,
                        use_start_lang_item: bool) {
-        // Signature of native main(), corresponding to C's `int main(int, char **)`
-        let llfty = Type::func(&[Type::c_int(ccx), Type::i8p(ccx).ptr_to()], &Type::c_int(ccx));
+        // The libstd lang_start function does not return anything, while user defined lang start
+        // returns a isize
+        let start_output_ty = if use_start_lang_item { Type::void(ccx) } else { Type::c_int(ccx) };
+        let llfty = Type::func(&[Type::c_int(ccx), Type::i8p(ccx).ptr_to()], &start_output_ty);
+
+        let main_ret_ty = ccx.tcx().fn_sig(rust_main_def_id).output();
+        // Given that `main()` has no arguments,
+        // then its return type cannot have
+        // late-bound regions, since late-bound
+        // regions must appear in the argument
+        // listing.
+        let main_ret_ty = ccx.tcx().no_late_bound_regions(&main_ret_ty).unwrap();
 
         if declare::get_defined_value(ccx, "main").is_some() {
             // FIXME: We should be smart and show a better diagnostic here.
@@ -577,8 +590,8 @@ fn maybe_create_entry_wrapper(ccx: &CrateContext) {
 
         let (start_fn, args) = if use_start_lang_item {
             let start_def_id = ccx.tcx().require_lang_item(StartFnLangItem);
-            let start_instance = Instance::mono(ccx.tcx(), start_def_id);
-            let start_fn = callee::get_fn(ccx, start_instance);
+            let start_fn = callee::resolve_and_get_fn(ccx, start_def_id, ccx.tcx().mk_substs(
+                iter::once(Kind::from(main_ret_ty))));
             (start_fn, vec![bld.pointercast(rust_main, Type::i8p(ccx).ptr_to()),
                             arg_argc, arg_argv])
         } else {
@@ -588,8 +601,11 @@ fn maybe_create_entry_wrapper(ccx: &CrateContext) {
 
         let result = bld.call(start_fn, &args, None);
 
-        // Return rust start function's result from native main()
-        bld.ret(bld.intcast(result, Type::c_int(ccx), true));
+        if use_start_lang_item {
+            bld.ret_void();
+        } else {
+            bld.ret(bld.intcast(result, Type::c_int(ccx), true));
+        }
     }
 }
 
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index ab81d26e771..159340d19a7 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -93,17 +93,18 @@ use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin};
 use rustc::infer::anon_types::AnonTypeDecl;
 use rustc::infer::type_variable::{TypeVariableOrigin};
 use rustc::middle::region;
+use rustc::middle::lang_items::TerminationTraitLangItem;
 use rustc::ty::subst::{Kind, Subst, Substs};
 use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCode};
 use rustc::ty::{ParamTy, LvaluePreference, NoPreference, PreferMutLvalue};
-use rustc::ty::{self, Ty, TyCtxt, Visibility};
+use rustc::ty::{self, Ty, TyCtxt, Visibility, ToPredicate};
 use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
 use rustc::ty::fold::TypeFoldable;
 use rustc::ty::maps::Providers;
 use rustc::ty::util::{Representability, IntTypeExt};
 use errors::{DiagnosticBuilder, DiagnosticId};
 use require_c_abi_if_variadic;
-use session::{CompileIncomplete, Session};
+use session::{CompileIncomplete, config, Session};
 use TypeAndSubsts;
 use lint;
 use util::common::{ErrorReported, indenter};
@@ -115,6 +116,7 @@ use std::collections::hash_map::Entry;
 use std::cmp;
 use std::fmt::Display;
 use std::mem::replace;
+use std::iter;
 use std::ops::{self, Deref};
 use syntax::abi::Abi;
 use syntax::ast;
@@ -1064,6 +1066,24 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
     }
     fcx.demand_suptype(span, ret_ty, actual_return_ty);
 
+    if let Some((id, _)) = *fcx.tcx.sess.entry_fn.borrow() {
+        if id == fn_id {
+            match fcx.sess().entry_type.get() {
+                Some(config::EntryMain) => {
+                    let term_id = fcx.tcx.require_lang_item(TerminationTraitLangItem);
+
+                    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()));
+                },
+                _ => {},
+            }
+        }
+    }
+
     (fcx, gen_ty)
 }
 
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index 129511ee64c..47022eb152d 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -115,7 +115,6 @@ use syntax::ast;
 use syntax::abi::Abi;
 use syntax_pos::Span;
 
-use std::iter;
 // NB: This module needs to be declared first so diagnostics are
 // registered before they are used.
 mod diagnostics;
@@ -200,21 +199,6 @@ fn check_main_fn_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                 }
                 _ => ()
             }
-            let se_ty = tcx.mk_fn_ptr(ty::Binder(
-                tcx.mk_fn_sig(
-                    iter::empty(),
-                    tcx.mk_nil(),
-                    false,
-                    hir::Unsafety::Normal,
-                    Abi::Rust
-                )
-            ));
-
-            require_same_types(
-                tcx,
-                &ObligationCause::new(main_span, main_id, ObligationCauseCode::MainFunctionType),
-                se_ty,
-                tcx.mk_fn_ptr(tcx.fn_sig(main_def_id)));
         }
         _ => {
             span_bug!(main_span,
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index 12e6231136e..3a7a57fe2b8 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -308,6 +308,7 @@
 #![feature(str_char)]
 #![feature(str_internals)]
 #![feature(str_utf16)]
+#![feature(termination_trait)]
 #![feature(test, rustc_private)]
 #![feature(thread_local)]
 #![feature(toowned_clone_into)]
@@ -499,6 +500,11 @@ mod memchr;
 // The runtime entry point and a few unstable public functions used by the
 // compiler
 pub mod rt;
+// The trait to support returning arbitrary types in the main function
+mod termination;
+
+#[unstable(feature = "termination_trait", issue = "0")]
+pub use self::termination::Termination;
 
 // Include a number of private modules that exist solely to provide
 // the rustdoc documentation for primitive types. Using `include!`
diff --git a/src/libstd/rt.rs b/src/libstd/rt.rs
index 40b24cedcdc..2b75201ad2b 100644
--- a/src/libstd/rt.rs
+++ b/src/libstd/rt.rs
@@ -26,7 +26,50 @@
 // Reexport some of our utilities which are expected by other crates.
 pub use panicking::{begin_panic, begin_panic_fmt, update_panic_count};
 
-#[cfg(not(test))]
+#[cfg(not(any(test, stage0)))]
+#[lang = "start"]
+fn lang_start<T: ::termination::Termination + 'static>
+    (main: fn() -> T, argc: isize, argv: *const *const u8) -> !
+{
+    use panic;
+    use sys;
+    use sys_common;
+    use sys_common::thread_info;
+    use thread::Thread;
+    use process;
+    #[cfg(not(feature = "backtrace"))]
+    use mem;
+
+    sys::init();
+
+    process::exit(unsafe {
+        let main_guard = sys::thread::guard::init();
+        sys::stack_overflow::init();
+
+        // Next, set up the current Thread with the guard information we just
+        // created. Note that this isn't necessary in general for new threads,
+        // but we just do this to name the main thread and to give it correct
+        // info about the stack bounds.
+        let thread = Thread::new(Some("main".to_owned()));
+        thread_info::set(main_guard, thread);
+
+        // Store our args if necessary in a squirreled away location
+        sys::args::init(argc, argv);
+
+        // Let's run some code!
+        #[cfg(feature = "backtrace")]
+        let exit_code = panic::catch_unwind(|| {
+            ::sys_common::backtrace::__rust_begin_short_backtrace(move || main().report())
+        });
+        #[cfg(not(feature = "backtrace"))]
+        let exit_code = panic::catch_unwind(mem::transmute::<_, fn()>(main).report());
+
+        sys_common::cleanup();
+        exit_code.unwrap_or(101)
+    });
+}
+
+#[cfg(all(not(test), stage0))]
 #[lang = "start"]
 fn lang_start(main: fn(), argc: isize, argv: *const *const u8) -> isize {
     use panic;
diff --git a/src/libstd/termination.rs b/src/libstd/termination.rs
new file mode 100644
index 00000000000..a08d3dd2d52
--- /dev/null
+++ b/src/libstd/termination.rs
@@ -0,0 +1,74 @@
+// 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.
+
+use error::Error;
+use libc;
+
+/// A trait for implementing arbitrary return types in the `main` function.
+///
+/// The c-main function only supports to return integers as return type.
+/// So, every type implementing the `Termination` trait has to be converted
+/// to an integer.
+///
+/// The default implementations are returning `libc::EXIT_SUCCESS` to indicate
+/// a successful execution. In case of a failure, `libc::EXIT_FAILURE` is returned.
+#[cfg_attr(not(stage0), lang = "termination")]
+#[unstable(feature = "termination_trait", issue = "0")]
+pub trait Termination {
+    /// Is called to get the representation of the value as status code.
+    /// This status code is returned to the operating system.
+    fn report(self) -> i32;
+}
+
+#[unstable(feature = "termination_trait", issue = "0")]
+impl Termination for () {
+    fn report(self) -> i32 { libc::EXIT_SUCCESS }
+}
+
+#[unstable(feature = "termination_trait", issue = "0")]
+impl<T: Termination, E: Error> Termination for Result<T, E> {
+    fn report(self) -> i32 {
+        match self {
+            Ok(val) => val.report(),
+            Err(err) => {
+                print_error(err);
+                libc::EXIT_FAILURE
+            }
+        }
+    }
+}
+
+#[unstable(feature = "termination_trait", issue = "0")]
+fn print_error<E: Error>(err: E) {
+    eprintln!("Error: {}", err.description());
+
+    if let Some(ref err) = err.cause() {
+        eprintln!("Caused by: {}", err.description());
+    }
+}
+
+#[unstable(feature = "termination_trait", issue = "0")]
+impl Termination for ! {
+    fn report(self) -> i32 { unreachable!(); }
+}
+
+#[unstable(feature = "termination_trait", issue = "0")]
+impl Termination for bool {
+    fn report(self) -> i32 {
+        if self { libc::EXIT_SUCCESS } else { libc::EXIT_FAILURE }
+    }
+}
+
+#[unstable(feature = "termination_trait", issue = "0")]
+impl Termination for i32 {
+    fn report(self) -> i32 {
+        self
+    }
+}
diff --git a/src/test/compile-fail/termination-trait-not-satisfied.rs b/src/test/compile-fail/termination-trait-not-satisfied.rs
new file mode 100644
index 00000000000..178a9b8cf59
--- /dev/null
+++ b/src/test/compile-fail/termination-trait-not-satisfied.rs
@@ -0,0 +1,15 @@
+// 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.
+
+struct ReturnType {}
+
+fn main() -> ReturnType { //~ ERROR `ReturnType: std::Termination` is not satisfied
+    ReturnType {}
+}
diff --git a/src/test/run-pass/termination-trait-for-empty.rs b/src/test/run-pass/termination-trait-for-empty.rs
new file mode 100644
index 00000000000..5e534da0128
--- /dev/null
+++ b/src/test/run-pass/termination-trait-for-empty.rs
@@ -0,0 +1,13 @@
+// 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.
+
+#![feature(termination_trait)]
+
+fn main() {}
diff --git a/src/test/run-pass/termination-trait-for-i32.rs b/src/test/run-pass/termination-trait-for-i32.rs
new file mode 100644
index 00000000000..fa7cb023b44
--- /dev/null
+++ b/src/test/run-pass/termination-trait-for-i32.rs
@@ -0,0 +1,15 @@
+// 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.
+
+#![feature(termination_trait)]
+
+fn main() -> i32 {
+    0
+}
diff --git a/src/test/run-pass/termination-trait-for-result.rs b/src/test/run-pass/termination-trait-for-result.rs
new file mode 100644
index 00000000000..751db0fb500
--- /dev/null
+++ b/src/test/run-pass/termination-trait-for-result.rs
@@ -0,0 +1,17 @@
+// 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.
+
+#![feature(termination_trait)]
+
+use std::io::Error;
+
+fn main() -> Result<(), Error> {
+    Ok(())
+}