Rework check_expr substantially.

The bulk of check_expr is now check_expr_with_unifier, which takes an
expected type and a unification function and will perform the
unification on the type it produces. check_expr calls
check_expr_with_unifier with a dummy unifier and a new function,
check_expr_with, takes an expected type and uses the simple unifier.

I think this generally makes thing cleaner, but the purpose for doing
this is to enable type inferred lambda-blocks to be useful by allowing
the argument types to be unified before the body of the lambda is
checked.
This commit is contained in:
Michael Sullivan 2011-08-10 19:49:58 -07:00
parent 67e361a940
commit c5c8258937

View File

@ -1521,9 +1521,24 @@ fn require_pure_call(ccx: @crate_ctxt, caller_purity: &ast::purity,
}
}
type unifier = fn(fcx: &@fn_ctxt, sp: &span,
expected: &ty::t, actual: &ty::t) -> ty::t;
fn check_expr(fcx: &@fn_ctxt, expr: &@ast::expr) -> bool {
// fcx.ccx.tcx.sess.span_warn(expr.span, "typechecking expr " +
// syntax::print::pprust::expr_to_str(expr));
fn dummy_unify(fcx: &@fn_ctxt, sp: &span,
expected: &ty::t, actual: &ty::t) -> ty::t {
actual
}
ret check_expr_with_unifier(fcx, expr, dummy_unify, 0u);
}
fn check_expr_with(fcx: &@fn_ctxt, expr: &@ast::expr, expected: &ty::t)
-> bool {
ret check_expr_with_unifier(fcx, expr, demand::simple, expected);
}
fn check_expr_with_unifier(fcx: &@fn_ctxt, expr: &@ast::expr,
unify: &unifier, expected: &ty::t) -> bool {
//log_err "typechecking expr " + syntax::print::pprust::expr_to_str(expr);
// A generic function to factor out common logic from call and bind
// expressions.
@ -1594,14 +1609,14 @@ fn check_call_or_bind(fcx: &@fn_ctxt, sp: &span, f: &@ast::expr,
}
// Check the arguments.
let unifier =
bind demand::autoderef(_, _, _, _, AUTODEREF_BLOCK_COERCE);
let i = 0u;
for a_opt: option::t[@ast::expr] in args {
alt a_opt {
some(a) {
bot |= check_expr(fcx, a);
demand::autoderef(fcx, a.span, arg_tys.(i).ty,
expr_ty(fcx.ccx.tcx, a),
AUTODEREF_BLOCK_COERCE);
bot |= check_expr_with_unifier(fcx, a, unifier,
arg_tys.(i).ty);
}
none. { }
}
@ -1613,9 +1628,8 @@ fn check_call_or_bind(fcx: &@fn_ctxt, sp: &span, f: &@ast::expr,
fn check_assignment(fcx: &@fn_ctxt, sp: &span, lhs: &@ast::expr,
rhs: &@ast::expr, id: &ast::node_id) -> bool {
let bot = check_expr(fcx, lhs) | check_expr(fcx, rhs);
demand::simple(fcx, sp, expr_ty(fcx.ccx.tcx, lhs),
expr_ty(fcx.ccx.tcx, rhs));
let t = next_ty_var(fcx);
let bot = check_expr_with(fcx, lhs, t) | check_expr_with(fcx, rhs, t);
write::ty_only_fixup(fcx, id, ty::mk_nil(fcx.ccx.tcx));
ret bot;
}
@ -1671,18 +1685,14 @@ fn check_for_or_for_each(fcx: &@fn_ctxt, local: &@ast::local,
demand::simple(fcx, local.span,
ty::node_id_to_type(fcx.ccx.tcx, local.node.id),
element_ty);
let typ = ty::mk_nil(fcx.ccx.tcx);
write::ty_only_fixup(fcx, node_id, typ);
write::nil_ty(fcx.ccx.tcx, node_id);
ret bot;
}
// A generic function for checking the pred in a check
// or if-check
fn check_pred_expr(fcx: &@fn_ctxt, e: &@ast::expr) -> bool {
let bot = check_expr(fcx, e);
demand::simple(fcx, e.span, ty::mk_bool(fcx.ccx.tcx),
expr_ty(fcx.ccx.tcx, e));
let bot = check_expr_with(fcx, e, ty::mk_bool(fcx.ccx.tcx));
/* e must be a call expr where all arguments are either
literals or slots */
@ -1732,10 +1742,9 @@ fn check_then_else(fcx: &@fn_ctxt, thn: &ast::blk,
let if_t =
alt elsopt {
some(els) {
els_bot = check_expr(fcx, els);
let thn_t = block_ty(fcx.ccx.tcx, thn);
els_bot = check_expr_with(fcx, els, thn_t);
let elsopt_t = expr_ty(fcx.ccx.tcx, els);
demand::simple(fcx, sp, thn_t, elsopt_t);
if !ty::type_is_bot(fcx.ccx.tcx, elsopt_t) {
elsopt_t
} else { thn_t }
@ -1769,17 +1778,13 @@ fn check_binop_type_compat(fcx: &@fn_ctxt, span: span, ty: ty::t,
write::ty_only_fixup(fcx, id, typ);
}
ast::expr_binary(binop, lhs, rhs) {
bot = check_expr(fcx, lhs);
if ast::lazy_binop(binop) {
check_expr(fcx, rhs);
} else {
bot |= check_expr(fcx, rhs);
}
let lhs_t = next_ty_var(fcx);
bot = check_expr_with(fcx, lhs, lhs_t);
let lhs_t = expr_ty(tcx, lhs);
let rhs_t = expr_ty(tcx, rhs);
let unifier = bind demand::autoderef(_, _, _, _, AUTODEREF_OK);
let rhs_bot = check_expr_with_unifier(fcx, rhs, unifier, lhs_t);
if !ast::lazy_binop(binop) { bot |= rhs_bot; }
demand::autoderef(fcx, rhs.span, lhs_t, rhs_t, AUTODEREF_OK);
let deref_t = do_autoderef(fcx, expr.span, lhs_t);
check_binop_type_compat(fcx, expr.span, deref_t, binop);
@ -1864,9 +1869,7 @@ fn check_binop_type_compat(fcx: &@fn_ctxt, span: span, ty: ty::t,
alt expr_opt {
none. {/* do nothing */ }
some(e) {
check_expr(fcx, e);
let ety = expr_ty(tcx, e);
demand::simple(fcx, e.span, ty::mk_str(tcx), ety);
check_expr_with(fcx, e, ty::mk_str(tcx));
}
}
write::bot_ty(tcx, id);
@ -1882,15 +1885,12 @@ fn check_binop_type_compat(fcx: &@fn_ctxt, span: span, ty: ty::t,
tcx.sess.span_fatal(expr.span,
"ret; in function returning non-nil");
}
write::bot_ty(tcx, id);
}
some(e) {
check_expr(fcx, e);
demand::simple(fcx, expr.span, fcx.ret_ty,
expr_ty(tcx, e));
write::bot_ty(tcx, id);
check_expr_with(fcx, e, fcx.ret_ty);
}
}
write::bot_ty(tcx, id);
}
ast::expr_put(expr_opt) {
require_impure(tcx.sess, fcx.purity, expr.span);
@ -1906,9 +1906,7 @@ fn check_binop_type_compat(fcx: &@fn_ctxt, span: span, ty: ty::t,
}
}
some(e) {
bot = check_expr(fcx, e);
demand::simple(fcx, expr.span, fcx.ret_ty,
expr_ty(tcx, e));
bot = check_expr_with(fcx, e, fcx.ret_ty);
}
}
write::nil_ty(tcx, id);
@ -1916,9 +1914,8 @@ fn check_binop_type_compat(fcx: &@fn_ctxt, span: span, ty: ty::t,
ast::expr_be(e) {
// FIXME: prove instead of assert
assert (ast::is_call_expr(e));
check_expr(fcx, e);
check_expr_with(fcx, e, fcx.ret_ty);
bot = true;
demand::simple(fcx, e.span, fcx.ret_ty, expr_ty(tcx, e));
write::nil_ty(tcx, id);
}
ast::expr_log(l, e) {
@ -1937,9 +1934,7 @@ fn check_binop_type_compat(fcx: &@fn_ctxt, span: span, ty: ty::t,
bot = check_expr(fcx, ast::ternary_to_if(expr));
}
ast::expr_assert(e) {
bot = check_expr(fcx, e);
let ety = expr_ty(tcx, e);
demand::simple(fcx, expr.span, ty::mk_bool(tcx), ety);
bot = check_expr_with(fcx, e, ty::mk_bool(tcx));
write::nil_ty(tcx, id);
}
ast::expr_move(lhs, rhs) {
@ -1962,34 +1957,23 @@ fn check_binop_type_compat(fcx: &@fn_ctxt, span: span, ty: ty::t,
}
ast::expr_send(lhs, rhs) {
require_impure(tcx.sess, fcx.purity, expr.span);
bot = check_expr(fcx, lhs) | check_expr(fcx, rhs);
let rhs_t = expr_ty(tcx, rhs);
let rhs_t = next_ty_var(fcx);
let chan_t = ty::mk_chan(tcx, rhs_t);
let lhs_t = expr_ty(tcx, lhs);
alt structure_of(fcx, expr.span, lhs_t) {
ty::ty_chan(it) { }
_ {
let s = #fmt("mismatched types: expected chan but found %s",
ty_to_str(tcx, lhs_t));
tcx.sess.span_fatal(expr.span, s);
}
}
demand::simple(fcx, expr.span, chan_t, lhs_t);
bot = check_expr_with(fcx, lhs, chan_t) |
check_expr_with(fcx, rhs, rhs_t);
write::ty_only_fixup(fcx, id, chan_t);
}
ast::expr_recv(lhs, rhs) {
require_impure(tcx.sess, fcx.purity, expr.span);
bot = check_expr(fcx, lhs) | check_expr(fcx, rhs);
let item_t = expr_ty(tcx, rhs);
let port_t = ty::mk_port(tcx, item_t);
demand::simple(fcx, expr.span, port_t, expr_ty(tcx, lhs));
write::ty_only_fixup(fcx, id, item_t);
let rhs_t = next_ty_var(fcx);
let port_t = ty::mk_port(tcx, rhs_t);
bot = check_expr_with(fcx, lhs, port_t) |
check_expr_with(fcx, rhs, rhs_t);
write::ty_only_fixup(fcx, id, rhs_t);
}
ast::expr_if(cond, thn, elsopt) {
bot = check_expr(fcx, cond) |
bot = check_expr_with(fcx, cond, ty::mk_bool(tcx)) |
check_then_else(fcx, thn, elsopt, id, expr.span);
demand::simple(fcx, cond.span, ty::mk_bool(tcx),
expr_ty(tcx, cond));
}
ast::expr_for(decl, seq, body) {
bot = check_expr(fcx, seq);
@ -2021,24 +2005,19 @@ fn check_binop_type_compat(fcx: &@fn_ctxt, span: span, ty: ty::t,
body, id);
}
ast::expr_while(cond, body) {
bot = check_expr(fcx, cond);
bot = check_expr_with(fcx, cond, ty::mk_bool(tcx));
check_block(fcx, body);
demand::simple(fcx, cond.span, ty::mk_bool(tcx),
expr_ty(tcx, cond));
let typ = ty::mk_nil(tcx);
write::ty_only_fixup(fcx, id, typ);
write::ty_only_fixup(fcx, id, ty::mk_nil(tcx));
}
ast::expr_do_while(body, cond) {
bot = check_expr(fcx, cond);
check_block(fcx, body);
let typ = block_ty(tcx, body);
write::ty_only_fixup(fcx, id, typ);
bot = check_expr(fcx, cond) | check_block(fcx, body);
write::ty_only_fixup(fcx, id, block_ty(tcx, body));
}
ast::expr_alt(expr, arms) {
bot = check_expr(fcx, expr);
// Typecheck the patterns first, so that we get types for all the
// bindings.
let pattern_ty = ty::expr_ty(tcx, expr);
for arm: ast::arm in arms {
let id_map = ast::pat_id_map(arm.pats.(0));
@ -2052,12 +2031,7 @@ fn check_binop_type_compat(fcx: &@fn_ctxt, span: span, ty: ty::t,
for arm: ast::arm in arms {
if !check_block(fcx, arm.block) { arm_non_bot = true; }
let bty = block_ty(tcx, arm.block);
// Failing alt arms don't need to have a matching type
if !ty::type_is_bot(tcx, bty) {
result_ty =
demand::simple(fcx, arm.block.span, result_ty, bty);
}
result_ty = demand::simple(fcx, arm.block.span, result_ty, bty);
}
bot |= !arm_non_bot;
if !arm_non_bot { result_ty = ty::mk_bot(tcx); }
@ -2076,16 +2050,11 @@ fn check_binop_type_compat(fcx: &@fn_ctxt, span: span, ty: ty::t,
}
ast::expr_block(b) {
bot = check_block(fcx, b);
alt b.node.expr {
some(expr) {
let typ = expr_ty(tcx, expr);
write::ty_only_fixup(fcx, id, typ);
}
none. {
let typ = ty::mk_nil(tcx);
write::ty_only_fixup(fcx, id, typ);
}
}
let typ = alt b.node.expr {
some(expr) { expr_ty(tcx, expr) }
none. { ty::mk_nil(tcx) }
};
write::ty_only_fixup(fcx, id, typ);
}
ast::expr_bind(f, args) {
// Call the generic checker.
@ -2216,17 +2185,9 @@ fn check_binop_type_compat(fcx: &@fn_ctxt, span: span, ty: ty::t,
write::ty_only_fixup(fcx, id, t_1);
}
ast::expr_vec(args, mut, kind) {
let t: ty::t;
if ivec::len[@ast::expr](args) == 0u {
t = next_ty_var(fcx);
} else {
bot |= check_expr(fcx, args.(0));
t = expr_ty(tcx, args.(0));
}
let t: ty::t = next_ty_var(fcx);;
for e: @ast::expr in args {
bot |= check_expr(fcx, e);
let expr_t = expr_ty(tcx, e);
demand::simple(fcx, expr.span, t, expr_t);
bot |= check_expr_with(fcx, e, t);
}
let typ;
alt kind {
@ -2350,29 +2311,13 @@ fn get_node(f: &spanned[field]) -> field { f.node }
}
}
ast::expr_port(typ) {
let t = next_ty_var(fcx);
alt ast_ty_to_ty_crate_infer(fcx.ccx, typ) {
some(_t) {
demand::simple(fcx, expr.span, _t, t);
}
none. { }
}
let pt = ty::mk_port(tcx, t);
let pt = ty::mk_port(tcx, ast_ty_to_ty_crate_tyvar(fcx, typ));
write::ty_only_fixup(fcx, id, pt);
}
ast::expr_chan(x) {
check_expr(fcx, x);
let port_t = expr_ty(tcx, x);
alt structure_of(fcx, expr.span, port_t) {
ty::ty_port(subtype) {
let ct = ty::mk_chan(tcx, subtype);
write::ty_only_fixup(fcx, id, ct);
}
_ {
tcx.sess.span_fatal(expr.span,
"bad port type: " + ty_to_str(tcx, port_t));
}
}
let t = next_ty_var(fcx);
check_expr_with(fcx, x, ty::mk_port(tcx, t));
write::ty_only_fixup(fcx, id, ty::mk_chan(tcx, t));
}
ast::expr_anon_obj(ao) {
let fields: [ast::anon_obj_field] = ~[];
@ -2512,6 +2457,8 @@ fn filtering_fn(ccx: @crate_ctxt,
if bot {
write::ty_only_fixup(fcx, expr.id, ty::mk_bot(tcx));
}
unify(fcx, expr.span, expected, expr_ty(tcx, expr));
ret bot;
}
@ -2531,19 +2478,8 @@ fn get_obj_info(ccx: &@crate_ctxt) -> option::t[obj_info] {
fn check_decl_initializer(fcx: &@fn_ctxt, nid: ast::node_id,
init: &ast::initializer) -> bool {
let bot = check_expr(fcx, init.expr);
let lty = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, init.expr.span, nid));
alt init.op {
ast::init_assign. {
demand::simple(fcx, init.expr.span, lty,
expr_ty(fcx.ccx.tcx, init.expr));
}
ast::init_move. {
demand::simple(fcx, init.expr.span, lty,
expr_ty(fcx.ccx.tcx, init.expr));
}
}
ret bot;
ret check_expr_with(fcx, init.expr, lty);
}
fn check_decl_local(fcx: &@fn_ctxt, local: &@ast::local) -> bool {