From 45757fb79d49f6d7f4acbb879ab23b6c9e0a5186 Mon Sep 17 00:00:00 2001 From: F001 Date: Sat, 26 May 2018 19:48:00 +0800 Subject: [PATCH] implement rfc 1260 --- src/librustc/diagnostics.rs | 40 ++++++ src/librustc/middle/dead.rs | 16 ++- src/librustc/middle/entry.rs | 132 ++++++++++-------- src/librustc/session/config.rs | 41 +++++- src/librustc/session/mod.rs | 2 +- src/librustc_codegen_llvm/base.rs | 33 ++--- src/librustc_codegen_llvm/debuginfo/mod.rs | 9 +- src/librustc_codegen_utils/lib.rs | 6 +- src/librustc_mir/monomorphize/collector.rs | 10 +- src/librustc_mir/monomorphize/item.rs | 4 +- src/librustc_resolve/check_unused.rs | 11 +- src/librustc_typeck/check/mod.rs | 28 ++-- src/librustc_typeck/check_unused.rs | 2 +- src/librustc_typeck/diagnostics.rs | 12 ++ src/librustc_typeck/lib.rs | 104 +++++++++----- src/libsyntax/entry.rs | 14 +- src/libsyntax/feature_gate.rs | 3 + src/libsyntax/test.rs | 1 + .../ui/rfc-1260-main-reexport/generic-fn.rs | 20 +++ .../rfc-1260-main-reexport/generic-fn.stderr | 9 ++ .../nested-re-export.rs | 24 ++++ .../ui/rfc-1260-main-reexport/not-an-fn.rs | 17 +++ .../rfc-1260-main-reexport/not-an-fn.stderr | 14 ++ .../ui/rfc-1260-main-reexport/re-export.rs | 21 +++ .../ui/rfc-1260-main-reexport/termination.rs | 25 ++++ .../ui/rfc-1260-main-reexport/with-args-fn.rs | 19 +++ .../with-args-fn.stderr | 12 ++ 27 files changed, 480 insertions(+), 149 deletions(-) create mode 100644 src/test/ui/rfc-1260-main-reexport/generic-fn.rs create mode 100644 src/test/ui/rfc-1260-main-reexport/generic-fn.stderr create mode 100644 src/test/ui/rfc-1260-main-reexport/nested-re-export.rs create mode 100644 src/test/ui/rfc-1260-main-reexport/not-an-fn.rs create mode 100644 src/test/ui/rfc-1260-main-reexport/not-an-fn.stderr create mode 100644 src/test/ui/rfc-1260-main-reexport/re-export.rs create mode 100644 src/test/ui/rfc-1260-main-reexport/termination.rs create mode 100644 src/test/ui/rfc-1260-main-reexport/with-args-fn.rs create mode 100644 src/test/ui/rfc-1260-main-reexport/with-args-fn.stderr diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 2662e70999196..22b00e09a24f7 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -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 diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index e3b2078971439..1c705e2d68dc3 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -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 @@ -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 diff --git a/src/librustc/middle/entry.rs b/src/librustc/middle/entry.rs index ebc796466629c..eef600364c79b 100644 --- a/src/librustc/middle/entry.rs +++ b/src/librustc/middle/entry.rs @@ -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, @@ -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)> , @@ -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(), }; @@ -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); diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index b3f1b9c8e627c..685df190fb264 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -28,7 +28,7 @@ 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; @@ -36,6 +36,10 @@ 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; @@ -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)] diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 8df66d8d68855..2929e30533629 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -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>, + pub entry_fn: Once>, pub plugin_registrar_fn: Once>, pub derive_registrar_fn: Once>, pub default_sysroot: Option, diff --git a/src/librustc_codegen_llvm/base.rs b/src/librustc_codegen_llvm/base.rs index 8660c0bcc6c3f..71797f90245f3 100644 --- a/src/librustc_codegen_llvm/base.rs +++ b/src/librustc_codegen_llvm/base.rs @@ -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, diff --git a/src/librustc_codegen_llvm/debuginfo/mod.rs b/src/librustc_codegen_llvm/debuginfo/mod.rs index 803966145f773..397b77a54a6bc 100644 --- a/src/librustc_codegen_llvm/debuginfo/mod.rs +++ b/src/librustc_codegen_llvm/debuginfo/mod.rs @@ -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; diff --git a/src/librustc_codegen_utils/lib.rs b/src/librustc_codegen_utils/lib.rs index 0c18571f4ffe8..527bd5f567163 100644 --- a/src/librustc_codegen_utils/lib.rs +++ b/src/librustc_codegen_utils/lib.rs @@ -51,11 +51,11 @@ pub mod symbol_names_test; /// that actually test that compilation succeeds without /// reporting an error. pub fn check_for_rustc_errors_attr(tcx: TyCtxt) { - if let Some((id, span, _)) = *tcx.sess.entry_fn.borrow() { - let main_def_id = tcx.hir.local_def_id(id); + if let Some(entry) = tcx.sess.entry_fn.borrow() { + let main_def_id = entry.get_def_id(&tcx.hir); if tcx.has_attr(main_def_id, "rustc_error") { - tcx.sess.span_fatal(span, "compilation successful"); + tcx.sess.span_fatal(entry.get_span(), "compilation successful"); } } } diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index a8a50e14c6822..dc18db156e219 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -12,7 +12,7 @@ //! =========================== //! //! This module is responsible for discovering all items that will contribute to -//! to code generation of the crate. The important part here is that it not only +//! code generation of the crate. The important part here is that it not only //! needs to find syntax-level items (functions, structs, etc) but also all //! their monomorphized instantiations. Every non-generic, non-const function //! maps to one LLVM artifact. Every generic function can produce @@ -325,8 +325,8 @@ fn collect_roots<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let mut roots = Vec::new(); { - let entry_fn = tcx.sess.entry_fn.borrow().map(|(node_id, _, _)| { - tcx.hir.local_def_id(node_id) + let entry_fn = tcx.sess.entry_fn.borrow().as_ref().map(|entry|{ + entry.get_def_id(&tcx.hir) }); debug!("collect_roots: entry_fn = {:?}", entry_fn); @@ -1038,8 +1038,8 @@ impl<'b, 'a, 'v> RootCollector<'b, 'a, 'v> { /// the return type of `main`. This is not needed when /// the user writes their own `start` manually. fn push_extra_entry_roots(&mut self) { - if self.tcx.sess.entry_fn.get().map(|e| e.2) != Some(config::EntryMain) { - return + if let Some(config::EntryStart(..)) = self.tcx.sess.entry_fn.get() { + return; } let main_def_id = if let Some(def_id) = self.entry_fn { diff --git a/src/librustc_mir/monomorphize/item.rs b/src/librustc_mir/monomorphize/item.rs index 27f8254bf8a78..18137aba3f2f6 100644 --- a/src/librustc_mir/monomorphize/item.rs +++ b/src/librustc_mir/monomorphize/item.rs @@ -92,7 +92,9 @@ pub trait MonoItemExt<'a, 'tcx>: fmt::Debug { match *self.as_mono_item() { MonoItem::Fn(ref instance) => { let entry_def_id = - tcx.sess.entry_fn.borrow().map(|(id, _, _)| tcx.hir.local_def_id(id)); + tcx.sess.entry_fn.borrow().as_ref().map(|entry|{ + entry.get_def_id(&tcx.hir) + }); // If this function isn't inlined or otherwise has explicit // linkage, then we'll be creating a globally shared version. if self.explicit_linkage(tcx).is_some() || diff --git a/src/librustc_resolve/check_unused.rs b/src/librustc_resolve/check_unused.rs index 590ce168d5d0f..a64656c11d421 100644 --- a/src/librustc_resolve/check_unused.rs +++ b/src/librustc_resolve/check_unused.rs @@ -85,10 +85,17 @@ impl<'a, 'b> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b> { // whether they're used or not. Also ignore imports with a dummy span // because this means that they were generated in some fashion by the // compiler and we don't need to consider them. - if let ast::ItemKind::Use(..) = item.node { - if item.vis.node == ast::VisibilityKind::Public || item.span.source_equal(&DUMMY_SP) { + if let ast::ItemKind::Use(ref tree) = item.node { + if item.vis.node == ast::VisibilityKind::Public || + item.span.source_equal(&DUMMY_SP) { return; } + // And ignore the item imported as "main". + if let ast::UseTreeKind::Simple(Some(ident)) = tree.kind { + if ident.name == "main" { + return; + } + } } visit::walk_item(self, item); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 90b974fb972c0..41b89685056c2 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1109,22 +1109,20 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, // Check that the main return type implements the termination trait. if let Some(term_id) = fcx.tcx.lang_items().termination() { - if let Some((id, _, entry_type)) = *fcx.tcx.sess.entry_fn.borrow() { - if id == fn_id { - match entry_type { - config::EntryMain => { - let substs = fcx.tcx.mk_substs_trait(declared_ret_ty, &[]); - let trait_ref = ty::TraitRef::new(term_id, substs); - let return_ty_span = decl.output.span(); - let cause = traits::ObligationCause::new( - return_ty_span, fn_id, ObligationCauseCode::MainFunctionType); - - inherited.register_predicate( - traits::Obligation::new( - cause, param_env, trait_ref.to_predicate())); - }, - config::EntryStart => {}, + if let Some(ref entry) = fcx.tcx.sess.entry_fn.borrow() { + match entry { + config::EntryMain(id, _) if *id == fn_id => { + let substs = fcx.tcx.mk_substs_trait(declared_ret_ty, &[]); + let trait_ref = ty::TraitRef::new(term_id, substs); + let return_ty_span = decl.output.span(); + let cause = traits::ObligationCause::new( + return_ty_span, fn_id, ObligationCauseCode::MainFunctionType); + + inherited.register_predicate( + traits::Obligation::new( + cause, param_env, trait_ref.to_predicate())); } + _ => {} } } } diff --git a/src/librustc_typeck/check_unused.rs b/src/librustc_typeck/check_unused.rs index bff849d7ae8e9..d2c306b2ba824 100644 --- a/src/librustc_typeck/check_unused.rs +++ b/src/librustc_typeck/check_unused.rs @@ -47,7 +47,7 @@ impl<'a, 'tcx> CheckVisitor<'a, 'tcx> { impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for CheckVisitor<'a, 'tcx> { fn visit_item(&mut self, item: &hir::Item) { - if item.vis == hir::Public || item.span == DUMMY_SP { + if item.vis == hir::Public || item.span == DUMMY_SP || item.name == "main" { return; } if let hir::ItemUse(ref path, _) = item.node { diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index e6a66cd613e9c..4ca2797f1f9fc 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -1544,6 +1544,18 @@ fn my_start(argc: isize, argv: *const *const u8) -> isize { ``` "##, +E0133: r##" +A function exported as `main` was declared with type parameters. + +Erroneous code example: + +```compile_fail,E0133 +#![feature(main_reexport)] + + +``` +"##, + E0164: r##" This error means that an attempt was made to match a struct type enum variant as a non-struct type: diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 5b7d92944edf2..913f24bbf5fd6 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -101,6 +101,7 @@ use rustc::session; use rustc::util; use hir::map as hir_map; +use hir::def_id::DefId; use rustc::infer::InferOk; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty, TyCtxt}; @@ -110,6 +111,7 @@ use session::{CompileIncomplete, config}; use util::common::time; use syntax::ast; +use syntax::feature_gate; use rustc_target::spec::abi::Abi; use syntax_pos::Span; @@ -175,10 +177,39 @@ fn require_same_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, }) } +fn report_generics<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + generics: &hir::Generics) -> bool { + let mut error = false; + if !generics.params.is_empty() { + let param_type = if generics.is_lt_parameterized() { + "lifetime" + } else { + "type" + }; + let msg = + format!("`main` function is not allowed to have {} parameters", + param_type); + let label = + format!("`main` cannot have {} parameters", param_type); + struct_span_err!(tcx.sess, generics.span, E0131, "{}", msg) + .span_label(generics.span, label) + .emit(); + error = true; + } + if let Some(sp) = generics.where_clause.span() { + struct_span_err!(tcx.sess, sp, E0646, + "`main` function is not allowed to have a `where` clause") + .span_label(sp, "`main` cannot have a `where` clause") + .emit(); + error = true; + } + return error; +} + fn check_main_fn_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, main_id: ast::NodeId, + main_def_id: DefId, main_span: Span) { - let main_def_id = tcx.hir.local_def_id(main_id); let main_t = tcx.type_of(main_def_id); match main_t.sty { ty::TyFnDef(..) => { @@ -186,38 +217,30 @@ fn check_main_fn_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, Some(hir_map::NodeItem(it)) => { match it.node { hir::ItemFn(.., ref generics, _) => { - let mut error = false; - if !generics.params.is_empty() { - let param_type = if generics.is_lt_parameterized() { - "lifetime" - } else { - "type" - }; - let msg = - format!("`main` function is not allowed to have {} parameters", - param_type); - let label = - format!("`main` cannot have {} parameters", param_type); - struct_span_err!(tcx.sess, generics.span, E0131, "{}", msg) - .span_label(generics.span, label) - .emit(); - error = true; - } - if let Some(sp) = generics.where_clause.span() { - struct_span_err!(tcx.sess, sp, E0646, - "`main` function is not allowed to have a `where` clause") - .span_label(sp, "`main` cannot have a `where` clause") - .emit(); - error = true; + if report_generics(tcx, generics) { + return; } - if error { + } + _ => { + span_bug!(main_span, "`main` is not a function"); + } + } + } + Some(hir_map::NodeForeignItem(fi)) => { + match fi.node { + hir::ForeignItemFn(.., ref generics) => { + if report_generics(tcx, generics) { return; } } - _ => () + _ => { + span_bug!(main_span, "`main` is not a function"); + } } } - _ => () + _ => { + span_bug!(main_span, "`main` is not a function"); + } } let actual = tcx.fn_sig(main_def_id); @@ -229,7 +252,6 @@ fn check_main_fn_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // standard () main return type tcx.mk_nil() }; - let se_ty = tcx.mk_fn_ptr(ty::Binder::bind( tcx.mk_fn_sig( iter::empty(), @@ -319,10 +341,28 @@ fn check_start_fn_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } fn check_for_entry_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { - if let Some((id, sp, entry_type)) = *tcx.sess.entry_fn.borrow() { - match entry_type { - config::EntryMain => check_main_fn_ty(tcx, id, sp), - config::EntryStart => check_start_fn_ty(tcx, id, sp), + if let Some(ref entry) = *tcx.sess.entry_fn.borrow() { + match entry { + config::EntryMain(id, sp) => { + let def_id = tcx.hir.local_def_id(*id); + check_main_fn_ty(tcx, *id, def_id, *sp); + } + config::EntryStart(id, sp) => { + check_start_fn_ty(tcx, *id, *sp); + } + config::EntryImported(_, def_id, sp) => { + if let Some(node_id) = tcx.hir.as_local_node_id(*def_id) { + check_main_fn_ty(tcx, node_id, *def_id, *sp); + } else { + span_bug!(*sp, "NodeId of `main` is not found"); + } + + if !tcx.features().main_reexport { + feature_gate::emit_feature_err(&tcx.sess.parse_sess, "main_reexport", + *sp, feature_gate::GateIssue::Language, + "A re-export of a function as entry point is unstable"); + } + } } } } diff --git a/src/libsyntax/entry.rs b/src/libsyntax/entry.rs index 93ca1948ed84b..e72721ffb34cd 100644 --- a/src/libsyntax/entry.rs +++ b/src/libsyntax/entry.rs @@ -16,11 +16,10 @@ pub enum EntryPointType { MainNamed, MainAttr, Start, + Import, OtherMain, // Not an entry point, but some other function named main } -// Beware, this is duplicated in librustc/middle/entry.rs, make sure to keep -// them in sync. pub fn entry_point_type(item: &Item, depth: usize) -> EntryPointType { match item.node { ItemKind::Fn(..) => { @@ -39,6 +38,17 @@ pub fn entry_point_type(item: &Item, depth: usize) -> EntryPointType { EntryPointType::None } } + ItemKind::Use(..) => { + if item.ident.name == "main" { + if depth == 1 { + EntryPointType::Import + } else { + EntryPointType::OtherMain + } + } else { + EntryPointType::None + } + } _ => EntryPointType::None, } } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 9b84713b0f90f..8fb2fbe305159 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -475,6 +475,9 @@ declare_features! ( // 'a: { break 'a; } (active, label_break_value, "1.28.0", Some(48594), None), + + // Allow a re-export of a function as entry point `main`. + (active, main_reexport, "1.28.0", Some(28937), None), ); declare_features! ( diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index e63a3d47a828f..46a7d7cf40466 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -219,6 +219,7 @@ impl fold::Folder for EntryPointCleaner { } }), EntryPointType::None | + EntryPointType::Import | EntryPointType::OtherMain => folded, }; diff --git a/src/test/ui/rfc-1260-main-reexport/generic-fn.rs b/src/test/ui/rfc-1260-main-reexport/generic-fn.rs new file mode 100644 index 0000000000000..cd7702159b7d4 --- /dev/null +++ b/src/test/ui/rfc-1260-main-reexport/generic-fn.rs @@ -0,0 +1,20 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(main_reexport)] + +mod foo { + use std::mem::size_of; + pub fn bar() { //~ ERROR + println!("{}", size_of::()); + } +} + +use foo::bar as main; diff --git a/src/test/ui/rfc-1260-main-reexport/generic-fn.stderr b/src/test/ui/rfc-1260-main-reexport/generic-fn.stderr new file mode 100644 index 0000000000000..7474d649b34ae --- /dev/null +++ b/src/test/ui/rfc-1260-main-reexport/generic-fn.stderr @@ -0,0 +1,9 @@ +error[E0131]: `main` function is not allowed to have type parameters + --> $DIR/generic-fn.rs:15:15 + | +LL | pub fn bar() { //~ ERROR + | ^^^ `main` cannot have type parameters + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0131`. diff --git a/src/test/ui/rfc-1260-main-reexport/nested-re-export.rs b/src/test/ui/rfc-1260-main-reexport/nested-re-export.rs new file mode 100644 index 0000000000000..62c3751ccc2fb --- /dev/null +++ b/src/test/ui/rfc-1260-main-reexport/nested-re-export.rs @@ -0,0 +1,24 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// run-pass + +#![feature(main_reexport)] + +mod foo { + pub use self::foo1::bar; + mod foo1 { + pub fn bar() { + println!("Re-exported fn."); + } + } +} + +use foo::bar as main; diff --git a/src/test/ui/rfc-1260-main-reexport/not-an-fn.rs b/src/test/ui/rfc-1260-main-reexport/not-an-fn.rs new file mode 100644 index 0000000000000..3bbdbe80a143f --- /dev/null +++ b/src/test/ui/rfc-1260-main-reexport/not-an-fn.rs @@ -0,0 +1,17 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(main_reexport)] + +mod foo { + pub const bar: &'static str = "hello"; +} + +use foo::bar as main; //~ ERROR diff --git a/src/test/ui/rfc-1260-main-reexport/not-an-fn.stderr b/src/test/ui/rfc-1260-main-reexport/not-an-fn.stderr new file mode 100644 index 0000000000000..0346a856185e8 --- /dev/null +++ b/src/test/ui/rfc-1260-main-reexport/not-an-fn.stderr @@ -0,0 +1,14 @@ +error[E0135]: The item re-exported as `main` is not a function + --> $DIR/not-an-fn.rs:17:1 + | +LL | use foo::bar as main; //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^ + +error[E0601]: `main` function not found in crate `not_an_fn` + | + = note: consider adding a `main` function to `$DIR/not-an-fn.rs` + +error: aborting due to 2 previous errors + +Some errors occurred: E0135, E0601. +For more information about an error, try `rustc --explain E0135`. diff --git a/src/test/ui/rfc-1260-main-reexport/re-export.rs b/src/test/ui/rfc-1260-main-reexport/re-export.rs new file mode 100644 index 0000000000000..c9b108694f56d --- /dev/null +++ b/src/test/ui/rfc-1260-main-reexport/re-export.rs @@ -0,0 +1,21 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// run-pass + +#![feature(main_reexport)] + +mod foo { + pub fn bar() { + println!("Re-exported fn."); + } +} + +use foo::bar as main; diff --git a/src/test/ui/rfc-1260-main-reexport/termination.rs b/src/test/ui/rfc-1260-main-reexport/termination.rs new file mode 100644 index 0000000000000..493152d96d406 --- /dev/null +++ b/src/test/ui/rfc-1260-main-reexport/termination.rs @@ -0,0 +1,25 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// run-pass + +#![feature(main_reexport)] +#![feature(process_exitcode_placeholder)] +#![feature(termination_trait_lib)] + +mod foo { + use std::process::ExitCode; + + pub fn bar() -> ExitCode { + return ExitCode::SUCCESS; + } +} + +use foo::bar as main; diff --git a/src/test/ui/rfc-1260-main-reexport/with-args-fn.rs b/src/test/ui/rfc-1260-main-reexport/with-args-fn.rs new file mode 100644 index 0000000000000..6899ed24a65ec --- /dev/null +++ b/src/test/ui/rfc-1260-main-reexport/with-args-fn.rs @@ -0,0 +1,19 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(main_reexport)] + +mod foo { + pub fn bar(value: i32) { + println!("{}", value); + } +} + +use foo::bar as main; //~ ERROR \ No newline at end of file diff --git a/src/test/ui/rfc-1260-main-reexport/with-args-fn.stderr b/src/test/ui/rfc-1260-main-reexport/with-args-fn.stderr new file mode 100644 index 0000000000000..531bc251e7185 --- /dev/null +++ b/src/test/ui/rfc-1260-main-reexport/with-args-fn.stderr @@ -0,0 +1,12 @@ +error[E0580]: main function has wrong type + --> $DIR/with-args-fn.rs:19:1 + | +LL | use foo::bar as main; //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters + | + = note: expected type `fn()` + found type `fn(i32)` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0580`.