about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2010-11-03 16:43:12 -0700
committerPatrick Walton <pcwalton@mimiga.net>2010-11-19 17:50:45 -0800
commitc00bda539d79e4411e086d505c697662942ee00b (patch)
treeac121a5bf1275b57770add6e443434fa13eb6915
parentc410d685294e73a944f3e7cfe403f415a0cff849 (diff)
rustc: First stab at a typechecker
-rw-r--r--src/comp/driver/rustc.rs2
-rw-r--r--src/comp/front/ast.rs5
-rw-r--r--src/comp/middle/trans.rs96
-rw-r--r--src/comp/middle/typeck.rs857
-rw-r--r--src/comp/rustc.rc1
5 files changed, 912 insertions, 49 deletions
diff --git a/src/comp/driver/rustc.rs b/src/comp/driver/rustc.rs
index 154b87cbcf4..baf00780da9 100644
--- a/src/comp/driver/rustc.rs
+++ b/src/comp/driver/rustc.rs
@@ -4,6 +4,7 @@ import front.parser;
 import front.token;
 import middle.trans;
 import middle.resolve;
+import middle.typeck;
 
 import std.option;
 import std.option.some;
@@ -15,6 +16,7 @@ impure fn compile_input(session.session sess, str input, str output) {
           auto p = parser.new_parser(sess, 0, input);
           auto crate = parser.parse_crate(p);
           crate = resolve.resolve_crate(sess, crate);
+          crate = typeck.check_crate(sess, crate);
           trans.trans_crate(sess, crate, output);
 }
 
diff --git a/src/comp/front/ast.rs b/src/comp/front/ast.rs
index c3b38cb6a15..3a67d2f273c 100644
--- a/src/comp/front/ast.rs
+++ b/src/comp/front/ast.rs
@@ -1,6 +1,7 @@
 
 import std.map.hashmap;
 import std.option;
+import middle.typeck;
 import util.common.span;
 import util.common.spanned;
 
