auto merge of #5809 : Aatch/rust/start-attr, r=thestinger
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...)
This commit is contained in:
commit
63e2724cdb
@ -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,
|
||||
|
@ -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<EntryFnType>,
|
||||
span_diagnostic: @diagnostic::span_handler,
|
||||
filesearch: @filesearch::FileSearch,
|
||||
building_library: @mut bool,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2202,28 +2202,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);
|
||||
@ -2247,7 +2251,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
|
||||
@ -2269,34 +2273,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 {
|
||||
|
@ -50,6 +50,8 @@ independently:
|
||||
|
||||
use core::prelude::*;
|
||||
|
||||
use driver::session;
|
||||
|
||||
use middle::resolve;
|
||||
use middle::ty;
|
||||
use util::common::time;
|
||||
@ -63,7 +65,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;
|
||||
@ -327,12 +330,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")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -359,7 +418,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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
src/test/run-pass/attr-start.rs
Normal file
16
src/test/run-pass/attr-start.rs
Normal file
@ -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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user