Skip to content

Commit

Permalink
Auto merge of #46479 - bkchr:termination_trait, r=arielb1
Browse files Browse the repository at this point in the history
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 :)
  • Loading branch information
bors committed Dec 27, 2017
2 parents 63efff5 + 09f94be commit bfbb1f5
Show file tree
Hide file tree
Showing 53 changed files with 575 additions and 113 deletions.
2 changes: 2 additions & 0 deletions src/librustc/middle/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
18 changes: 13 additions & 5 deletions src/librustc/session/code_stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,19 @@ impl CodeStats {

// Include field alignment in output only if it caused padding injection
if min_offset != offset {
let pad = offset - min_offset;
println!("print-type-size {}padding: {} bytes",
indent, pad);
println!("print-type-size {}field `.{}`: {} bytes, alignment: {} bytes",
indent, name, size, align);
if offset > min_offset {
let pad = offset - min_offset;
println!("print-type-size {}padding: {} bytes",
indent, pad);
println!("print-type-size {}field `.{}`: {} bytes, \
alignment: {} bytes",
indent, name, size, align);
} else {
println!("print-type-size {}field `.{}`: {} bytes, \
offset: {} bytes, \
alignment: {} bytes",
indent, name, size, offset, align);
}
} else {
println!("print-type-size {}field `.{}`: {} bytes",
indent, name, size);
Expand Down
83 changes: 61 additions & 22 deletions src/librustc_mir/monomorphize/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,12 @@ 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::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;
Expand All @@ -212,6 +213,8 @@ use rustc_data_structures::bitvec::BitVector;

use syntax::attr;

use std::iter;

#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
pub enum MonoItemCollectionMode {
Eager,
Expand Down Expand Up @@ -329,6 +332,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,
Expand Down Expand Up @@ -951,16 +956,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);
}
}
}
Expand All @@ -973,16 +970,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 */ }
}
Expand All @@ -1003,6 +992,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_mono_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 = main_ret_ty.no_late_bound_regions().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_mono_item(start_instance));
}
}

fn item_has_type_parameters<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
Expand Down
26 changes: 24 additions & 2 deletions src/librustc_mir/monomorphize/partitioning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,14 +305,36 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let codegen_unit = codegen_units.entry(codegen_unit_name.clone())
.or_insert_with(make_codegen_unit);

let mut can_be_internalized = true;
let (linkage, visibility) = match trans_item.explicit_linkage(tcx) {
Some(explicit_linkage) => (explicit_linkage, Visibility::Default),
None => {
match trans_item {
MonoItem::Fn(ref instance) => {
let visibility = match instance.def {
InstanceDef::Item(def_id) => {
if def_id.is_local() {
// The `start_fn` lang item is actually a
// monomorphized instance of a function in the
// standard library, used for the `main`
// function. We don't want to export it so we
// tag it with `Hidden` visibility but this
// symbol is only referenced from the actual
// `main` symbol which we unfortunately don't
// know anything about during
// partitioning/collection. As a result we
// forcibly keep this symbol out of the
// `internalization_candidates` set.
//
// FIXME: eventually we don't want to always
// force this symbol to have hidden
// visibility, it should indeed be a candidate
// for internalization, but we have to
// understand that it's referenced from the
// `main` symbol we'll generate later.
if tcx.lang_items().start_fn() == Some(def_id) {
can_be_internalized = false;
Visibility::Hidden
} else if def_id.is_local() {
if tcx.is_exported_symbol(def_id) {
Visibility::Default
} else {
Expand Down Expand Up @@ -346,7 +368,7 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
}
}
};
if visibility == Visibility::Hidden {
if visibility == Visibility::Hidden && can_be_internalized {
internalization_candidates.insert(trans_item);
}

Expand Down
24 changes: 16 additions & 8 deletions src/librustc_trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -540,18 +542,26 @@ 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));

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 = main_ret_ty.no_late_bound_regions().unwrap();

if declare::get_defined_value(ccx, "main").is_some() {
// FIXME: We should be smart and show a better diagnostic here.
ccx.sess().struct_span_err(sp, "entry symbol `main` defined multiple times")
Expand All @@ -577,8 +587,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 {
Expand All @@ -587,8 +597,6 @@ 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));
}
}
Expand Down
29 changes: 27 additions & 2 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,14 @@ use rustc::middle::region;
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};
Expand All @@ -115,6 +115,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;
Expand Down Expand Up @@ -1064,6 +1065,30 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
}
fcx.demand_suptype(span, ret_ty, actual_return_ty);

if fcx.tcx.sess.features.borrow().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()));
},
_ => {},
}
}
}
}
}

(fcx, gen_ty)
}

Expand Down
17 changes: 15 additions & 2 deletions src/librustc_typeck/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ 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;
Expand Down Expand Up @@ -200,10 +201,22 @@ 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.sess.features.borrow().termination_trait {
// we take the return type of the given main function, the real check is done
// in `check_fn`
actual.output().skip_binder()
} else {
// standard () main return type
tcx.mk_nil()
};

let se_ty = tcx.mk_fn_ptr(ty::Binder(
tcx.mk_fn_sig(
iter::empty(),
tcx.mk_nil(),
expected_return_type,
false,
hir::Unsafety::Normal,
Abi::Rust
Expand All @@ -214,7 +227,7 @@ fn check_main_fn_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
tcx,
&ObligationCause::new(main_span, main_id, ObligationCauseCode::MainFunctionType),
se_ty,
tcx.mk_fn_ptr(tcx.fn_sig(main_def_id)));
tcx.mk_fn_ptr(actual));
}
_ => {
span_bug!(main_span,
Expand Down
6 changes: 6 additions & 0 deletions src/libstd/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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 = "43301")]
pub use self::termination::Termination;

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

0 comments on commit bfbb1f5

Please sign in to comment.