@@ -17,7 +18,7 @@ type def_id = tup(crate_num, def_num);
 // Annotations added during successive passes.
 tag ann {
     ann_none;
-    ann_type(@ty);
+    ann_type(@typeck.ty);
 }
 
 tag def {
@@ -119,6 +120,8 @@ tag lit_ {
     lit_bool(bool);
 }
 
+// NB: If you change this, you'll probably want to change the corresponding
+// type structure in middle/typeck.rs as well.
 type ty = spanned[ty_];
 tag ty_ {
     ty_nil;
diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs
index 0c85d50b130..d2199faed21 100644
--- a/src/comp/middle/trans.rs
+++ b/src/comp/middle/trans.rs
@@ -9,6 +9,7 @@ import std.option.none;
 
 import front.ast;
 import driver.session;
+import middle.typeck;
 import back.x86;
 import back.abi;
 
@@ -237,13 +238,13 @@ fn T_taskptr() -> TypeRef {
     ret T_ptr(T_task());
 }
 
-fn type_of(@trans_ctxt cx, @ast.ty t) -> TypeRef {
-    alt (t.node) {
-        case (ast.ty_nil) { ret T_nil(); }
-        case (ast.ty_bool) { ret T_bool(); }
-        case (ast.ty_int) { ret T_int(); }
-        case (ast.ty_uint) { ret T_int(); }
-        case (ast.ty_machine(?tm)) {
+fn type_of(@trans_ctxt cx, @typeck.ty t) -> TypeRef {
+    alt (t.struct) {
+        case (typeck.ty_nil) { ret T_nil(); }
+        case (typeck.ty_bool) { ret T_bool(); }
+        case (typeck.ty_int) { ret T_int(); }
+        case (typeck.ty_uint) { ret T_int(); }
+        case (typeck.ty_machine(?tm)) {
             alt (tm) {
                 case (common.ty_i8) { ret T_i8(); }
                 case (common.ty_u8) { ret T_i8(); }
@@ -257,24 +258,25 @@ fn type_of(@trans_ctxt cx, @ast.ty t) -> TypeRef {
                 case (common.ty_f64) { ret T_f64(); }
             }
         }
-        case (ast.ty_char) { ret T_char(); }
-        case (ast.ty_str) { ret T_str(0u); }
-        case (ast.ty_box(?t)) {
+        case (typeck.ty_char) { ret T_char(); }
+        case (typeck.ty_str) { ret T_str(0u); }
+        case (typeck.ty_box(?t)) {
             ret T_ptr(T_box(type_of(cx, t)));
         }
-        case (ast.ty_vec(?t)) {
+        case (typeck.ty_vec(?t)) {
             ret T_ptr(T_vec(type_of(cx, t), 0u));
         }
-        case (ast.ty_tup(?elts)) {
+        case (typeck.ty_tup(?elts)) {
             let vec[TypeRef] tys = vec();
-            for (tup(bool, @ast.ty) elt in elts) {
+            for (tup(bool, @typeck.ty) elt in elts) {
                 tys += type_of(cx, elt._1);
             }
             ret T_struct(tys);
         }
-        case (ast.ty_path(?pth,  ?def)) {
+        case (typeck.ty_var(_)) {
             // FIXME: implement.
-            cx.sess.unimpl("ty_path in trans.type_of");
+            log "ty_var in trans.type_of";
+            ret T_i8();
         }
     }
     fail;
@@ -340,27 +342,23 @@ fn C_tydesc(TypeRef t) -> ValueRef {
                      C_null(T_opaque())));      // is_stateful
 }
 
-fn decl_fn(ModuleRef llmod, str name,
-           uint cc, vec[TypeRef] inputs, TypeRef output) -> ValueRef {
-    let TypeRef llty = T_fn(inputs, output);
+fn decl_fn(ModuleRef llmod, str name, uint cc, TypeRef llty) -> ValueRef {
     let ValueRef llfn =
         llvm.LLVMAddFunction(llmod, _str.buf(name), llty);
     llvm.LLVMSetFunctionCallConv(llfn, cc);
     ret llfn;
 }
 
-fn decl_cdecl_fn(ModuleRef llmod, str name,
-                 vec[TypeRef] inputs, TypeRef output) -> ValueRef {
-    ret decl_fn(llmod, name, lib.llvm.LLVMCCallConv, inputs, output);
+fn decl_cdecl_fn(ModuleRef llmod, str name, TypeRef llty) -> ValueRef {
+    ret decl_fn(llmod, name, lib.llvm.LLVMCCallConv, llty);
 }
 
-fn decl_fastcall_fn(ModuleRef llmod, str name,
-                    vec[TypeRef] inputs, TypeRef output) -> ValueRef {
-    ret decl_fn(llmod, name, lib.llvm.LLVMFastCallConv, inputs, output);
+fn decl_fastcall_fn(ModuleRef llmod, str name, TypeRef llty) -> ValueRef {
+    ret decl_fn(llmod, name, lib.llvm.LLVMFastCallConv, llty);
 }
 
 fn decl_glue(ModuleRef llmod, str s) -> ValueRef {
-    ret decl_cdecl_fn(llmod, s, vec(T_taskptr()), T_void());
+    ret decl_cdecl_fn(llmod, s, T_fn(vec(T_taskptr()), T_void()));
 }
 
 fn decl_upcall(ModuleRef llmod, uint _n) -> ValueRef {
@@ -375,7 +373,7 @@ fn decl_upcall(ModuleRef llmod, uint _n) -> ValueRef {
             T_int())     // callee
         + _vec.init_elt[TypeRef](T_int(), n as uint);
 
-    ret decl_fastcall_fn(llmod, s, args, T_int());
+    ret decl_fastcall_fn(llmod, s, T_fn(args, T_int()));
 }
 
 fn get_upcall(@trans_ctxt cx, str name, int n_args) -> ValueRef {
@@ -385,7 +383,7 @@ fn get_upcall(@trans_ctxt cx, str name, int n_args) -> ValueRef {
     auto inputs = vec(T_taskptr());
     inputs += _vec.init_elt[TypeRef](T_int(), n_args as uint);
     auto output = T_int();
-    auto f = decl_cdecl_fn(cx.llmod, name, inputs, output);
+    auto f = decl_cdecl_fn(cx.llmod, name, T_fn(inputs, output));
     cx.upcalls.insert(name, f);
     ret f;
 }
@@ -937,6 +935,7 @@ impure fn trans_expr(@block_ctxt cx, &ast.expr e) -> result {
         case (ast.expr_call(?f, ?args, _)) {
             auto f_res = trans_lval(cx, *f);
             check (! f_res._1);
+
             auto args_res = trans_exprs(f_res._0.bcx, args);
             auto llargs = vec(cx.fcx.lltaskptr);
             llargs += args_res._1;
@@ -1141,15 +1140,7 @@ impure fn trans_block(@block_ctxt cx, &ast.block b) -> result {
     auto bcx = cx;
 
     for each (@ast.local local in block_locals(b)) {
-        auto ty = T_nil();
-        alt (local.ty) {
-            case (some[@ast.ty](?t)) {
-                ty = type_of(cx.fcx.tcx, t);
-            }
-            case (none[@ast.ty]) {
-                cx.fcx.tcx.sess.err("missing type for local " + local.ident);
-            }
-        }
+        auto ty = node_type(cx.fcx.tcx, local.ann);
         auto val = bcx.build.Alloca(ty);
         cx.fcx.lllocals.insert(local.id, val);
     }
@@ -1236,15 +1227,24 @@ impure fn trans_mod(@trans_ctxt cx, &ast._mod m) {
 
 fn collect_item(&@trans_ctxt cx, @ast.item i) -> @trans_ctxt {
     alt (i.node) {
-        case (ast.item_fn(?name, ?f, ?fid, _)) {
-            cx.items.insert(fid, i);
-            let TypeRef out = type_of(cx, f.output);
-            auto args = vec(T_taskptr());
-            for (ast.arg arg in f.inputs) {
-                args += type_of(cx, arg.ty);
+        case (ast.item_fn(?name, ?f, ?fid, ?ann)) {
+            auto fn_inputs;
+            auto fn_output;
+            alt (typeck.ann_to_type(ann).struct) {
+                case (typeck.ty_fn(?ins, ?out)) {
+                    fn_inputs = ins;
+                    fn_output = out;
+                }
+                case (_) {
+                    log "trans: fn item doesn't have function type!";
+                    fail;
+                }
             }
+
+            cx.items.insert(fid, i);
+            auto llty = node_type(cx, ann);
             let str s = cx.names.next("_rust_fn") + "." + name;
-            let ValueRef llfn = decl_fastcall_fn(cx.llmod, s, args, out);
+            let ValueRef llfn = decl_fastcall_fn(cx.llmod, s, llty);
             cx.fn_ids.insert(fid, llfn);
         }
 
@@ -1338,10 +1338,10 @@ fn trans_main_fn(@trans_ctxt cx, ValueRef llcrate) {
     }
 
     auto llmain =
-        decl_cdecl_fn(cx.llmod, main_name, T_main_args, T_int());
+        decl_cdecl_fn(cx.llmod, main_name, T_fn(T_main_args, T_int()));
 
-    auto llrust_start =
-        decl_cdecl_fn(cx.llmod, "rust_start", T_rust_start_args, T_int());
+    auto llrust_start = decl_cdecl_fn(cx.llmod, "rust_start",
+                                      T_fn(T_rust_start_args, T_int()));
 
     auto llargc = llvm.LLVMGetParam(llmain, 0u);
     auto llargv = llvm.LLVMGetParam(llmain, 1u);
@@ -1367,7 +1367,7 @@ fn trans_main_fn(@trans_ctxt cx, ValueRef llcrate) {
 
 fn declare_intrinsics(ModuleRef llmod) {
     let vec[TypeRef] T_trap_args = vec();
-    decl_cdecl_fn(llmod, "llvm.trap", T_trap_args, T_void());
+    decl_cdecl_fn(llmod, "llvm.trap", T_fn(T_trap_args, T_void()));
 }
 
 fn trans_crate(session.session sess, @ast.crate crate, str output) {
@@ -1396,7 +1396,7 @@ fn trans_crate(session.session sess, @ast.crate crate, str output) {
                        */
                       exit_task_glue =
                       decl_cdecl_fn(llmod, abi.exit_task_glue_name(),
-                                    vec(T_taskptr()), T_void()),
+                                    T_fn(vec(T_taskptr()), T_void())),
 
                       upcall_glues =
                       _vec.init_fn[ValueRef](bind decl_upcall(llmod, _),
diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs
new file mode 100644
index 00000000000..8ef75ffa003
--- /dev/null
+++ b/src/comp/middle/typeck.rs
@@ -0,0 +1,857 @@
+//
+// comp/middle/typeck.rs
+//
+
+import front.ast;
+import front.ast.ann;
+import middle.fold;
+import driver.session;
+import util.common;
+import util.common.span;
+
+import std._str;
+import std._uint;
+import std._vec;
+import std.map;
+import std.map.hashmap;
+import std.option;
+import std.option.none;
+import std.option.some;
+
+type ty_table = hashmap[ast.def_id, @ty];
+type env = rec(session.session sess,
+               @ty_table item_types,
+               mutable int next_var_id);
+
+type arg = rec(ast.mode mode, @ty ty);
+
+// NB: If you change this, you'll probably want to change the corresponding
+// AST structure in front/ast.rs as well.
+type ty = rec(sty struct, option.t[str] cname);
+tag sty {
+    ty_nil;
+    ty_bool;
+    ty_int;
+    ty_uint;
+    ty_machine(util.common.ty_mach);
+    ty_char;
+    ty_str;
+    ty_box(@ty);
+    ty_vec(@ty);
+    ty_tup(vec[tup(bool /* mutability */, @ty)]);
+    ty_fn(vec[arg], @ty);                           // TODO: effect
+    ty_var(int);
+}
+
+tag type_err {
+    terr_mismatch;
+    terr_tuple_size(uint, uint);
+    terr_tuple_mutability;
+    terr_arg_count;
+}
+
+tag unify_result {
+    ures_ok(@ty);
+    ures_err(type_err, @ty, @ty);
+}
+
+// Used for ast_ty_to_ty() below.
+type ty_getter = fn(ast.def_id) -> @ty;
+
+// Error-reporting utility functions
+
+fn ast_ty_to_str(&@ast.ty ty) -> str {
+    fn ast_tup_elem_to_str(&tup(bool, @ast.ty) elem) -> str {
+        auto s;
+        if (elem._0) {
+            s = "mutable ";
+        } else {
+            s = "";
+        }
+
+        ret s + ast_ty_to_str(elem._1);
+    }
+
+    fn ast_fn_input_to_str(&rec(ast.mode mode, @ast.ty ty) input) -> str {
+        auto s;
+        if (mode_is_alias(input.mode)) {
+            s = "&";
+        } else {
+            s = "";
+        }
+
+        ret s + ast_ty_to_str(input.ty);
+    }
+
+    auto s;
+    alt (ty.node) {
+        case (ast.ty_nil)          { s = "()";                            }
+        case (ast.ty_bool)         { s = "bool";                          }
+        case (ast.ty_int)          { s = "int";                           }
+        case (ast.ty_uint)         { s = "uint";                          }
+        case (ast.ty_machine(?tm)) { s = common.ty_mach_to_str(tm);       }
+        case (ast.ty_char)         { s = "char";                          }
+        case (ast.ty_str)          { s = "str";                           }
+        case (ast.ty_box(?t))      { s = "@" + ast_ty_to_str(t);          }
+        case (ast.ty_vec(?t))      { s = "vec[" + ast_ty_to_str(t) + "]"; }
+
+        case (ast.ty_tup(?elems)) {
+            auto f = ast_tup_elem_to_str;
+            s = "tup(";
+            s += _str.connect(_vec.map[tup(bool,@ast.ty),str](f, elems), ",");
+            s += ")";
+        }
+
+        case (ast.ty_fn(?inputs, ?output)) {
+            auto f = ast_fn_input_to_str;
+            s = "fn(";
+            auto is = _vec.map[rec(ast.mode mode, @ast.ty ty),str](f, inputs);
+            s += _str.connect(is, ", ");
+            s += ")";
+
+            if (output.node != ast.ty_nil) {
+                s += " -> " + ast_ty_to_str(output);
+            }
+        }
+
+        case (ast.ty_path(?path, _)) {
+            s = path_to_str(path);
+        }
+
+        case (_) {
+            fail;   // FIXME: typestate bug
+        }
+    }
+
+    ret s;
+}
+
+fn name_to_str(&ast.name nm) -> str {
+    auto result = nm.node.ident;
+    if (_vec.len[@ast.ty](nm.node.types) > 0u) {
+        auto f = ast_ty_to_str;
+        result += "[";
+        result += _str.connect(_vec.map[@ast.ty,str](f, nm.node.types), ",");
+        result += "]";
+    }
+    ret result;
+}
+
+fn path_to_str(&ast.path path) -> str {
+    auto f = name_to_str;
+    ret _str.connect(_vec.map[ast.name,str](f, path), ".");
+}
+
+fn ty_to_str(@ty typ) -> str {
+    fn tup_elem_to_str(&tup(bool, @ty) elem) -> str {
+        auto s;
+        if (elem._0) {
+            s = "mutable ";
+        } else {
+            s = "";
+        }
+
+        ret s + ty_to_str(elem._1);
+    }
+
+    fn fn_input_to_str(&rec(ast.mode mode, @ty ty) input) -> str {
+        auto s;
+        if (mode_is_alias(input.mode)) {
+            s = "&";
+        } else {
+            s = "";
+        }
+
+        ret s + ty_to_str(input.ty);
+    }
+
+    auto s;
+    alt (typ.struct) {
+        case (ty_nil)          { s = "()";                        }
+        case (ty_bool)         { s = "bool";                      }
+        case (ty_int)          { s = "int";                       }
+        case (ty_uint)         { s = "uint";                      }
+        case (ty_machine(?tm)) { s = common.ty_mach_to_str(tm);   }
+        case (ty_char)         { s = "char";                      }
+        case (ty_str)          { s = "str";                       }
+        case (ty_box(?t))      { s = "@" + ty_to_str(t);          }
+        case (ty_vec(?t))      { s = "vec[" + ty_to_str(t) + "]"; }
+
+        case (ty_tup(?elems)) {
+            auto f = tup_elem_to_str;
+            auto strs = _vec.map[tup(bool,@ty),str](f, elems);
+            s = "tup(" + _str.connect(strs, ",") + ")";
+        }
+
+        case (ty_fn(?inputs, ?output)) {
+            auto f = fn_input_to_str;
+            s = "fn(" + _str.connect(_vec.map[arg,str](f, inputs), ", ") + ")";
+            if (output.struct != ty_nil) {
+                s += " -> " + ty_to_str(output);
+            }
+        }
+
+        case (ty_var(?v)) {
+            // FIXME: wrap around in the case of many variables
+            auto ch = ('T' as u8) + (v as u8);
+            s = _str.from_bytes(vec(ch));
+        }
+    }
+
+    ret s;
+}
+
+// Parses the programmer's textual representation of a type into our internal
+// notion of a type. `getter` is a function that returns the type
+// corresponding to a definition ID.
+fn ast_ty_to_ty(ty_getter getter, &@ast.ty ast_ty) -> @ty {
+    fn ast_arg_to_arg(ty_getter getter, &rec(ast.mode mode, @ast.ty ty) arg)
+            -> rec(ast.mode mode, @ty ty) {
+        ret rec(mode=arg.mode, ty=ast_ty_to_ty(getter, arg.ty));
+    }
+
+    auto sty;
+    auto cname = none[str];
+    alt (ast_ty.node) {
+        case (ast.ty_nil)          { sty = ty_nil; }
+        case (ast.ty_bool)         { sty = ty_bool; }
+        case (ast.ty_int)          { sty = ty_int; }
+        case (ast.ty_uint)         { sty = ty_uint; }
+        case (ast.ty_machine(?tm)) { sty = ty_machine(tm); }
+        case (ast.ty_char)         { sty = ty_char; }
+        case (ast.ty_str)          { sty = ty_str; }
+        case (ast.ty_box(?t))      { sty = ty_box(ast_ty_to_ty(getter, t)); }
+        case (ast.ty_vec(?t))      { sty = ty_vec(ast_ty_to_ty(getter, t)); }
+
+        case (ast.ty_fn(?inputs, ?output)) {
+            auto f = bind ast_arg_to_arg(getter, _);
+            auto i = _vec.map[rec(ast.mode mode, @ast.ty ty),arg](f, inputs);
+            sty = ty_fn(i, ast_ty_to_ty(getter, output));
+        }
+
+        case (ast.ty_path(?path, ?def)) {
+            auto def_id;
+            alt (option.get[ast.def](def)) {
+                case (ast.def_ty(?id)) { def_id = id; }
+                case (_) { fail; }
+            }
+
+            // TODO: maybe record cname chains so we can do "foo = int" like
+            // OCaml?
+            sty = getter(def_id).struct;
+            cname = some(path_to_str(path));
+        }
+    }
+
+    ret @rec(struct=sty, cname=cname);
+}
+
+// A convenience function to use an environment to resolve names for
+// 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 {
+        ret e.item_types.get(id);
+    }
+    auto f = bind getter(e, _);
+    ret ast_ty_to_ty(f, ast_ty);
+}
+
+fn type_err_to_str(&type_err err) -> str {
+    alt (err) {
+        case (terr_mismatch) {
+            ret "types differ";
+        }
+        case (terr_tuple_size(?e_sz, ?a_sz)) {
+            ret "expected a tuple with " + _uint.to_str(e_sz, 10u) +
+                " elements but found one with " + _uint.to_str(a_sz, 10u) +
+                " elements";
+        }
+        case (terr_tuple_mutability) {
+            ret "tuple elements differ in mutability";
+        }
+        case (terr_arg_count) {
+            ret "incorrect number of function parameters";
+        }
+    }
+}
+
+// Item collection - a pair of bootstrap passes:
+//
+// 1. Collect the IDs of all type items (typedefs) and store them in a table.
+//
+// 2. Translate the AST fragments that describe types to determine a type for
+//    each item. When we encounter a named type, we consult the table built in
+//    pass 1 to find its item, and recursively translate it.
+//
+// We then annotate the AST with the resulting types and return the annotated
+// AST, along with a table mapping item IDs to their types.
+
+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 {
+        auto item = id_to_ty_item.get(id);
+        ret trans_ty_item_to_ty(id_to_ty_item, item_to_ty, item);
+    }
+
+    fn trans_fn_arg_to_ty(@hashmap[ast.def_id,@ast.item] id_to_ty_item,
+                          @ty_table item_to_ty,
+                          &ast.arg a) -> arg {
+        auto f = bind trans_ty_item_id_to_ty(id_to_ty_item, item_to_ty, _);
+        ret rec(mode=a.mode, ty=ast_ty_to_ty(f, a.ty));
+    }
+
+    fn trans_ty_item_to_ty(@hashmap[ast.def_id,@ast.item] id_to_ty_item,
+                           @ty_table item_to_ty,
+                           @ast.item it) -> @ty {
+        alt (it.node) {
+            case (ast.item_fn(?ident, ?fn_info, ?def_id, _)) {
+                auto f = bind trans_fn_arg_to_ty(id_to_ty_item, item_to_ty,
+                                                 _);
+                auto input_tys = _vec.map[ast.arg,arg](f, fn_info.inputs);
+
+                auto g = bind trans_ty_item_id_to_ty(id_to_ty_item,
+                                                     item_to_ty, _);
+                auto output_ty = ast_ty_to_ty(g, fn_info.output);
+
+                auto t_fn = plain_ty(ty_fn(input_tys, output_ty));
+                item_to_ty.insert(def_id, t_fn);
+                ret t_fn;
+            }
+
+            case (ast.item_ty(?ident, ?referent_ty, ?def_id, _)) {
+                if (item_to_ty.contains_key(def_id)) {
+                    // Avoid repeating work.
+                    ret item_to_ty.get(def_id);
+                }
+
+                // Tell ast_ty_to_ty() that we want to perform a recursive
+                // call to resolve any named types.
+                auto f = bind trans_ty_item_id_to_ty(id_to_ty_item,
+                                                     item_to_ty, _);
+                auto ty = ast_ty_to_ty(f, referent_ty);
+                item_to_ty.insert(def_id, ty);
+                ret ty;
+            }
+
+            case (ast.item_mod(_, _, _)) { fail; }
+        }
+    }
+
+    // First pass: collect all type item IDs.
+    auto module = crate.node.module;
+    auto id_to_ty_item = @common.new_def_hash[@ast.item]();
+    for (@ast.item item in module.items) {
+        alt (item.node) {
+            case (ast.item_ty(_, _, ?def_id, _)) {
+                id_to_ty_item.insert(def_id, item);
+            }
+            case (_) { /* empty */ }
+        }
+    }
+
+    // Second pass: translate the types of all items.
+    auto item_to_ty = @common.new_def_hash[@ty]();
+    let vec[@ast.item] items_t = vec();
+    for (@ast.item it in module.items) {
+        let ast.item_ result;
+        alt (it.node) {
+            case (ast.item_fn(?ident, ?fn_info, ?def_id, _)) {
+                auto t = trans_ty_item_to_ty(id_to_ty_item, item_to_ty, it);
+                result = ast.item_fn(ident, fn_info, def_id, ast.ann_type(t));
+            }
+            case (ast.item_ty(?ident, ?referent_ty, ?def_id, _)) {
+                auto t = trans_ty_item_to_ty(id_to_ty_item, item_to_ty, it);
+                auto ann = ast.ann_type(t);
+                result = ast.item_ty(ident, referent_ty, def_id, ann);
+            }
+            case (ast.item_mod(_, _, _)) {
+                result = it.node;
+            }
+        }
+        items_t += vec(@fold.respan[ast.item_](it.span, result));
+    }
+
+    auto module_t = rec(items=items_t, index=module.index);
+    ret tup(@fold.respan[ast.crate_](crate.span, rec(module=module_t)),
+            item_to_ty);
+}
+
+// Type utilities
+
+// FIXME: remove me when == works on these tags.
+fn mode_is_alias(ast.mode m) -> bool {
+    alt (m) {
+        case (ast.val) { ret false; }
+        case (ast.alias) { ret true; }
+    }
+}
+
+fn plain_ty(&sty st) -> @ty {
+    ret @rec(struct=st, cname=none[str]);
+}
+
+fn ann_to_type(&ast.ann ann) -> @ty {
+    alt (ann) {
+        case (ast.ann_none) {
+            // shouldn't happen, but can until the typechecker is complete
+            ret plain_ty(ty_var(0));    // FIXME: broken, broken, broken
+        }
+        case (ast.ann_type(?ty)) {
+            ret ty;
+        }
+    }
+}
+
+fn type_of(@ast.expr expr) -> @ty {
+    alt (expr.node) {
+        case (ast.expr_vec(_, ?ann))          { ret ann_to_type(ann); }
+        case (ast.expr_tup(_, ?ann))          { ret ann_to_type(ann); }
+        case (ast.expr_rec(_, ?ann))          { ret ann_to_type(ann); }
+        case (ast.expr_call(_, _, ?ann))      { ret ann_to_type(ann); }
+        case (ast.expr_binary(_, _, _, ?ann)) { ret ann_to_type(ann); }
+        case (ast.expr_unary(_, _, ?ann))     { ret ann_to_type(ann); }
+        case (ast.expr_lit(_, ?ann))          { ret ann_to_type(ann); }
+        case (ast.expr_cast(_, _, ?ann))      { ret ann_to_type(ann); }
+        case (ast.expr_if(_, _, _, ?ann))     { ret ann_to_type(ann); }
+        case (ast.expr_while(_, _, ?ann))     { ret ann_to_type(ann); }
+        case (ast.expr_do_while(_, _, ?ann))  { ret ann_to_type(ann); }
+        case (ast.expr_block(_, ?ann))        { ret ann_to_type(ann); }
+        case (ast.expr_assign(_, _, ?ann))    { ret ann_to_type(ann); }
+        case (ast.expr_field(_, _, ?ann))     { ret ann_to_type(ann); }
+        case (ast.expr_index(_, _, ?ann))     { ret ann_to_type(ann); }
+        case (ast.expr_name(_, _, ?ann))      { ret ann_to_type(ann); }
+    }
+
+    fail;
+}
+
+// Type unification
+
+fn unify(@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
+    // the expected and/or actual types when unification results in a type
+    // identical to one or both of the two. The precise algorithm for this is
+    // something we'll probably need to develop over time.
+
+    // Simple structural type comparison.
+    fn struct_cmp(@ty expected, @ty actual) -> unify_result {
+        if (expected == actual) {
+            ret ures_ok(expected);
+        }
+
+        ret ures_err(terr_mismatch, expected, actual);
+    }
+
+    fn unify_step(@ty expected, @ty actual, &hashmap[int,@ty] bindings)
+            -> unify_result {
+        // TODO: rewrite this using tuple pattern matching when available, to
+        // avoid all this rightward drift and spikiness.
+        alt (expected.struct) {
+            case (ty_nil)        { ret struct_cmp(expected, actual); }
+            case (ty_bool)       { ret struct_cmp(expected, actual); }
+            case (ty_int)        { ret struct_cmp(expected, actual); }
+            case (ty_uint)       { ret struct_cmp(expected, actual); }
+            case (ty_machine(_)) { ret struct_cmp(expected, actual); }
+            case (ty_char)       { ret struct_cmp(expected, actual); }
+            case (ty_str)        { ret struct_cmp(expected, actual); }
+
+            case (ty_box(?expected_sub)) {
+                alt (actual.struct) {
+                    case (ty_box(?actual_sub)) {
+                        auto result = unify_step(expected_sub, actual_sub,
+                                                 bindings);
+                        alt (result) {
+                            case (ures_ok(?result_sub)) {
+                                ret ures_ok(plain_ty(ty_box(result_sub)));
+                            } 
+                            case (_) {
+                                ret result;
+                            }
+                        }
+                    }
+
+                    // TODO: ty_var
+
+                    case (_) {
+                        ret ures_err(terr_mismatch, expected, actual);
+                    }
+                }
+            }
+
+            case (ty_vec(?expected_sub)) {
+                alt (actual.struct) {
+                    case (ty_vec(?actual_sub)) {
+                        auto result = unify_step(expected_sub, actual_sub,
+                                                 bindings);
+                        alt (result) {
+                            case (ures_ok(?result_sub)) {
+                                ret ures_ok(plain_ty(ty_vec(result_sub)));
+                            }
+                            case (_) {
+                                ret result;
+                            }
+                        }
+                    }
+
+                    // TODO: ty_var
+
+                    case (_) {
+                        ret ures_err(terr_mismatch, expected, actual);
+                   }
+                }
+            }
+
+            case (ty_tup(?expected_elems)) {
+                alt (actual.struct) {
+                    case (ty_tup(?actual_elems)) {
+                        auto expected_len =
+                            _vec.len[tup(bool,@ty)](expected_elems);
+                        auto actual_len =
+                            _vec.len[tup(bool,@ty)](actual_elems);
+                        if (expected_len != actual_len) {
+                            auto err = terr_tuple_size(expected_len,
+                                                       actual_len);
+                            ret ures_err(err, expected, actual);
+                        }
+
+                        // TODO: implement an iterator that can iterate over
+                        // two arrays simultaneously.
+                        let vec[tup(bool, @ty)] result_elems = vec();
+                        auto i = 0u;
+                        while (i < expected_len) {
+                            auto expected_elem = expected_elems.(i);
+                            auto actual_elem = actual_elems.(i);
+                            if (expected_elem._0 != actual_elem._0) {
+                                auto err = terr_tuple_mutability;
+                                ret ures_err(err, expected, actual);
+                            }
+
+                            auto result = unify_step(expected_elem._1,
+                                                     actual_elem._1,
+                                                     bindings);
+                            alt (result) {
+                                case (ures_ok(?rty)) {
+                                    result_elems += vec(tup(expected_elem._0,
+                                                            rty));
+                                }
+                                case (_) {
+                                    ret result;
+                                }
+                            }
+
+                            i += 1u;
+                        }
+
+                        ret ures_ok(plain_ty(ty_tup(result_elems)));
+                    }
+
+                    // TODO: ty_var
+
+                    case (_) {
+                        ret ures_err(terr_mismatch, expected, actual);
+                    }
+                }
+            }
+
+            case (ty_fn(?expected_inputs, ?expected_output)) {
+                alt (actual.struct) {
+                    case (ty_fn(?actual_inputs, ?actual_output)) {
+                        auto expected_len = _vec.len[arg](expected_inputs);
+                        auto actual_len = _vec.len[arg](actual_inputs);
+                        if (expected_len != actual_len) {
+                            ret ures_err(terr_arg_count, expected, actual);
+                        }
+
+                        // TODO: as above, we should have an iter2 iterator.
+                        let vec[arg] result_ins = vec();
+                        auto i = 0u;
+                        while (i < expected_len) {
+                            auto expected_input = expected_inputs.(i);
+                            auto actual_input = actual_inputs.(i);
+
+                            // This should be safe, I think?
+                            auto result_mode;
+                            if (mode_is_alias(expected_input.mode) ||
+                                    mode_is_alias(actual_input.mode)) {
+                                result_mode = ast.alias;
+                            } else {
+                                result_mode = ast.val;
+                            }
+
+                            auto result = unify_step(expected_input.ty,
+                                                     actual_input.ty,
+                                                     bindings);
+
+                            alt (result) {
+                                case (ures_ok(?rty)) {
+                                    result_ins += vec(rec(mode=result_mode,
+                                                          ty=rty));
+                                }
+
+                                case (_) {
+                                    ret result;
+                                }
+                            }
+
+                            i += 1u;
+                        }
+
+                        // Check the output.
+                        auto result_out;
+                        auto result = unify_step(expected_output,
+                                                 actual_output, bindings);
+                        alt (result) {
+                            case (ures_ok(?rty)) {  
+                                result_out = rty;
+                            }
+
+                            case (_) {
+                                ret result;
+                            }
+                        }
+
+                        ret ures_ok(plain_ty(ty_fn(result_ins, result_out)));
+                    }
+
+                    case (_) {
+                        ret ures_err(terr_mismatch, expected, actual);
+                    }
+                }
+            }
+
+            case (ty_var(?expected_id)) {
+                if (bindings.contains_key(expected_id)) {
+                     auto binding = bindings.get(expected_id);
+                     ret unify_step(binding, actual, bindings);
+                }
+
+                bindings.insert(expected_id, actual);
+                ret ures_ok(actual);
+            }
+        }
+
+        // TODO: remove me once match-exhaustiveness checking works
+        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);
+}
+
+// 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)) {
+        case (ures_ok(?ty)) {
+            ret ty;
+        }
+
+        case (ures_err(?err, ?expected, ?actual)) {
+            e.sess.err("mismatched types: expected " + ty_to_str(expected) +
+                       " but found " + ty_to_str(actual) + " (" +
+                       type_err_to_str(err) + ")");
+
+            // TODO: In the future, try returning "expected", reporting the
+            // error, and continue.
+            fail;
+        }
+    }
+}
+
+// Unifies the supplied type with the type of the local `id`, and stores the
+// unified type in the local table. Emits an error if the type is incompatible
+// 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) {
+    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)) {
+        case (ures_ok(_))        { ret true;  }
+        case (ures_err(_, _, _)) { ret false; }
+    }
+}
+
+// Writeback: the phase that writes inferred types back into the AST.
+
+fn writeback_local(&@ty_table locals, &span sp, @ast.local local)
+        -> @ast.decl {
+    auto local_wb = @rec(ann=ast.ann_type(locals.get(local.id)) 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
+    fld = @rec(fold_decl_local = f with *fld);
+    ret fold.fold_block[@ty_table](locals, fld, block);
+}
+
+// AST fragment checking
+
+fn check_lit(&@env e, @ast.lit lit) -> @ty {
+    auto sty;
+    alt (lit.node) {
+        case (ast.lit_str(_))   { sty = ty_str;  }
+        case (ast.lit_char(_))  { sty = ty_char; }
+        case (ast.lit_int(_))   { sty = ty_int;  }
+        case (ast.lit_uint(_))  { sty = ty_uint; }
+        case (ast.lit_nil)      { sty = ty_nil;  }
+        case (ast.lit_bool(_))  { sty = ty_bool; }
+    }
+
+    ret plain_ty(sty);
+}
+
+fn check_expr(&@env e, &@ty_table locals, @ast.expr expr) -> @ast.expr {
+    alt (expr.node) {
+        case (ast.expr_lit(?lit, _)) {
+            auto ty = check_lit(e, lit);
+            ret @fold.respan[ast.expr_](expr.span,
+                                        ast.expr_lit(lit, ast.ann_type(ty)));
+        }
+        
+        case (_) {
+            // TODO
+            ret expr;
+        }
+    }
+}
+
+fn check_stmt(@env e, @ty_table locals, @ty ret_ty, &@ast.stmt stmt)
+        -> @ast.stmt {
+    alt (stmt.node) {
+        case (ast.stmt_decl(?decl)) {
+            alt (decl.node) {
+                case (ast.decl_local(?local)) {
+                    alt (local.init) {
+                        case (none[@ast.expr]) {
+                            // empty
+                        }
+
+                        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);
+                                }
+                            }
+                        }
+                    }
+                }
+
+                case (ast.decl_item(_)) {
+                    // Ignore for now. We'll return later.
+                }
+            }
+
+            ret stmt;
+        }
+
+        case (ast.stmt_ret(?expr_opt)) {
+            alt (expr_opt) {
+                case (none[@ast.expr]) {
+                    if (!are_compatible(ret_ty, plain_ty(ty_nil))) {
+                        e.sess.err("ret; in function returning non-void");
+                    }
+
+                    ret stmt;
+                }
+
+                case (some[@ast.expr](?expr)) {
+                    auto expr_t = check_expr(e, locals, expr);
+                    demand(e, expr.span, ret_ty, type_of(expr_t));
+                    ret @fold.respan[ast.stmt_](stmt.span,
+                                                ast.stmt_ret(some(expr_t)));
+                }
+            }
+        }
+
+        case (ast.stmt_log(?expr)) {
+            auto expr_t = check_expr(e, locals, expr);
+            ret @fold.respan[ast.stmt_](stmt.span, ast.stmt_log(expr_t));
+        }
+
+        case (ast.stmt_check_expr(?expr)) {
+            auto expr_t = check_expr(e, locals, expr);
+            demand(e, expr.span, plain_ty(ty_bool), type_of(expr_t));
+            ret @fold.respan[ast.stmt_](stmt.span,
+                                        ast.stmt_check_expr(expr_t));
+        }
+
+        case (ast.stmt_expr(?expr)) {
+            auto expr_t = check_expr(e, locals, expr);
+            if (!are_compatible(type_of(expr_t), plain_ty(ty_nil))) {
+                // TODO: real warning function
+                log "warning: expression used as statement should have " +
+                    "void type";
+            }
+
+            ret @fold.respan[ast.stmt_](stmt.span, ast.stmt_expr(expr_t));
+        }
+    }
+
+    fail;
+}
+
+fn check_block(&@env e, &@ty_table locals, @ty ret_ty, &ast.block block)
+        -> ast.block {
+    auto f = bind check_stmt(e, locals, ret_ty, _);
+    auto stmts_t = _vec.map[@ast.stmt,@ast.stmt](f, block.node.stmts);
+    ret fold.respan[ast.block_](block.span,
+                                rec(stmts=stmts_t, index=block.node.index));
+}
+
+fn check_fn(&@env e, &span sp, ast.ident ident, &ast._fn f, ast.def_id id,
+            ast.ann ann) -> @ast.item {
+    auto local_ty_table = @common.new_def_hash[@ty]();
+
+    // Store the type of each argument in the table.
+    let vec[arg] inputs = vec();
+    for (ast.arg arg in f.inputs) {
+        auto input_ty = ast_ty_to_ty_env(e, arg.ty);
+        inputs += vec(rec(mode=arg.mode, ty=input_ty));
+        local_ty_table.insert(arg.id, input_ty);
+    }
+
+    auto output_ty = ast_ty_to_ty_env(e, f.output);
+    auto fn_sty = ty_fn(inputs, output_ty);
+    auto fn_ann = ast.ann_type(plain_ty(fn_sty));
+
+    auto block_t = check_block(e, local_ty_table, output_ty, f.body);
+    auto block_wb = writeback(e, local_ty_table, block_t);
+    auto fn_t = rec(inputs=f.inputs, output=f.output, body=block_wb);
+    ret @fold.respan[ast.item_](sp, ast.item_fn(ident, fn_t, id, fn_ann));
+}
+
+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);
+
+    auto fld = fold.new_identity_fold[@env]();
+    auto f = check_fn;  // FIXME: trans_const_lval bug
+    fld = @rec(fold_item_fn = f with *fld);
+    ret fold.fold_crate[@env](e, fld, result._0);
+}
+
diff --git a/src/comp/rustc.rc b/src/comp/rustc.rc
index 7b314ad737a..ef9be053585 100644
--- a/src/comp/rustc.rc
+++ b/src/comp/rustc.rc
@@ -14,6 +14,7 @@ mod middle {
     mod fold;
     mod resolve;
     mod trans;
+    mod typeck;
 }
 
 mod back {