Skip to content

Commit d7918fb

Browse files
committed
Implements RFC 1937: ? in main
This is the first part of the RFC 1937 that supports new `Termination` trait in the rust `main` function.
1 parent 8cdde6d commit d7918fb

File tree

13 files changed

+304
-52
lines changed

13 files changed

+304
-52
lines changed

src/librustc/middle/lang_items.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,8 @@ language_item_table! {
338338
U128ShloFnLangItem, "u128_shlo", u128_shlo_fn;
339339
I128ShroFnLangItem, "i128_shro", i128_shro_fn;
340340
U128ShroFnLangItem, "u128_shro", u128_shro_fn;
341+
342+
TerminationTraitLangItem, "termination", termination;
341343
}
342344

343345
impl<'a, 'tcx, 'gcx> TyCtxt<'a, 'tcx, 'gcx> {

src/librustc_mir/monomorphize/collector.rs

Lines changed: 62 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -194,11 +194,13 @@ use rustc::hir::itemlikevisit::ItemLikeVisitor;
194194
use rustc::hir::map as hir_map;
195195
use rustc::hir::def_id::DefId;
196196
use rustc::middle::const_val::ConstVal;
197-
use rustc::middle::lang_items::{ExchangeMallocFnLangItem};
197+
use rustc::middle::lang_items::{ExchangeMallocFnLangItem,StartFnLangItem};
198+
use rustc::middle::trans::TransItem;
198199
use rustc::traits;
199-
use rustc::ty::subst::Substs;
200+
use rustc::ty::subst::{Substs, Kind};
200201
use rustc::ty::{self, TypeFoldable, Ty, TyCtxt};
201202
use rustc::ty::adjustment::CustomCoerceUnsized;
203+
use rustc::session::config;
202204
use rustc::mir::{self, Location};
203205
use rustc::mir::visit::Visitor as MirVisitor;
204206
use rustc::mir::mono::MonoItem;
@@ -212,6 +214,8 @@ use rustc_data_structures::bitvec::BitVector;
212214

213215
use syntax::attr;
214216

217+
use std::iter;
218+
215219
#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
216220
pub enum MonoItemCollectionMode {
217221
Eager,
@@ -329,6 +333,8 @@ fn collect_roots<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
329333
tcx.hir.local_def_id(node_id)
330334
});
331335

336+
debug!("collect_roots: entry_fn = {:?}", entry_fn);
337+
332338
let mut visitor = RootCollector {
333339
tcx,
334340
mode,
@@ -951,16 +957,8 @@ impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> {
951957
// actually used somewhere. Just declaring them is insufficient.
952958
}
953959
hir::ItemFn(..) => {
954-
let tcx = self.tcx;
955-
let def_id = tcx.hir.local_def_id(item.id);
956-
957-
if self.is_root(def_id) {
958-
debug!("RootCollector: ItemFn({})",
959-
def_id_to_string(tcx, def_id));
960-
961-
let instance = Instance::mono(tcx, def_id);
962-
self.output.push(MonoItem::Fn(instance));
963-
}
960+
let def_id = self.tcx.hir.local_def_id(item.id);
961+
self.push_if_root(def_id);
964962
}
965963
}
966964
}
@@ -973,16 +971,8 @@ impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> {
973971
fn visit_impl_item(&mut self, ii: &'v hir::ImplItem) {
974972
match ii.node {
975973
hir::ImplItemKind::Method(hir::MethodSig { .. }, _) => {
976-
let tcx = self.tcx;
977-
let def_id = tcx.hir.local_def_id(ii.id);
978-
979-
if self.is_root(def_id) {
980-
debug!("RootCollector: MethodImplItem({})",
981-
def_id_to_string(tcx, def_id));
982-
983-
let instance = Instance::mono(tcx, def_id);
984-
self.output.push(MonoItem::Fn(instance));
985-
}
974+
let def_id = self.tcx.hir.local_def_id(ii.id);
975+
self.push_if_root(def_id);
986976
}
987977
_ => { /* Nothing to do here */ }
988978
}
@@ -1003,6 +993,56 @@ impl<'b, 'a, 'v> RootCollector<'b, 'a, 'v> {
1003993
}
1004994
}
1005995
}
996+
997+
/// If `def_id` represents a root, then push it onto the list of
998+
/// outputs. (Note that all roots must be monomorphic.)
999+
fn push_if_root(&mut self, def_id: DefId) {
1000+
if self.is_root(def_id) {
1001+
debug!("RootCollector::push_if_root: found root def_id={:?}", def_id);
1002+
1003+
let instance = Instance::mono(self.tcx, def_id);
1004+
self.output.push(create_fn_trans_item(instance));
1005+
1006+
self.push_extra_entry_roots(def_id);
1007+
}
1008+
}
1009+
1010+
/// As a special case, when/if we encounter the
1011+
/// `main()` function, we also have to generate a
1012+
/// monomorphized copy of the start lang item based on
1013+
/// the return type of `main`. This is not needed when
1014+
/// the user writes their own `start` manually.
1015+
fn push_extra_entry_roots(&mut self, def_id: DefId) {
1016+
if self.entry_fn != Some(def_id) {
1017+
return;
1018+
}
1019+
1020+
if self.tcx.sess.entry_type.get() != Some(config::EntryMain) {
1021+
return;
1022+
}
1023+
1024+
let start_def_id = match self.tcx.lang_items().require(StartFnLangItem) {
1025+
Ok(s) => s,
1026+
Err(err) => self.tcx.sess.fatal(&err),
1027+
};
1028+
let main_ret_ty = self.tcx.fn_sig(def_id).output();
1029+
1030+
// Given that `main()` has no arguments,
1031+
// then its return type cannot have
1032+
// late-bound regions, since late-bound
1033+
// regions must appear in the argument
1034+
// listing.
1035+
let main_ret_ty = self.tcx.no_late_bound_regions(&main_ret_ty).unwrap();
1036+
1037+
let start_instance = Instance::resolve(
1038+
self.tcx,
1039+
ty::ParamEnv::empty(traits::Reveal::All),
1040+
start_def_id,
1041+
self.tcx.mk_substs(iter::once(Kind::from(main_ret_ty)))
1042+
).unwrap();
1043+
1044+
self.output.push(create_fn_trans_item(start_instance));
1045+
}
10061046
}
10071047

