Skip to content

Commit c830f58

Browse files
committed
auto merge of #5809 : Aatch/rust/start-attr, r=graydon
This implements #5158. Currently it takes the command line args and the crate map. Since it doesn't take a `main` function pointer, you can't actually start the runtime easily, but that seems to be a shim to allow the current `rust_start` function to call into main. However, you can do an end-run round the io library and do this: ```rust use core::libc::{write, c_int, c_void, size_t, STDOUT_FILENO}; #[start] fn my_start(_argc:int, _argv: **u8, _crate_map: *u8) -> int { do str::as_buf("Hello World!\n") |s,len| { unsafe { write(STDOUT_FILENO, s as *c_void, len as size_t); } } return 0; } ``` Which is the most basic "Hello World" you can do in rust without starting up the runtime (though that has quite a lot to do with the fact that `core::io` uses `@` everywhere...)
2 parents 6100bb5 + cd41ee2 commit c830f58

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
@@ -2202,28 +2202,32 @@ pub fn register_fn_fuller(ccx: @CrateContext,
22022202
ccx.item_symbols.insert(node_id, ps);
22032203
22042204
// FIXME #4404 android JNI hacks
2205-
let is_main = is_main_fn(&ccx.sess, node_id) &&
2205+
let is_entry = is_entry_fn(&ccx.sess, node_id) &&
22062206
(!*ccx.sess.building_library ||
22072207
(*ccx.sess.building_library &&
22082208
ccx.sess.targ_cfg.os == session::os_android));
2209-
if is_main { create_main_wrapper(ccx, sp, llfn); }
2209+
if is_entry { create_entry_wrapper(ccx, sp, llfn); }
22102210
llfn
22112211
}
22122212
2213-
pub fn is_main_fn(sess: &Session, node_id: ast::node_id) -> bool {
2214-
match *sess.main_fn {
2215-
Some((main_id, _)) => node_id == main_id,
2213+
pub fn is_entry_fn(sess: &Session, node_id: ast::node_id) -> bool {
2214+
match *sess.entry_fn {
2215+
Some((entry_id, _)) => node_id == entry_id,
22162216
None => false
22172217
}
22182218
}
22192219
22202220
// Create a _rust_main(args: ~[str]) function which will be called from the
22212221
// runtime rust_start function
2222-
pub fn create_main_wrapper(ccx: @CrateContext,
2222+
pub fn create_entry_wrapper(ccx: @CrateContext,
22232223
_sp: span, main_llfn: ValueRef) {
2224-
2225-
let llfn = create_main(ccx, main_llfn);
2226-
create_entry_fn(ccx, llfn);
2224+
let et = ccx.sess.entry_type.unwrap();
2225+
if et == session::EntryMain {
2226+
let llfn = create_main(ccx, main_llfn);
2227+
create_entry_fn(ccx, llfn, true);
2228+
} else {
2229+
create_entry_fn(ccx, main_llfn, false);
2230+
}
22272231
22282232
fn create_main(ccx: @CrateContext, main_llfn: ValueRef) -> ValueRef {
22292233
let nt = ty::mk_nil(ccx.tcx);
@@ -2247,7 +2251,7 @@ pub fn create_main_wrapper(ccx: @CrateContext,
22472251
return llfdecl;
22482252
}
22492253
2250-
fn create_entry_fn(ccx: @CrateContext, rust_main: ValueRef) {
2254+
fn create_entry_fn(ccx: @CrateContext, rust_main: ValueRef, use_start_lang_item:bool) {
22512255
let llfty = T_fn(~[ccx.int_type, T_ptr(T_ptr(T_i8()))], ccx.int_type);
22522256
22532257
// FIXME #4404 android JNI hacks
@@ -2269,34 +2273,51 @@ pub fn create_main_wrapper(ccx: @CrateContext,
22692273
unsafe {
22702274
llvm::LLVMPositionBuilderAtEnd(bld, llbb);
22712275
}
2272-
let crate_map = ccx.crate_map;
2273-
let start_def_id = ccx.tcx.lang_items.start_fn();
2274-
let start_fn = if start_def_id.crate == ast::local_crate {
2275-
ccx.sess.bug(~"start lang item is never in the local crate")
2276-
} else {
2277-
let start_fn_type = csearch::get_type(ccx.tcx,
2278-
start_def_id).ty;
2279-
trans_external_path(ccx, start_def_id, start_fn_type)
2280-
};
22812276
22822277
let retptr = unsafe {
22832278
llvm::LLVMBuildAlloca(bld, ccx.int_type, noname())
22842279
};
22852280
2286-
let args = unsafe {
2287-
let opaque_rust_main = llvm::LLVMBuildPointerCast(
2288-
bld, rust_main, T_ptr(T_i8()), noname());
2289-
let opaque_crate_map = llvm::LLVMBuildPointerCast(
2290-
bld, crate_map, T_ptr(T_i8()), noname());
2291-
2292-
~[
2293-
retptr,
2294-
C_null(T_opaque_box_ptr(ccx)),
2295-
opaque_rust_main,
2296-
llvm::LLVMGetParam(llfn, 0 as c_uint),
2297-
llvm::LLVMGetParam(llfn, 1 as c_uint),
2298-
opaque_crate_map
2299-
]
2281+
let crate_map = ccx.crate_map;
2282+
let opaque_crate_map = unsafe {llvm::LLVMBuildPointerCast(
2283+
bld, crate_map, T_ptr(T_i8()), noname())};
2284+
2285+
let (start_fn, args) = if use_start_lang_item {
2286+
let start_def_id = ccx.tcx.lang_items.start_fn();
2287+
let start_fn = if start_def_id.crate == ast::local_crate {
2288+
ccx.sess.bug(~"start lang item is never in the local crate")
2289+
} else {
2290+
let start_fn_type = csearch::get_type(ccx.tcx,
2291+
start_def_id).ty;
2292+
trans_external_path(ccx, start_def_id, start_fn_type)
2293+
};
2294+
2295+
let args = unsafe {
2296+
let opaque_rust_main = llvm::LLVMBuildPointerCast(
2297+
bld, rust_main, T_ptr(T_i8()), noname());
2298+
2299+
~[
2300+
retptr,
2301+
C_null(T_opaque_box_ptr(ccx)),
2302+
opaque_rust_main,
2303+
llvm::LLVMGetParam(llfn, 0 as c_uint),
2304+
llvm::LLVMGetParam(llfn, 1 as c_uint),
2305+
opaque_crate_map
2306+
]
2307+
};
2308+
(start_fn, args)
2309+
} else {
2310+
debug!("using user-defined start fn");
2311+
let args = unsafe {
2312+
~[ retptr,
2313+
C_null(T_opaque_box_ptr(ccx)),
2314+
llvm::LLVMGetParam(llfn, 0 as c_uint),
2315+
llvm::LLVMGetParam(llfn, 1 as c_uint),
2316+
opaque_crate_map
2317+
]
2318+
};
2319+
2320+
(rust_main, args)
23002321
};
23012322
23022323
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;
@@ -63,7 +65,8 @@ use std::list::List;
6365
use std::list;
6466
use syntax::codemap::span;
6567
use syntax::print::pprust::*;
66-
use syntax::{ast, ast_map};
68+
use syntax::{ast, ast_map, abi};
69+
use syntax::opt_vec;
6770

6871
#[path = "check/mod.rs"]
6972
pub mod check;
@@ -327,12 +330,68 @@ fn check_main_fn_ty(ccx: @mut CrateCtxt,
327330
}
328331
}
329332

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

362-
check_for_main_fn(ccx);
421+
check_for_entry_fn(ccx);
363422
tcx.sess.abort_if_errors();
364423
(ccx.method_map, ccx.vtable_map)
365424
}

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)