about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-04-12 02:34:02 -0700
committerbors <bors@rust-lang.org>2013-04-12 02:34:02 -0700
commit63e2724cdb42c97051f03b10b644aed6df7cfa72 (patch)
treef22010e9ad72874bc47a297a9a5160de246fbc59
parent9db15f321598b5c3f246b284ab1c1daffae9e145 (diff)
parent35c73c80887cca62f8bba2eb9b9e277a98c37f93 (diff)
downloadrust-63e2724cdb42c97051f03b10b644aed6df7cfa72.tar.gz
rust-63e2724cdb42c97051f03b10b644aed6df7cfa72.zip
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...)
-rw-r--r--src/librustc/driver/driver.rs3
-rw-r--r--src/librustc/driver/session.rs13
-rw-r--r--src/librustc/middle/resolve.rs30
-rw-r--r--src/librustc/middle/trans/base.rs87
-rw-r--r--src/librustc/middle/typeck/mod.rs71
-rw-r--r--src/test/compile-fail/elided-test.rs2
-rw-r--r--src/test/compile-fail/missing-main.rs2
-rw-r--r--src/test/run-pass/attr-start.rs16
8 files changed, 177 insertions, 47 deletions
diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs
index cd1af369570..d725b700037 100644
--- a/src/librustc/driver/driver.rs
+++ b/src/librustc/driver/driver.rs
@@ -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,
diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs
index 95740e0f837..226905bbab7 100644
--- a/src/librustc/driver/session.rs
+++ b/src/librustc/driver/session.rs
@@ -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,
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index adc348b692b..fa902b7bf56 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -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);
         }
     }
 
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index 4faff086098..7e64a219ecd 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -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());
-
-            ~[
-                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 {
diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs
index fdb8049d79b..98117278f38 100644
--- a/src/librustc/middle/typeck/mod.rs
+++ b/src/librustc/middle/typeck/mod.rs
@@ -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)
 }
diff --git a/src/test/compile-fail/elided-test.rs b/src/test/compile-fail/elided-test.rs
index b62214b12f9..eaae721e0e5 100644
--- a/src/test/compile-fail/elided-test.rs
+++ b/src/test/compile-fail/elided-test.rs
@@ -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
diff --git a/src/test/compile-fail/missing-main.rs b/src/test/compile-fail/missing-main.rs
index 4bfdaf69480..4f1b604b507 100644
--- a/src/test/compile-fail/missing-main.rs
+++ b/src/test/compile-fail/missing-main.rs
@@ -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() { }
diff --git a/src/test/run-pass/attr-start.rs b/src/test/run-pass/attr-start.rs
new file mode 100644
index 00000000000..933405bfc44
--- /dev/null
+++ b/src/test/run-pass/attr-start.rs
@@ -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;
+}