From be8dc615c566104c528ca8389fa0807c8453c8a8 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 29 Apr 2013 14:56:05 -0700 Subject: [PATCH 1/3] rustc: Move code for discovering the crate entry point into its own pass It doesn't have anything to do with resolve and the logic will likely get more involved in the future, after #4433 --- src/librustc/driver/driver.rs | 2 + src/librustc/middle/entry.rs | 119 +++++++++++++++++++++++++++++++++ src/librustc/middle/resolve.rs | 79 ---------------------- src/librustc/rustc.rc | 1 + 4 files changed, 122 insertions(+), 79 deletions(-) create mode 100644 src/librustc/middle/entry.rs diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 2e64c0c45bffe..e421246da8da3 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -225,6 +225,8 @@ pub fn compile_rest(sess: Session, time(time_passes, ~"resolution", || middle::resolve::resolve_crate(sess, lang_items, crate)); + time(time_passes, ~"looking for entry point", || middle::entry::find_entry_point(sess, crate)); + let freevars = time(time_passes, ~"freevar finding", || freevars::annotate_freevars(def_map, crate)); diff --git a/src/librustc/middle/entry.rs b/src/librustc/middle/entry.rs new file mode 100644 index 0000000000000..18f4a5a307200 --- /dev/null +++ b/src/librustc/middle/entry.rs @@ -0,0 +1,119 @@ +// Copyright 2012 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. + +use driver::session; +use driver::session::Session; +use syntax::parse::token::special_idents; +use syntax::ast::{crate, node_id, item, item_fn}; +use syntax::codemap::span; +use syntax::visit::{default_visitor, mk_vt, vt, Visitor, visit_crate, visit_item}; +use syntax::attr::{attrs_contains_name}; + +struct EntryContext { + session: Session, + + // The function that has attribute named 'main' + attr_main_fn: Option<(node_id, span)>, + + // The functions that could be main functions + main_fns: ~[Option<(node_id, span)>], + + // The function that has the attribute 'start' on it + start_fn: Option<(node_id, span)>, +} + +type EntryVisitor = vt<@mut EntryContext>; + +pub fn find_entry_point(session: Session, crate: @crate) { + + let ctxt = @mut EntryContext { + session: session, + attr_main_fn: None, + main_fns: ~[], + start_fn: None, + }; + + visit_crate(crate, ctxt, mk_vt(@Visitor { + visit_item: |item, ctxt, visitor| find_item(item, ctxt, visitor), + .. *default_visitor() + })); + + check_duplicate_main(ctxt); +} + +fn find_item(item: @item, ctxt: @mut EntryContext, visitor: EntryVisitor) { + match item.node { + item_fn(*) => { + // If this is the main function, we must record it in the + // session. + + // FIXME #4404 android JNI hacks + if !*ctxt.session.building_library || + ctxt.session.targ_cfg.os == session::os_android { + + if ctxt.attr_main_fn.is_none() && + item.ident == special_idents::main { + + ctxt.main_fns.push(Some((item.id, item.span))); + } + + if attrs_contains_name(item.attrs, ~"main") { + if ctxt.attr_main_fn.is_none() { + ctxt.attr_main_fn = Some((item.id, item.span)); + } else { + ctxt.session.span_err( + item.span, + ~"multiple 'main' functions"); + } + } + + if attrs_contains_name(item.attrs, ~"start") { + if ctxt.start_fn.is_none() { + ctxt.start_fn = Some((item.id, item.span)); + } else { + ctxt.session.span_err( + item.span, + ~"multiple 'start' functions"); + } + } + } + } + _ => () + } + + visit_item(item, ctxt, visitor); +} + +// main function checking +// +// be sure that there is only one main function +fn check_duplicate_main(ctxt: @mut EntryContext) { + let this = &mut *ctxt; + if this.attr_main_fn.is_none() && this.start_fn.is_none() { + if this.main_fns.len() >= 1u { + let mut i = 1u; + while i < this.main_fns.len() { + let (_, dup_main_span) = this.main_fns[i].unwrap(); + this.session.span_err( + dup_main_span, + ~"multiple 'main' functions"); + i += 1; + } + *this.session.entry_fn = this.main_fns[0]; + *this.session.entry_type = Some(session::EntryMain); + } + } else if !this.start_fn.is_none() { + *this.session.entry_fn = this.start_fn; + *this.session.entry_type = Some(session::EntryStart); + } else { + *this.session.entry_fn = this.attr_main_fn; + *this.session.entry_type = Some(session::EntryMain); + } +} diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 5354ffc5d3c82..b0dd627c59f6a 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use driver::session; use driver::session::Session; use metadata::csearch::{each_path, get_trait_method_def_ids}; use metadata::csearch::get_method_name_and_self_ty; @@ -794,11 +793,6 @@ pub fn Resolver(session: Session, namespaces: ~[ TypeNS, ValueNS ], - attr_main_fn: None, - main_fns: ~[], - - start_fn: None, - def_map: @mut HashMap::new(), export_map2: @mut HashMap::new(), trait_map: HashMap::new(), @@ -856,15 +850,6 @@ pub struct Resolver { // The four namespaces. namespaces: ~[Namespace], - // The function that has attribute named 'main' - attr_main_fn: Option<(node_id, span)>, - - // The functions that could be main functions - main_fns: ~[Option<(node_id, span)>], - - // The function that has the attribute 'start' on it - start_fn: Option<(node_id, span)>, - def_map: DefMap, export_map2: ExportMap2, trait_map: TraitMap, @@ -885,7 +870,6 @@ pub impl Resolver { self.resolve_crate(); self.session.abort_if_errors(); - self.check_duplicate_main(); self.check_for_unused_imports_if_necessary(); } @@ -3545,40 +3529,6 @@ pub impl Resolver { } item_fn(ref fn_decl, _, _, ref generics, ref block) => { - // If this is the main function, we must record it in the - // session. - - // FIXME #4404 android JNI hacks - if !*self.session.building_library || - self.session.targ_cfg.os == session::os_android { - - if self.attr_main_fn.is_none() && - item.ident == special_idents::main { - - self.main_fns.push(Some((item.id, item.span))); - } - - if attrs_contains_name(item.attrs, ~"main") { - if self.attr_main_fn.is_none() { - self.attr_main_fn = Some((item.id, item.span)); - } else { - self.session.span_err( - item.span, - ~"multiple 'main' functions"); - } - } - - if attrs_contains_name(item.attrs, ~"start") { - if self.start_fn.is_none() { - self.start_fn = Some((item.id, item.span)); - } else { - self.session.span_err( - item.span, - ~"multiple 'start' functions"); - } - } - } - self.resolve_function(OpaqueFunctionRibKind, Some(fn_decl), HasTypeParameters @@ -5109,35 +5059,6 @@ pub impl Resolver { } } - // - // main function checking - // - // be sure that there is only one main function - // - fn check_duplicate_main(@mut self) { - let this = &mut *self; - if this.attr_main_fn.is_none() && this.start_fn.is_none() { - if this.main_fns.len() >= 1u { - let mut i = 1u; - while i < this.main_fns.len() { - let (_, dup_main_span) = this.main_fns[i].unwrap(); - this.session.span_err( - dup_main_span, - ~"multiple 'main' functions"); - i += 1; - } - *this.session.entry_fn = this.main_fns[0]; - *this.session.entry_type = Some(session::EntryMain); - } - } else if !this.start_fn.is_none() { - *this.session.entry_fn = this.start_fn; - *this.session.entry_type = Some(session::EntryStart); - } else { - *this.session.entry_fn = this.attr_main_fn; - *this.session.entry_type = Some(session::EntryMain); - } - } - // // Unused import checking // diff --git a/src/librustc/rustc.rc b/src/librustc/rustc.rc index 54c51cf2e487a..82cf4cbaf54b1 100644 --- a/src/librustc/rustc.rc +++ b/src/librustc/rustc.rc @@ -96,6 +96,7 @@ pub mod middle { pub mod lang_items; pub mod privacy; pub mod moves; + pub mod entry; } pub mod front { From 98f5c6d5b683b82e347247192061182cdd844cf5 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 29 Apr 2013 16:16:58 -0700 Subject: [PATCH 2/3] rustc: Only accept main functions at the crate level. #4433 --- src/librustc/driver/driver.rs | 3 +- src/librustc/middle/entry.rs | 129 +++++++++++------- src/librustc/middle/typeck/mod.rs | 2 +- src/test/compile-fail/elided-test.rs | 2 +- ...ultiple-main.rs => main-wrong-location.rs} | 12 +- src/test/compile-fail/missing-main.rs | 2 +- src/test/run-pass/dupe-first-attr.rc | 2 + src/test/run-pass/intrinsic-alignment.rs | 4 + 8 files changed, 93 insertions(+), 63 deletions(-) rename src/test/compile-fail/{multiple-main.rs => main-wrong-location.rs} (72%) diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index e421246da8da3..a9454b595a502 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -225,7 +225,8 @@ pub fn compile_rest(sess: Session, time(time_passes, ~"resolution", || middle::resolve::resolve_crate(sess, lang_items, crate)); - time(time_passes, ~"looking for entry point", || middle::entry::find_entry_point(sess, crate)); + time(time_passes, ~"looking for entry point", + || middle::entry::find_entry_point(sess, crate, ast_map)); let freevars = time(time_passes, ~"freevar finding", || freevars::annotate_freevars(def_map, crate)); diff --git a/src/librustc/middle/entry.rs b/src/librustc/middle/entry.rs index 18f4a5a307200..47873b3fa1f6a 100644 --- a/src/librustc/middle/entry.rs +++ b/src/librustc/middle/entry.rs @@ -15,29 +15,46 @@ use syntax::ast::{crate, node_id, item, item_fn}; use syntax::codemap::span; use syntax::visit::{default_visitor, mk_vt, vt, Visitor, visit_crate, visit_item}; use syntax::attr::{attrs_contains_name}; +use syntax::ast_map; +use core::util; struct EntryContext { session: Session, + ast_map: ast_map::map, + + // The top-level function called 'main' + main_fn: Option<(node_id, span)>, + // The function that has attribute named 'main' attr_main_fn: Option<(node_id, span)>, - // The functions that could be main functions - main_fns: ~[Option<(node_id, span)>], - // The function that has the attribute 'start' on it start_fn: Option<(node_id, 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: ~[(node_id, span)], } type EntryVisitor = vt<@mut EntryContext>; -pub fn find_entry_point(session: Session, crate: @crate) { +pub fn find_entry_point(session: Session, crate: @crate, ast_map: ast_map::map) { + + // FIXME #4404 android JNI hacks + if *session.building_library || + session.targ_cfg.os == session::os_android { + // No need to find a main function + return; + } let ctxt = @mut EntryContext { session: session, + ast_map: ast_map, + main_fn: None, attr_main_fn: None, - main_fns: ~[], start_fn: None, + non_main_fns: ~[], }; visit_crate(crate, ctxt, mk_vt(@Visitor { @@ -45,43 +62,50 @@ pub fn find_entry_point(session: Session, crate: @crate) { .. *default_visitor() })); - check_duplicate_main(ctxt); + configure_main(ctxt); } fn find_item(item: @item, ctxt: @mut EntryContext, visitor: EntryVisitor) { match item.node { item_fn(*) => { - // If this is the main function, we must record it in the - // session. - - // FIXME #4404 android JNI hacks - if !*ctxt.session.building_library || - ctxt.session.targ_cfg.os == session::os_android { - - if ctxt.attr_main_fn.is_none() && - item.ident == special_idents::main { - - ctxt.main_fns.push(Some((item.id, item.span))); + if item.ident == special_idents::main { + match ctxt.ast_map.find(&item.id) { + Some(&ast_map::node_item(_, path)) => { + if path.len() == 0 { + // This is a top-level function so can be 'main' + if ctxt.main_fn.is_none() { + ctxt.main_fn = Some((item.id, item.span)); + } else { + ctxt.session.span_err( + item.span, + ~"multiple 'main' functions"); + } + } else { + // This isn't main + ctxt.non_main_fns.push((item.id, item.span)); + } + } + _ => util::unreachable() } + } - if attrs_contains_name(item.attrs, ~"main") { - if ctxt.attr_main_fn.is_none() { - ctxt.attr_main_fn = Some((item.id, item.span)); - } else { - ctxt.session.span_err( - item.span, - ~"multiple 'main' functions"); - } + if attrs_contains_name(item.attrs, ~"main") { + if ctxt.attr_main_fn.is_none() { + ctxt.attr_main_fn = Some((item.id, item.span)); + } else { + ctxt.session.span_err( + item.span, + ~"multiple 'main' functions"); } + } - if attrs_contains_name(item.attrs, ~"start") { - if ctxt.start_fn.is_none() { - ctxt.start_fn = Some((item.id, item.span)); - } else { - ctxt.session.span_err( - item.span, - ~"multiple 'start' functions"); - } + if attrs_contains_name(item.attrs, ~"start") { + if ctxt.start_fn.is_none() { + ctxt.start_fn = Some((item.id, item.span)); + } else { + ctxt.session.span_err( + item.span, + ~"multiple 'start' functions"); } } } @@ -91,29 +115,30 @@ fn find_item(item: @item, ctxt: @mut EntryContext, visitor: EntryVisitor) { visit_item(item, ctxt, visitor); } -// main function checking -// -// be sure that there is only one main function -fn check_duplicate_main(ctxt: @mut EntryContext) { +fn configure_main(ctxt: @mut EntryContext) { let this = &mut *ctxt; - if this.attr_main_fn.is_none() && this.start_fn.is_none() { - if this.main_fns.len() >= 1u { - let mut i = 1u; - while i < this.main_fns.len() { - let (_, dup_main_span) = this.main_fns[i].unwrap(); - this.session.span_err( - dup_main_span, - ~"multiple 'main' functions"); - i += 1; - } - *this.session.entry_fn = this.main_fns[0]; - *this.session.entry_type = Some(session::EntryMain); - } - } else if !this.start_fn.is_none() { + if this.start_fn.is_some() { *this.session.entry_fn = this.start_fn; *this.session.entry_type = Some(session::EntryStart); - } else { + } else if this.attr_main_fn.is_some() { *this.session.entry_fn = this.attr_main_fn; *this.session.entry_type = Some(session::EntryMain); + } else if this.main_fn.is_some() { + *this.session.entry_fn = this.main_fn; + *this.session.entry_type = Some(session::EntryMain); + } else { + // No main function + this.session.err(~"main function not found"); + if !this.non_main_fns.is_empty() { + // There were some functions named 'main' though. Try to give the user a hint. + this.session.note(~"the main function must be defined at the crate level \ + but you have one or more functions named 'main' that are not \ + defined at the crate level. Either move the definition or attach \ + the `#[main]` attribute to override this behavior."); + for this.non_main_fns.each |&(_, span)| { + this.session.span_note(span, ~"here is a function named 'main'"); + } + } + this.session.abort_if_errors(); } } diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs index 14ef3feb7d53e..3ea53aaa9765e 100644 --- a/src/librustc/middle/typeck/mod.rs +++ b/src/librustc/middle/typeck/mod.rs @@ -387,7 +387,7 @@ fn check_for_entry_fn(ccx: @mut CrateCtxt) { Some(session::EntryStart) => check_start_fn_ty(ccx, id, sp), None => tcx.sess.bug(~"entry function without a type") }, - None => tcx.sess.err(~"entry function not found") + None => tcx.sess.bug(~"type checking without entry function") } } } diff --git a/src/test/compile-fail/elided-test.rs b/src/test/compile-fail/elided-test.rs index eaae721e0e555..b62214b12f9a0 100644 --- a/src/test/compile-fail/elided-test.rs +++ b/src/test/compile-fail/elided-test.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: entry function not found +// error-pattern: main function not found // Since we're not compiling a test runner this function should be elided // and the build will fail because main doesn't exist diff --git a/src/test/compile-fail/multiple-main.rs b/src/test/compile-fail/main-wrong-location.rs similarity index 72% rename from src/test/compile-fail/multiple-main.rs rename to src/test/compile-fail/main-wrong-location.rs index ef8cd58abf992..90ef7843d4bf9 100644 --- a/src/test/compile-fail/multiple-main.rs +++ b/src/test/compile-fail/main-wrong-location.rs @@ -8,10 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn main() { -} - -mod foo { - fn main() { //~ ERROR multiple 'main' functions - } -} +mod m { + // An inferred main entry point (that doesn't use #[main]) + // must appear at the top of the crate + fn main() { } //~ NOTE here is a function named 'main' +} \ No newline at end of file diff --git a/src/test/compile-fail/missing-main.rs b/src/test/compile-fail/missing-main.rs index 4f1b604b5070d..4bfdaf69480e6 100644 --- a/src/test/compile-fail/missing-main.rs +++ b/src/test/compile-fail/missing-main.rs @@ -8,5 +8,5 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:entry function not found +// error-pattern:main function not found fn mian() { } diff --git a/src/test/run-pass/dupe-first-attr.rc b/src/test/run-pass/dupe-first-attr.rc index d39a2aa447623..9bd63a8d646d3 100644 --- a/src/test/run-pass/dupe-first-attr.rc +++ b/src/test/run-pass/dupe-first-attr.rc @@ -25,3 +25,5 @@ mod hello; #[cfg(target_os = "android")] mod hello; + +fn main() { } \ No newline at end of file diff --git a/src/test/run-pass/intrinsic-alignment.rs b/src/test/run-pass/intrinsic-alignment.rs index adc085d210853..cce3d8066ec19 100644 --- a/src/test/run-pass/intrinsic-alignment.rs +++ b/src/test/run-pass/intrinsic-alignment.rs @@ -22,6 +22,7 @@ mod rusti { #[cfg(target_os = "macos")] #[cfg(target_os = "freebsd")] mod m { + #[main] #[cfg(target_arch = "x86")] pub fn main() { unsafe { @@ -30,6 +31,7 @@ mod m { } } + #[main] #[cfg(target_arch = "x86_64")] pub fn main() { unsafe { @@ -41,6 +43,7 @@ mod m { #[cfg(target_os = "win32")] mod m { + #[main] #[cfg(target_arch = "x86")] pub fn main() { unsafe { @@ -52,6 +55,7 @@ mod m { #[cfg(target_os = "android")] mod m { + #[main] #[cfg(target_arch = "arm")] pub fn main() { unsafe { From 3970d02ec5fb33b2e10132b48dd193b86c245596 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 6 May 2013 18:48:24 -0700 Subject: [PATCH 3/3] rustc: Fix the logic for finding the Android main function I don't understand what this logic is doing --- src/librustc/middle/entry.rs | 32 +++++++++++-------- src/test/compile-fail/issue-2995.rs | 2 ++ src/test/compile-fail/tag-variant-disr-dup.rs | 2 ++ 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/librustc/middle/entry.rs b/src/librustc/middle/entry.rs index 47873b3fa1f6a..9ffd0e6f22c3c 100644 --- a/src/librustc/middle/entry.rs +++ b/src/librustc/middle/entry.rs @@ -42,8 +42,8 @@ type EntryVisitor = vt<@mut EntryContext>; pub fn find_entry_point(session: Session, crate: @crate, ast_map: ast_map::map) { // FIXME #4404 android JNI hacks - if *session.building_library || - session.targ_cfg.os == session::os_android { + if *session.building_library && + session.targ_cfg.os != session::os_android { // No need to find a main function return; } @@ -127,18 +127,24 @@ fn configure_main(ctxt: @mut EntryContext) { *this.session.entry_fn = this.main_fn; *this.session.entry_type = Some(session::EntryMain); } else { - // No main function - this.session.err(~"main function not found"); - if !this.non_main_fns.is_empty() { - // There were some functions named 'main' though. Try to give the user a hint. - this.session.note(~"the main function must be defined at the crate level \ - but you have one or more functions named 'main' that are not \ - defined at the crate level. Either move the definition or attach \ - the `#[main]` attribute to override this behavior."); - for this.non_main_fns.each |&(_, span)| { - this.session.span_note(span, ~"here is a function named 'main'"); + if !*this.session.building_library { + // No main function + this.session.err(~"main function not found"); + if !this.non_main_fns.is_empty() { + // There were some functions named 'main' though. Try to give the user a hint. + this.session.note(~"the main function must be defined at the crate level \ + but you have one or more functions named 'main' that are not \ + defined at the crate level. Either move the definition or \ + attach the `#[main]` attribute to override this behavior."); + for this.non_main_fns.each |&(_, span)| { + this.session.span_note(span, ~"here is a function named 'main'"); + } } + this.session.abort_if_errors(); + } else { + // If we *are* building a library, then we're on android where we still might + // optionally want to translate main $4404 + assert!(this.session.targ_cfg.os == session::os_android); } - this.session.abort_if_errors(); } } diff --git a/src/test/compile-fail/issue-2995.rs b/src/test/compile-fail/issue-2995.rs index 5c48416667fe1..3e771eef970f7 100644 --- a/src/test/compile-fail/issue-2995.rs +++ b/src/test/compile-fail/issue-2995.rs @@ -11,3 +11,5 @@ fn bad (p: *int) { let _q: &int = p as ∫ //~ ERROR non-scalar cast } + +fn main() { } \ No newline at end of file diff --git a/src/test/compile-fail/tag-variant-disr-dup.rs b/src/test/compile-fail/tag-variant-disr-dup.rs index be53b6a0ba3fa..216779fac7c46 100644 --- a/src/test/compile-fail/tag-variant-disr-dup.rs +++ b/src/test/compile-fail/tag-variant-disr-dup.rs @@ -19,3 +19,5 @@ enum color { black = 0x000000, white = 0x000000, } + +fn main() { } \ No newline at end of file