Track failures in typeck, assign proper type to failing blocks

(and warn for unreachable statements)

Closes #727
This commit is contained in:
Marijn Haverbeke 2011-07-29 18:26:50 +02:00
parent 9705f97ead
commit aa3b89610e
2 changed files with 127 additions and 91 deletions

View file

@ -186,7 +186,6 @@ fn instantiate_path(fcx: &@fn_ctxt, pth: &ast::path,
fcx.ccx.tcx.sess.span_fatal(sp, fcx.ccx.tcx.sess.span_fatal(sp,
"this item does not take type \ "this item does not take type \
parameters"); parameters");
fail;
} }
} else { } else {
// We will acquire the type parameters through unification. // We will acquire the type parameters through unification.
@ -1516,7 +1515,7 @@ fn require_pure_call(ccx: @crate_ctxt, caller_purity: &ast::purity,
} }
} }
fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) { fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) -> bool {
// fcx.ccx.tcx.sess.span_warn(expr.span, "typechecking expr " + // fcx.ccx.tcx.sess.span_warn(expr.span, "typechecking expr " +
// syntax::print::pprust::expr_to_str(expr)); // syntax::print::pprust::expr_to_str(expr));
@ -1524,9 +1523,9 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
// expressions. // expressions.
fn check_call_or_bind(fcx: &@fn_ctxt, sp: &span, f: &@ast::expr, fn check_call_or_bind(fcx: &@fn_ctxt, sp: &span, f: &@ast::expr,
args: &(option::t[@ast::expr])[], args: &(option::t[@ast::expr])[],
call_kind: call_kind) { call_kind: call_kind) -> bool {
// Check the function. // Check the function.
check_expr(fcx, f); let bot = check_expr(fcx, f);
// Get the function type. // Get the function type.
let fty = expr_ty(fcx.ccx.tcx, f); let fty = expr_ty(fcx.ccx.tcx, f);
@ -1541,7 +1540,7 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
// Grab the argument types and the return type. // Grab the argument types and the return type.
let arg_tys; let arg_tys;
alt structure_of(fcx, sp, fty_stripped) { alt structure_of(fcx, sp, fty_stripped) {
ty::ty_fn(_, arg_tys_0, _, _, _) { arg_tys = arg_tys_0; } ty::ty_fn(_, arg_tys_0, _, _, _) |
ty::ty_native_fn(_, arg_tys_0, _) { arg_tys = arg_tys_0; } ty::ty_native_fn(_, arg_tys_0, _) { arg_tys = arg_tys_0; }
_ { _ {
fcx.ccx.tcx.sess.span_fatal(f.span, fcx.ccx.tcx.sess.span_fatal(f.span,
@ -1576,7 +1575,7 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
for a_opt: option::t[@ast::expr] in args { for a_opt: option::t[@ast::expr] in args {
alt a_opt { alt a_opt {
some(a) { some(a) {
check_expr(fcx, a); bot |= check_expr(fcx, a);
demand::full(fcx, a.span, arg_tys.(i).ty, demand::full(fcx, a.span, arg_tys.(i).ty,
expr_ty(fcx.ccx.tcx, a), ~[], expr_ty(fcx.ccx.tcx, a), ~[],
AUTODEREF_BLOCK_COERCE); AUTODEREF_BLOCK_COERCE);
@ -1585,47 +1584,49 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
} }
i += 1u; i += 1u;
} }
ret bot;
} }
// A generic function for checking assignment expressions // A generic function for checking assignment expressions
fn check_assignment(fcx: &@fn_ctxt, sp: &span, lhs: &@ast::expr, fn check_assignment(fcx: &@fn_ctxt, sp: &span, lhs: &@ast::expr,
rhs: &@ast::expr, id: &ast::node_id) { rhs: &@ast::expr, id: &ast::node_id) -> bool {
check_expr(fcx, lhs); let bot = check_expr(fcx, lhs) | check_expr(fcx, rhs);
check_expr(fcx, rhs);
demand::simple(fcx, sp, expr_ty(fcx.ccx.tcx, lhs), demand::simple(fcx, sp, expr_ty(fcx.ccx.tcx, lhs),
expr_ty(fcx.ccx.tcx, rhs)); expr_ty(fcx.ccx.tcx, rhs));
write::ty_only_fixup(fcx, id, ty::mk_nil(fcx.ccx.tcx)); 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, fn check_call(fcx: &@fn_ctxt, sp: &span, f: &@ast::expr,
args: &(@ast::expr)[], call_kind: call_kind) { args: &(@ast::expr)[], call_kind: call_kind) -> bool {
let args_opt_0: (option::t[@ast::expr])[] = ~[]; let args_opt_0: (option::t[@ast::expr])[] = ~[];
for arg: @ast::expr in args { for arg: @ast::expr in args {
args_opt_0 += ~[some[@ast::expr](arg)]; args_opt_0 += ~[some[@ast::expr](arg)];
} }
// Call the generic checker. // Call the generic checker.
check_call_or_bind(fcx, sp, f, args_opt_0, call_kind); 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 checking for or for-each loops
fn check_for_or_for_each(fcx: &@fn_ctxt, local: &@ast::local, fn check_for_or_for_each(fcx: &@fn_ctxt, local: &@ast::local,
element_ty: &ty::t, body: &ast::blk, element_ty: &ty::t, body: &ast::blk,
node_id: ast::node_id) { node_id: ast::node_id) -> bool {
check_decl_local(fcx, local); let bot = check_decl_local(fcx, local);
check_block(fcx, body); check_block(fcx, body);
// Unify type of decl with element type of the seq // Unify type of decl with element type of the seq
demand::simple(fcx, local.span, ty::decl_local_ty(fcx.ccx.tcx, local), demand::simple(fcx, local.span, ty::decl_local_ty(fcx.ccx.tcx, local),
element_ty); element_ty);
let typ = ty::mk_nil(fcx.ccx.tcx); let typ = ty::mk_nil(fcx.ccx.tcx);
write::ty_only_fixup(fcx, node_id, typ); write::ty_only_fixup(fcx, node_id, typ);
ret bot;
} }
// A generic function for checking the pred in a check // A generic function for checking the pred in a check
// or if-check // or if-check
fn check_pred_expr(fcx: &@fn_ctxt, e: &@ast::expr) { fn check_pred_expr(fcx: &@fn_ctxt, e: &@ast::expr) -> bool {
check_expr(fcx, e); let bot = check_expr(fcx, e);
demand::simple(fcx, e.span, ty::mk_bool(fcx.ccx.tcx), demand::simple(fcx, e.span, ty::mk_bool(fcx.ccx.tcx),
expr_ty(fcx.ccx.tcx, e)); expr_ty(fcx.ccx.tcx, e));
@ -1665,18 +1666,20 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
} }
_ { fcx.ccx.tcx.sess.span_fatal(e.span, "check on non-predicate"); } _ { fcx.ccx.tcx.sess.span_fatal(e.span, "check on non-predicate"); }
} }
ret bot;
} }
// A generic function for checking the then and else in an if // A generic function for checking the then and else in an if
// or if-check // or if-check
fn check_then_else(fcx: &@fn_ctxt, thn: &ast::blk, fn check_then_else(fcx: &@fn_ctxt, thn: &ast::blk,
elsopt: &option::t[@ast::expr], id: ast::node_id, elsopt: &option::t[@ast::expr], id: ast::node_id,
sp: &span) { sp: &span) -> bool {
check_block(fcx, thn); let then_bot = check_block(fcx, thn);
let els_bot = false;
let if_t = let if_t =
alt elsopt { alt elsopt {
some(els) { some(els) {
check_expr(fcx, els); els_bot = check_expr(fcx, els);
let thn_t = block_ty(fcx.ccx.tcx, thn); let thn_t = block_ty(fcx.ccx.tcx, thn);
let elsopt_t = expr_ty(fcx.ccx.tcx, els); let elsopt_t = expr_ty(fcx.ccx.tcx, els);
demand::simple(fcx, sp, thn_t, elsopt_t); demand::simple(fcx, sp, thn_t, elsopt_t);
@ -1687,6 +1690,7 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
none. { ty::mk_nil(fcx.ccx.tcx) } none. { ty::mk_nil(fcx.ccx.tcx) }
}; };
write::ty_only_fixup(fcx, id, if_t); write::ty_only_fixup(fcx, id, if_t);
ret then_bot & els_bot;
} }
// Checks the compatibility // Checks the compatibility
@ -1704,14 +1708,19 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
} }
let id = expr.id; let id = expr.id;
let bot = false;
alt expr.node { alt expr.node {
ast::expr_lit(lit) { ast::expr_lit(lit) {
let typ = check_lit(fcx.ccx, lit); let typ = check_lit(fcx.ccx, lit);
write::ty_only_fixup(fcx, id, typ); write::ty_only_fixup(fcx, id, typ);
} }
ast::expr_binary(binop, lhs, rhs) { ast::expr_binary(binop, lhs, rhs) {
check_expr(fcx, lhs); bot = check_expr(fcx, lhs);
check_expr(fcx, rhs); if ast::lazy_binop(binop) {
check_expr(fcx, rhs);
} else {
bot |= check_expr(fcx, rhs);
}
let lhs_t = expr_ty(fcx.ccx.tcx, lhs); let lhs_t = expr_ty(fcx.ccx.tcx, lhs);
let rhs_t = expr_ty(fcx.ccx.tcx, rhs); let rhs_t = expr_ty(fcx.ccx.tcx, rhs);
@ -1733,7 +1742,7 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
write::ty_only_fixup(fcx, id, t); write::ty_only_fixup(fcx, id, t);
} }
ast::expr_unary(unop, oper) { ast::expr_unary(unop, oper) {
check_expr(fcx, oper); bot = check_expr(fcx, oper);
let oper_t = expr_ty(fcx.ccx.tcx, oper); let oper_t = expr_ty(fcx.ccx.tcx, oper);
alt unop { alt unop {
ast::box(mut) { ast::box(mut) {
@ -1787,7 +1796,7 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
if ty::def_has_ty_params(defn) { if ty::def_has_ty_params(defn) {
let path_tpot = instantiate_path(fcx, pth, tpt, expr.span); let path_tpot = instantiate_path(fcx, pth, tpt, expr.span);
write::ty_fixup(fcx, id, path_tpot); write::ty_fixup(fcx, id, path_tpot);
ret; ret false;
} }
// The definition doesn't take type parameters. If the programmer // The definition doesn't take type parameters. If the programmer
// supplied some, that's an error. // supplied some, that's an error.
@ -1801,6 +1810,7 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
} }
ast::expr_mac(_) { fcx.ccx.tcx.sess.bug("unexpanded macro"); } ast::expr_mac(_) { fcx.ccx.tcx.sess.bug("unexpanded macro"); }
ast::expr_fail(expr_opt) { ast::expr_fail(expr_opt) {
bot = true;
alt expr_opt { alt expr_opt {
none. {/* do nothing */ } none. {/* do nothing */ }
some(e) { some(e) {
@ -1812,9 +1822,10 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
} }
write::bot_ty(fcx.ccx.tcx, id); write::bot_ty(fcx.ccx.tcx, id);
} }
ast::expr_break. { write::bot_ty(fcx.ccx.tcx, id); } ast::expr_break. { write::bot_ty(fcx.ccx.tcx, id); bot = true; }
ast::expr_cont. { write::bot_ty(fcx.ccx.tcx, id); } ast::expr_cont. { write::bot_ty(fcx.ccx.tcx, id); bot = true; }
ast::expr_ret(expr_opt) { ast::expr_ret(expr_opt) {
bot = true;
alt expr_opt { alt expr_opt {
none. { none. {
let nil = ty::mk_nil(fcx.ccx.tcx); let nil = ty::mk_nil(fcx.ccx.tcx);
@ -1846,7 +1857,7 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
write::nil_ty(fcx.ccx.tcx, id); write::nil_ty(fcx.ccx.tcx, id);
} }
some(e) { some(e) {
check_expr(fcx, e); bot = check_expr(fcx, e);
demand::simple(fcx, expr.span, fcx.ret_ty, demand::simple(fcx, expr.span, fcx.ret_ty,
expr_ty(fcx.ccx.tcx, e)); expr_ty(fcx.ccx.tcx, e));
write::nil_ty(fcx.ccx.tcx, id); write::nil_ty(fcx.ccx.tcx, id);
@ -1855,55 +1866,54 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
} }
ast::expr_be(e) { ast::expr_be(e) {
// FIXME: prove instead of assert // FIXME: prove instead of assert
assert (ast::is_call_expr(e)); assert (ast::is_call_expr(e));
check_expr(fcx, e); check_expr(fcx, e);
bot = true;
demand::simple(fcx, e.span, fcx.ret_ty, expr_ty(fcx.ccx.tcx, e)); demand::simple(fcx, e.span, fcx.ret_ty, expr_ty(fcx.ccx.tcx, e));
write::nil_ty(fcx.ccx.tcx, id); write::nil_ty(fcx.ccx.tcx, id);
} }
ast::expr_log(l, e) { ast::expr_log(l, e) {
check_expr(fcx, e); bot = check_expr(fcx, e);
write::nil_ty(fcx.ccx.tcx, id); write::nil_ty(fcx.ccx.tcx, id);
} }
ast::expr_check(_, e) { ast::expr_check(_, e) {
check_pred_expr(fcx, e); bot = check_pred_expr(fcx, e);
write::nil_ty(fcx.ccx.tcx, id); write::nil_ty(fcx.ccx.tcx, id);
} }
ast::expr_if_check(cond, thn, elsopt) { ast::expr_if_check(cond, thn, elsopt) {
check_pred_expr(fcx, cond); bot = check_pred_expr(fcx, cond) |
check_then_else(fcx, thn, elsopt, id, expr.span); check_then_else(fcx, thn, elsopt, id, expr.span);
} }
ast::expr_ternary(_, _, _) { ast::expr_ternary(_, _, _) {
check_expr(fcx, ast::ternary_to_if(expr)); bot = check_expr(fcx, ast::ternary_to_if(expr));
} }
ast::expr_assert(e) { ast::expr_assert(e) {
check_expr(fcx, e); bot = check_expr(fcx, e);
let ety = expr_ty(fcx.ccx.tcx, e); let ety = expr_ty(fcx.ccx.tcx, e);
demand::simple(fcx, expr.span, ty::mk_bool(fcx.ccx.tcx), ety); demand::simple(fcx, expr.span, ty::mk_bool(fcx.ccx.tcx), ety);
write::nil_ty(fcx.ccx.tcx, id); write::nil_ty(fcx.ccx.tcx, id);
} }
ast::expr_move(lhs, rhs) { ast::expr_move(lhs, rhs) {
require_impure(fcx.ccx.tcx.sess, fcx.purity, expr.span); require_impure(fcx.ccx.tcx.sess, fcx.purity, expr.span);
check_assignment(fcx, expr.span, lhs, rhs, id); bot = check_assignment(fcx, expr.span, lhs, rhs, id);
} }
ast::expr_assign(lhs, rhs) { ast::expr_assign(lhs, rhs) {
require_impure(fcx.ccx.tcx.sess, fcx.purity, expr.span); require_impure(fcx.ccx.tcx.sess, fcx.purity, expr.span);
check_assignment(fcx, expr.span, lhs, rhs, id); bot = check_assignment(fcx, expr.span, lhs, rhs, id);
} }
ast::expr_swap(lhs, rhs) { ast::expr_swap(lhs, rhs) {
require_impure(fcx.ccx.tcx.sess, fcx.purity, expr.span); require_impure(fcx.ccx.tcx.sess, fcx.purity, expr.span);
check_assignment(fcx, expr.span, lhs, rhs, id); bot = check_assignment(fcx, expr.span, lhs, rhs, id);
} }
ast::expr_assign_op(op, lhs, rhs) { ast::expr_assign_op(op, lhs, rhs) {
require_impure(fcx.ccx.tcx.sess, fcx.purity, expr.span); require_impure(fcx.ccx.tcx.sess, fcx.purity, expr.span);
check_assignment(fcx, expr.span, lhs, rhs, id); bot = check_assignment(fcx, expr.span, lhs, rhs, id);
check_binop_type_compat(fcx, expr.span, expr_ty(fcx.ccx.tcx, lhs), check_binop_type_compat(fcx, expr.span, expr_ty(fcx.ccx.tcx, lhs),
op); op);
} }
ast::expr_send(lhs, rhs) { ast::expr_send(lhs, rhs) {
require_impure(fcx.ccx.tcx.sess, fcx.purity, expr.span); require_impure(fcx.ccx.tcx.sess, fcx.purity, expr.span);
check_expr(fcx, lhs); bot = check_expr(fcx, lhs) | check_expr(fcx, rhs);
check_expr(fcx, rhs);
let rhs_t = expr_ty(fcx.ccx.tcx, rhs); let rhs_t = expr_ty(fcx.ccx.tcx, rhs);
let chan_t = ty::mk_chan(fcx.ccx.tcx, rhs_t); let chan_t = ty::mk_chan(fcx.ccx.tcx, rhs_t);
let lhs_t = expr_ty(fcx.ccx.tcx, lhs); let lhs_t = expr_ty(fcx.ccx.tcx, lhs);
@ -1922,21 +1932,20 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
} }
ast::expr_recv(lhs, rhs) { ast::expr_recv(lhs, rhs) {
require_impure(fcx.ccx.tcx.sess, fcx.purity, expr.span); require_impure(fcx.ccx.tcx.sess, fcx.purity, expr.span);
check_expr(fcx, lhs); bot = check_expr(fcx, lhs) | check_expr(fcx, rhs);
check_expr(fcx, rhs);
let item_t = expr_ty(fcx.ccx.tcx, rhs); let item_t = expr_ty(fcx.ccx.tcx, rhs);
let port_t = ty::mk_port(fcx.ccx.tcx, item_t); let port_t = ty::mk_port(fcx.ccx.tcx, item_t);
demand::simple(fcx, expr.span, port_t, expr_ty(fcx.ccx.tcx, lhs)); demand::simple(fcx, expr.span, port_t, expr_ty(fcx.ccx.tcx, lhs));
write::ty_only_fixup(fcx, id, item_t); write::ty_only_fixup(fcx, id, item_t);
} }
ast::expr_if(cond, thn, elsopt) { ast::expr_if(cond, thn, elsopt) {
check_expr(fcx, cond); bot = check_expr(fcx, cond) |
check_then_else(fcx, thn, elsopt, id, expr.span);
demand::simple(fcx, cond.span, ty::mk_bool(fcx.ccx.tcx), demand::simple(fcx, cond.span, ty::mk_bool(fcx.ccx.tcx),
expr_ty(fcx.ccx.tcx, cond)); expr_ty(fcx.ccx.tcx, cond));
check_then_else(fcx, thn, elsopt, id, expr.span);
} }
ast::expr_for(decl, seq, body) { ast::expr_for(decl, seq, body) {
check_expr(fcx, seq); bot = check_expr(fcx, seq);
let elt_ty; let elt_ty;
let ety = expr_ty(fcx.ccx.tcx, seq); let ety = expr_ty(fcx.ccx.tcx, seq);
alt structure_of(fcx, expr.span, ety) { alt structure_of(fcx, expr.span, ety) {
@ -1950,14 +1959,15 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
+ "found " + ty_to_str(fcx.ccx.tcx, ety)); + "found " + ty_to_str(fcx.ccx.tcx, ety));
} }
} }
check_for_or_for_each(fcx, decl, elt_ty, body, id); bot |= check_for_or_for_each(fcx, decl, elt_ty, body, id);
} }
ast::expr_for_each(decl, seq, body) { ast::expr_for_each(decl, seq, body) {
check_expr(fcx, seq); bot = check_expr(fcx, seq) |
check_for_or_for_each(fcx, decl, expr_ty(fcx.ccx.tcx, seq), body, id); check_for_or_for_each(fcx, decl, expr_ty(fcx.ccx.tcx, seq),
body, id);
} }
ast::expr_while(cond, body) { ast::expr_while(cond, body) {
check_expr(fcx, cond); bot = check_expr(fcx, cond);
check_block(fcx, body); check_block(fcx, body);
demand::simple(fcx, cond.span, ty::mk_bool(fcx.ccx.tcx), demand::simple(fcx, cond.span, ty::mk_bool(fcx.ccx.tcx),
expr_ty(fcx.ccx.tcx, cond)); expr_ty(fcx.ccx.tcx, cond));
@ -1965,13 +1975,13 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
write::ty_only_fixup(fcx, id, typ); write::ty_only_fixup(fcx, id, typ);
} }
ast::expr_do_while(body, cond) { ast::expr_do_while(body, cond) {
check_expr(fcx, cond); bot = check_expr(fcx, cond);
check_block(fcx, body); check_block(fcx, body);
let typ = block_ty(fcx.ccx.tcx, body); let typ = block_ty(fcx.ccx.tcx, body);
write::ty_only_fixup(fcx, id, typ); write::ty_only_fixup(fcx, id, typ);
} }
ast::expr_alt(expr, arms) { ast::expr_alt(expr, arms) {
check_expr(fcx, expr); bot = check_expr(fcx, expr);
// Typecheck the patterns first, so that we get types for all the // Typecheck the patterns first, so that we get types for all the
// bindings. // bindings.
@ -1983,19 +1993,19 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
} }
} }
// Now typecheck the blocks. // Now typecheck the blocks.
let result_ty = next_ty_var(fcx); let result_ty = next_ty_var(fcx);
let arm_non_bot = false;
for arm: ast::arm in arms { for arm: ast::arm in arms {
check_block(fcx, arm.block); if !check_block(fcx, arm.block) { arm_non_bot = true; }
let bty = block_ty(fcx.ccx.tcx, arm.block); let bty = block_ty(fcx.ccx.tcx, arm.block);
// Failing alt arms don't need to have a matching type // Failing alt arms don't need to have a matching type
if !ty::type_is_bot(fcx.ccx.tcx, bty) { if !ty::type_is_bot(fcx.ccx.tcx, bty) {
result_ty = result_ty =
demand::simple(fcx, arm.block.span, result_ty, bty); demand::simple(fcx, arm.block.span, result_ty, bty);
} }
} }
bot |= !arm_non_bot;
write::ty_only_fixup(fcx, id, result_ty); write::ty_only_fixup(fcx, id, result_ty);
} }
ast::expr_fn(f) { ast::expr_fn(f) {
@ -2010,7 +2020,7 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
check_fn(fcx.ccx, f, id, some(fcx)); check_fn(fcx.ccx, f, id, some(fcx));
} }
ast::expr_block(b) { ast::expr_block(b) {
check_block(fcx, b); bot = check_block(fcx, b);
alt b.node.expr { alt b.node.expr {
some(expr) { some(expr) {
let typ = expr_ty(fcx.ccx.tcx, expr); let typ = expr_ty(fcx.ccx.tcx, expr);
@ -2024,7 +2034,7 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
} }
ast::expr_bind(f, args) { ast::expr_bind(f, args) {
// Call the generic checker. // Call the generic checker.
check_call_or_bind(fcx, expr.span, f, args, kind_bind); bot = check_call_or_bind(fcx, expr.span, f, args, kind_bind);
// Pull the argument and return types out. // Pull the argument and return types out.
let proto_1; let proto_1;
@ -2067,13 +2077,16 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
function name onto purity-designation */ function name onto purity-designation */
require_pure_call(fcx.ccx, fcx.purity, f, expr.span); require_pure_call(fcx.ccx, fcx.purity, f, expr.span);
check_call(fcx, expr.span, f, args, kind_call); bot = check_call(fcx, expr.span, f, args, kind_call);
// Pull the return type out of the type of the function. // Pull the return type out of the type of the function.
let rt_1; let rt_1;
let fty = do_autoderef(fcx, expr.span, ty::expr_ty(fcx.ccx.tcx, f)); let fty = do_autoderef(fcx, expr.span, ty::expr_ty(fcx.ccx.tcx, f));
alt structure_of(fcx, expr.span, fty) { alt structure_of(fcx, expr.span, fty) {
ty::ty_fn(_, _, rt, _, _) { rt_1 = rt; } ty::ty_fn(_, _, rt, cf, _) {
bot |= cf == ast::noreturn;
rt_1 = rt;
}
ty::ty_native_fn(_, _, rt) { rt_1 = rt; } ty::ty_native_fn(_, _, rt) { rt_1 = rt; }
_ { _ {
log_err "LHS of call expr didn't have a function type?!"; log_err "LHS of call expr didn't have a function type?!";
@ -2092,7 +2105,6 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
alt oinfo { alt oinfo {
regular_obj(_, obj_id) { regular_obj(_, obj_id) {
let did = local_def(obj_id); let did = local_def(obj_id);
// Try looking up the current object in the type // Try looking up the current object in the type
// cache. // cache.
alt fcx.ccx.tcx.tcache.find(did) { alt fcx.ccx.tcx.tcache.find(did) {
@ -2139,7 +2151,7 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
require_impure(fcx.ccx.tcx.sess, fcx.purity, expr.span); require_impure(fcx.ccx.tcx.sess, fcx.purity, expr.span);
} }
ast::expr_spawn(_, _, f, args) { ast::expr_spawn(_, _, f, args) {
check_call(fcx, expr.span, f, args, kind_spawn); bot = check_call(fcx, expr.span, f, args, kind_spawn);
let fty = expr_ty(fcx.ccx.tcx, f); let fty = expr_ty(fcx.ccx.tcx, f);
let ret_ty = ty::ret_ty_of_fn_ty(fcx.ccx.tcx, fty); let ret_ty = ty::ret_ty_of_fn_ty(fcx.ccx.tcx, fty);
demand::simple(fcx, f.span, ty::mk_nil(fcx.ccx.tcx), ret_ty); demand::simple(fcx, f.span, ty::mk_nil(fcx.ccx.tcx), ret_ty);
@ -2156,7 +2168,7 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
write::ty_only_fixup(fcx, id, typ); write::ty_only_fixup(fcx, id, typ);
} }
ast::expr_cast(e, t) { ast::expr_cast(e, t) {
check_expr(fcx, e); bot = check_expr(fcx, e);
let t_1 = ast_ty_to_ty_crate(fcx.ccx, t); let t_1 = ast_ty_to_ty_crate(fcx.ccx, t);
// FIXME: there are more forms of cast to support, eventually. // FIXME: there are more forms of cast to support, eventually.
@ -2176,11 +2188,11 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
if ivec::len[@ast::expr](args) == 0u { if ivec::len[@ast::expr](args) == 0u {
t = next_ty_var(fcx); t = next_ty_var(fcx);
} else { } else {
check_expr(fcx, args.(0)); bot |= check_expr(fcx, args.(0));
t = expr_ty(fcx.ccx.tcx, args.(0)); t = expr_ty(fcx.ccx.tcx, args.(0));
} }
for e: @ast::expr in args { for e: @ast::expr in args {
check_expr(fcx, e); bot |= check_expr(fcx, e);
let expr_t = expr_ty(fcx.ccx.tcx, e); let expr_t = expr_ty(fcx.ccx.tcx, e);
demand::simple(fcx, expr.span, t, expr_t); demand::simple(fcx, expr.span, t, expr_t);
} }
@ -2197,7 +2209,7 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
alt base { none. {/* no-op */ } some(b_0) { check_expr(fcx, b_0); } } alt base { none. {/* no-op */ } some(b_0) { check_expr(fcx, b_0); } }
let fields_t: (spanned[field])[] = ~[]; let fields_t: (spanned[field])[] = ~[];
for f: ast::field in fields { for f: ast::field in fields {
check_expr(fcx, f.node.expr); bot |= check_expr(fcx, f.node.expr);
let expr_t = expr_ty(fcx.ccx.tcx, f.node.expr); let expr_t = expr_ty(fcx.ccx.tcx, f.node.expr);
let expr_mt = {ty: expr_t, mut: f.node.mut}; let expr_mt = {ty: expr_t, mut: f.node.mut};
// for the most precise error message, // for the most precise error message,
@ -2213,7 +2225,7 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
write::ty_only_fixup(fcx, id, typ); write::ty_only_fixup(fcx, id, typ);
} }
some(bexpr) { some(bexpr) {
check_expr(fcx, bexpr); bot |= check_expr(fcx, bexpr);
let bexpr_t = expr_ty(fcx.ccx.tcx, bexpr); let bexpr_t = expr_ty(fcx.ccx.tcx, bexpr);
let base_fields: field[] = ~[]; let base_fields: field[] = ~[];
alt structure_of(fcx, expr.span, bexpr_t) { alt structure_of(fcx, expr.span, bexpr_t) {
@ -2244,7 +2256,7 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
} }
} }
ast::expr_field(base, field) { ast::expr_field(base, field) {
check_expr(fcx, base); bot |= check_expr(fcx, base);
let base_t = expr_ty(fcx.ccx.tcx, base); let base_t = expr_ty(fcx.ccx.tcx, base);
base_t = do_autoderef(fcx, expr.span, base_t); base_t = do_autoderef(fcx, expr.span, base_t);
alt structure_of(fcx, expr.span, base_t) { alt structure_of(fcx, expr.span, base_t) {
@ -2278,10 +2290,10 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
} }
} }
ast::expr_index(base, idx) { ast::expr_index(base, idx) {
check_expr(fcx, base); bot |= check_expr(fcx, base);
let base_t = expr_ty(fcx.ccx.tcx, base); let base_t = expr_ty(fcx.ccx.tcx, base);
base_t = do_autoderef(fcx, expr.span, base_t); base_t = do_autoderef(fcx, expr.span, base_t);
check_expr(fcx, idx); bot |= check_expr(fcx, idx);
let idx_t = expr_ty(fcx.ccx.tcx, idx); let idx_t = expr_ty(fcx.ccx.tcx, idx);
if !type_is_integral(fcx, idx.span, idx_t) { if !type_is_integral(fcx, idx.span, idx_t) {
fcx.ccx.tcx.sess.span_fatal(idx.span, fcx.ccx.tcx.sess.span_fatal(idx.span,
@ -2335,7 +2347,6 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
} }
} }
ast::expr_anon_obj(ao) { ast::expr_anon_obj(ao) {
let fields: ast::anon_obj_field[] = ~[]; let fields: ast::anon_obj_field[] = ~[];
alt ao.fields { none. { } some(v) { fields = v; } } alt ao.fields { none. { } some(v) { fields = v; } }
@ -2369,15 +2380,11 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
constrs: out_constrs}; constrs: out_constrs};
} }
fn get_anon_obj_method_types(fcx: @fn_ctxt, ao: &ast::anon_obj, let method_types: ty::method[] = ~[];
fields: &ast::anon_obj_field[]) -> {
ty::method[] {
let methods: ty::method[] = ~[];
// Outer methods. // Outer methods.
for m: @ast::method in ao.methods { for m: @ast::method in ao.methods {
methods += ~[ty_of_method(fcx.ccx, m)]; method_types += ~[ty_of_method(fcx.ccx, m)];
} }
// Inner methods. // Inner methods.
@ -2392,7 +2399,7 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
some(e) { some(e) {
// If there's a inner_obj, we push it onto the obj_infos stack // If there's a inner_obj, we push it onto the obj_infos stack
// so that self-calls can be checked within its context later. // so that self-calls can be checked within its context later.
check_expr(fcx, e); bot |= check_expr(fcx, e);
inner_obj_ty = expr_ty(fcx.ccx.tcx, e); inner_obj_ty = expr_ty(fcx.ccx.tcx, e);
inner_obj_sty = some(structure_of(fcx, e.span, inner_obj_ty)); inner_obj_sty = some(structure_of(fcx, e.span, inner_obj_ty));
@ -2437,11 +2444,9 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
std::ivec::filter_map[ty::method, std::ivec::filter_map[ty::method,
ty::method](f, inner_obj_methods); ty::method](f, inner_obj_methods);
methods += inner_obj_methods; method_types += inner_obj_methods;
ret methods;
} }
let method_types = get_anon_obj_method_types(fcx, ao, fields);
let ot = ty::mk_obj(fcx.ccx.tcx, ty::sort_methods(method_types)); let ot = ty::mk_obj(fcx.ccx.tcx, ty::sort_methods(method_types));
write::ty_only_fixup(fcx, id, ot); write::ty_only_fixup(fcx, id, ot);
@ -2467,6 +2472,10 @@ fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) {
} }
_ { fcx.ccx.tcx.sess.unimpl("expr type in typeck::check_expr"); } _ { fcx.ccx.tcx.sess.unimpl("expr type in typeck::check_expr"); }
} }
if bot {
write::ty_only_fixup(fcx, expr.id, ty::mk_bot(fcx.ccx.tcx));
}
ret bot;
} }
fn next_ty_var_id(fcx: @fn_ctxt) -> int { fn next_ty_var_id(fcx: @fn_ctxt) -> int {
@ -2484,8 +2493,8 @@ fn get_obj_info(ccx: &@crate_ctxt) -> option::t[obj_info] {
} }
fn check_decl_initializer(fcx: &@fn_ctxt, nid: ast::node_id, fn check_decl_initializer(fcx: &@fn_ctxt, nid: ast::node_id,
init: &ast::initializer) { init: &ast::initializer) -> bool {
check_expr(fcx, init.expr); let bot = check_expr(fcx, init.expr);
let lty = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, init.expr.span, nid)); let lty = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, init.expr.span, nid));
alt init.op { alt init.op {
ast::init_assign. { ast::init_assign. {
@ -2502,10 +2511,12 @@ fn check_decl_initializer(fcx: &@fn_ctxt, nid: ast::node_id,
expr_ty(fcx.ccx.tcx, init.expr)); expr_ty(fcx.ccx.tcx, init.expr));
} }
} }
ret bot;
} }
fn check_decl_local(fcx: &@fn_ctxt, local: &@ast::local) { fn check_decl_local(fcx: &@fn_ctxt, local: &@ast::local) -> bool {
let a_id = local.node.id; let a_id = local.node.id;
let bot = false;
alt fcx.locals.find(a_id) { alt fcx.locals.find(a_id) {
none. { none. {
fcx.ccx.tcx.sess.bug("check_decl_local: local id not found " + fcx.ccx.tcx.sess.bug("check_decl_local: local id not found " +
@ -2515,40 +2526,65 @@ fn check_decl_local(fcx: &@fn_ctxt, local: &@ast::local) {
let t = ty::mk_var(fcx.ccx.tcx, i); let t = ty::mk_var(fcx.ccx.tcx, i);
write::ty_only_fixup(fcx, a_id, t); write::ty_only_fixup(fcx, a_id, t);
alt local.node.init { alt local.node.init {
some(init) { check_decl_initializer(fcx, local.node.id, init); } some(init) {
bot = check_decl_initializer(fcx, local.node.id, init);
}
_ {/* fall through */ } _ {/* fall through */ }
} }
} }
} }
ret bot;
} }
fn check_stmt(fcx: &@fn_ctxt, stmt: &@ast::stmt) { fn check_stmt(fcx: &@fn_ctxt, stmt: &@ast::stmt) -> bool {
let node_id; let node_id;
let bot = false;
alt stmt.node { alt stmt.node {
ast::stmt_decl(decl, id) { ast::stmt_decl(decl, id) {
node_id = id; node_id = id;
alt decl.node { alt decl.node {
ast::decl_local(ls) { ast::decl_local(ls) {
for l: @ast::local in ls { check_decl_local(fcx, l); } for l: @ast::local in ls { bot |= check_decl_local(fcx, l); }
} }
ast::decl_item(_) {/* ignore for now */ } ast::decl_item(_) {/* ignore for now */ }
} }
} }
ast::stmt_expr(expr, id) { node_id = id; check_expr(fcx, expr); } ast::stmt_expr(expr, id) { node_id = id; bot = check_expr(fcx, expr); }
} }
write::nil_ty(fcx.ccx.tcx, node_id); write::nil_ty(fcx.ccx.tcx, node_id);
ret bot;
} }
fn check_block(fcx: &@fn_ctxt, blk: &ast::blk) { fn check_block(fcx: &@fn_ctxt, blk: &ast::blk) -> bool {
for s: @ast::stmt in blk.node.stmts { check_stmt(fcx, s); } let bot = false;
let warned = false;
for s: @ast::stmt in blk.node.stmts {
if bot && !warned &&
alt s.node {
ast::stmt_decl(@{node: ast::decl_local(_), _}, _) |
ast::stmt_expr(_, _) { true }
_ { false }
} {
fcx.ccx.tcx.sess.span_warn(s.span, "unreachable statement");
warned = true;
}
bot |= check_stmt(fcx, s);
}
alt blk.node.expr { alt blk.node.expr {
none. { write::nil_ty(fcx.ccx.tcx, blk.node.id); } none. { write::nil_ty(fcx.ccx.tcx, blk.node.id); }
some(e) { some(e) {
check_expr(fcx, e); if bot && !warned {
fcx.ccx.tcx.sess.span_warn(e.span, "unreachable expression");
}
bot |= check_expr(fcx, e);
let ety = expr_ty(fcx.ccx.tcx, e); let ety = expr_ty(fcx.ccx.tcx, e);
write::ty_only_fixup(fcx, blk.node.id, ety); write::ty_only_fixup(fcx, blk.node.id, ety);
} }
} }
if bot {
write::ty_only_fixup(fcx, blk.node.id, ty::mk_bot(fcx.ccx.tcx));
}
ret bot;
} }
fn check_const(ccx: &@crate_ctxt, sp: &span, e: &@ast::expr, fn check_const(ccx: &@crate_ctxt, sp: &span, e: &@ast::expr,

View file

@ -81,7 +81,7 @@ fn from_mut[T](v: &T[mutable ]) -> T[] {
// Predicates // Predicates
pred is_empty[T](v: &T[mutable? ]) -> bool { pred is_empty[T](v: &T[mutable? ]) -> bool {
// FIXME: This would be easier if we could just call len // FIXME: This would be easier if we could just call len
for t: T in v { ret false; } for t: T in v { ret false; }
ret true; ret true;
} }