aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGraydon Hoare <[email protected]>2010-11-21 01:16:16 -0800
committerGraydon Hoare <[email protected]>2010-11-21 01:16:16 -0800
commit5955e23343e606daf0341cb7ea66b8b436868ba6 (patch)
tree0609b63451bb79561bdd997981a17be815dd79b6
parentTypecheck binary, unary and name expressions. Re-enable 3 more tests. (diff)
downloadrust-5955e23343e606daf0341cb7ea66b8b436868ba6.tar.xz
rust-5955e23343e606daf0341cb7ea66b8b436868ba6.zip
Typecheck assignment exprs, redo decl checking, propagate ty_vars. Enable another test.
-rw-r--r--src/Makefile1
-rw-r--r--src/comp/middle/typeck.rs149
2 files changed, 97 insertions, 53 deletions
diff --git a/src/Makefile b/src/Makefile
index 8afe0d45..bcf16f57 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -525,6 +525,7 @@ TEST_XFAILS_SELF := $(filter-out \
hello.rs \
int.rs \
item-name-overload.rs \
+ lazy-init.rs \
multiline-comment.rs \
), \
$(wildcard test/*/*.rs test/*/*.rc))
diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs
index 73f6fa85..64d7e111 100644
--- a/src/comp/middle/typeck.rs
+++ b/src/comp/middle/typeck.rs
@@ -17,6 +17,7 @@ import std.option.some;
type ty_table = hashmap[ast.def_id, @ty];
type env = rec(session.session sess,
@ty_table item_types,
+ hashmap[int,@ty] bindings,
mutable int next_var_id);
type arg = rec(ast.mode mode, @ty ty);
@@ -245,6 +246,7 @@ fn ast_ty_to_ty(ty_getter getter, &@ast.ty ast_ty) -> @ty {
// ast_ty_to_ty.
fn ast_ty_to_ty_env(@env e, &@ast.ty ast_ty) -> @ty {
fn getter(@env e, ast.def_id id) -> @ty {
+ check (e.item_types.contains_key(id));
ret e.item_types.get(id);
}
auto f = bind getter(e, _);
@@ -285,6 +287,7 @@ fn collect_item_types(@ast.crate crate) -> tup(@ast.crate, @ty_table) {
fn trans_ty_item_id_to_ty(@hashmap[ast.def_id,@ast.item] id_to_ty_item,
@ty_table item_to_ty,
ast.def_id id) -> @ty {
+ check (id_to_ty_item.contains_key(id));
auto item = id_to_ty_item.get(id);
ret trans_ty_item_to_ty(id_to_ty_item, item_to_ty, item);
}
@@ -317,6 +320,7 @@ fn collect_item_types(@ast.crate crate) -> tup(@ast.crate, @ty_table) {
case (ast.item_ty(?ident, ?referent_ty, ?def_id, _)) {
if (item_to_ty.contains_key(def_id)) {
// Avoid repeating work.
+ check (item_to_ty.contains_key(def_id));
ret item_to_ty.get(def_id);
}
@@ -423,7 +427,7 @@ fn type_of(@ast.expr expr) -> @ty {
// Type unification
-fn unify(@ty expected, @ty actual) -> unify_result {
+fn unify(&@env e, @ty expected, @ty actual) -> unify_result {
// Wraps the given type in an appropriate cname.
//
// TODO: This doesn't do anything yet. We should carry the cname up from
@@ -440,7 +444,7 @@ fn unify(@ty expected, @ty actual) -> unify_result {
ret ures_err(terr_mismatch, expected, actual);
}
- fn unify_step(@ty expected, @ty actual, &hashmap[int,@ty] bindings)
+ fn unify_step(&@env e, @ty expected, @ty actual)
-> unify_result {
// TODO: rewrite this using tuple pattern matching when available, to
// avoid all this rightward drift and spikiness.
@@ -456,8 +460,7 @@ fn unify(@ty expected, @ty actual) -> unify_result {
case (ty_box(?expected_sub)) {
alt (actual.struct) {
case (ty_box(?actual_sub)) {
- auto result = unify_step(expected_sub, actual_sub,
- bindings);
+ auto result = unify_step(e, expected_sub, actual_sub);
alt (result) {
case (ures_ok(?result_sub)) {
ret ures_ok(plain_ty(ty_box(result_sub)));
@@ -479,8 +482,7 @@ fn unify(@ty expected, @ty actual) -> unify_result {
case (ty_vec(?expected_sub)) {
alt (actual.struct) {
case (ty_vec(?actual_sub)) {
- auto result = unify_step(expected_sub, actual_sub,
- bindings);
+ auto result = unify_step(e, expected_sub, actual_sub);
alt (result) {
case (ures_ok(?result_sub)) {
ret ures_ok(plain_ty(ty_vec(result_sub)));
@@ -524,9 +526,8 @@ fn unify(@ty expected, @ty actual) -> unify_result {
ret ures_err(err, expected, actual);
}
- auto result = unify_step(expected_elem._1,
- actual_elem._1,
- bindings);
+ auto result = unify_step(e, expected_elem._1,
+ actual_elem._1);
alt (result) {
case (ures_ok(?rty)) {
result_elems += vec(tup(expected_elem._0,
@@ -576,9 +577,8 @@ fn unify(@ty expected, @ty actual) -> unify_result {
result_mode = ast.val;
}
- auto result = unify_step(expected_input.ty,
- actual_input.ty,
- bindings);
+ auto result = unify_step(e, expected_input.ty,
+ actual_input.ty);
alt (result) {
case (ures_ok(?rty)) {
@@ -596,8 +596,8 @@ fn unify(@ty expected, @ty actual) -> unify_result {
// Check the output.
auto result_out;
- auto result = unify_step(expected_output,
- actual_output, bindings);
+ auto result = unify_step(e, expected_output,
+ actual_output);
alt (result) {
case (ures_ok(?rty)) {
result_out = rty;
@@ -618,12 +618,13 @@ fn unify(@ty expected, @ty actual) -> unify_result {
}
case (ty_var(?expected_id)) {
- if (bindings.contains_key(expected_id)) {
- auto binding = bindings.get(expected_id);
- ret unify_step(binding, actual, bindings);
+ if (e.bindings.contains_key(expected_id)) {
+ check (e.bindings.contains_key(expected_id));
+ auto binding = e.bindings.get(expected_id);
+ ret unify_step(e, binding, actual);
}
- bindings.insert(expected_id, actual);
+ e.bindings.insert(expected_id, actual);
ret ures_ok(actual);
}
}
@@ -632,19 +633,13 @@ fn unify(@ty expected, @ty actual) -> unify_result {
fail;
}
- fn hash_int(&int x) -> uint { ret x as uint; }
- fn eq_int(&int a, &int b) -> bool { ret a == b; }
- auto hasher = hash_int;
- auto eqer = eq_int;
- auto bindings = map.mk_hashmap[int,@ty](hasher, eqer);
-
- ret unify_step(expected, actual, bindings);
+ ret unify_step(e, expected, actual);
}
// Requires that the two types unify, and prints an error message if they
// don't. Returns the unified type.
fn demand(&@env e, &span sp, @ty expected, @ty actual) -> @ty {
- alt (unify(expected, actual)) {
+ alt (unify(e, expected, actual)) {
case (ures_ok(?ty)) {
ret ty;
}
@@ -666,14 +661,15 @@ fn demand(&@env e, &span sp, @ty expected, @ty actual) -> @ty {
// with the previously-stored type for this local.
fn demand_local(&@env e, &span sp, &@ty_table locals, ast.def_id local_id,
@ty t) {
+ check (locals.contains_key(local_id));
auto prev_ty = locals.get(local_id);
auto unified_ty = demand(e, sp, prev_ty, t);
locals.insert(local_id, unified_ty);
}
// Returns true if the two types unify and false if they don't.
-fn are_compatible(@ty expected, @ty actual) -> bool {
- alt (unify(expected, actual)) {
+fn are_compatible(&@env e, @ty expected, @ty actual) -> bool {
+ alt (unify(e, expected, actual)) {
case (ures_ok(_)) { ret true; }
case (ures_err(_, _, _)) { ret false; }
}
@@ -681,15 +677,29 @@ fn are_compatible(@ty expected, @ty actual) -> bool {
// Writeback: the phase that writes inferred types back into the AST.
-fn writeback_local(&@ty_table locals, &span sp, @ast.local local)
+fn resolve_vars(@env e, @ty t) -> @ty {
+ alt (t.struct) {
+ case (ty_var(?v)) {
+ check (e.bindings.contains_key(v));
+ ret resolve_vars(e, e.bindings.get(v));
+ }
+ }
+ ret t;
+}
+
+fn writeback_local(@env e, &@ty_table locals, &span sp, @ast.local local)
-> @ast.decl {
- auto local_wb = @rec(ann=ast.ann_type(locals.get(local.id)) with *local);
+ if (!locals.contains_key(local.id)) {
+ e.sess.err("unable to determine type of local: " + local.ident);
+ }
+ auto local_ty = resolve_vars(e, locals.get(local.id));
+ auto local_wb = @rec(ann=ast.ann_type(local_ty) with *local);
ret @fold.respan[ast.decl_](sp, ast.decl_local(local_wb));
}
fn writeback(&@env e, &@ty_table locals, &ast.block block) -> ast.block {
auto fld = fold.new_identity_fold[@ty_table]();
- auto f = writeback_local; // FIXME: trans_const_lval bug
+ auto f = bind writeback_local(e, _, _, _);
fld = @rec(fold_decl_local = f with *fld);
ret fold.fold_block[@ty_table](locals, fld, block);
}
@@ -753,8 +763,14 @@ fn check_expr(&@env e, &@ty_table locals, @ast.expr expr) -> @ast.expr {
case (ast.expr_name(?name, ?defopt, _)) {
auto ty = @rec(struct=ty_nil, cname=none[str]);
alt (option.get[ast.def](defopt)) {
- case (ast.def_arg(?id)) { ty = locals.get(id); }
- case (ast.def_local(?id)) { ty = locals.get(id); }
+ case (ast.def_arg(?id)) {
+ check (locals.contains_key(id));
+ ty = locals.get(id);
+ }
+ case (ast.def_local(?id)) {
+ check (locals.contains_key(id));
+ ty = locals.get(id);
+ }
case (_) {
// FIXME: handle other names.
e.sess.unimpl("definition variant for: "
@@ -767,6 +783,17 @@ fn check_expr(&@env e, &@ty_table locals, @ast.expr expr) -> @ast.expr {
ast.ann_type(ty)));
}
+ case (ast.expr_assign(?lhs, ?rhs, _)) {
+ auto lhs_1 = check_expr(e, locals, lhs);
+ auto rhs_1 = check_expr(e, locals, rhs);
+ auto lhs_t = type_of(lhs_1);
+ auto rhs_t = type_of(rhs_1);
+ demand(e, expr.span, lhs_t, rhs_t);
+ ret @fold.respan[ast.expr_](expr.span,
+ ast.expr_assign(lhs_1, rhs_1,
+ ast.ann_type(rhs_t)));
+ }
+
case (_) {
// TODO
ret expr;
@@ -780,29 +807,36 @@ fn check_stmt(@env e, @ty_table locals, @ty ret_ty, &@ast.stmt stmt)
case (ast.stmt_decl(?decl)) {
alt (decl.node) {
case (ast.decl_local(?local)) {
- alt (local.init) {
- case (none[@ast.expr]) {
- // empty
+
+ auto local_ty;
+ alt (local.ty) {
+ case (none[@ast.ty]) {
+ // Auto slot. Assign a ty_var.
+ local_ty = plain_ty(ty_var(e.next_var_id));
+ e.next_var_id += 1;
+ }
+
+ case (some[@ast.ty](?ast_ty)) {
+ local_ty = ast_ty_to_ty_env(e, ast_ty);
}
+ }
+ locals.insert(local.id, local_ty);
+ auto rhs_ty = local_ty;
+ auto init = local.init;
+ alt (local.init) {
case (some[@ast.expr](?expr)) {
auto expr_t = check_expr(e, locals, expr);
- locals.insert(local.id, type_of(expr_t));
-
- alt (local.ty) {
- case (none[@ast.ty]) {
- // Nothing to do. We'll figure out the
- // type later.
- }
-
- case (some[@ast.ty](?ast_ty)) {
- auto ty = ast_ty_to_ty_env(e, ast_ty);
- demand_local(e, decl.span, locals,
- local.id, ty);
- }
- }
+ rhs_ty = type_of(expr_t);
+ init = some[@ast.expr](expr_t);
}
}
+ demand(e, decl.span, local_ty, rhs_ty);
+ auto local_1 = @rec(init = init with *local);
+ auto decl_1 = @rec(node=ast.decl_local(local_1)
+ with *decl);
+ ret @fold.respan[ast.stmt_](stmt.span,
+ ast.stmt_decl(decl_1));
}
case (ast.decl_item(_)) {
@@ -816,7 +850,7 @@ fn check_stmt(@env e, @ty_table locals, @ty ret_ty, &@ast.stmt stmt)
case (ast.stmt_ret(?expr_opt)) {
alt (expr_opt) {
case (none[@ast.expr]) {
- if (!are_compatible(ret_ty, plain_ty(ty_nil))) {
+ if (!are_compatible(e, ret_ty, plain_ty(ty_nil))) {
e.sess.err("ret; in function returning non-void");
}
@@ -846,7 +880,7 @@ fn check_stmt(@env e, @ty_table locals, @ty ret_ty, &@ast.stmt stmt)
case (ast.stmt_expr(?expr)) {
auto expr_t = check_expr(e, locals, expr);
- if (!are_compatible(type_of(expr_t), plain_ty(ty_nil))) {
+ if (!are_compatible(e, type_of(expr_t), plain_ty(ty_nil))) {
// TODO: real warning function
log "warning: expression used as statement should have " +
"void type";
@@ -891,7 +925,16 @@ fn check_fn(&@env e, &span sp, ast.ident ident, &ast._fn f, ast.def_id id,
fn check_crate(session.session sess, @ast.crate crate) -> @ast.crate {
auto result = collect_item_types(crate);
- auto e = @rec(sess=sess, item_types=result._1, mutable next_var_id=0);
+
+ fn hash_int(&int x) -> uint { ret x as uint; }
+ fn eq_int(&int a, &int b) -> bool { ret a == b; }
+ auto hasher = hash_int;
+ auto eqer = eq_int;
+
+ auto e = @rec(sess=sess,
+ item_types=result._1,
+ bindings = map.mk_hashmap[int,@ty](hasher, eqer),
+ mutable next_var_id=0);
auto fld = fold.new_identity_fold[@env]();
auto f = check_fn; // FIXME: trans_const_lval bug