Skip to content

Commit bfbb1f5

Browse files
committed
Auto merge of rust-lang#46479 - bkchr:termination_trait, r=arielb1
Implements RFC 1937: `?` in `main` This is the first part of the RFC 1937 that supports new `Termination` trait in the rust `main` function. Thanks @nikomatsakis, @arielb1 and all other people in the gitter channel for all your help! The support for doctest and `#[test]` is still missing, bu as @nikomatsakis said, smaller pull requests are better :)
2 parents 63efff5 + 09f94be commit bfbb1f5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+575
-113
lines changed

Diff for: src/librustc/middle/lang_items.rs

+2
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> {

Diff for: src/librustc/session/code_stats.rs

+13-5
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,19 @@ impl CodeStats {
155155

156156
// Include field alignment in output only if it caused padding injection
157157
if min_offset != offset {
158-
let pad = offset - min_offset;
159-
println!("print-type-size {}padding: {} bytes",
160-
indent, pad);
161-
println!("print-type-size {}field `.{}`: {} bytes, alignment: {} bytes",
162-
indent, name, size, align);
158+
if offset > min_offset {
159+
let pad = offset - min_offset;
160+
println!("print-type-size {}padding: {} bytes",
161+
indent, pad);
162+
println!("print-type-size {}field `.{}`: {} bytes, \
163+
alignment: {} bytes",
164+
indent, name, size, align);
165+
} else {
166+
println!("print-type-size {}field `.{}`: {} bytes, \
167+
offset: {} bytes, \
168+
alignment: {} bytes",
169+
indent, name, size, offset, align);
170+
}
163171
} else {
164172
println!("print-type-size {}field `.{}`: {} bytes",
165173
indent, name, size);

Diff for: src/librustc_mir/monomorphize/collector.rs

+61-22
Original file line numberDiff line numberDiff line change
@@ -194,11 +194,12 @@ 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};
198198
use rustc::traits;
199-
use rustc::ty::subst::Substs;
199+
use rustc::ty::subst::{Substs, Kind};
200200
use rustc::ty::{self, TypeFoldable, Ty, TyCtxt};
201201
use rustc::ty::adjustment::CustomCoerceUnsized;
202+
use rustc::session::config;
202203
use rustc::mir::{self, Location};
203204
use rustc::mir::visit::Visitor as MirVisitor;
204205
use rustc::mir::mono::MonoItem;
@@ -212,6 +213,8 @@ use rustc_data_structures::bitvec::BitVector;
212213

213214
use syntax::attr;
214215

216+
use std::iter;
217+
215218
#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
216219
pub enum MonoItemCollectionMode {
217220
Eager,
@@ -329,6 +332,8 @@ fn collect_roots<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
329332
tcx.hir.local_def_id(node_id)
330333
});
331334

335+
debug!("collect_roots: entry_fn = {:?}", entry_fn);
336+
332337
let mut visitor = RootCollector {
333338
tcx,
334339
mode,
@@ -951,16 +956,8 @@ impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> {
951956
// actually used somewhere. Just declaring them is insufficient.
952957
}
953958
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-
}
959+
let def_id = self.tcx.hir.local_def_id(item.id);
960+
self.push_if_root(def_id);
964961
}
965962
}
966963
}
@@ -973,16 +970,8 @@ impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> {
973970
fn visit_impl_item(&mut self, ii: &'v hir::ImplItem) {
974971
match ii.node {
975972
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-
}
973+
let def_id = self.tcx.hir.local_def_id(ii.id);
974+
self.push_if_root(def_id);
986975
}
987976
_ => { /* Nothing to do here */ }
988977
}
@@ -1003,6 +992,56 @@ impl<'b, 'a, 'v> RootCollector<'b, 'a, 'v> {
1003992
}
1004993
}
1005994
}
995+
996+
/// If `def_id` represents a root, then push it onto the list of
997+
/// outputs. (Note that all roots must be monomorphic.)
998+
fn push_if_root(&mut self, def_id: DefId) {
999+
if self.is_root(def_id) {
1000+
debug!("RootCollector::push_if_root: found root def_id={:?}", def_id);
1001+
1002+
let instance = Instance::mono(self.tcx, def_id);
1003+
self.output.push(create_fn_mono_item(instance));
1004+
1005+
self.push_extra_entry_roots(def_id);
1006+
}
1007+
}
1008+
1009+
/// As a special case, when/if we encounter the
1010+
/// `main()` function, we also have to generate a
1011+
/// monomorphized copy of the start lang item based on
1012+
/// the return type of `main`. This is not needed when
1013+
/// the user writes their own `start` manually.
1014+
fn push_extra_entry_roots(&mut self, def_id: DefId) {
1015+
if self.entry_fn != Some(def_id) {
1016+
return;
1017+
}
1018+
1019+
if self.tcx.sess.entry_type.get() != Some(config::EntryMain) {
1020+
return;
1021+
}
1022+
1023+
let start_def_id = match self.tcx.lang_items().require(StartFnLangItem) {
1024+
Ok(s) => s,
1025+
Err(err) => self.tcx.sess.fatal(&err),
1026+
};
1027+
let main_ret_ty = self.tcx.fn_sig(def_id).output();
1028+
1029+
// Given that `main()` has no arguments,
1030+
// then its return type cannot have
1031+
// late-bound regions, since late-bound
1032+
// regions must appear in the argument
1033+
// listing.
1034+
let main_ret_ty = main_ret_ty.no_late_bound_regions().unwrap();
1035+
1036+
let start_instance = Instance::resolve(
1037+
self.tcx,
1038+
ty::ParamEnv::empty(traits::Reveal::All),
1039+
start_def_id,
1040+
self.tcx.mk_substs(iter::once(Kind::from(main_ret_ty)))
1041+
).unwrap();
1042+
1043+
self.output.push(create_fn_mono_item(start_instance));
1044+
}
10061045
}
10071046

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

