Skip to content

Commit cd41ee2

Browse files
author
James Miller
committed
Add #[start] attribute to define a new entry point function
1 parent 412a070 commit cd41ee2

File tree

6 files changed

+173
-45
lines changed

6 files changed

+173
-45
lines changed

src/librustc/driver/driver.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -698,7 +698,8 @@ pub fn build_session_(sopts: @session::options,
698698
parse_sess: p_s,
699699
codemap: cm,
700700
// For a library crate, this is always none
701-
main_fn: @mut None,
701+
entry_fn: @mut None,
702+
entry_type: @mut None,
702703
span_diagnostic: span_diagnostic_handler,
703704
filesearch: filesearch,
704705
building_library: @mut false,

src/librustc/driver/session.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -144,14 +144,25 @@ pub struct crate_metadata {
144144
data: ~[u8]
145145
}
146146

147+
// The type of entry function, so
148+
// users can have their own entry
149+
// functions that don't start a
150+
// scheduler
151+
#[deriving(Eq)]
152+
pub enum EntryFnType {
153+
EntryMain,
154+
EntryStart
155+
}
156+
147157
pub struct Session_ {
148158
targ_cfg: @config,
149159
opts: @options,
150160
cstore: @mut metadata::cstore::CStore,
151161
parse_sess: @mut ParseSess,
152162
codemap: @codemap::CodeMap,
153163
// For a library crate, this is always none
154-
main_fn: @mut Option<(node_id, codemap::span)>,
164+
entry_fn: @mut Option<(node_id, codemap::span)>,
165+
entry_type: @mut Option<EntryFnType>,
155166
span_diagnostic: @diagnostic::span_handler,
156167
filesearch: @filesearch::FileSearch,
157168
building_library: @mut bool,

src/librustc/middle/resolve.rs

+26-4
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,8 @@ pub fn Resolver(session: Session,
801801
attr_main_fn: None,
802802
main_fns: ~[],
803803
804+
start_fn: None,
805+
804806
def_map: @mut HashMap::new(),
805807
export_map2: @mut HashMap::new(),
806808
trait_map: HashMap::new(),
@@ -860,9 +862,13 @@ pub struct Resolver {
860862
861863
// The function that has attribute named 'main'
862864
attr_main_fn: Option<(node_id, span)>,
863-
// The functions named 'main'
865+
866+
// The functions that could be main functions
864867
main_fns: ~[Option<(node_id, span)>],
865868
869+
// The function that has the attribute 'start' on it
870+
start_fn: Option<(node_id, span)>,
871+
866872
def_map: DefMap,
867873
export_map2: ExportMap2,
868874
trait_map: TraitMap,
@@ -3538,6 +3544,7 @@ pub impl Resolver {
35383544
item_fn(ref fn_decl, _, _, ref generics, ref block) => {
35393545
// If this is the main function, we must record it in the
35403546
// session.
3547+
35413548
// FIXME #4404 android JNI hacks
35423549
if !*self.session.building_library ||
35433550
self.session.targ_cfg.os == session::os_android {
@@ -3557,6 +3564,16 @@ pub impl Resolver {
35573564
~"multiple 'main' functions");
35583565
}
35593566
}
3567+
3568+
if attrs_contains_name(item.attrs, ~"start") {
3569+
if self.start_fn.is_none() {
3570+
self.start_fn = Some((item.id, item.span));
3571+
} else {
3572+
self.session.span_err(
3573+
item.span,
3574+
~"multiple 'start' functions");
3575+
}
3576+
}
35603577
}
35613578

35623579
self.resolve_function(OpaqueFunctionRibKind,
@@ -5096,7 +5113,7 @@ pub impl Resolver {
50965113
//
50975114
fn check_duplicate_main(@mut self) {
50985115
let this = &mut *self;
5099-
if this.attr_main_fn.is_none() {
5116+
if this.attr_main_fn.is_none() && this.start_fn.is_none() {
51005117
if this.main_fns.len() >= 1u {
51015118
let mut i = 1u;
51025119
while i < this.main_fns.len() {
@@ -5106,10 +5123,15 @@ pub impl Resolver {
51065123
~"multiple 'main' functions");
51075124
i += 1;
51085125
}
5109-
*this.session.main_fn = this.main_fns[0];
5126+
*this.session.entry_fn = this.main_fns[0];
5127+
*this.session.entry_type = Some(session::EntryMain);
51105128
}
5129+
} else if !this.start_fn.is_none() {
5130+
*this.session.entry_fn = this.start_fn;
5131+
*this.session.entry_type = Some(session::EntryStart);
51115132
} else {
5112-
*this.session.main_fn = this.attr_main_fn;
5133+
*this.session.entry_fn = this.attr_main_fn;
5134+
*this.session.entry_type = Some(session::EntryMain);
51135135
}
51145136
}
51155137

src/librustc/middle/trans/base.rs

+54-33
Original file line numberDiff line numberDiff line change
@@ -2197,28 +2197,32 @@ pub fn register_fn_fuller(ccx: @CrateContext,
21972197
ccx.item_symbols.insert(node_id, ps);
21982198
21992199
// FIXME #4404 android JNI hacks
2200-
let is_main = is_main_fn(&ccx.sess, node_id) &&
2200+
let is_entry = is_entry_fn(&ccx.sess, node_id) &&
22012201
(!*ccx.sess.building_library ||
22022202
(*ccx.sess.building_library &&
22032203
ccx.sess.targ_cfg.os == session::os_android));
2204-
if is_main { create_main_wrapper(ccx, sp, llfn); }
2204+
if is_entry { create_entry_wrapper(ccx, sp, llfn); }
22052205
llfn
22062206
}
22072207
2208-
pub fn is_main_fn(sess: &Session, node_id: ast::node_id) -> bool {
2209-
match *sess.main_fn {
2210-
Some((main_id, _)) => node_id == main_id,
2208+
pub fn is_entry_fn(sess: &Session, node_id: ast::node_id) -> bool {
2209+
match *sess.entry_fn {
2210+
Some((entry_id, _)) => node_id == entry_id,
22112211
None => false
22122212
}
22132213
}
22142214
22152215
// Create a _rust_main(args: ~[str]) function which will be called from the
22162216
// runtime rust_start function
2217-
pub fn create_main_wrapper(ccx: @CrateContext,
2217+
pub fn create_entry_wrapper(ccx: @CrateContext,
22182218
_sp: span, main_llfn: ValueRef) {
2219-
2220-
let llfn = create_main(ccx, main_llfn);
2221-
create_entry_fn(ccx, llfn);
2219+
let et = ccx.sess.entry_type.unwrap();
2220+
if et == session::EntryMain {
2221+
let llfn = create_main(ccx, main_llfn);
2222+
create_entry_fn(ccx, llfn, true);
2223+
} else {
2224+
create_entry_fn(ccx, main_llfn, false);
2225+
}
22222226
22232227
fn create_main(ccx: @CrateContext, main_llfn: ValueRef) -> ValueRef {
22242228
let nt = ty::mk_nil(ccx.tcx);
@@ -2242,7 +2246,7 @@ pub fn create_main_wrapper(ccx: @CrateContext,
22422246
return llfdecl;
22432247
}
22442248
2245-
fn create_entry_fn(ccx: @CrateContext, rust_main: ValueRef) {
2249+
fn create_entry_fn(ccx: @CrateContext, rust_main: ValueRef, use_start_lang_item:bool) {
22462250
let llfty = T_fn(~[ccx.int_type, T_ptr(T_ptr(T_i8()))], ccx.int_type);
22472251
22482252
// FIXME #4404 android JNI hacks
@@ -2264,34 +2268,51 @@ pub fn create_main_wrapper(ccx: @CrateContext,
22642268
unsafe {
22652269
llvm::LLVMPositionBuilderAtEnd(bld, llbb);
22662270
}
2267-
let crate_map = ccx.crate_map;
2268-
let start_def_id = ccx.tcx.lang_items.start_fn();
2269-
let start_fn = if start_def_id.crate == ast::local_crate {
2270-
ccx.sess.bug(~"start lang item is never in the local crate")
2271-
} else {
2272-
let start_fn_type = csearch::get_type(ccx.tcx,
2273-
start_def_id).ty;
2274-
trans_external_path(ccx, start_def_id, start_fn_type)
2275-
};
22762271
22772272
let retptr = unsafe {
22782273
llvm::LLVMBuildAlloca(bld, ccx.int_type, noname())
22792274
};
22802275
2281-
let args = unsafe {
2282-
let opaque_rust_main = llvm::LLVMBuildPointerCast(
2283-
bld, rust_main, T_ptr(T_i8()), noname());
2284-
let opaque_crate_map = llvm::LLVMBuildPointerCast(
2285-
bld, crate_map, T_ptr(T_i8()), noname());
2286-
2287-
~[
2288-
retptr,
2289-
C_null(T_opaque_box_ptr(ccx)),
2290-
opaque_rust_main,
2291-
llvm::LLVMGetParam(llfn, 0 as c_uint),
2292-
llvm::LLVMGetParam(llfn, 1 as c_uint),
2293-
opaque_crate_map
2294-
]
2276+
let crate_map = ccx.crate_map;
2277+
let opaque_crate_map = unsafe {llvm::LLVMBuildPointerCast(
2278+
bld, crate_map, T_ptr(T_i8()), noname())};
2279+
2280+
let (start_fn, args) = if use_start_lang_item {
2281+
let start_def_id = ccx.tcx.lang_items.start_fn();
2282+
let start_fn = if start_def_id.crate == ast::local_crate {
2283+
ccx.sess.bug(~"start lang item is never in the local crate")
2284+
} else {
2285+
let start_fn_type = csearch::get_type(ccx.tcx,
2286+
start_def_id).ty;
2287+
trans_external_path(ccx, start_def_id, start_fn_type)
2288+
};
2289+
2290+
let args = unsafe {
2291+
let opaque_rust_main = llvm::LLVMBuildPointerCast(
2292+
bld, rust_main, T_ptr(T_i8()), noname());
2293+
2294+
~[
2295+
retptr,
2296+
C_null(T_opaque_box_ptr(ccx)),
2297+
opaque_rust_main,
2298+
llvm::LLVMGetParam(llfn, 0 as c_uint),
2299+
llvm::LLVMGetParam(llfn, 1 as c_uint),
2300+
opaque_crate_map
2301+
]
2302+
};
2303+
(start_fn, args)
2304+
} else {
2305+
debug!("using user-defined start fn");
2306+
let args = unsafe {
2307+
~[ retptr,
2308+
C_null(T_opaque_box_ptr(ccx)),
2309+
llvm::LLVMGetParam(llfn, 0 as c_uint),
2310+
llvm::LLVMGetParam(llfn, 1 as c_uint),
2311+
opaque_crate_map
2312+
]
2313+
};
2314+
2315+
(rust_main, args)
22952316
};
22962317
22972318
unsafe {

src/librustc/middle/typeck/mod.rs

+65-6
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ independently:
5050

5151
use core::prelude::*;
5252

53+
use driver::session;
54+
5355
use middle::resolve;
5456
use middle::ty;
5557
use util::common::time;
@@ -62,7 +64,8 @@ use std::list::List;
6264
use std::list;
6365
use syntax::codemap::span;
6466
use syntax::print::pprust::*;
65-
use syntax::{ast, ast_map};
67+
use syntax::{ast, ast_map, abi};
68+
use syntax::opt_vec;
6669

6770
#[path = "check/mod.rs"]
6871
pub mod check;
@@ -325,12 +328,68 @@ fn check_main_fn_ty(ccx: @mut CrateCtxt,
325328
}
326329
}
327330

328-
fn check_for_main_fn(ccx: @mut CrateCtxt) {
331+
fn check_start_fn_ty(ccx: @mut CrateCtxt,
332+
start_id: ast::node_id,
333+
start_span: span) {
334+
335+
let tcx = ccx.tcx;
336+
let start_t = ty::node_id_to_type(tcx, start_id);
337+
match ty::get(start_t).sty {
338+
ty::ty_bare_fn(_) => {
339+
match tcx.items.find(&start_id) {
340+
Some(&ast_map::node_item(it,_)) => {
341+
match it.node {
342+
ast::item_fn(_,_,_,ref ps,_)
343+
if ps.is_parameterized() => {
344+
tcx.sess.span_err(
345+
start_span,
346+
~"start function is not allowed to have type \
347+
parameters");
348+
return;
349+
}
350+
_ => ()
351+
}
352+
}
353+
_ => ()
354+
}
355+
356+
fn arg(m: ast::rmode, ty: ty::t) -> ty::arg {
357+
ty::arg {mode: ast::expl(m), ty: ty}
358+
}
359+
360+
let se_ty = ty::mk_bare_fn(tcx, ty::BareFnTy {
361+
purity: ast::impure_fn,
362+
abis: abi::AbiSet::Rust(),
363+
sig: ty::FnSig {bound_lifetime_names: opt_vec::Empty,
364+
inputs: ~[arg(ast::by_copy, ty::mk_int(tcx)),
365+
arg(ast::by_copy, ty::mk_imm_ptr(tcx,
366+
ty::mk_imm_ptr(tcx, ty::mk_u8(tcx)))),
367+
arg(ast::by_copy, ty::mk_imm_ptr(tcx, ty::mk_u8(tcx)))],
368+
output: ty::mk_int(tcx)}
369+
});
370+
371+
require_same_types(tcx, None, false, start_span, start_t, se_ty,
372+
|| fmt!("start function expects type: `%s`", ppaux::ty_to_str(ccx.tcx, se_ty)));
373+
374+
}
375+
_ => {
376+
tcx.sess.span_bug(start_span,
377+
~"start has a non-function type: found `" +
378+
ppaux::ty_to_str(tcx, start_t) + ~"`");
379+
}
380+
}
381+
}
382+
383+
fn check_for_entry_fn(ccx: @mut CrateCtxt) {
329384
let tcx = ccx.tcx;
330385
if !*tcx.sess.building_library {
331-
match *tcx.sess.main_fn {
332-
Some((id, sp)) => check_main_fn_ty(ccx, id, sp),
333-
None => tcx.sess.err(~"main function not found")
386+
match *tcx.sess.entry_fn {
387+
Some((id, sp)) => match *tcx.sess.entry_type {
388+
Some(session::EntryMain) => check_main_fn_ty(ccx, id, sp),
389+
Some(session::EntryStart) => check_start_fn_ty(ccx, id, sp),
390+
None => tcx.sess.bug(~"entry function without a type")
391+
},
392+
None => tcx.sess.err(~"entry function not found")
334393
}
335394
}
336395
}
@@ -357,7 +416,7 @@ pub fn check_crate(tcx: ty::ctxt,
357416
time(time_passes, ~"type checking", ||
358417
check::check_item_types(ccx, crate));
359418

360-
check_for_main_fn(ccx);
419+
check_for_entry_fn(ccx);
361420
tcx.sess.abort_if_errors();
362421
(ccx.method_map, ccx.vtable_map)
363422
}

src/test/run-pass/attr-start.rs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#[start]
12+
fn start(argc:int, argv: **u8, crate_map: *u8) -> int {
13+
return 0;
14+
}

0 commit comments

Comments
 (0)