Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements RFC 1937: ? in main #46479

Merged
merged 25 commits into from
Dec 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d7918fb
Implements RFC 1937: `?` in `main`
bkchr Dec 3, 2017
b452c43
avoid ICE when fields are not laid out in order
nikomatsakis Dec 5, 2017
19adeaa
convert print-type-sizes to use `start` instead of `main`
nikomatsakis Dec 5, 2017
011c9ea
Fixes compile bug caused by upstream changes
bkchr Dec 5, 2017
99a108c
Fixes compilation errors and adds proposed improvements
bkchr Dec 21, 2017
f842f75
Fixes tests
bkchr Dec 21, 2017
8232734
Fall back to main -> () when termination trait language item is not e…
bkchr Dec 21, 2017
267800a
New generated main returns void
bkchr Dec 21, 2017
8f539b0
Fixes codegen-units tests
bkchr Dec 21, 2017
c7a57d2
Adds termination_trait feature gate
bkchr Dec 22, 2017
a8a9a05
Convert codegen-unit tests to use `start` instead of `main`
bkchr Dec 22, 2017
83cb299
Just compare the symbol names and types, not the addresses
bkchr Dec 22, 2017
88bf2b4
Removes some obscure transmute call in `lang_start`
bkchr Dec 22, 2017
347165f
The test functions are now in the same compile unit
bkchr Dec 22, 2017
faff382
Don't emit the termination lang item in tests
bkchr Dec 22, 2017
072f3eb
Use move for optimization purposes
bkchr Dec 22, 2017
7efeeba
Use `start` for the `sepcomp-inlining` test
bkchr Dec 22, 2017
f972f52
Revert "Just compare the symbol names and types, not the addresses"
bkchr Dec 22, 2017
dbbba55
Rework the exit failure and success declaration for wasm32
bkchr Dec 22, 2017
c2f22f0
Adds whitespace
bkchr Dec 23, 2017
7dfec34
Split `lang_start` in two functions to reduce generated code
bkchr Dec 23, 2017
81e375d
Change name of `lang_start_real` to `lang_start_internal`
bkchr Dec 23, 2017
2cdd1c4
rustc: Switch `start_fn` to hidden visibility
alexcrichton Dec 26, 2017
5a4298b
Don't use `process::exit` as it is an `unreachable` on wasm32
bkchr Dec 26, 2017
09f94be
Revert "New generated main returns void"
bkchr Dec 27, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this line expected?

Copy link
Contributor Author

@bkchr bkchr Dec 4, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that is expected. There are multiple empty lines (scroll a little bit up in the file).

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(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because user-defined start is supposed to be non-generic, passing a substs to it could cause problems, so please don't.

Copy link
Contributor Author

@bkchr bkchr Dec 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, but user-defined start is handled in the else branch.

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