Do a bunch more typechecking for iters and for each loops.

Closes #771.
Closes #772.
Closes #796.
This commit is contained in:
Michael Sullivan 2011-08-03 10:57:33 -07:00
parent 3254fb9806
commit 8871462575
5 changed files with 70 additions and 31 deletions

View File

@ -1531,9 +1531,27 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) -> bool {
_ { fty }
};
// Grab the argument types and the return type.
let sty = structure_of(fcx, sp, fty_stripped);
// Check that we aren't confusing iter calls and fn calls
alt sty {
ty::ty_fn(ast::proto_iter., _, _, _, _) {
if call_kind != kind_for_each {
fcx.ccx.tcx.sess.span_err(
sp, "calling iter outside of for each loop");
}
}
_ {
if call_kind == kind_for_each {
fcx.ccx.tcx.sess.span_err(
sp, "calling non-iter as sequence of for each loop");
}
}
}
// Grab the argument types
let arg_tys;
alt structure_of(fcx, sp, fty_stripped) {
alt sty {
ty::ty_fn(_, arg_tys_0, _, _, _) |
ty::ty_native_fn(_, arg_tys_0, _) { arg_tys = arg_tys_0; }
_ {
@ -1590,8 +1608,8 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) -> bool {
write::ty_only_fixup(fcx, id, ty::mk_nil(fcx.ccx.tcx));
ret bot;
}
// A generic function for checking call expressions
// A generic function for checking call expressions
fn check_call(fcx: &@fn_ctxt, sp: &span, f: &@ast::expr,
args: &(@ast::expr)[], call_kind: call_kind) -> bool {
let args_opt_0: (option::t[@ast::expr])[] = ~[];
@ -1602,8 +1620,34 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) -> bool {
// Call the generic checker.
ret check_call_or_bind(fcx, sp, f, args_opt_0, call_kind);
}
// A generic function for checking for or for-each loops
// A generic function for doing all of the checking for call expressions
fn check_call_full(fcx: &@fn_ctxt, sp: &span, f: &@ast::expr,
args: &(@ast::expr)[], call_kind: call_kind,
id: ast::node_id) -> bool {
/* here we're kind of hosed, as f can be any expr
need to restrict it to being an explicit expr_path if we're
inside a pure function, and need an environment mapping from
function name onto purity-designation */
require_pure_call(fcx.ccx, fcx.purity, f, sp);
let bot = check_call(fcx, sp, f, args, call_kind);
// Pull the return type out of the type of the function.
let rt_1;
let fty = do_autoderef(fcx, sp, ty::expr_ty(fcx.ccx.tcx, f));
alt structure_of(fcx, sp, fty) {
ty::ty_fn(_, _, rt, cf, _) {
bot |= cf == ast::noreturn;
rt_1 = rt;
}
ty::ty_native_fn(_, _, rt) { rt_1 = rt; }
_ { fail "LHS of call expr didn't have a function type?!"; }
}
write::ty_only_fixup(fcx, id, rt_1);
ret bot;
}
// A generic function for checking for or for-each loops
fn check_for_or_for_each(fcx: &@fn_ctxt, local: &@ast::local,
element_ty: ty::t, body: &ast::blk,
node_id: ast::node_id) -> bool {
@ -1952,9 +1996,16 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) -> bool {
bot |= check_for_or_for_each(fcx, decl, elt_ty, body, id);
}
ast::expr_for_each(decl, seq, body) {
bot = check_expr(fcx, seq) |
check_for_or_for_each(fcx, decl, expr_ty(tcx, seq),
body, id);
alt (seq.node) {
ast::expr_call(f, args) {
bot = check_call_full(fcx, seq.span, f, args,
kind_for_each, seq.id);
}
_ { tcx.sess.span_fatal(
expr.span, "sequence in for each loop not a call"); }
}
bot |= check_for_or_for_each(fcx, decl, expr_ty(tcx, seq),
body, id);
}
ast::expr_while(cond, body) {
bot = check_expr(fcx, cond);
@ -1973,7 +2024,7 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) -> bool {
ast::expr_alt(expr, arms) {
bot = check_expr(fcx, expr);
// Typecheck the patterns first, so that we get types for all the
// bindings.
// bindings.
let pattern_ty = ty::expr_ty(tcx, expr);
for arm: ast::arm in arms {
@ -2060,28 +2111,7 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) -> bool {
write::ty_only_fixup(fcx, id, t_1);
}
ast::expr_call(f, args) {
/* here we're kind of hosed, as f can be any expr
need to restrict it to being an explicit expr_path if we're
inside a pure function, and need an environment mapping from
function name onto purity-designation */
require_pure_call(fcx.ccx, fcx.purity, f, expr.span);
bot = check_call(fcx, expr.span, f, args, kind_call);
// Pull the return type out of the type of the function.
let rt_1;
let fty = do_autoderef(fcx, expr.span, ty::expr_ty(tcx, f));
alt structure_of(fcx, expr.span, fty) {
ty::ty_fn(_, _, rt, cf, _) {
bot |= cf == ast::noreturn;
rt_1 = rt;
}
ty::ty_native_fn(_, _, rt) { rt_1 = rt; }
_ {
fail "LHS of call expr didn't have a function type?!";
}
}
write::ty_only_fixup(fcx, id, rt_1);
bot = check_call_full(fcx, expr.span, f, args, kind_call, expr.id);
}
ast::expr_self_method(ident) {
let t = ty::mk_nil(tcx);

View File

@ -150,13 +150,14 @@ fn lit_eq(l: &@ast::lit, m: &@ast::lit) -> bool {
}
}
tag call_kind { kind_call; kind_spawn; kind_bind; }
tag call_kind { kind_call; kind_spawn; kind_bind; kind_for_each; }
fn call_kind_str(c: call_kind) -> str {
alt c {
kind_call. { "Call" }
kind_spawn. { "Spawn" }
kind_bind. { "Bind" }
kind_for_each. { "For-Each" }
}
}

View File

@ -0,0 +1,3 @@
// error-pattern:calling non-iter as sequence of for each loop
fn f() -> int { ret 4; }
fn main() { for each i in f() { } }

View File

@ -0,0 +1,2 @@
// error-pattern:sequence in for each loop not a call
fn main() { for each p in 1 {} }

View File

@ -0,0 +1,3 @@
// error-pattern:calling iter outside of for each loop
iter i() { }
fn main() { i(); }