From f26ca025dee246500552076cc650137e12c04464 Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Wed, 13 Jul 2011 17:26:06 -0700 Subject: [PATCH] Make resolve and the typechecker check for a main fn of the correct type This means if a non-library program leaves out the main program, the error gets caught earlier than link. Closes #626. --- src/comp/driver/rustc.rs | 2 +- src/comp/driver/session.rs | 7 +++ src/comp/middle/resolve.rs | 14 +++++- src/comp/middle/trans.rs | 7 +-- src/comp/middle/typeck.rs | 52 +++++++++++++++++++++- src/comp/util/common.rs | 5 ++- src/test/compile-fail/main-wrong-type-2.rs | 4 ++ src/test/compile-fail/main-wrong-type.rs | 4 ++ src/test/compile-fail/missing-main.rs | 4 ++ 9 files changed, 89 insertions(+), 10 deletions(-) create mode 100644 src/test/compile-fail/main-wrong-type-2.rs create mode 100644 src/test/compile-fail/main-wrong-type.rs create mode 100644 src/test/compile-fail/missing-main.rs diff --git a/src/comp/driver/rustc.rs b/src/comp/driver/rustc.rs index a8c9738eb4a1..a6409209b75a 100644 --- a/src/comp/driver/rustc.rs +++ b/src/comp/driver/rustc.rs @@ -361,7 +361,7 @@ fn build_session(@session::options sopts) -> session::session { auto cstore = cstore::mk_cstore(); ret session::session(target_cfg, sopts, cstore, @rec(cm=codemap::new_codemap(), mutable next_id=0), - 0u); + none, 0u); } fn parse_pretty(session::session sess, &str name) -> pp_mode { diff --git a/src/comp/driver/session.rs b/src/comp/driver/session.rs index 8da3e40baa6c..4adcb7cee5f4 100644 --- a/src/comp/driver/session.rs +++ b/src/comp/driver/session.rs @@ -1,5 +1,6 @@ import syntax::ast; +import syntax::ast::node_id; import syntax::codemap; import codemap::span; import syntax::ast::ty_mach; @@ -49,6 +50,8 @@ obj session(@config targ_cfg, @options opts, metadata::cstore::cstore cstore, parse_sess parse_sess, + // For a library crate, this is always none + mutable option::t[node_id] main_fn, mutable uint err_count) { fn get_targ_cfg() -> @config { ret targ_cfg; } fn get_opts() -> @options { ret opts; } @@ -110,6 +113,10 @@ obj session(@config targ_cfg, fn span_str(span sp) -> str { ret codemap::span_to_str(sp, self.get_codemap()); } + fn set_main_id(node_id d) { + main_fn = some(d); + } + fn get_main_id() -> option::t[node_id] { main_fn } } // Local Variables: // fill-column: 78; diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs index d312372ea55c..d32e4b7f4e75 100644 --- a/src/comp/middle/resolve.rs +++ b/src/comp/middle/resolve.rs @@ -10,7 +10,7 @@ import ast::local_def; import metadata::csearch; import metadata::cstore; import driver::session::session; -import util::common::new_def_hash; +import util::common::*; import std::map::new_int_hash; import std::map::new_str_hash; import syntax::codemap::span; @@ -337,6 +337,18 @@ fn visit_native_item_with_scope(&@ast::native_item ni, &scopes sc, fn visit_fn_with_scope(&@env e, &ast::_fn f, &ast::ty_param[] tp, &span sp, &fn_ident name, node_id id, &scopes sc, &vt[scopes] v) { + // is this a main fn declaration? + alt (name) { + case (some(?nm)) { + if (is_main_name(~[nm]) && !e.sess.get_opts().library) { + // This is a main function -- set it in the session + // as the main ID + e.sess.set_main_id(id); + } + } + case (_) {} + } + // here's where we need to set up the mapping // for f's constrs in the table. diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index f5894ece83e7..b80fccab0767 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -33,10 +33,9 @@ import back::upcall; import syntax::visit; import visit::vt; import util::common; -import util::common::new_def_hash; +import util::common::*; import std::map::new_int_hash; import std::map::new_str_hash; -import util::common::local_rhs_span; import syntax::codemap::span; import lib::llvm::llvm; import lib::llvm::builder; @@ -8586,9 +8585,7 @@ fn decl_fn_and_pair_full(&@crate_ctxt ccx, &span sp, &str[] path, str flav, ccx.sess.bug("decl_fn_and_pair(): fn item doesn't have fn type!"); } } - let bool is_main = - str::eq(option::get(std::ivec::last(path)), "main") && - !ccx.sess.get_opts().library; + let bool is_main = is_main_name(path) && !ccx.sess.get_opts().library; // Declare the function itself. let str s = diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs index 29acb7d9e53c..8d28e1e53a4d 100644 --- a/src/comp/middle/typeck.rs +++ b/src/comp/middle/typeck.rs @@ -1624,8 +1624,7 @@ fn check_expr(&@fn_ctxt fcx, &@ast::expr expr) { alt (operator.node) { case (ast::expr_path(?oper_name)) { alt (fcx.ccx.tcx.def_map.find(operator.id)) { - case (some(ast::def_fn(?_d_id, - ast::pure_fn))) { + case (some(ast::def_fn(_, ast::pure_fn))) { // do nothing } case (_) { @@ -2655,6 +2654,54 @@ fn check_item(@crate_ctxt ccx, &@ast::item it) { } } +fn arg_is_argv_ty(&ty::ctxt tcx, &ty::arg a) -> bool { + alt (ty::struct(tcx, a.ty)) { + case (ty::ty_vec(?mt)) { + if (mt.mut != ast::imm) { ret false; } + alt (ty::struct(tcx, mt.ty)) { + case (ty::ty_str) { ret true; } + case (_) { ret false; } + } + } + case (_) { ret false; } + } +} + +fn check_main_fn_ty(&ty::ctxt tcx, &ast::node_id main_id) { + auto main_t = ty::node_id_to_monotype(tcx, main_id); + alt (ty::struct(tcx, main_t)) { + case (ty::ty_fn(ast::proto_fn, ?args, ?rs, ast::return, ?constrs)) { + auto ok = ivec::len(constrs) == 0u; + ok &= ty::type_is_nil(tcx, rs); + auto num_args = ivec::len(args); + ok &= num_args == 0u || (num_args == 1u && + arg_is_argv_ty(tcx, args.(0))); + if (!ok) { + tcx.sess.err("Wrong type in main function: found " + + ty_to_str(tcx, main_t)); + } + } + case (_) { + tcx.sess.err("Main has a non-function type: found" + + ty_to_str(tcx, main_t)); + } + } +} + +fn check_for_main_fn(&ty::ctxt tcx, &@ast::crate crate) { + if (!tcx.sess.get_opts().library) { + alt (tcx.sess.get_main_id()) { + case (some(?id)) { + check_main_fn_ty(tcx, id); + } + case (none) { + tcx.sess.span_err(crate.span, + "Main function not found"); + } + } + } +} + fn check_crate(&ty::ctxt tcx, &@ast::crate crate) { collect::collect_item_types(tcx, crate); @@ -2666,6 +2713,7 @@ fn check_crate(&ty::ctxt tcx, &@ast::crate crate) { rec(visit_item_pre=bind check_item(ccx, _) with walk::default_visitor()); walk::walk_crate(visit, *crate); + check_for_main_fn(tcx, crate); tcx.sess.abort_if_errors(); } // diff --git a/src/comp/util/common.rs b/src/comp/util/common.rs index d55c8bba3d5a..3c8b6b7ca098 100644 --- a/src/comp/util/common.rs +++ b/src/comp/util/common.rs @@ -1,4 +1,4 @@ - +import std::str; import std::map; import std::map::hashmap; import std::uint; @@ -187,6 +187,9 @@ fn call_kind_str(call_kind c) -> str { } } +fn is_main_name(&str[] path) -> bool { + str::eq(option::get(std::ivec::last(path)), "main") +} // // Local Variables: // mode: rust diff --git a/src/test/compile-fail/main-wrong-type-2.rs b/src/test/compile-fail/main-wrong-type-2.rs new file mode 100644 index 000000000000..50bf187284f7 --- /dev/null +++ b/src/test/compile-fail/main-wrong-type-2.rs @@ -0,0 +1,4 @@ +// xfail-stage0 +// error-pattern:Wrong type in main function: found fn() -> char +fn main() -> char { +} diff --git a/src/test/compile-fail/main-wrong-type.rs b/src/test/compile-fail/main-wrong-type.rs new file mode 100644 index 000000000000..4998867e6b21 --- /dev/null +++ b/src/test/compile-fail/main-wrong-type.rs @@ -0,0 +1,4 @@ +// xfail-stage0 +// error-pattern:Wrong type in main function: found fn(rec(int x +fn main(rec(int x, int y) foo) { +} diff --git a/src/test/compile-fail/missing-main.rs b/src/test/compile-fail/missing-main.rs new file mode 100644 index 000000000000..6eed36591bfb --- /dev/null +++ b/src/test/compile-fail/missing-main.rs @@ -0,0 +1,4 @@ +// xfail-stage0 +// error-pattern:Main function not found +fn mian() { +}