Skip to content

Add #[start] attribute to define a new entry point function #5809

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

Merged
merged 3 commits into from
Apr 12, 2013
Merged
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
3 changes: 2 additions & 1 deletion src/librustc/driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
13 changes: 12 additions & 1 deletion src/librustc/driver/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,25 @@ 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,
cstore: @mut metadata::cstore::CStore,
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<EntryFnType>,
span_diagnostic: @diagnostic::span_handler,
filesearch: @filesearch::FileSearch,
building_library: @mut bool,
Expand Down
30 changes: 26 additions & 4 deletions src/librustc/middle/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand All @@ -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,
Expand Down Expand Up @@ -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() {
Expand All @@ -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);
}
}

Expand Down
87 changes: 54 additions & 33 deletions src/librustc/middle/trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
Expand All @@ -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());

~[
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 crate_map = ccx.crate_map;
let opaque_crate_map = unsafe {llvm::LLVMBuildPointerCast(
bld, crate_map, T_ptr(T_i8()), noname())};

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 {
Expand Down
71 changes: 65 additions & 6 deletions src/librustc/middle/typeck/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ independently:

use core::prelude::*;

use driver::session;

use middle::resolve;
use middle::ty;
use util::common::time;
Expand All @@ -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;
Expand Down Expand Up @@ -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")
}
}
}
Expand All @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/elided-test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/missing-main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() { }
16 changes: 16 additions & 0 deletions src/test/run-pass/attr-start.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// 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;
}