From cd41ee2044c48c2948969ab9f28e3fd8063ba450 Mon Sep 17 00:00:00 2001 From: James Miller Date: Tue, 9 Apr 2013 20:16:06 +1200 Subject: [PATCH 1/3] Add #[start] attribute to define a new entry point function --- src/librustc/driver/driver.rs | 3 +- src/librustc/driver/session.rs | 13 ++++- src/librustc/middle/resolve.rs | 30 +++++++++-- src/librustc/middle/trans/base.rs | 85 +++++++++++++++++++------------ src/librustc/middle/typeck/mod.rs | 71 +++++++++++++++++++++++--- src/test/run-pass/attr-start.rs | 14 +++++ 6 files changed, 172 insertions(+), 44 deletions(-) create mode 100644 src/test/run-pass/attr-start.rs diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index cd1af369570..d725b700037 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -698,7 +698,8 @@ pub fn build_session_(sopts: @session::options, parse_sess: p_s, codemap: cm, // For a library crate, this is always none - main_fn: @mut None, + entry_fn: @mut None, + entry_type: @mut None, span_diagnostic: span_diagnostic_handler, filesearch: filesearch, building_library: @mut false, diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs index 95740e0f837..226905bbab7 100644 --- a/src/librustc/driver/session.rs +++ b/src/librustc/driver/session.rs @@ -144,6 +144,16 @@ pub struct crate_metadata { data: ~[u8] } +// The type of entry function, so +// users can have their own entry +// functions that don't start a +// scheduler +#[deriving(Eq)] +pub enum EntryFnType { + EntryMain, + EntryStart +} + pub struct Session_ { targ_cfg: @config, opts: @options, @@ -151,7 +161,8 @@ pub struct Session_ { parse_sess: @mut ParseSess, codemap: @codemap::CodeMap, // For a library crate, this is always none - main_fn: @mut Option<(node_id, codemap::span)>, + entry_fn: @mut Option<(node_id, codemap::span)>, + entry_type: @mut Option, span_diagnostic: @diagnostic::span_handler, filesearch: @filesearch::FileSearch, building_library: @mut bool, diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index fcf0b7022a7..36f396d4dfc 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -801,6 +801,8 @@ pub fn Resolver(session: Session, attr_main_fn: None, main_fns: ~[], + start_fn: None, + def_map: @mut HashMap::new(), export_map2: @mut HashMap::new(), trait_map: HashMap::new(), @@ -860,9 +862,13 @@ pub struct Resolver { // The function that has attribute named 'main' attr_main_fn: Option<(node_id, span)>, - // The functions named 'main' + + // 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, @@ -3538,6 +3544,7 @@ 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 { @@ -3557,6 +3564,16 @@ pub impl Resolver { ~"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, @@ -5096,7 +5113,7 @@ pub impl Resolver { // fn check_duplicate_main(@mut self) { let this = &mut *self; - if this.attr_main_fn.is_none() { + 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() { @@ -5106,10 +5123,15 @@ pub impl Resolver { ~"multiple 'main' functions"); i += 1; } - *this.session.main_fn = this.main_fns[0]; + *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.main_fn = this.attr_main_fn; + *this.session.entry_fn = this.attr_main_fn; + *this.session.entry_type = Some(session::EntryMain); } } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index a8fe60ba68e..91b1b50c81c 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2197,28 +2197,32 @@ pub fn register_fn_fuller(ccx: @CrateContext, ccx.item_symbols.insert(node_id, ps); // FIXME #4404 android JNI hacks - let is_main = is_main_fn(&ccx.sess, node_id) && + let is_entry = is_entry_fn(&ccx.sess, node_id) && (!*ccx.sess.building_library || (*ccx.sess.building_library && ccx.sess.targ_cfg.os == session::os_android)); - if is_main { create_main_wrapper(ccx, sp, llfn); } + if is_entry { create_entry_wrapper(ccx, sp, llfn); } llfn } -pub fn is_main_fn(sess: &Session, node_id: ast::node_id) -> bool { - match *sess.main_fn { - Some((main_id, _)) => node_id == main_id, +pub fn is_entry_fn(sess: &Session, node_id: ast::node_id) -> bool { + match *sess.entry_fn { + Some((entry_id, _)) => node_id == entry_id, None => false } } // Create a _rust_main(args: ~[str]) function which will be called from the // runtime rust_start function -pub fn create_main_wrapper(ccx: @CrateContext, +pub fn create_entry_wrapper(ccx: @CrateContext, _sp: span, main_llfn: ValueRef) { - - let llfn = create_main(ccx, main_llfn); - create_entry_fn(ccx, llfn); + let et = ccx.sess.entry_type.unwrap(); + if et == session::EntryMain { + let llfn = create_main(ccx, main_llfn); + create_entry_fn(ccx, llfn, true); + } else { + create_entry_fn(ccx, main_llfn, false); + } fn create_main(ccx: @CrateContext, main_llfn: ValueRef) -> ValueRef { let nt = ty::mk_nil(ccx.tcx); @@ -2242,7 +2246,7 @@ pub fn create_main_wrapper(ccx: @CrateContext, return llfdecl; } - fn create_entry_fn(ccx: @CrateContext, rust_main: ValueRef) { + fn create_entry_fn(ccx: @CrateContext, rust_main: ValueRef, use_start_lang_item:bool) { let llfty = T_fn(~[ccx.int_type, T_ptr(T_ptr(T_i8()))], ccx.int_type); // FIXME #4404 android JNI hacks @@ -2264,34 +2268,51 @@ pub fn create_main_wrapper(ccx: @CrateContext, unsafe { llvm::LLVMPositionBuilderAtEnd(bld, llbb); } - let crate_map = ccx.crate_map; - let start_def_id = ccx.tcx.lang_items.start_fn(); - let start_fn = if start_def_id.crate == ast::local_crate { - ccx.sess.bug(~"start lang item is never in the local crate") - } else { - let start_fn_type = csearch::get_type(ccx.tcx, - start_def_id).ty; - trans_external_path(ccx, start_def_id, start_fn_type) - }; let retptr = unsafe { llvm::LLVMBuildAlloca(bld, ccx.int_type, noname()) }; - let args = unsafe { - let opaque_rust_main = llvm::LLVMBuildPointerCast( - bld, rust_main, T_ptr(T_i8()), noname()); - let opaque_crate_map = llvm::LLVMBuildPointerCast( - bld, crate_map, T_ptr(T_i8()), noname()); + let crate_map = ccx.crate_map; + let opaque_crate_map = unsafe {llvm::LLVMBuildPointerCast( + bld, crate_map, T_ptr(T_i8()), noname())}; - ~[ - retptr, - C_null(T_opaque_box_ptr(ccx)), - opaque_rust_main, - llvm::LLVMGetParam(llfn, 0 as c_uint), - llvm::LLVMGetParam(llfn, 1 as c_uint), - opaque_crate_map - ] + let (start_fn, args) = if use_start_lang_item { + let start_def_id = ccx.tcx.lang_items.start_fn(); + let start_fn = if start_def_id.crate == ast::local_crate { + ccx.sess.bug(~"start lang item is never in the local crate") + } else { + let start_fn_type = csearch::get_type(ccx.tcx, + start_def_id).ty; + trans_external_path(ccx, start_def_id, start_fn_type) + }; + + let args = unsafe { + let opaque_rust_main = llvm::LLVMBuildPointerCast( + bld, rust_main, T_ptr(T_i8()), noname()); + + ~[ + retptr, + C_null(T_opaque_box_ptr(ccx)), + opaque_rust_main, + llvm::LLVMGetParam(llfn, 0 as c_uint), + llvm::LLVMGetParam(llfn, 1 as c_uint), + opaque_crate_map + ] + }; + (start_fn, args) + } else { + debug!("using user-defined start fn"); + let args = unsafe { + ~[ retptr, + C_null(T_opaque_box_ptr(ccx)), + llvm::LLVMGetParam(llfn, 0 as c_uint), + llvm::LLVMGetParam(llfn, 1 as c_uint), + opaque_crate_map + ] + }; + + (rust_main, args) }; unsafe { diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs index d3502adb33a..c10a2c80d7e 100644 --- a/src/librustc/middle/typeck/mod.rs +++ b/src/librustc/middle/typeck/mod.rs @@ -50,6 +50,8 @@ independently: use core::prelude::*; +use driver::session; + use middle::resolve; use middle::ty; use util::common::time; @@ -62,7 +64,8 @@ use std::list::List; use std::list; use syntax::codemap::span; use syntax::print::pprust::*; -use syntax::{ast, ast_map}; +use syntax::{ast, ast_map, abi}; +use syntax::opt_vec; #[path = "check/mod.rs"] pub mod check; @@ -325,12 +328,68 @@ fn check_main_fn_ty(ccx: @mut CrateCtxt, } } -fn check_for_main_fn(ccx: @mut CrateCtxt) { +fn check_start_fn_ty(ccx: @mut CrateCtxt, + start_id: ast::node_id, + start_span: span) { + + let tcx = ccx.tcx; + let start_t = ty::node_id_to_type(tcx, start_id); + match ty::get(start_t).sty { + ty::ty_bare_fn(_) => { + match tcx.items.find(&start_id) { + Some(&ast_map::node_item(it,_)) => { + match it.node { + ast::item_fn(_,_,_,ref ps,_) + if ps.is_parameterized() => { + tcx.sess.span_err( + start_span, + ~"start function is not allowed to have type \ + parameters"); + return; + } + _ => () + } + } + _ => () + } + + fn arg(m: ast::rmode, ty: ty::t) -> ty::arg { + ty::arg {mode: ast::expl(m), ty: ty} + } + + let se_ty = ty::mk_bare_fn(tcx, ty::BareFnTy { + purity: ast::impure_fn, + abis: abi::AbiSet::Rust(), + sig: ty::FnSig {bound_lifetime_names: opt_vec::Empty, + inputs: ~[arg(ast::by_copy, ty::mk_int(tcx)), + arg(ast::by_copy, ty::mk_imm_ptr(tcx, + ty::mk_imm_ptr(tcx, ty::mk_u8(tcx)))), + arg(ast::by_copy, ty::mk_imm_ptr(tcx, ty::mk_u8(tcx)))], + output: ty::mk_int(tcx)} + }); + + require_same_types(tcx, None, false, start_span, start_t, se_ty, + || fmt!("start function expects type: `%s`", ppaux::ty_to_str(ccx.tcx, se_ty))); + + } + _ => { + tcx.sess.span_bug(start_span, + ~"start has a non-function type: found `" + + ppaux::ty_to_str(tcx, start_t) + ~"`"); + } + } +} + +fn check_for_entry_fn(ccx: @mut CrateCtxt) { let tcx = ccx.tcx; if !*tcx.sess.building_library { - match *tcx.sess.main_fn { - Some((id, sp)) => check_main_fn_ty(ccx, id, sp), - None => tcx.sess.err(~"main function not found") + match *tcx.sess.entry_fn { + Some((id, sp)) => match *tcx.sess.entry_type { + Some(session::EntryMain) => check_main_fn_ty(ccx, id, sp), + 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") } } } @@ -357,7 +416,7 @@ pub fn check_crate(tcx: ty::ctxt, time(time_passes, ~"type checking", || check::check_item_types(ccx, crate)); - check_for_main_fn(ccx); + check_for_entry_fn(ccx); tcx.sess.abort_if_errors(); (ccx.method_map, ccx.vtable_map) } diff --git a/src/test/run-pass/attr-start.rs b/src/test/run-pass/attr-start.rs new file mode 100644 index 00000000000..efbd7da4797 --- /dev/null +++ b/src/test/run-pass/attr-start.rs @@ -0,0 +1,14 @@ +// Copyright 2013 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. + +#[start] +fn start(argc:int, argv: **u8, crate_map: *u8) -> int { + return 0; +} From 1edfed7914f458172f182c13a2451a0120334092 Mon Sep 17 00:00:00 2001 From: James Miller Date: Wed, 10 Apr 2013 18:02:15 +1200 Subject: [PATCH 2/3] Change tests to use new error message --- src/test/compile-fail/elided-test.rs | 2 +- src/test/compile-fail/missing-main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/compile-fail/elided-test.rs b/src/test/compile-fail/elided-test.rs index b62214b12f9..eaae721e0e5 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: main function not found +// error-pattern: entry 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/missing-main.rs b/src/test/compile-fail/missing-main.rs index 4bfdaf69480..4f1b604b507 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:main function not found +// error-pattern:entry function not found fn mian() { } From 35c73c80887cca62f8bba2eb9b9e277a98c37f93 Mon Sep 17 00:00:00 2001 From: James Miller Date: Fri, 12 Apr 2013 20:59:46 +1200 Subject: [PATCH 3/3] Added xfail-fast to test so the windows buildbot doesn't choke --- src/test/run-pass/attr-start.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/run-pass/attr-start.rs b/src/test/run-pass/attr-start.rs index efbd7da4797..933405bfc44 100644 --- a/src/test/run-pass/attr-start.rs +++ b/src/test/run-pass/attr-start.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//xfail-fast + #[start] fn start(argc:int, argv: **u8, crate_map: *u8) -> int { return 0;