Diff for: src/librustc_mir/monomorphize/partitioning.rs

+24-2
Original file line numberDiff line numberDiff line change
@@ -305,14 +305,36 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
305305
let codegen_unit = codegen_units.entry(codegen_unit_name.clone())
306306
.or_insert_with(make_codegen_unit);
307307

308+
let mut can_be_internalized = true;
308309
let (linkage, visibility) = match trans_item.explicit_linkage(tcx) {
309310
Some(explicit_linkage) => (explicit_linkage, Visibility::Default),
310311
None => {
311312
match trans_item {
312313
MonoItem::Fn(ref instance) => {
313314
let visibility = match instance.def {
314315
InstanceDef::Item(def_id) => {
315-
if def_id.is_local() {
316+
// The `start_fn` lang item is actually a
317+
// monomorphized instance of a function in the
318+
// standard library, used for the `main`
319+
// function. We don't want to export it so we
320+
// tag it with `Hidden` visibility but this
321+
// symbol is only referenced from the actual
322+
// `main` symbol which we unfortunately don't
323+
// know anything about during
324+
// partitioning/collection. As a result we
325+
// forcibly keep this symbol out of the
326+
// `internalization_candidates` set.
327+
//
328+
// FIXME: eventually we don't want to always
329+
// force this symbol to have hidden
330+
// visibility, it should indeed be a candidate
331+
// for internalization, but we have to
332+
// understand that it's referenced from the
333+
// `main` symbol we'll generate later.
334+
if tcx.lang_items().start_fn() == Some(def_id) {
335+
can_be_internalized = false;
336+
Visibility::Hidden
337+
} else if def_id.is_local() {
316338
if tcx.is_exported_symbol(def_id) {
317339
Visibility::Default
318340
} else {
@@ -346,7 +368,7 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
346368
}
347369
}
348370
};
349-
if visibility == Visibility::Hidden {
371+
if visibility == Visibility::Hidden && can_be_internalized {
350372
internalization_candidates.insert(trans_item);
351373
}
352374

Diff for: src/librustc_trans/base.rs

+16-8
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,18 +542,26 @@ 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 **)`
553555
let llfty = Type::func(&[Type::c_int(ccx), Type::i8p(ccx).ptr_to()], &Type::c_int(ccx));
554556

557+
let main_ret_ty = ccx.tcx().fn_sig(rust_main_def_id).output();
558+
// Given that `main()` has no arguments,
559+
// then its return type cannot have
560+
// late-bound regions, since late-bound
561+
// regions must appear in the argument
562+
// listing.
563+
let main_ret_ty = main_ret_ty.no_late_bound_regions().unwrap();
564+
555565
if declare::get_defined_value(ccx, "main").is_some() {
556566
// FIXME: We should be smart and show a better diagnostic here.
557567
ccx.sess().struct_span_err(sp, "entry symbol `main` defined multiple times")
@@ -577,8 +587,8 @@ fn maybe_create_entry_wrapper(ccx: &CrateContext) {
577587

578588
let (start_fn, args) = if use_start_lang_item {
579589
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);
590+
let start_fn = callee::resolve_and_get_fn(ccx, start_def_id, ccx.tcx().mk_substs(
591+
iter::once(Kind::from(main_ret_ty))));
582592
(start_fn, vec![bld.pointercast(rust_main, Type::i8p(ccx).ptr_to()),
583593
arg_argc, arg_argv])
584594
} else {
@@ -587,8 +597,6 @@ fn maybe_create_entry_wrapper(ccx: &CrateContext) {
587597
};
588598

589599
let result = bld.call(start_fn, &args, None);
590-
591-
// Return rust start function's result from native main()
592600
bld.ret(bld.intcast(result, Type::c_int(ccx), true));
593601
}
594602
}

Diff for: src/librustc_typeck/check/mod.rs

+27-2
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,14 @@ use rustc::middle::region;
9696
use rustc::ty::subst::{Kind, Subst, Substs};
9797
use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCode};
9898
use rustc::ty::{ParamTy, LvaluePreference, NoPreference, PreferMutLvalue};
99-
use rustc::ty::{self, Ty, TyCtxt, Visibility};
99+
use rustc::ty::{self, Ty, TyCtxt, Visibility, ToPredicate};
100100
use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
101101
use rustc::ty::fold::TypeFoldable;
102102
use rustc::ty::maps::Providers;
103103
use rustc::ty::util::{Representability, IntTypeExt};
104104
use errors::{DiagnosticBuilder, DiagnosticId};
105105
use require_c_abi_if_variadic;
106-
use session::{CompileIncomplete, Session};
106+
use session::{CompileIncomplete, config, Session};
107107
use TypeAndSubsts;
108108
use lint;
109109
use util::common::{ErrorReported, indenter};
@@ -115,6 +115,7 @@ use std::collections::hash_map::Entry;
115115
use std::cmp;
116116
use std::fmt::Display;
117117
use std::mem::replace;
118+
use std::iter;
118119
use std::ops::{self, Deref};
119120
use syntax::abi::Abi;
120121
use syntax::ast;
@@ -1064,6 +1065,30 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
10641065
}
10651066
fcx.demand_suptype(span, ret_ty, actual_return_ty);
10661067

1068+
if fcx.tcx.sess.features.borrow().termination_trait {
1069+
// If the termination trait language item is activated, check that the main return type
1070+
// implements the termination trait.
1071+
if let Some(term_id) = fcx.tcx.lang_items().termination() {
1072+
if let Some((id, _)) = *fcx.tcx.sess.entry_fn.borrow() {
1073+
if id == fn_id {
1074+
match fcx.sess().entry_type.get() {
1075+
Some(config::EntryMain) => {
1076+
let substs = fcx.tcx.mk_substs(iter::once(Kind::from(ret_ty)));
1077+
let trait_ref = ty::TraitRef::new(term_id, substs);
1078+
let cause = traits::ObligationCause::new(
1079+
span, fn_id, ObligationCauseCode::MainFunctionType);
1080+
1081+
inherited.register_predicate(
1082+
traits::Obligation::new(
1083+
cause, param_env, trait_ref.to_predicate()));
1084+
},
1085+
_ => {},
1086+
}
1087+
}
1088+
}
1089+
}
1090+
}
1091+
10671092
(fcx, gen_ty)
10681093
}
10691094

Diff for: src/librustc_typeck/lib.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ use syntax::abi::Abi;
116116
use syntax_pos::Span;
117117

118118
use std::iter;
119+
119120
// NB: This module needs to be declared first so diagnostics are
120121
// registered before they are used.
121122
mod diagnostics;
@@ -200,10 +201,22 @@ fn check_main_fn_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
200201
}
201202
_ => ()
202203
}
204+
205+
let actual = tcx.fn_sig(main_def_id);
206+
let expected_return_type = if tcx.lang_items().termination().is_some()
207+
&& tcx.sess.features.borrow().termination_trait {
208+
// we take the return type of the given main function, the real check is done
209+
// in `check_fn`
210+
actual.output().skip_binder()
211+
} else {
212+
// standard () main return type
213+
tcx.mk_nil()
214+
};
215+
203216
let se_ty = tcx.mk_fn_ptr(ty::Binder(
204217
tcx.mk_fn_sig(
205218
iter::empty(),
206-
tcx.mk_nil(),
219+
expected_return_type,
207220
false,
208221
hir::Unsafety::Normal,
209222
Abi::Rust
@@ -214,7 +227,7 @@ fn check_main_fn_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
214227
tcx,
215228
&ObligationCause::new(main_span, main_id, ObligationCauseCode::MainFunctionType),
216229
se_ty,
217-
tcx.mk_fn_ptr(tcx.fn_sig(main_def_id)));
230+
tcx.mk_fn_ptr(actual));
218231
}
219232
_ => {
220233
span_bug!(main_span,

Diff for: src/libstd/lib.rs

+6
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 = "43301")]
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!`

0 commit comments

Comments
 (0)