Skip to content

[WIP] Implement RFC 1260 #51216

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 40 additions & 0 deletions src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,46 @@ fn main() {
```
"##,

E0134: r##"
More than one item was re-exported as `main`.

Erroneous code example:

```compile_fail,E0134
#![feature(main_reexport)]

mod foo {
pub fn bar() { }
pub fn baz() { }
}

use foo::bar as main;
use foo::baz as main;
```

This error indicates that the compiler found multiple items re-exported as
`main`. This is an error because there must be a unique entry point into a
Rust program.
"##,

E0135: r##"
The item re-exported as `main` is not a function.

Erroneous code example:

```compile_fail,E0135
#![feature(main_reexport)]

mod foo {
pub const bar: &'static str = "hello";
}

use foo::bar as main;
```

This is an error because the entry point of a Rust program must be a function.
"##,

// This shouldn't really ever trigger since the repeated value error comes first
E0136: r##"
A binary can only have one entry point, and by default that entry point is the
Expand Down
16 changes: 14 additions & 2 deletions src/librustc/middle/dead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ use syntax::{ast, codemap};
use syntax::attr;
use syntax_pos;

use session::config::EntryFnType;

// Any local node that may call something in its body block should be
// explored. For example, if it's a live NodeItem that is a
// function, then we should explore its block to check for codes that
Expand Down Expand Up @@ -389,8 +391,18 @@ fn create_and_seed_worklist<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
}

// Seed entry point
if let Some((id, _, _)) = *tcx.sess.entry_fn.borrow() {
worklist.push(id);
if let Some(ref entry) = *tcx.sess.entry_fn.borrow() {
match entry {
| EntryFnType::EntryMain(id, ..)
| EntryFnType::EntryStart(id, ..) => worklist.push(*id),
| EntryFnType::EntryImported(_, def_id, _) => {
if let Some(node) = tcx.hir.get_if_local(*def_id) {
if let hir_map::Node::NodeItem(it) = node {
worklist.push(it.id);
}
}
}
}
}

// Seed implemented trait items
Expand Down
132 changes: 75 additions & 57 deletions src/librustc/middle/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@


use hir::map as hir_map;
use hir::def_id::{CRATE_DEF_INDEX};
use hir::def_id::{CRATE_DEF_INDEX, DefId};
use session::{config, Session};
use syntax::ast::NodeId;
use syntax::attr;
use syntax::entry::EntryPointType;
use syntax_pos::Span;
use hir::{Item, ItemFn, ImplItem, TraitItem};
use hir::{Item, ItemFn, ImplItem, TraitItem, ItemUse, Path, def};
use hir::itemlikevisit::ItemLikeVisitor;
use session::config::EntryFnType;

struct EntryContext<'a, 'tcx: 'a> {
session: &'a Session,
Expand All @@ -33,6 +33,9 @@ struct EntryContext<'a, 'tcx: 'a> {
// The function that has the attribute 'start' on it
start_fn: Option<(NodeId, Span)>,

// The function is imported and renamed as 'main'
imported_fn: Option<(NodeId, DefId, Span)>,

// The functions that one might think are 'main' but aren't, e.g.
// main functions not defined at the top level. For diagnostics.
non_main_fns: Vec<(NodeId, Span)> ,
Expand Down Expand Up @@ -79,6 +82,7 @@ pub fn find_entry_point(session: &Session,
main_fn: None,
attr_main_fn: None,
start_fn: None,
imported_fn: None,
non_main_fns: Vec::new(),
};

Expand All @@ -87,79 +91,93 @@ pub fn find_entry_point(session: &Session,
configure_main(&mut ctxt, crate_name);
}

// Beware, this is duplicated in libsyntax/entry.rs, make sure to keep
// them in sync.
fn entry_point_type(item: &Item, at_root: bool) -> EntryPointType {
fn update_start_attr(item: &Item, ctxt: &mut EntryContext) {
if ctxt.start_fn.is_none() {
ctxt.start_fn = Some((item.id, item.span));
} else {
struct_span_err!(
ctxt.session, item.span, E0138,
"multiple 'start' functions")
.span_label(ctxt.start_fn.unwrap().1,
"previous `start` function here")
.span_label(item.span, "multiple `start` functions")
.emit();
}
}

fn update_main_attr(item: &Item, ctxt: &mut EntryContext) {
if ctxt.attr_main_fn.is_none() {
ctxt.attr_main_fn = Some((item.id, item.span));
} else {
struct_span_err!(ctxt.session, item.span, E0137,
"multiple functions with a #[main] attribute")
.span_label(item.span, "additional #[main] function")
.span_label(ctxt.attr_main_fn.unwrap().1, "first #[main] function")
.emit();
}
}

fn update_imported_fn(p: &Path, item: &Item, ctxt: &mut EntryContext) {
if let def::Def::Fn(def_id) = p.def {
if ctxt.imported_fn.is_none() {
ctxt.imported_fn = Some((item.id, def_id, item.span));
} else {
span_err!(ctxt.session, item.span, E0134,
"Re-exported multiple items as `main`");
}
} else {
span_err!(ctxt.session, item.span, E0135,
"The item re-exported as `main` is not a function");
}
}

fn update_main_fn(item: &Item, ctxt: &mut EntryContext) {
if ctxt.main_fn.is_none() {
ctxt.main_fn = Some((item.id, item.span));
} else {
span_err!(ctxt.session, item.span, E0136,
"multiple 'main' functions");
}
}

fn update_non_main_fns(item: &Item, ctxt: &mut EntryContext) {
ctxt.non_main_fns.push((item.id, item.span));
}

fn find_item(item: &Item, ctxt: &mut EntryContext, at_root: bool) {
match item.node {
ItemFn(..) => {
if attr::contains_name(&item.attrs, "start") {
EntryPointType::Start
update_start_attr(item, ctxt);
} else if attr::contains_name(&item.attrs, "main") {
EntryPointType::MainAttr
update_main_attr(item, ctxt);
} else if item.name == "main" {
if at_root {
// This is a top-level function so can be 'main'
EntryPointType::MainNamed
update_main_fn(item, ctxt);
} else {
EntryPointType::OtherMain
update_non_main_fns(item, ctxt);
}
} else {
EntryPointType::None
}
}
_ => EntryPointType::None,
}
}


fn find_item(item: &Item, ctxt: &mut EntryContext, at_root: bool) {
match entry_point_type(item, at_root) {
EntryPointType::MainNamed => {
if ctxt.main_fn.is_none() {
ctxt.main_fn = Some((item.id, item.span));
} else {
span_err!(ctxt.session, item.span, E0136,
"multiple 'main' functions");
ItemUse(ref p, _) => {
if item.name == "main" {
update_imported_fn(p, item, ctxt);
}
},
EntryPointType::OtherMain => {
ctxt.non_main_fns.push((item.id, item.span));
},
EntryPointType::MainAttr => {
if ctxt.attr_main_fn.is_none() {
ctxt.attr_main_fn = Some((item.id, item.span));
} else {
struct_span_err!(ctxt.session, item.span, E0137,
"multiple functions with a #[main] attribute")
.span_label(item.span, "additional #[main] function")
.span_label(ctxt.attr_main_fn.unwrap().1, "first #[main] function")
.emit();
}
},
EntryPointType::Start => {
if ctxt.start_fn.is_none() {
ctxt.start_fn = Some((item.id, item.span));
} else {
struct_span_err!(
ctxt.session, item.span, E0138,
"multiple 'start' functions")
.span_label(ctxt.start_fn.unwrap().1,
"previous `start` function here")
.span_label(item.span, "multiple `start` functions")
.emit();
}
},
EntryPointType::None => ()
}
_ => {}
}
}

fn configure_main(this: &mut EntryContext, crate_name: &str) {
if let Some((node_id, span)) = this.start_fn {
this.session.entry_fn.set(Some((node_id, span, config::EntryStart)));
this.session.entry_fn.set(Some(EntryFnType::EntryStart(node_id, span)));
} else if let Some((node_id, span)) = this.attr_main_fn {
this.session.entry_fn.set(Some((node_id, span, config::EntryMain)));
this.session.entry_fn.set(Some(EntryFnType::EntryMain(node_id, span)));
} else if let Some((node_id, span)) = this.main_fn {
this.session.entry_fn.set(Some((node_id, span, config::EntryMain)));
this.session.entry_fn.set(Some(EntryFnType::EntryMain(node_id, span)));
} else if let Some((node_id, def_id, span)) = this.imported_fn {
this.session.entry_fn.set(Some(EntryFnType::EntryImported(node_id, def_id, span)));
} else {
// No main function
this.session.entry_fn.set(None);
Expand Down
41 changes: 37 additions & 4 deletions src/librustc/session/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,18 @@ use rustc_data_structures::stable_hasher::ToStableHashKey;
use lint;
use middle::cstore;

use syntax::ast::{self, IntTy, UintTy};
use syntax::ast::{self, IntTy, UintTy, NodeId};
use syntax::codemap::{FileName, FilePathMapping};
use syntax::edition::{Edition, EDITION_NAME_LIST, DEFAULT_EDITION};
use syntax::parse::token;
use syntax::parse;
use syntax::symbol::Symbol;
use syntax::feature_gate::UnstableFeatures;

use syntax_pos::Span;
use hir::def_id::DefId;
use hir::map::Map;

use errors::{ColorConfig, FatalError, Handler};

use getopts;
Expand Down Expand Up @@ -641,10 +645,39 @@ impl Options {
// The type of entry function, so
// users can have their own entry
// functions
#[derive(Copy, Clone, PartialEq)]
#[derive(Clone, PartialEq)]
pub enum EntryFnType {
EntryMain,
EntryStart,
EntryMain(NodeId, Span),
EntryStart(NodeId, Span),
EntryImported(NodeId, DefId, Span),
}

impl EntryFnType {
pub fn get_local_id(&self) -> NodeId {
match self {
EntryFnType::EntryMain(node_id, ..)
| EntryFnType::EntryStart(node_id, ..)
| EntryFnType::EntryImported(node_id, ..) => *node_id,
}
}

pub fn get_def_id<'hir>(&self, map: &Map<'hir>) -> DefId {
match self {
EntryFnType::EntryMain(node_id, ..)
| EntryFnType::EntryStart(node_id, ..) => {
map.local_def_id(*node_id)
}
EntryFnType::EntryImported(_, def_id, _) => *def_id,
}
}

pub fn get_span(&self) -> Span {
match self {
EntryFnType::EntryMain(_, sp)
| EntryFnType::EntryStart(_, sp)
| EntryFnType::EntryImported(_, _, sp) => *sp
}
}
}

#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug)]
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ pub struct Session {
pub opts: config::Options,
pub parse_sess: ParseSess,
/// For a library crate, this is always none
pub entry_fn: Once<Option<(NodeId, Span, config::EntryFnType)>>,
pub entry_fn: Once<Option<config::EntryFnType>>,
pub plugin_registrar_fn: Once<Option<ast::NodeId>>,
pub derive_registrar_fn: Once<Option<ast::NodeId>>,
pub default_sysroot: Option<PathBuf>,
Expand Down
33 changes: 15 additions & 18 deletions src/librustc_codegen_llvm/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,28 +525,25 @@ pub fn set_link_section(cx: &CodegenCx,
/// Create the `main` function which will initialize the rust runtime and call
/// users main function.
fn maybe_create_entry_wrapper(cx: &CodegenCx) {
let (main_def_id, span) = match *cx.sess().entry_fn.borrow() {
Some((id, span, _)) => {
(cx.tcx.hir.local_def_id(id), span)
}
None => return,
};
if let Some(ref et) = cx.sess().entry_fn.get() {
let main_def_id = et.get_def_id(&cx.tcx.hir);
let span = et.get_span();

let instance = Instance::mono(cx.tcx, main_def_id);
let instance = Instance::mono(cx.tcx, main_def_id);

if !cx.codegen_unit.contains_item(&MonoItem::Fn(instance)) {
// We want to create the wrapper in the same codegen unit as Rust's main
// function.
return;
}
if !cx.codegen_unit.contains_item(&MonoItem::Fn(instance)) {
// We want to create the wrapper in the same codegen unit as Rust's main
// function.
return;
}

let main_llfn = callee::get_fn(cx, instance);
let main_llfn = callee::get_fn(cx, instance);

let et = cx.sess().entry_fn.get().map(|e| e.2);
match et {
Some(config::EntryMain) => create_entry_fn(cx, span, main_llfn, main_def_id, true),
Some(config::EntryStart) => create_entry_fn(cx, span, main_llfn, main_def_id, false),
None => {} // Do nothing.
match et {
config::EntryImported(..)
| config::EntryMain(..) => create_entry_fn(cx, span, main_llfn, main_def_id, true),
config::EntryStart(..) => create_entry_fn(cx, span, main_llfn, main_def_id, false),
}
}

fn create_entry_fn<'cx>(cx: &'cx CodegenCx,
Expand Down
9 changes: 3 additions & 6 deletions src/librustc_codegen_llvm/debuginfo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,13 +262,10 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
let mut flags = DIFlags::FlagPrototyped;

let local_id = cx.tcx.hir.as_local_node_id(def_id);
match *cx.sess().entry_fn.borrow() {
Some((id, _, _)) => {
if local_id == Some(id) {
flags = flags | DIFlags::FlagMainSubprogram;
}
if let Some(e) = cx.sess().entry_fn.borrow() {
if local_id == Some(e.get_local_id()) {
flags = flags | DIFlags::FlagMainSubprogram;
}
None => {}
};
if cx.layout_of(sig.output()).abi == ty::layout::Abi::Uninhabited {
flags = flags | DIFlags::FlagNoReturn;
Expand Down
Loading