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
8 changed files with 176 additions and 46 deletions
|
@ -698,7 +698,8 @@ pub fn build_session_(sopts: @session::options,
|
||||||
parse_sess: p_s,
|
parse_sess: p_s,
|
||||||
codemap: cm,
|
codemap: cm,
|
||||||
// For a library crate, this is always none
|
// 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,
|
span_diagnostic: span_diagnostic_handler,
|
||||||
filesearch: filesearch,
|
filesearch: filesearch,
|
||||||
building_library: @mut false,
|
building_library: @mut false,
|
||||||
|
|
|
@ -144,6 +144,16 @@ pub struct crate_metadata {
|
||||||
data: ~[u8]
|
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_ {
|
pub struct Session_ {
|
||||||
targ_cfg: @config,
|
targ_cfg: @config,
|
||||||
opts: @options,
|
opts: @options,
|
||||||
|
@ -151,7 +161,8 @@ pub struct Session_ {
|
||||||
parse_sess: @mut ParseSess,
|
parse_sess: @mut ParseSess,
|
||||||
codemap: @codemap::CodeMap,
|
codemap: @codemap::CodeMap,
|
||||||
// For a library crate, this is always none
|
// 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,
|
span_diagnostic: @diagnostic::span_handler,
|
||||||
filesearch: @filesearch::FileSearch,
|
filesearch: @filesearch::FileSearch,
|
||||||
building_library: @mut bool,
|
building_library: @mut bool,
|
||||||
|
|
|
@ -801,6 +801,8 @@ pub fn Resolver(session: Session,
|
||||||
attr_main_fn: None,
|
attr_main_fn: None,
|
||||||
main_fns: ~[],
|
main_fns: ~[],
|
||||||
|
|
||||||
|
start_fn: None,
|
||||||
|
|
||||||
def_map: @mut HashMap::new(),
|
def_map: @mut HashMap::new(),
|
||||||
export_map2: @mut HashMap::new(),
|
export_map2: @mut HashMap::new(),
|
||||||
trait_map: HashMap::new(),
|
trait_map: HashMap::new(),
|
||||||
|
@ -860,9 +862,13 @@ pub struct Resolver {
|
||||||
|
|
||||||
// The function that has attribute named 'main'
|
// The function that has attribute named 'main'
|
||||||
attr_main_fn: Option<(node_id, span)>,
|
attr_main_fn: Option<(node_id, span)>,
|
||||||
// The functions named 'main'
|
|
||||||
|
// The functions that could be main functions
|
||||||
main_fns: ~[Option<(node_id, span)>],
|
main_fns: ~[Option<(node_id, span)>],
|
||||||
|
|
||||||
|
// The function that has the attribute 'start' on it
|
||||||
|
start_fn: Option<(node_id, span)>,
|
||||||
|
|
||||||
def_map: DefMap,
|
def_map: DefMap,
|
||||||
export_map2: ExportMap2,
|
export_map2: ExportMap2,
|
||||||
trait_map: TraitMap,
|
trait_map: TraitMap,
|
||||||
|
@ -3538,6 +3544,7 @@ pub impl Resolver {
|
||||||
item_fn(ref fn_decl, _, _, ref generics, ref block) => {
|
item_fn(ref fn_decl, _, _, ref generics, ref block) => {
|
||||||
// If this is the main function, we must record it in the
|
// If this is the main function, we must record it in the
|
||||||
// session.
|
// session.
|
||||||
|
|
||||||
// FIXME #4404 android JNI hacks
|
// FIXME #4404 android JNI hacks
|
||||||
if !*self.session.building_library ||
|
if !*self.session.building_library ||
|
||||||
self.session.targ_cfg.os == session::os_android {
|
self.session.targ_cfg.os == session::os_android {
|
||||||
|
@ -3557,6 +3564,16 @@ pub impl Resolver {
|
||||||
~"multiple 'main' functions");
|
~"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,
|
self.resolve_function(OpaqueFunctionRibKind,
|
||||||
|
@ -5096,7 +5113,7 @@ pub impl Resolver {
|
||||||
//
|
//
|
||||||
fn check_duplicate_main(@mut self) {
|
fn check_duplicate_main(@mut self) {
|
||||||
let this = &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 {
|
if this.main_fns.len() >= 1u {
|
||||||
let mut i = 1u;
|
let mut i = 1u;
|
||||||
while i < this.main_fns.len() {
|
while i < this.main_fns.len() {
|
||||||
|
@ -5106,10 +5123,15 @@ pub impl Resolver {
|
||||||
~"multiple 'main' functions");
|
~"multiple 'main' functions");
|
||||||
i += 1;
|
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 {
|
} 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);
|
ccx.item_symbols.insert(node_id, ps);
|
||||||
|
|
||||||
// FIXME #4404 android JNI hacks
|
// 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.building_library &&
|
(*ccx.sess.building_library &&
|
||||||
ccx.sess.targ_cfg.os == session::os_android));
|
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
|
llfn
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_main_fn(sess: &Session, node_id: ast::node_id) -> bool {
|
pub fn is_entry_fn(sess: &Session, node_id: ast::node_id) -> bool {
|
||||||
match *sess.main_fn {
|
match *sess.entry_fn {
|
||||||
Some((main_id, _)) => node_id == main_id,
|
Some((entry_id, _)) => node_id == entry_id,
|
||||||
None => false
|
None => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a _rust_main(args: ~[str]) function which will be called from the
|
// Create a _rust_main(args: ~[str]) function which will be called from the
|
||||||
// runtime rust_start function
|
// runtime rust_start function
|
||||||
pub fn create_main_wrapper(ccx: @CrateContext,
|
pub fn create_entry_wrapper(ccx: @CrateContext,
|
||||||
_sp: span, main_llfn: ValueRef) {
|
_sp: span, main_llfn: ValueRef) {
|
||||||
|
let et = ccx.sess.entry_type.unwrap();
|
||||||
|
if et == session::EntryMain {
|
||||||
let llfn = create_main(ccx, main_llfn);
|
let llfn = create_main(ccx, main_llfn);
|
||||||
create_entry_fn(ccx, llfn);
|
create_entry_fn(ccx, llfn, true);
|
||||||
|
} else {
|
||||||
|
create_entry_fn(ccx, main_llfn, false);
|
||||||
|
}
|
||||||
|
|
||||||
fn create_main(ccx: @CrateContext, main_llfn: ValueRef) -> ValueRef {
|
fn create_main(ccx: @CrateContext, main_llfn: ValueRef) -> ValueRef {
|
||||||
let nt = ty::mk_nil(ccx.tcx);
|
let nt = ty::mk_nil(ccx.tcx);
|
||||||
|
@ -2247,7 +2251,7 @@ pub fn create_main_wrapper(ccx: @CrateContext,
|
||||||
return llfdecl;
|
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);
|
let llfty = T_fn(~[ccx.int_type, T_ptr(T_ptr(T_i8()))], ccx.int_type);
|
||||||
|
|
||||||
// FIXME #4404 android JNI hacks
|
// FIXME #4404 android JNI hacks
|
||||||
|
@ -2269,7 +2273,16 @@ pub fn create_main_wrapper(ccx: @CrateContext,
|
||||||
unsafe {
|
unsafe {
|
||||||
llvm::LLVMPositionBuilderAtEnd(bld, llbb);
|
llvm::LLVMPositionBuilderAtEnd(bld, llbb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let retptr = unsafe {
|
||||||
|
llvm::LLVMBuildAlloca(bld, ccx.int_type, noname())
|
||||||
|
};
|
||||||
|
|
||||||
let crate_map = ccx.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_def_id = ccx.tcx.lang_items.start_fn();
|
||||||
let start_fn = if start_def_id.crate == ast::local_crate {
|
let start_fn = if start_def_id.crate == ast::local_crate {
|
||||||
ccx.sess.bug(~"start lang item is never in the local crate")
|
ccx.sess.bug(~"start lang item is never in the local crate")
|
||||||
|
@ -2279,15 +2292,9 @@ pub fn create_main_wrapper(ccx: @CrateContext,
|
||||||
trans_external_path(ccx, start_def_id, start_fn_type)
|
trans_external_path(ccx, start_def_id, start_fn_type)
|
||||||
};
|
};
|
||||||
|
|
||||||
let retptr = unsafe {
|
|
||||||
llvm::LLVMBuildAlloca(bld, ccx.int_type, noname())
|
|
||||||
};
|
|
||||||
|
|
||||||
let args = unsafe {
|
let args = unsafe {
|
||||||
let opaque_rust_main = llvm::LLVMBuildPointerCast(
|
let opaque_rust_main = llvm::LLVMBuildPointerCast(
|
||||||
bld, rust_main, T_ptr(T_i8()), noname());
|
bld, rust_main, T_ptr(T_i8()), noname());
|
||||||
let opaque_crate_map = llvm::LLVMBuildPointerCast(
|
|
||||||
bld, crate_map, T_ptr(T_i8()), noname());
|
|
||||||
|
|
||||||
~[
|
~[
|
||||||
retptr,
|
retptr,
|
||||||
|
@ -2298,6 +2305,20 @@ pub fn create_main_wrapper(ccx: @CrateContext,
|
||||||
opaque_crate_map
|
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 {
|
unsafe {
|
||||||
llvm::LLVMBuildCall(bld, start_fn, vec::raw::to_ptr(args),
|
llvm::LLVMBuildCall(bld, start_fn, vec::raw::to_ptr(args),
|
||||||
|
|
|
@ -50,6 +50,8 @@ independently:
|
||||||
|
|
||||||
use core::prelude::*;
|
use core::prelude::*;
|
||||||
|
|
||||||
|
use driver::session;
|
||||||
|
|
||||||
use middle::resolve;
|
use middle::resolve;
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
use util::common::time;
|
use util::common::time;
|
||||||
|
@ -63,7 +65,8 @@ use std::list::List;
|
||||||
use std::list;
|
use std::list;
|
||||||
use syntax::codemap::span;
|
use syntax::codemap::span;
|
||||||
use syntax::print::pprust::*;
|
use syntax::print::pprust::*;
|
||||||
use syntax::{ast, ast_map};
|
use syntax::{ast, ast_map, abi};
|
||||||
|
use syntax::opt_vec;
|
||||||
|
|
||||||
#[path = "check/mod.rs"]
|
#[path = "check/mod.rs"]
|
||||||
pub mod check;
|
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;
|
let tcx = ccx.tcx;
|
||||||
if !*tcx.sess.building_library {
|
if !*tcx.sess.building_library {
|
||||||
match *tcx.sess.main_fn {
|
match *tcx.sess.entry_fn {
|
||||||
Some((id, sp)) => check_main_fn_ty(ccx, id, sp),
|
Some((id, sp)) => match *tcx.sess.entry_type {
|
||||||
None => tcx.sess.err(~"main function not found")
|
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", ||
|
time(time_passes, ~"type checking", ||
|
||||||
check::check_item_types(ccx, crate));
|
check::check_item_types(ccx, crate));
|
||||||
|
|
||||||
check_for_main_fn(ccx);
|
check_for_entry_fn(ccx);
|
||||||
tcx.sess.abort_if_errors();
|
tcx.sess.abort_if_errors();
|
||||||
(ccx.method_map, ccx.vtable_map)
|
(ccx.method_map, ccx.vtable_map)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// 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
|
// Since we're not compiling a test runner this function should be elided
|
||||||
// and the build will fail because main doesn't exist
|
// and the build will fail because main doesn't exist
|
||||||
|
|
|
@ -8,5 +8,5 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
// error-pattern:main function not found
|
// error-pattern:entry function not found
|
||||||
fn mian() { }
|
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…
Add table
Add a link
Reference in a new issue