10081048
fn item_has_type_parameters<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {

src/librustc_mir/monomorphize/partitioning.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ use monomorphize::collector::InliningMap;
106106
use rustc::dep_graph::WorkProductId;
107107
use rustc::hir::def_id::DefId;
108108
use rustc::hir::map::DefPathData;
109-
use rustc::mir::mono::{Linkage, Visibility};
109+
use rustc::middle::lang_items::StartFnLangItem;
110+
use rustc::middle::trans::{Linkage, Visibility};
110111
use rustc::ty::{self, TyCtxt, InstanceDef};
111112
use rustc::ty::item_path::characteristic_def_id_of_type;
112113
use rustc::util::nodemap::{FxHashMap, FxHashSet};
@@ -312,7 +313,13 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
312313
MonoItem::Fn(ref instance) => {
313314
let visibility = match instance.def {
314315
InstanceDef::Item(def_id) => {
315-
if def_id.is_local() {
316+
let start_def_id = tcx.lang_items().require(StartFnLangItem);
317+
318+
// If we encounter the lang start item, we set the visibility to
319+
// default.
320+
if start_def_id == Ok(def_id) {
321+
Visibility::Default
322+
} else if def_id.is_local() {
316323
if tcx.is_exported_symbol(def_id) {
317324
Visibility::Default
318325
} else {

src/librustc_trans/base.rs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ use rustc::ty::{self, Ty, TyCtxt};
4444
use rustc::ty::layout::{self, Align, TyLayout, LayoutOf};
4545
use rustc::ty::maps::Providers;
4646
use rustc::dep_graph::{DepNode, DepConstructor};
47+
use rustc::ty::subst::Kind;
4748
use rustc::middle::cstore::{self, LinkMeta, LinkagePreference};
4849
use rustc::util::common::{time, print_time_passes_entry};
4950
use rustc::session::config::{self, NoDebugInfo};
@@ -79,6 +80,7 @@ use std::str;
7980
use std::sync::Arc;
8081
use std::time::{Instant, Duration};
8182
use std::i32;
83+
use std::iter;
8284
use std::sync::mpsc;
8385
use syntax_pos::Span;
8486
use syntax_pos::symbol::InternedString;
@@ -540,17 +542,28 @@ fn maybe_create_entry_wrapper(ccx: &CrateContext) {
540542

541543
let et = ccx.sess().entry_type.get().unwrap();
542544
match et {
543-
config::EntryMain => create_entry_fn(ccx, span, main_llfn, true),
544-
config::EntryStart => create_entry_fn(ccx, span, main_llfn, false),
545+
config::EntryMain => create_entry_fn(ccx, span, main_llfn, main_def_id, true),
546+
config::EntryStart => create_entry_fn(ccx, span, main_llfn, main_def_id, false),
545547
config::EntryNone => {} // Do nothing.
546548
}
547549

548-
fn create_entry_fn(ccx: &CrateContext,
550+
fn create_entry_fn<'ccx>(ccx: &'ccx CrateContext,
549551
sp: Span,
550552
rust_main: ValueRef,
553+
rust_main_def_id: DefId,
551554
use_start_lang_item: bool) {
552-
// Signature of native main(), corresponding to C's `int main(int, char **)`
553-
let llfty = Type::func(&[Type::c_int(ccx), Type::i8p(ccx).ptr_to()], &Type::c_int(ccx));
555+
// The libstd lang_start function does not return anything, while user defined lang start
556+
// returns a isize
557+
let start_output_ty = if use_start_lang_item { Type::void(ccx) } else { Type::c_int(ccx) };
558+
let llfty = Type::func(&[Type::c_int(ccx), Type::i8p(ccx).ptr_to()], &start_output_ty);
559+
560+
let main_ret_ty = ccx.tcx().fn_sig(rust_main_def_id).output();
561+
// Given that `main()` has no arguments,
562+
// then its return type cannot have
563+
// late-bound regions, since late-bound
564+
// regions must appear in the argument
565+
// listing.
566+
let main_ret_ty = ccx.tcx().no_late_bound_regions(&main_ret_ty).unwrap();
554567

555568
if declare::get_defined_value(ccx, "main").is_some() {
556569
// FIXME: We should be smart and show a better diagnostic here.
@@ -577,8 +590,8 @@ fn maybe_create_entry_wrapper(ccx: &CrateContext) {
577590

578591
let (start_fn, args) = if use_start_lang_item {
579592
let start_def_id = ccx.tcx().require_lang_item(StartFnLangItem);
580-
let start_instance = Instance::mono(ccx.tcx(), start_def_id);
581-
let start_fn = callee::get_fn(ccx, start_instance);
593+
let start_fn = callee::resolve_and_get_fn(ccx, start_def_id, ccx.tcx().mk_substs(
594+
iter::once(Kind::from(main_ret_ty))));
582595
(start_fn, vec![bld.pointercast(rust_main, Type::i8p(ccx).ptr_to()),
583596
arg_argc, arg_argv])
584597
} else {
@@ -588,8 +601,11 @@ fn maybe_create_entry_wrapper(ccx: &CrateContext) {
588601

589602
let result = bld.call(start_fn, &args, None);
590603

591-
// Return rust start function's result from native main()
592-
bld.ret(bld.intcast(result, Type::c_int(ccx), true));
604+
if use_start_lang_item {
605+
bld.ret_void();
606+
} else {
607+
bld.ret(bld.intcast(result, Type::c_int(ccx), true));
608+
}
593609
}
594610
}
595611

src/librustc_typeck/check/mod.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,17 +93,18 @@ use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin};
9393
use rustc::infer::anon_types::AnonTypeDecl;
9494
use rustc::infer::type_variable::{TypeVariableOrigin};
9595
use rustc::middle::region;
96+
use rustc::middle::lang_items::TerminationTraitLangItem;
9697
use rustc::ty::subst::{Kind, Subst, Substs};
9798
use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCode};
9899
use rustc::ty::{ParamTy, LvaluePreference, NoPreference, PreferMutLvalue};
99-
use rustc::ty::{self, Ty, TyCtxt, Visibility};
100+
use rustc::ty::{self, Ty, TyCtxt, Visibility, ToPredicate};
100101
use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
101102
use rustc::ty::fold::TypeFoldable;
102103
use rustc::ty::maps::Providers;
103104
use rustc::ty::util::{Representability, IntTypeExt};
104105
use errors::{DiagnosticBuilder, DiagnosticId};
105106
use require_c_abi_if_variadic;
106-
use session::{CompileIncomplete, Session};
107+
use session::{CompileIncomplete, config, Session};
107108
use TypeAndSubsts;
108109
use lint;
109110
use util::common::{ErrorReported, indenter};
@@ -115,6 +116,7 @@ use std::collections::hash_map::Entry;
115116
use std::cmp;
116117
use std::fmt::Display;
117118
use std::mem::replace;
119+
use std::iter;
118120
use std::ops::{self, Deref};
119121
use syntax::abi::Abi;
120122
use syntax::ast;
@@ -1064,6 +1066,24 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
10641066
}
10651067
fcx.demand_suptype(span, ret_ty, actual_return_ty);
10661068

1069+
if let Some((id, _)) = *fcx.tcx.sess.entry_fn.borrow() {
1070+
if id == fn_id {
1071+
match fcx.sess().entry_type.get() {
1072+
Some(config::EntryMain) => {
1073+
let term_id = fcx.tcx.require_lang_item(TerminationTraitLangItem);
1074+
1075+
let substs = fcx.tcx.mk_substs(iter::once(Kind::from(ret_ty)));
1076+
let trait_ref = ty::TraitRef::new(term_id, substs);
1077+
let cause = traits::ObligationCause::new(span, fn_id,
1078+
ObligationCauseCode::MainFunctionType);
1079+
inherited.register_predicate(
1080+
traits::Obligation::new(cause, param_env, trait_ref.to_predicate()));
1081+
},
1082+
_ => {},
1083+
}
1084+
}
1085+
}
1086+
10671087
(fcx, gen_ty)
10681088
}
10691089

src/librustc_typeck/lib.rs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ use syntax::ast;
115115
use syntax::abi::Abi;
116116
use syntax_pos::Span;
117117

118-
use std::iter;
119118
// NB: This module needs to be declared first so diagnostics are
120119
// registered before they are used.
121120
mod diagnostics;
@@ -200,21 +199,6 @@ fn check_main_fn_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
200199
}
201200
_ => ()
202201
}
203-
let se_ty = tcx.mk_fn_ptr(ty::Binder(
204-
tcx.mk_fn_sig(
205-
iter::empty(),
206-
tcx.mk_nil(),
207-
false,
208-
hir::Unsafety::Normal,
209-
Abi::Rust
210-
)
211-
));
212-
213-
require_same_types(
214-
tcx,
215-
&ObligationCause::new(main_span, main_id, ObligationCauseCode::MainFunctionType),
216-
se_ty,
217-
tcx.mk_fn_ptr(tcx.fn_sig(main_def_id)));
218202
}
219203
_ => {
220204
span_bug!(main_span,

src/libstd/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@
308308
#![feature(str_char)]
309309
#![feature(str_internals)]
310310
#![feature(str_utf16)]
311+
#![feature(termination_trait)]
311312
#![feature(test, rustc_private)]
312313
#![feature(thread_local)]
313314
#![feature(toowned_clone_into)]
@@ -499,6 +500,11 @@ mod memchr;
499500
// The runtime entry point and a few unstable public functions used by the
500501
// compiler
501502
pub mod rt;
503+
// The trait to support returning arbitrary types in the main function
504+
mod termination;
505+
506+
#[unstable(feature = "termination_trait", issue = "0")]
507+
pub use self::termination::Termination;
502508

503509
// Include a number of private modules that exist solely to provide
504510
// the rustdoc documentation for primitive types. Using `include!`

src/libstd/rt.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,50 @@
2626
// Reexport some of our utilities which are expected by other crates.
2727
pub use panicking::{begin_panic, begin_panic_fmt, update_panic_count};
2828

29-
#[cfg(not(test))]
29+
#[cfg(not(any(test, stage0)))]
30+
#[lang = "start"]
31+
fn lang_start<T: ::termination::Termination + 'static>
32+
(main: fn() -> T, argc: isize, argv: *const *const u8) -> !
33+
{
34+
use panic;
35+
use sys;
36+
use sys_common;
37+
use sys_common::thread_info;
38+
use thread::Thread;
39+
use process;
40+
#[cfg(not(feature = "backtrace"))]
41+
use mem;
42+
43+
sys::init();
44+
45+
process::exit(unsafe {
46+
let main_guard = sys::thread::guard::init();
47+
sys::stack_overflow::init();
48+
49+
// Next, set up the current Thread with the guard information we just
50+
// created. Note that this isn't necessary in general for new threads,
51+
// but we just do this to name the main thread and to give it correct
52+
// info about the stack bounds.
53+
let thread = Thread::new(Some("main".to_owned()));
54+
thread_info::set(main_guard, thread);
55+
56+
// Store our args if necessary in a squirreled away location
57+
sys::args::init(argc, argv);
58+
59+
// Let's run some code!
60+
#[cfg(feature = "backtrace")]
61+
let exit_code = panic::catch_unwind(|| {
62+
::sys_common::backtrace::__rust_begin_short_backtrace(move || main().report())
63+
});
64+
#[cfg(not(feature = "backtrace"))]
65+
let exit_code = panic::catch_unwind(mem::transmute::<_, fn()>(main).report());
66+
67+
sys_common::cleanup();
68+
exit_code.unwrap_or(101)
69+
});
70+
}
71+
72+
#[cfg(all(not(test), stage0))]
3073
#[lang = "start"]
3174
fn lang_start(main: fn(), argc: isize, argv: *const *const u8) -> isize {
3275
use panic;

0 commit comments

Comments
 (0)