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 2e64c0c45bff..e421246da8da 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 000000000000..18f4a5a30720 --- /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 5354ffc5d3c8..b0dd627c59f6 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 54c51cf2e487..82cf4cbaf54b 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 | 131 +++++++++++------- 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, 94 insertions(+), 64 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 e421246da8da..a9454b595a50 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 18f4a5a30720..47873b3fa1f6 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 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 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, ~"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, ~"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"); } } } @@ -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 14ef3feb7d53..3ea53aaa9765 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 eaae721e0e55..b62214b12f9a 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 ef8cd58abf99..90ef7843d4bf 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 4f1b604b5070..4bfdaf69480e 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 d39a2aa44762..9bd63a8d646d 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 adc085d21085..cce3d8066ec1 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 47873b3fa1f6..9ffd0e6f22c3 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 5c48416667fe..3e771eef970f 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 be53b6a0ba3f..216779fac7c4 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