about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2012-05-15 08:29:22 -0700
committerNiko Matsakis <niko@alum.mit.edu>2012-05-15 13:38:29 -0700
commit26dc48d67f7f1281e40f5d4e89db17e19c3aea53 (patch)
treef16759eaecb74ced53f9a793e2e4d9b15924445c
parentadb61e3e992aa6539dd178f8b2c2f55aca942b16 (diff)
downloadrust-26dc48d67f7f1281e40f5d4e89db17e19c3aea53.tar.gz
rust-26dc48d67f7f1281e40f5d4e89db17e19c3aea53.zip
break apart typeck a little (more to come)
-rw-r--r--src/rustc/middle/typeck.rs1515
-rw-r--r--src/rustc/middle/typeck/astconv.rs711
-rw-r--r--src/rustc/middle/typeck/collect.rs343
-rw-r--r--src/rustc/middle/typeck/vtable.rs271
-rw-r--r--src/rustc/middle/typeck/writeback.rs184
-rw-r--r--src/rustc/rustc.rc7
6 files changed, 1531 insertions, 1500 deletions
diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs
index 6f9b767a870..f5f82bf2690 100644
--- a/src/rustc/middle/typeck.rs
+++ b/src/rustc/middle/typeck.rs
@@ -27,6 +27,11 @@ import util::common::{indent, indenter};
 import std::list;
 import list::{list, nil, cons};
 
+// from internal typeck modules:
+import astconv::{ast_ty_to_ty, in_anon_rscope,
+                 ast_region_to_region, region_scope,
+                 ast_conv};
+
 export check_crate;
 export method_map;
 export method_origin, serialize_method_origin, deserialize_method_origin;
@@ -329,11 +334,12 @@ fn ast_expr_vstore_to_vstore(fcx: @fn_ctxt, e: @ast::expr, n: uint,
       }
       ast::vstore_uniq { ty::vstore_uniq }
       ast::vstore_box { ty::vstore_box }
-      ast::vstore_slice(r) {
+      ast::vstore_slice(a_r) {
         alt fcx.block_region() {
           result::ok(b_r) {
             let rscope = in_anon_rscope(fcx, b_r);
-            ty::vstore_slice(ast_region_to_region(fcx, rscope, e.span, r))
+            let r = astconv::ast_region_to_region(fcx, rscope, e.span, a_r);
+            ty::vstore_slice(r)
           }
           result::err(msg) {
             fcx.ccx.tcx.sess.span_err(e.span, msg);
@@ -344,142 +350,6 @@ fn ast_expr_vstore_to_vstore(fcx: @fn_ctxt, e: @ast::expr, n: uint,
     }
 }
 
-iface ast_conv {
-    fn tcx() -> ty::ctxt;
-    fn ccx() -> @crate_ctxt;
-    fn get_item_ty(id: ast::def_id) -> ty::ty_param_bounds_and_ty;
-
-    // what type should we use when a type is omitted?
-    fn ty_infer(span: span) -> ty::t;
-}
-
-impl of ast_conv for @crate_ctxt {
-    fn tcx() -> ty::ctxt { self.tcx }
-    fn ccx() -> @crate_ctxt { self }
-
-    fn get_item_ty(id: ast::def_id) -> ty::ty_param_bounds_and_ty {
-        if id.crate != ast::local_crate {
-            csearch::get_type(self.tcx, id)
-        } else {
-            alt self.tcx.items.find(id.node) {
-              some(ast_map::node_item(item, _)) {
-                ty_of_item(self, item)
-              }
-              some(ast_map::node_native_item(native_item, _, _)) {
-                ty_of_native_item(self, native_item)
-              }
-              x {
-                self.tcx.sess.bug(#fmt["unexpected sort of item \
-                                        in get_item_ty(): %?", x]);
-              }
-            }
-        }
-    }
-
-    fn ty_infer(span: span) -> ty::t {
-        self.tcx.sess.span_bug(span,
-                               "found `ty_infer` in unexpected place");
-    }
-}
-
-impl of ast_conv for @fn_ctxt {
-    fn tcx() -> ty::ctxt { self.ccx.tcx }
-    fn ccx() -> @crate_ctxt { self.ccx }
-
-    fn get_item_ty(id: ast::def_id) -> ty::ty_param_bounds_and_ty {
-        ty::lookup_item_type(self.tcx(), id)
-    }
-
-    fn ty_infer(_span: span) -> ty::t {
-        self.next_ty_var()
-    }
-}
-
-iface region_scope {
-    fn anon_region() -> result<ty::region, str>;
-    fn named_region(id: str) -> result<ty::region, str>;
-}
-
-enum empty_rscope { empty_rscope }
-impl of region_scope for empty_rscope {
-    fn anon_region() -> result<ty::region, str> {
-        result::err("region types are not allowed here")
-    }
-    fn named_region(id: str) -> result<ty::region, str> {
-        if id == "static" { result::ok(ty::re_static) }
-        else { result::err("only the static region is allowed here") }
-    }
-}
-
-enum type_rscope = ast::region_param;
-impl of region_scope for type_rscope {
-    fn anon_region() -> result<ty::region, str> {
-        alt *self {
-          ast::rp_self { result::ok(ty::re_bound(ty::br_self)) }
-          ast::rp_none {
-            result::err("to use region types here, the containing type \
-                         must be declared with a region bound")
-          }
-        }
-    }
-    fn named_region(id: str) -> result<ty::region, str> {
-        empty_rscope.named_region(id).chain_err { |_e|
-            if id == "self" { self.anon_region() }
-            else {
-                result::err("named regions other than `self` are not \
-                             allowed as part of a type declaration")
-            }
-        }
-    }
-}
-
-impl of region_scope for @fn_ctxt {
-    fn anon_region() -> result<ty::region, str> {
-        result::ok(self.next_region_var())
-    }
-    fn named_region(id: str) -> result<ty::region, str> {
-        empty_rscope.named_region(id).chain_err { |_e|
-            alt self.in_scope_regions.find(ty::br_named(id)) {
-              some(r) { result::ok(r) }
-              none if id == "blk" { self.block_region() }
-              none {
-                result::err(#fmt["named region `%s` not in scope here", id])
-              }
-            }
-        }
-    }
-}
-
-enum anon_rscope = {anon: ty::region, base: region_scope};
-fn in_anon_rscope<RS: region_scope copy>(self: RS, r: ty::region)
-    -> @anon_rscope {
-    @anon_rscope({anon: r, base: self as region_scope})
-}
-impl of region_scope for @anon_rscope {
-    fn anon_region() -> result<ty::region, str> {
-        result::ok(self.anon)
-    }
-    fn named_region(id: str) -> result<ty::region, str> {
-        self.base.named_region(id)
-    }
-}
-
-enum binding_rscope = {base: region_scope};
-fn in_binding_rscope<RS: region_scope copy>(self: RS) -> @binding_rscope {
-    let base = self as region_scope;
-    @binding_rscope({base: base})
-}
-impl of region_scope for @binding_rscope {
-    fn anon_region() -> result<ty::region, str> {
-        result::ok(ty::re_bound(ty::br_anon))
-    }
-    fn named_region(id: str) -> result<ty::region, str> {
-        self.base.named_region(id).chain_err {|_e|
-            result::ok(ty::re_bound(ty::br_named(id)))
-        }
-    }
-}
-
 fn get_region_reporting_err(tcx: ty::ctxt,
                             span: span,
                             res: result<ty::region, str>) -> ty::region {
@@ -493,321 +363,6 @@ fn get_region_reporting_err(tcx: ty::ctxt,
     }
 }
 
-fn ast_region_to_region<AC: ast_conv, RS: region_scope>(
-    self: AC, rscope: RS, span: span, a_r: @ast::region) -> ty::region {
-
-    let res = alt a_r.node {
-      ast::re_anon { rscope.anon_region() }
-      ast::re_named(id) { rscope.named_region(id) }
-    };
-
-    get_region_reporting_err(self.tcx(), span, res)
-}
-
-fn ast_path_to_substs_and_ty<AC: ast_conv, RS: region_scope copy>(
-    self: AC, rscope: RS, did: ast::def_id,
-    path: @ast::path) -> ty_param_substs_and_ty {
-
-    let tcx = self.tcx();
-    let {bounds: decl_bounds, rp: decl_rp, ty: decl_ty} =
-        self.get_item_ty(did);
-
-    // If the type is parameterized by the self region, then replace self
-    // region with the current anon region binding (in other words,
-    // whatever & would get replaced with).
-    let self_r = alt (decl_rp, path.rp) {
-      (ast::rp_none, none) {
-        none
-      }
-      (ast::rp_none, some(_)) {
-        tcx.sess.span_err(
-            path.span,
-            #fmt["No region bound is permitted on %s, \
-                  which is not declared as containing region pointers",
-                 ty::item_path_str(tcx, did)]);
-        none
-      }
-      (ast::rp_self, none) {
-        let res = rscope.anon_region();
-        let r = get_region_reporting_err(self.tcx(), path.span, res);
-        some(r)
-      }
-      (ast::rp_self, some(r)) {
-        some(ast_region_to_region(self, rscope, path.span, r))
-      }
-    };
-
-    // Convert the type parameters supplied by the user.
-    if !vec::same_length(*decl_bounds, path.types) {
-        self.tcx().sess.span_fatal(
-            path.span,
-            #fmt["wrong number of type arguments, expected %u but found %u",
-                 (*decl_bounds).len(), path.types.len()]);
-    }
-    let tps = path.types.map { |a_t| ast_ty_to_ty(self, rscope, a_t) };
-
-    let substs = {self_r:self_r, self_ty:none, tps:tps};
-    {substs: substs, ty: ty::subst(tcx, substs, decl_ty)}
-}
-
-fn ast_path_to_ty<AC: ast_conv, RS: region_scope copy>(
-    self: AC,
-    rscope: RS,
-    did: ast::def_id,
-    path: @ast::path,
-    path_id: ast::node_id) -> ty_param_substs_and_ty {
-
-    // Lookup the polytype of the item and then substitute the provided types
-    // for any type/region parameters.
-    let tcx = self.tcx();
-    let {substs: substs, ty: ty} =
-        ast_path_to_substs_and_ty(self, rscope, did, path);
-    write_ty_to_tcx(tcx, path_id, ty);
-    write_substs_to_tcx(tcx, path_id, substs.tps);
-    ret {substs: substs, ty: ty};
-}
-
-/*
-  Instantiates the path for the given iface reference, assuming that
-  it's bound to a valid iface type. Returns the def_id for the defining
-  iface. Fails if the type is a type other than an iface type.
- */
-fn instantiate_iface_ref(ccx: @crate_ctxt, t: @ast::iface_ref,
-                         rp: ast::region_param)
-    -> (ast::def_id, ty_param_substs_and_ty) {
-
-    let sp = t.path.span, err = "can only implement interface types",
-        sess = ccx.tcx.sess;
-
-    let rscope = type_rscope(rp);
-
-    alt lookup_def_tcx(ccx.tcx, t.path.span, t.id) {
-      ast::def_ty(t_id) {
-        let tpt = ast_path_to_ty(ccx, rscope, t_id, t.path, t.id);
-        alt ty::get(tpt.ty).struct {
-           ty::ty_iface(*) {
-              (t_id, tpt)
-           }
-           _ { sess.span_fatal(sp, err); }
-        }
-      }
-      _ {
-          sess.span_fatal(sp, err);
-      }
-    }
-}
-
-const NO_REGIONS: uint = 1u;
-const NO_TPS: uint = 2u;
-
-// 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<AC: ast_conv, RS: region_scope copy>(
-    self: AC, rscope: RS, &&ast_ty: @ast::ty) -> ty::t {
-
-    fn ast_mt_to_mt<AC: ast_conv, RS: region_scope copy>(
-        self: AC, rscope: RS, mt: ast::mt) -> ty::mt {
-
-        ret {ty: ast_ty_to_ty(self, rscope, mt.ty), mutbl: mt.mutbl};
-    }
-
-    fn mk_vstore<AC: ast_conv, RS: region_scope copy>(
-        self: AC, rscope: RS, a_seq_ty: @ast::ty, vst: ty::vstore) -> ty::t {
-
-        let tcx = self.tcx();
-        let seq_ty = ast_ty_to_ty(self, rscope, a_seq_ty);
-
-        alt ty::get(seq_ty).struct {
-          ty::ty_vec(mt) {
-            ret ty::mk_evec(tcx, mt, vst);
-          }
-
-          ty::ty_str {
-            ret ty::mk_estr(tcx, vst);
-          }
-
-          _ {
-            tcx.sess.span_err(
-                a_seq_ty.span,
-                #fmt["Bound not allowed on a %s.",
-                     ty::ty_sort_str(tcx, seq_ty)]);
-            ret seq_ty;
-          }
-        }
-    }
-
-    fn check_path_args(tcx: ty::ctxt,
-                       path: @ast::path,
-                       flags: uint) {
-        if (flags & NO_TPS) != 0u {
-            if path.types.len() > 0u {
-                tcx.sess.span_err(
-                    path.span,
-                    "Type parameters are not allowed on this type.");
-            }
-        }
-
-        if (flags & NO_REGIONS) != 0u {
-            if path.rp.is_some() {
-                tcx.sess.span_err(
-                    path.span,
-                    "Region parameters are not allowed on this type.");
-            }
-        }
-    }
-
-    let tcx = self.tcx();
-
-    alt tcx.ast_ty_to_ty_cache.find(ast_ty) {
-      some(ty::atttce_resolved(ty)) { ret ty; }
-      some(ty::atttce_unresolved) {
-        tcx.sess.span_fatal(ast_ty.span, "illegal recursive type. \
-                                          insert a enum in the cycle, \
-                                          if this is desired)");
-      }
-      none { /* go on */ }
-    }
-
-    tcx.ast_ty_to_ty_cache.insert(ast_ty, ty::atttce_unresolved);
-    let typ = alt ast_ty.node {
-      ast::ty_nil { ty::mk_nil(tcx) }
-      ast::ty_bot { ty::mk_bot(tcx) }
-      ast::ty_box(mt) {
-        ty::mk_box(tcx, ast_mt_to_mt(self, rscope, mt))
-      }
-      ast::ty_uniq(mt) {
-        ty::mk_uniq(tcx, ast_mt_to_mt(self, rscope, mt))
-      }
-      ast::ty_vec(mt) {
-        ty::mk_vec(tcx, ast_mt_to_mt(self, rscope, mt))
-      }
-      ast::ty_ptr(mt) {
-        ty::mk_ptr(tcx, ast_mt_to_mt(self, rscope, mt))
-      }
-      ast::ty_rptr(region, mt) {
-        let r = ast_region_to_region(self, rscope, ast_ty.span, region);
-        let mt = ast_mt_to_mt(self, in_anon_rscope(rscope, r), mt);
-        ty::mk_rptr(tcx, r, mt)
-      }
-      ast::ty_tup(fields) {
-        let flds = vec::map(fields) { |t| ast_ty_to_ty(self, rscope, t) };
-        ty::mk_tup(tcx, flds)
-      }
-      ast::ty_rec(fields) {
-        let flds = fields.map {|f|
-            let tm = ast_mt_to_mt(self, rscope, f.node.mt);
-            {ident: f.node.ident, mt: tm}
-        };
-        ty::mk_rec(tcx, flds)
-      }
-      ast::ty_fn(proto, decl) {
-        ty::mk_fn(tcx, ty_of_fn_decl(self, rscope, proto, decl, none))
-      }
-      ast::ty_path(path, id) {
-        let a_def = alt tcx.def_map.find(id) {
-          none { tcx.sess.span_fatal(ast_ty.span, #fmt("unbound path %s",
-                                                       path_to_str(path))); }
-          some(d) { d }};
-        alt a_def {
-          ast::def_ty(did) | ast::def_class(did) {
-            ast_path_to_ty(self, rscope, did, path, id).ty
-          }
-          ast::def_prim_ty(nty) {
-            alt nty {
-              ast::ty_bool {
-                check_path_args(tcx, path, NO_TPS | NO_REGIONS);
-                ty::mk_bool(tcx)
-              }
-              ast::ty_int(it) {
-                check_path_args(tcx, path, NO_TPS | NO_REGIONS);
-                ty::mk_mach_int(tcx, it)
-              }
-              ast::ty_uint(uit) {
-                check_path_args(tcx, path, NO_TPS | NO_REGIONS);
-                ty::mk_mach_uint(tcx, uit)
-              }
-              ast::ty_float(ft) {
-                check_path_args(tcx, path, NO_TPS | NO_REGIONS);
-                ty::mk_mach_float(tcx, ft)
-              }
-              ast::ty_str {
-                check_path_args(tcx, path, NO_TPS);
-                // This is a bit of a hack, but basically str/& needs to be
-                // converted into a vstore:
-                alt path.rp {
-                  none {
-                    ty::mk_str(tcx)
-                  }
-                  some(ast_r) {
-                    let r = ast_region_to_region(self, rscope,
-                                                 ast_ty.span, ast_r);
-                    ty::mk_estr(tcx, ty::vstore_slice(r))
-                  }
-                }
-              }
-            }
-          }
-          ast::def_ty_param(id, n) {
-            check_path_args(tcx, path, NO_TPS | NO_REGIONS);
-            ty::mk_param(tcx, n, id)
-          }
-          ast::def_self(_) {
-            // n.b.: resolve guarantees that the self type only appears in an
-            // iface, which we rely upon in various places when creating
-            // substs
-            ty::mk_self(tcx)
-          }
-          _ {
-            tcx.sess.span_fatal(ast_ty.span,
-                                "found type name used as a variable");
-          }
-        }
-      }
-      ast::ty_vstore(a_t, ast::vstore_slice(a_r)) {
-        let r = ast_region_to_region(self, rscope, ast_ty.span, a_r);
-        mk_vstore(self, in_anon_rscope(rscope, r), a_t, ty::vstore_slice(r))
-      }
-      ast::ty_vstore(a_t, ast::vstore_uniq) {
-        mk_vstore(self, rscope, a_t, ty::vstore_uniq)
-      }
-      ast::ty_vstore(a_t, ast::vstore_box) {
-        mk_vstore(self, rscope, a_t, ty::vstore_box)
-      }
-      ast::ty_vstore(a_t, ast::vstore_fixed(some(u))) {
-        mk_vstore(self, rscope, a_t, ty::vstore_fixed(u))
-      }
-      ast::ty_vstore(_, ast::vstore_fixed(none)) {
-        tcx.sess.span_bug(
-            ast_ty.span,
-            "implied fixed length for bound");
-      }
-      ast::ty_constr(t, cs) {
-        let mut out_cs = [];
-        for cs.each {|constr|
-            out_cs += [ty::ast_constr_to_constr(tcx, constr)];
-        }
-        ty::mk_constr(tcx, ast_ty_to_ty(self, rscope, t), out_cs)
-      }
-      ast::ty_infer {
-        // ty_infer should only appear as the type of arguments or return
-        // values in a fn_expr, or as the type of local variables.  Both of
-        // these cases are handled specially and should not descend into this
-        // routine.
-        self.tcx().sess.span_bug(
-            ast_ty.span,
-            "found `ty_infer` in unexpected place");
-      }
-      ast::ty_mac(_) {
-        tcx.sess.span_bug(ast_ty.span,
-                          "found `ty_mac` in unexpected place");
-      }
-    };
-
-    tcx.ast_ty_to_ty_cache.insert(ast_ty, ty::atttce_resolved(typ));
-    ret typ;
-}
-
 fn check_bounds_are_used(ccx: @crate_ctxt,
                          span: span,
                          tps: [ast::ty_param],
@@ -851,102 +406,6 @@ fn check_bounds_are_used(ccx: @crate_ctxt,
     }
 }
 
-fn ty_of_item(ccx: @crate_ctxt, it: @ast::item)
-    -> ty::ty_param_bounds_and_ty {
-
-    let def_id = local_def(it.id);
-    let tcx = ccx.tcx;
-    alt tcx.tcache.find(def_id) {
-      some(tpt) { ret tpt; }
-      _ {}
-    }
-    alt it.node {
-      ast::item_const(t, _) {
-        let typ = ccx.to_ty(empty_rscope, t);
-        let tpt = no_params(typ);
-        tcx.tcache.insert(local_def(it.id), tpt);
-        ret tpt;
-      }
-      ast::item_fn(decl, tps, _) {
-        let bounds = ty_param_bounds(ccx, tps);
-        let tofd = ty_of_fn_decl(ccx, empty_rscope, ast::proto_bare,
-                                 decl, none);
-        let tpt = {bounds: bounds,
-                   rp: ast::rp_none, // functions do not have a self
-                   ty: ty::mk_fn(ccx.tcx, tofd)};
-        ccx.tcx.tcache.insert(local_def(it.id), tpt);
-        ret tpt;
-      }
-      ast::item_ty(t, tps, rp) {
-        alt tcx.tcache.find(local_def(it.id)) {
-          some(tpt) { ret tpt; }
-          none { }
-        }
-
-        let tpt = {
-            let ty = {
-                let t0 = ccx.to_ty(type_rscope(rp), t);
-                // Do not associate a def id with a named, parameterized type
-                // like "foo<X>".  This is because otherwise ty_to_str will
-                // print the name as merely "foo", as it has no way to
-                // reconstruct the value of X.
-                if !vec::is_empty(tps) { t0 } else {
-                    ty::mk_with_id(tcx, t0, def_id)
-                }
-            };
-            {bounds: ty_param_bounds(ccx, tps), rp: rp, ty: ty}
-        };
-
-        check_bounds_are_used(ccx, t.span, tps, rp, tpt.ty);
-
-        tcx.tcache.insert(local_def(it.id), tpt);
-        ret tpt;
-      }
-      ast::item_res(decl, tps, _, _, _, rp) {
-        let {bounds, substs} = mk_substs(ccx, tps, rp);
-        let t_arg = ty_of_arg(ccx, type_rscope(rp),
-                              decl.inputs[0], none);
-        let t = ty::mk_res(tcx, local_def(it.id), t_arg.ty, substs);
-        let t_res = {bounds: bounds, rp: rp, ty: t};
-        tcx.tcache.insert(local_def(it.id), t_res);
-        ret t_res;
-      }
-      ast::item_enum(_, tps, rp) {
-        // Create a new generic polytype.
-        let {bounds, substs} = mk_substs(ccx, tps, rp);
-        let t = ty::mk_enum(tcx, local_def(it.id), substs);
-        let tpt = {bounds: bounds, rp: rp, ty: t};
-        tcx.tcache.insert(local_def(it.id), tpt);
-        ret tpt;
-      }
-      ast::item_iface(tps, rp, ms) {
-        let {bounds, substs} = mk_substs(ccx, tps, rp);
-        let t = ty::mk_iface(tcx, local_def(it.id), substs);
-        let tpt = {bounds: bounds, rp: rp, ty: t};
-        tcx.tcache.insert(local_def(it.id), tpt);
-        ret tpt;
-      }
-      ast::item_class(tps, _, _, _, _, rp) {
-          let {bounds,substs} = mk_substs(ccx, tps, rp);
-          let t = ty::mk_class(tcx, local_def(it.id), substs);
-          let tpt = {bounds: bounds, rp: rp, ty: t};
-          tcx.tcache.insert(local_def(it.id), tpt);
-          ret tpt;
-      }
-      ast::item_impl(*) | ast::item_mod(_) |
-      ast::item_native_mod(_) { fail; }
-    }
-}
-fn ty_of_native_item(ccx: @crate_ctxt, it: @ast::native_item)
-    -> ty::ty_param_bounds_and_ty {
-    alt it.node {
-      ast::native_item_fn(fn_decl, params) {
-        ret ty_of_native_fn_decl(ccx, fn_decl, params,
-                                 local_def(it.id));
-      }
-    }
-}
-
 type next_region_param_id = { mut id: uint };
 
 fn collect_bound_regions_in_tys(
@@ -1047,166 +506,6 @@ fn replace_bound_regions(
     }
 }
 
-fn ty_of_arg<AC: ast_conv, RS: region_scope copy>(
-    self: AC, rscope: RS, a: ast::arg,
-    expected_ty: option<ty::arg>) -> ty::arg {
-
-    let ty = alt a.ty.node {
-      ast::ty_infer if expected_ty.is_some() {expected_ty.get().ty}
-      ast::ty_infer {self.ty_infer(a.ty.span)}
-      _ {ast_ty_to_ty(self, rscope, a.ty)}
-    };
-
-    let mode = {
-        alt a.mode {
-          ast::infer(_) if expected_ty.is_some() {
-            result::get(ty::unify_mode(self.tcx(), a.mode,
-                                       expected_ty.get().mode))
-          }
-          ast::infer(_) {
-            alt ty::get(ty).struct {
-              // If the type is not specified, then this must be a fn expr.
-              // Leave the mode as infer(_), it will get inferred based
-              // on constraints elsewhere.
-              ty::ty_var(_) {a.mode}
-
-              // If the type is known, then use the default for that type.
-              // Here we unify m and the default.  This should update the
-              // tables in tcx but should never fail, because nothing else
-              // will have been unified with m yet:
-              _ {
-                let m1 = ast::expl(ty::default_arg_mode_for_ty(ty));
-                result::get(ty::unify_mode(self.tcx(), a.mode, m1))
-              }
-            }
-          }
-          ast::expl(_) {a.mode}
-        }
-    };
-
-    {mode: mode, ty: ty}
-}
-
-type expected_tys = option<{inputs: [ty::arg],
-                            output: ty::t}>;
-
-fn ty_of_fn_decl<AC: ast_conv, RS: region_scope copy>(
-    self: AC, rscope: RS,
-    proto: ast::proto,
-    decl: ast::fn_decl,
-    expected_tys: expected_tys) -> ty::fn_ty {
-
-    #debug["ty_of_fn_decl"];
-    indent {||
-        // new region names that appear inside of the fn decl are bound to
-        // that function type
-        let rb = in_binding_rscope(rscope);
-
-        let input_tys = decl.inputs.mapi { |i, a|
-            let expected_arg_ty = expected_tys.chain { |e|
-                // no guarantee that the correct number of expected args
-                // were supplied
-                if i < e.inputs.len() {some(e.inputs[i])} else {none}
-            };
-            ty_of_arg(self, rb, a, expected_arg_ty)
-        };
-
-        let expected_ret_ty = expected_tys.map { |e| e.output };
-        let output_ty = alt decl.output.node {
-          ast::ty_infer if expected_ret_ty.is_some() {expected_ret_ty.get()}
-          ast::ty_infer {self.ty_infer(decl.output.span)}
-          _ {ast_ty_to_ty(self, rb, decl.output)}
-        };
-
-        let out_constrs = vec::map(decl.constraints) {|constr|
-            ty::ast_constr_to_constr(self.tcx(), constr)
-        };
-        {proto: proto, inputs: input_tys,
-         output: output_ty, ret_style: decl.cf, constraints: out_constrs}
-    }
-}
-
-fn ty_of_native_fn_decl(ccx: @crate_ctxt,
-                        decl: ast::fn_decl,
-                        ty_params: [ast::ty_param],
-                        def_id: ast::def_id) -> ty::ty_param_bounds_and_ty {
-
-    let bounds = ty_param_bounds(ccx, ty_params);
-    let rb = in_binding_rscope(empty_rscope);
-    let input_tys = decl.inputs.map { |a| ty_of_arg(ccx, rb, a, none) };
-    let output_ty = ast_ty_to_ty(ccx, rb, decl.output);
-
-    let t_fn = ty::mk_fn(ccx.tcx, {proto: ast::proto_bare,
-                                   inputs: input_tys,
-                                   output: output_ty,
-                                   ret_style: ast::return_val,
-                                   constraints: []});
-    let tpt = {bounds: bounds, rp: ast::rp_none, ty: t_fn};
-    ccx.tcx.tcache.insert(def_id, tpt);
-    ret tpt;
-}
-
-fn ty_param_bounds(ccx: @crate_ctxt,
-                   params: [ast::ty_param]) -> @[ty::param_bounds] {
-
-    fn compute_bounds(ccx: @crate_ctxt,
-                      param: ast::ty_param) -> ty::param_bounds {
-        @vec::flat_map(*param.bounds) { |b|
-            alt b {
-              ast::bound_send { [ty::bound_send] }
-              ast::bound_copy { [ty::bound_copy] }
-              ast::bound_iface(t) {
-                let ity = ast_ty_to_ty(ccx, empty_rscope, t);
-                alt ty::get(ity).struct {
-                  ty::ty_iface(*) {
-                    [ty::bound_iface(ity)]
-                  }
-                  _ {
-                    ccx.tcx.sess.span_err(
-                        t.span, "type parameter bounds must be \
-                                 interface types");
-                    []
-                  }
-                }
-              }
-            }
-        }
-    }
-
-    @params.map { |param|
-        alt ccx.tcx.ty_param_bounds.find(param.id) {
-          some(bs) { bs }
-          none {
-            let bounds = compute_bounds(ccx, param);
-            ccx.tcx.ty_param_bounds.insert(param.id, bounds);
-            bounds
-          }
-        }
-    }
-}
-
-fn ty_of_method(ccx: @crate_ctxt,
-                m: @ast::method,
-                rp: ast::region_param) -> ty::method {
-    {ident: m.ident,
-     tps: ty_param_bounds(ccx, m.tps),
-     fty: ty_of_fn_decl(ccx, type_rscope(rp), ast::proto_bare,
-                        m.decl, none),
-     purity: m.decl.purity,
-     vis: m.vis}
-}
-
-fn ty_of_ty_method(self: @crate_ctxt,
-                   m: ast::ty_method,
-                   rp: ast::region_param) -> ty::method {
-    {ident: m.ident,
-     tps: ty_param_bounds(self, m.tps),
-     fty: ty_of_fn_decl(self, type_rscope(rp), ast::proto_bare,
-                        m.decl, none),
-     // assume public, because this is only invoked on iface methods
-     purity: m.decl.purity, vis: ast::public}
-}
-
 // Functions that write types into the node type table
 fn write_ty_to_tcx(tcx: ty::ctxt, node_id: ast::node_id, ty: ty::t) {
     #debug["write_ty_to_tcx(%d, %s)", node_id, ty_to_str(tcx, ty)];
@@ -1365,7 +664,7 @@ fn mk_ty_params(ccx: @crate_ctxt, atps: [ast::ty_param])
     -> {bounds: @[ty::param_bounds], params: [ty::t]} {
 
     let mut i = 0u;
-    let bounds = ty_param_bounds(ccx, atps);
+    let bounds = astconv::ty_param_bounds(ccx, atps);
     {bounds: bounds,
      params: vec::map(atps, {|atp|
          let t = ty::mk_param(ccx.tcx, i, local_def(atp.id));
@@ -1430,326 +729,6 @@ fn compare_impl_method(tcx: ty::ctxt, sp: span, impl_m: ty::method,
     }
 }
 
-// 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.
-mod collect {
-    fn get_enum_variant_types(ccx: @crate_ctxt,
-                              enum_ty: ty::t,
-                              variants: [ast::variant],
-                              ty_params: [ast::ty_param],
-                              rp: ast::region_param) {
-        let tcx = ccx.tcx;
-
-        // Create a set of parameter types shared among all the variants.
-        for variants.each {|variant|
-            // Nullary enum constructors get turned into constants; n-ary enum
-            // constructors get turned into functions.
-            let result_ty = if vec::len(variant.node.args) == 0u {
-                enum_ty
-            } else {
-                let rs = type_rscope(rp);
-                let args = variant.node.args.map { |va|
-                    let arg_ty = ccx.to_ty(rs, va.ty);
-                    {mode: ast::expl(ast::by_copy), ty: arg_ty}
-                };
-                ty::mk_fn(tcx, {proto: ast::proto_box,
-                                inputs: args,
-                                output: enum_ty,
-                                ret_style: ast::return_val,
-                                constraints: []})
-            };
-            let tpt = {bounds: ty_param_bounds(ccx, ty_params),
-                       rp: rp,
-                       ty: result_ty};
-            tcx.tcache.insert(local_def(variant.node.id), tpt);
-            write_ty_to_tcx(tcx, variant.node.id, result_ty);
-        }
-    }
-
-    fn ensure_iface_methods(ccx: @crate_ctxt, id: ast::node_id) {
-        fn store_methods<T>(ccx: @crate_ctxt, id: ast::node_id,
-                            stuff: [T], f: fn@(T) -> ty::method) {
-            ty::store_iface_methods(ccx.tcx, id, @vec::map(stuff, f));
-        }
-
-        let tcx = ccx.tcx;
-        alt check tcx.items.get(id) {
-          ast_map::node_item(@{node: ast::item_iface(_, rp, ms), _}, _) {
-              store_methods::<ast::ty_method>(ccx, id, ms) {|m|
-                  ty_of_ty_method(ccx, m, rp)
-              };
-          }
-          ast_map::node_item(@{node: ast::item_class(_,_,its,_,_,rp), _}, _) {
-              let (_,ms) = split_class_items(its);
-              // All methods need to be stored, since lookup_method
-              // relies on the same method cache for self-calls
-              store_methods::<@ast::method>(ccx, id, ms) {|m|
-                  ty_of_method(ccx, m, rp)
-              };
-          }
-        }
-    }
-
-    fn check_methods_against_iface(ccx: @crate_ctxt,
-                                   tps: [ast::ty_param],
-                                   rp: ast::region_param,
-                                   selfty: ty::t,
-                                   a_ifacety: @ast::iface_ref,
-                                   ms: [@ast::method]) {
-
-        let tcx = ccx.tcx;
-        let i_bounds = ty_param_bounds(ccx, tps);
-        let my_methods = convert_methods(ccx, ms, rp, i_bounds, selfty);
-        let (did, tpt) = instantiate_iface_ref(ccx, a_ifacety, rp);
-        if did.crate == ast::local_crate {
-            ensure_iface_methods(ccx, did.node);
-        }
-        for vec::each(*ty::iface_methods(tcx, did)) {|if_m|
-            alt vec::find(my_methods, {|m| if_m.ident == m.mty.ident}) {
-              some({mty: m, id, span}) {
-                if m.purity != if_m.purity {
-                    ccx.tcx.sess.span_err(
-                        span, #fmt["method `%s`'s purity \
-                                    not match the iface method's \
-                                    purity", m.ident]);
-                }
-                let mt = compare_impl_method(
-                    ccx.tcx, span, m, vec::len(tps),
-                    if_m, tpt.substs, selfty);
-                let old = tcx.tcache.get(local_def(id));
-                if old.ty != mt {
-                    tcx.tcache.insert(
-                        local_def(id),
-                        {bounds: old.bounds,
-                         rp: old.rp,
-                         ty: mt});
-                    write_ty_to_tcx(tcx, id, mt);
-                }
-              }
-              none {
-                tcx.sess.span_err(
-                    a_ifacety.path.span,
-                    #fmt["missing method `%s`", if_m.ident]);
-              }
-            } // alt
-        } // |if_m|
-    } // fn
-
-    fn convert_class_item(ccx: @crate_ctxt,
-                          rp: ast::region_param,
-                          v: ast_util::ivar) {
-        /* we want to do something here, b/c within the
-         scope of the class, it's ok to refer to fields &
-        methods unqualified */
-        /* they have these types *within the scope* of the
-         class. outside the class, it's done with expr_field */
-        let tt = ccx.to_ty(type_rscope(rp), v.ty);
-        write_ty_to_tcx(ccx.tcx, v.id, tt);
-    }
-
-    fn convert_methods(ccx: @crate_ctxt,
-                       ms: [@ast::method],
-                       rp: ast::region_param,
-                       i_bounds: @[ty::param_bounds],
-                       self_ty: ty::t)
-        -> [{mty: ty::method, id: ast::node_id, span: span}] {
-
-        let tcx = ccx.tcx;
-        vec::map(ms) { |m|
-            write_ty_to_tcx(tcx, m.self_id, self_ty);
-            let bounds = ty_param_bounds(ccx, m.tps);
-            let mty = ty_of_method(ccx, m, rp);
-            let fty = ty::mk_fn(tcx, mty.fty);
-            tcx.tcache.insert(
-                local_def(m.id),
-                // n.b. This code is kind of sketchy (concat'ing i_bounds
-                // with bounds), but removing *i_bounds breaks other stuff
-                {bounds: @(*i_bounds + *bounds), rp: rp, ty: fty});
-            write_ty_to_tcx(tcx, m.id, fty);
-            {mty: mty, id: m.id, span: m.span}
-        }
-    }
-
-    fn convert(ccx: @crate_ctxt, it: @ast::item) {
-        let tcx = ccx.tcx;
-        alt it.node {
-          // These don't define types.
-          ast::item_mod(_) {}
-          ast::item_native_mod(m) {
-            if syntax::attr::native_abi(it.attrs) ==
-               either::right(ast::native_abi_rust_intrinsic) {
-                for m.items.each { |item| check_intrinsic_type(ccx, item); }
-            }
-          }
-          ast::item_enum(variants, ty_params, rp) {
-            let tpt = ty_of_item(ccx, it);
-            write_ty_to_tcx(tcx, it.id, tpt.ty);
-            get_enum_variant_types(ccx, tpt.ty, variants,
-                                   ty_params, rp);
-          }
-          ast::item_impl(tps, rp, ifce, selfty, ms) {
-            let i_bounds = ty_param_bounds(ccx, tps);
-            let selfty = ccx.to_ty(type_rscope(rp), selfty);
-            write_ty_to_tcx(tcx, it.id, selfty);
-            tcx.tcache.insert(local_def(it.id),
-                              {bounds: i_bounds,
-                               rp: rp,
-                               ty: selfty});
-            alt ifce {
-              some(t) {
-                check_methods_against_iface(
-                    ccx, tps, rp,
-                    selfty, t, ms);
-              }
-              _ {
-                // Still have to do this to write method types
-                // into the table
-                convert_methods(
-                    ccx, ms, rp,
-                    i_bounds, selfty);
-              }
-            }
-          }
-          ast::item_res(decl, tps, _, dtor_id, ctor_id, rp) {
-            let {bounds, substs} = mk_substs(ccx, tps, rp);
-            let def_id = local_def(it.id);
-            let t_arg = ty_of_arg(ccx, type_rscope(rp),
-                                  decl.inputs[0], none);
-            let t_res = ty::mk_res(tcx, def_id, t_arg.ty, substs);
-
-            let t_ctor = ty::mk_fn(tcx, {
-                proto: ast::proto_box,
-                inputs: [{mode: ast::expl(ast::by_copy), ty: t_arg.ty}],
-                output: t_res,
-                ret_style: ast::return_val, constraints: []
-            });
-            let t_dtor = ty::mk_fn(tcx, {
-                proto: ast::proto_box,
-                inputs: [t_arg], output: ty::mk_nil(tcx),
-                ret_style: ast::return_val, constraints: []
-            });
-            write_ty_to_tcx(tcx, it.id, t_res);
-            write_ty_to_tcx(tcx, ctor_id, t_ctor);
-            tcx.tcache.insert(local_def(ctor_id),
-                              {bounds: bounds,
-                               rp: rp,
-                               ty: t_ctor});
-            tcx.tcache.insert(def_id, {bounds: bounds,
-                                       rp: rp,
-                                       ty: t_res});
-            write_ty_to_tcx(tcx, dtor_id, t_dtor);
-          }
-          ast::item_iface(*) {
-            let tpt = ty_of_item(ccx, it);
-            #debug["item_iface(it.id=%d, tpt.ty=%s)",
-                   it.id, ty_to_str(tcx, tpt.ty)];
-            write_ty_to_tcx(tcx, it.id, tpt.ty);
-            ensure_iface_methods(ccx, it.id);
-          }
-          ast::item_class(tps, ifaces, members, ctor, m_dtor, rp) {
-            // Write the class type
-            let tpt = ty_of_item(ccx, it);
-            write_ty_to_tcx(tcx, it.id, tpt.ty);
-            // Write the ctor type
-            let t_ctor =
-                ty::mk_fn(tcx,
-                          ty_of_fn_decl(ccx,
-                                        empty_rscope,
-                                        ast::proto_any,
-                                        ctor.node.dec,
-                                        none));
-            write_ty_to_tcx(tcx, ctor.node.id, t_ctor);
-            tcx.tcache.insert(local_def(ctor.node.id),
-                              {bounds: tpt.bounds,
-                               rp: ast::rp_none,
-                               ty: t_ctor});
-            option::iter(m_dtor) {|dtor|
-              // Write the dtor type
-              let t_dtor = ty::mk_fn(tcx,
-                                   // not sure about empty_rscope
-                                   // FIXME
-                                   ty_of_fn_decl(ccx,
-                                                 empty_rscope,
-                                                 ast::proto_any,
-                                                 ast_util::dtor_dec(),
-                                                 none));
-              write_ty_to_tcx(tcx, dtor.node.id, t_dtor);
-              tcx.tcache.insert(local_def(dtor.node.id),
-                                {bounds: tpt.bounds,
-                                 rp: ast::rp_none,
-                                 ty: t_dtor});
-            };
-            ensure_iface_methods(ccx, it.id);
-            /* FIXME: check for proper public/privateness */
-            // Write the type of each of the members
-            let (fields, methods) = split_class_items(members);
-            for fields.each {|f|
-                convert_class_item(ccx, rp, f);
-            }
-            // The selfty is just the class type
-            let {bounds:_, substs} = mk_substs(ccx, tps, rp);
-            let selfty = ty::mk_class(tcx, local_def(it.id), substs);
-            // Need to convert all methods so we can check internal
-            // references to private methods
-
-            // NDM to TJC---I think we ought to be using bounds here, not @[].
-            // But doing so causes errors later on.
-            convert_methods(ccx, methods, rp, @[], selfty);
-
-            /*
-            Finally, check that the class really implements the ifaces
-            that it claims to implement.
-            */
-            for ifaces.each { |ifce|
-                check_methods_against_iface(ccx, tps, rp, selfty,
-                                            ifce, methods);
-                let t = ty::node_id_to_type(tcx, ifce.id);
-
-                // FIXME: This assumes classes only implement
-                // non-parameterized ifaces. add a test case for
-                // a class implementing a parameterized iface.
-                // -- tjc (#1726)
-                tcx.tcache.insert(local_def(ifce.id), no_params(t));
-            }
-          }
-          _ {
-            // This call populates the type cache with the converted type
-            // of the item in passing. All we have to do here is to write
-            // it into the node type table.
-            let tpt = ty_of_item(ccx, it);
-            write_ty_to_tcx(tcx, it.id, tpt.ty);
-          }
-       }
-    }
-    fn convert_native(ccx: @crate_ctxt, i: @ast::native_item) {
-        // As above, this call populates the type table with the converted
-        // type of the native item. We simply write it into the node type
-        // table.
-        let tpt = ty_of_native_item(ccx, i);
-        alt i.node {
-          ast::native_item_fn(_, _) {
-            write_ty_to_tcx(ccx.tcx, i.id, tpt.ty);
-          }
-        }
-    }
-    fn collect_item_types(ccx: @crate_ctxt, crate: @ast::crate) {
-        visit::visit_crate(*crate, (), visit::mk_simple_visitor(@{
-            visit_item: bind convert(ccx, _),
-            visit_native_item: bind convert_native(ccx, _)
-            with *visit::default_simple_visitor()
-        }));
-    }
-}
-
-
 fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t {
     let mut t1 = t;
     let mut enum_dids = [];
@@ -1866,196 +845,6 @@ fn are_compatible(fcx: @fn_ctxt, expected: ty::t, actual: ty::t) -> bool {
     }
 }
 
-
-// Type resolution: the phase that finds all the types in the AST with
-// unresolved type variables and replaces "ty_var" types with their
-// substitutions.
-mod writeback {
-
-    export resolve_type_vars_in_fn;
-    export resolve_type_vars_in_expr;
-
-    fn resolve_type_vars_in_type(fcx: @fn_ctxt, sp: span, typ: ty::t) ->
-       option<ty::t> {
-        if !ty::type_needs_infer(typ) { ret some(typ); }
-        alt infer::resolve_deep(fcx.infcx, typ, true) {
-          result::ok(new_type) { ret some(new_type); }
-          result::err(e) {
-            if !fcx.ccx.tcx.sess.has_errors() {
-                fcx.ccx.tcx.sess.span_err(
-                    sp,
-                    #fmt["cannot determine a type \
-                          for this expression: %s",
-                         infer::fixup_err_to_str(e)])
-            }
-            ret none;
-          }
-        }
-    }
-    fn resolve_type_vars_for_node(wbcx: wb_ctxt, sp: span, id: ast::node_id)
-        -> option<ty::t> {
-        let fcx = wbcx.fcx, tcx = fcx.ccx.tcx;
-        let n_ty = fcx.node_ty(id);
-        alt resolve_type_vars_in_type(fcx, sp, n_ty) {
-          none {
-            wbcx.success = false;
-            ret none;
-          }
-
-          some(t) {
-            #debug["resolve_type_vars_for_node(id=%d, n_ty=%s, t=%s)",
-                   id, ty_to_str(tcx, n_ty), ty_to_str(tcx, t)];
-            write_ty_to_tcx(tcx, id, t);
-            alt fcx.opt_node_ty_substs(id) {
-              some(substs) {
-                let mut new_tps = [];
-                for substs.tps.each {|subst|
-                    alt resolve_type_vars_in_type(fcx, sp, subst) {
-                      some(t) { new_tps += [t]; }
-                      none { wbcx.success = false; ret none; }
-                    }
-                }
-                write_substs_to_tcx(tcx, id, new_tps);
-              }
-              none {}
-            }
-            ret some(t);
-          }
-        }
-    }
-
-    fn maybe_resolve_type_vars_for_node(wbcx: wb_ctxt, sp: span,
-                                        id: ast::node_id)
-        -> option<ty::t> {
-        if wbcx.fcx.node_types.contains_key(id as uint) {
-            resolve_type_vars_for_node(wbcx, sp, id)
-        } else {
-            none
-        }
-    }
-
-    type wb_ctxt =
-        // As soon as we hit an error we have to stop resolving
-        // the entire function
-        {fcx: @fn_ctxt, mut success: bool};
-    type wb_vt = visit::vt<wb_ctxt>;
-
-    fn visit_stmt(s: @ast::stmt, wbcx: wb_ctxt, v: wb_vt) {
-        if !wbcx.success { ret; }
-        resolve_type_vars_for_node(wbcx, s.span, ty::stmt_node_id(s));
-        visit::visit_stmt(s, wbcx, v);
-    }
-    fn visit_expr(e: @ast::expr, wbcx: wb_ctxt, v: wb_vt) {
-        if !wbcx.success { ret; }
-        resolve_type_vars_for_node(wbcx, e.span, e.id);
-        alt e.node {
-          ast::expr_fn(_, decl, _, _) |
-          ast::expr_fn_block(decl, _, _) {
-            vec::iter(decl.inputs) {|input|
-                let r_ty = resolve_type_vars_for_node(wbcx, e.span, input.id);
-
-                // Just in case we never constrained the mode to anything,
-                // constrain it to the default for the type in question.
-                alt (r_ty, input.mode) {
-                  (some(t), ast::infer(_)) {
-                    let tcx = wbcx.fcx.ccx.tcx;
-                    let m_def = ty::default_arg_mode_for_ty(t);
-                    ty::set_default_mode(tcx, input.mode, m_def);
-                  }
-                  _ {}
-                }
-            }
-          }
-
-          ast::expr_new(_, alloc_id, _) {
-            resolve_type_vars_for_node(wbcx, e.span, alloc_id);
-          }
-
-          ast::expr_binary(_, _, _) | ast::expr_unary(_, _) |
-          ast::expr_assign_op(_, _, _) | ast::expr_index(_, _) {
-            maybe_resolve_type_vars_for_node(wbcx, e.span,
-                                             ast_util::op_expr_callee_id(e));
-          }
-
-          _ { }
-        }
-        visit::visit_expr(e, wbcx, v);
-    }
-    fn visit_block(b: ast::blk, wbcx: wb_ctxt, v: wb_vt) {
-        if !wbcx.success { ret; }
-        resolve_type_vars_for_node(wbcx, b.span, b.node.id);
-        visit::visit_block(b, wbcx, v);
-    }
-    fn visit_pat(p: @ast::pat, wbcx: wb_ctxt, v: wb_vt) {
-        if !wbcx.success { ret; }
-        resolve_type_vars_for_node(wbcx, p.span, p.id);
-        #debug["Type for pattern binding %s (id %d) resolved to %s",
-               pat_to_str(p), p.id,
-               wbcx.fcx.ty_to_str(
-                   ty::node_id_to_type(wbcx.fcx.ccx.tcx,
-                                       p.id))];
-        visit::visit_pat(p, wbcx, v);
-    }
-    fn visit_local(l: @ast::local, wbcx: wb_ctxt, v: wb_vt) {
-        if !wbcx.success { ret; }
-        let var_id = lookup_local(wbcx.fcx, l.span, l.node.id);
-        alt infer::resolve_deep_var(wbcx.fcx.infcx, var_id, true) {
-          result::ok(lty) {
-            #debug["Type for local %s (id %d) resolved to %s",
-                   pat_to_str(l.node.pat), l.node.id,
-                   wbcx.fcx.ty_to_str(lty)];
-            write_ty_to_tcx(wbcx.fcx.ccx.tcx, l.node.id, lty);
-          }
-          result::err(e) {
-            wbcx.fcx.ccx.tcx.sess.span_err(
-                l.span,
-                #fmt["cannot determine a type \
-                      for this local variable: %s",
-                     infer::fixup_err_to_str(e)]);
-            wbcx.success = false;
-          }
-        }
-        visit::visit_local(l, wbcx, v);
-    }
-    fn visit_item(_item: @ast::item, _wbcx: wb_ctxt, _v: wb_vt) {
-        // Ignore items
-    }
-
-    fn resolve_type_vars_in_expr(fcx: @fn_ctxt, e: @ast::expr) -> bool {
-        let wbcx = {fcx: fcx, mut success: true};
-        let visit =
-            visit::mk_vt(@{visit_item: visit_item,
-                           visit_stmt: visit_stmt,
-                           visit_expr: visit_expr,
-                           visit_block: visit_block,
-                           visit_pat: visit_pat,
-                           visit_local: visit_local
-                              with *visit::default_visitor()});
-        visit.visit_expr(e, wbcx, visit);
-        ret wbcx.success;
-    }
-
-    fn resolve_type_vars_in_fn(fcx: @fn_ctxt,
-                               decl: ast::fn_decl,
-                               blk: ast::blk) -> bool {
-        let wbcx = {fcx: fcx, mut success: true};
-        let visit =
-            visit::mk_vt(@{visit_item: visit_item,
-                           visit_stmt: visit_stmt,
-                           visit_expr: visit_expr,
-                           visit_block: visit_block,
-                           visit_pat: visit_pat,
-                           visit_local: visit_local
-                              with *visit::default_visitor()});
-        visit.visit_block(blk, wbcx, visit);
-        for decl.inputs.each {|arg|
-            resolve_type_vars_for_node(wbcx, arg.ty.span, arg.id);
-        }
-        ret wbcx.success;
-    }
-
-}
-
 fn check_intrinsic_type(ccx: @crate_ctxt, it: @ast::native_item) {
     fn param(ccx: @crate_ctxt, n: uint) -> ty::t {
         ty::mk_param(ccx.tcx, n, local_def(0))
@@ -2099,7 +888,7 @@ fn check_intrinsic_type(ccx: @crate_ctxt, it: @ast::native_item) {
                               inputs: inputs, output: output,
                               ret_style: ast::return_val,
                               constraints: []});
-    let i_ty = ty_of_native_item(ccx, it);
+    let i_ty = astconv::ty_of_native_item(ccx, it);
     let i_n_tps = (*i_ty.bounds).len();
     if i_n_tps != n_tps {
         tcx.sess.span_err(it.span, #fmt("intrinsic has wrong number \
@@ -2454,7 +1243,7 @@ fn impl_self_ty(fcx: @fn_ctxt, did: ast::def_id) -> ty_param_substs_and_ty {
                                   _}, _)) {
             {n_tps: ts.len(),
              rp: rp,
-             raw_ty: fcx.ccx.to_ty(type_rscope(rp), st)}
+             raw_ty: fcx.ccx.to_ty(astconv::type_rscope(rp), st)}
           }
           some(ast_map::node_item(@{node: ast::item_class(ts,
                                  _,_,_,_,rp), id: class_id, _},_)) {
@@ -3170,8 +1959,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
 
         // construct the function type
         let fty = ty::mk_fn(tcx,
-                            ty_of_fn_decl(fcx, fcx, proto, decl,
-                                          expected_tys));
+                            astconv::ty_of_fn_decl(fcx, fcx, proto, decl,
+                                                   expected_tys));
 
         #debug("check_expr_fn_with_unifier %s fty=%s",
                expr_to_str(expr), fcx.ty_to_str(fty));
@@ -4407,7 +3196,7 @@ fn class_types(ccx: @crate_ctxt, members: [@ast::class_member],
                rp: ast::region_param) -> class_map {
 
     let rslt = int_hash::<ty::t>();
-    let rs = type_rscope(rp);
+    let rs = astconv::type_rscope(rp);
     for members.each { |m|
       alt m.node {
          ast::instance_var(_,t,_,id,_) {
@@ -4416,7 +3205,7 @@ fn class_types(ccx: @crate_ctxt, members: [@ast::class_member],
          ast::class_method(mth) {
            rslt.insert(mth.id,
                        ty::mk_fn(ccx.tcx,
-                                 ty_of_method(ccx, mth, rp).fty));
+                                 collect::ty_of_method(ccx, mth, rp).fty));
          }
       }
     }
@@ -4447,7 +3236,7 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) {
         check_bare_fn(ccx, decl, body, dtor_id, none);
       }
       ast::item_impl(tps, rp, _, ty, ms) {
-        let self_ty = ccx.to_ty(type_rscope(rp), ty);
+        let self_ty = ccx.to_ty(astconv::type_rscope(rp), ty);
         for ms.each {|m| check_method(ccx, m, self_ty);}
       }
       ast::item_class(tps, ifaces, members, ctor, m_dtor, rp) {
@@ -4545,278 +3334,6 @@ fn check_for_main_fn(ccx: @crate_ctxt, crate: @ast::crate) {
     }
 }
 
-mod vtable {
-    fn has_iface_bounds(tps: [ty::param_bounds]) -> bool {
-        vec::any(tps, {|bs|
-            vec::any(*bs, {|b|
-                alt b { ty::bound_iface(_) { true } _ { false } }
-            })
-        })
-    }
-
-    fn lookup_vtables(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span,
-                      bounds: @[ty::param_bounds], substs: ty::substs,
-                      allow_unsafe: bool) -> vtable_res {
-        let tcx = fcx.ccx.tcx;
-        let mut result = [], i = 0u;
-        for substs.tps.each {|ty|
-            for vec::each(*bounds[i]) {|bound|
-                alt bound {
-                  ty::bound_iface(i_ty) {
-                    let i_ty = ty::subst(tcx, substs, i_ty);
-                    result += [lookup_vtable(fcx, isc, sp, ty, i_ty,
-                                             allow_unsafe)];
-                  }
-                  _ {}
-                }
-            }
-            i += 1u;
-        }
-        @result
-    }
-
-    fn fixup_substs(fcx: @fn_ctxt, sp: span,
-                    id: ast::def_id, substs: ty::substs) -> ty::substs {
-        let tcx = fcx.ccx.tcx;
-        // use a dummy type just to package up the substs that need fixing up
-        let t = ty::mk_iface(tcx, id, substs);
-        let t_f = fixup_ty(fcx, sp, t);
-        alt check ty::get(t_f).struct {
-          ty::ty_iface(_, substs_f) { substs_f }
-        }
-    }
-
-    fn relate_iface_tys(fcx: @fn_ctxt, sp: span,
-                        exp_iface_ty: ty::t, act_iface_ty: ty::t) {
-        demand::suptype(fcx, sp, exp_iface_ty, act_iface_ty)
-    }
-
-    /*
-      Look up the vtable to use when treating an item of type <t>
-      as if it has type <iface_ty>
-     */
-    fn lookup_vtable(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span,
-                     ty: ty::t, iface_ty: ty::t, allow_unsafe: bool)
-        -> vtable_origin {
-
-        #debug["lookup_vtable(ty=%s, iface_ty=%s)",
-               fcx.ty_to_str(ty), fcx.ty_to_str(iface_ty)];
-        let _i = indenter();
-
-        let tcx = fcx.ccx.tcx;
-        let (iface_id, iface_substs) = alt check ty::get(iface_ty).struct {
-            ty::ty_iface(did, substs) { (did, substs) }
-        };
-        let ty = fixup_ty(fcx, sp, ty);
-        alt ty::get(ty).struct {
-          ty::ty_param(n, did) {
-            let mut n_bound = 0u;
-            for vec::each(*tcx.ty_param_bounds.get(did.node)) { |bound|
-                alt bound {
-                  ty::bound_send | ty::bound_copy { /* ignore */ }
-                  ty::bound_iface(ity) {
-                    alt check ty::get(ity).struct {
-                      ty::ty_iface(idid, substs) {
-                        if iface_id == idid {
-                            relate_iface_tys(fcx, sp, iface_ty, ity);
-                            ret vtable_param(n, n_bound);
-                        }
-                      }
-                    }
-                    n_bound += 1u;
-                  }
-                }
-            }
-          }
-
-          ty::ty_iface(did, substs) if iface_id == did {
-            relate_iface_tys(fcx, sp, iface_ty, ty);
-            if !allow_unsafe {
-                for vec::each(*ty::iface_methods(tcx, did)) {|m|
-                    if ty::type_has_self(ty::mk_fn(tcx, m.fty)) {
-                        tcx.sess.span_err(
-                            sp, "a boxed iface with self types may not be \
-                                 passed as a bounded type");
-                    } else if (*m.tps).len() > 0u {
-                        tcx.sess.span_err(
-                            sp, "a boxed iface with generic methods may not \
-                                 be passed as a bounded type");
-
-                    }
-                }
-            }
-            ret vtable_iface(did, substs.tps);
-          }
-
-          _ {
-            let mut found = [];
-
-            for list::each(isc) {|impls|
-            /* For each impl in scope... */
-                for vec::each(*impls) {|im|
-                    // im = one specific impl
-                    // find the iface that im implements (if any)
-                    let of_ty = alt ty::impl_iface(tcx, im.did) {
-                      some(of_ty) { of_ty }
-                      _ { cont; }
-                    };
-
-                    // it must have the same id as the expected one
-                    alt ty::get(of_ty).struct {
-                      ty::ty_iface(id, _) if id != iface_id { cont; }
-                      _ { /* ok */ }
-                    }
-
-                    // check whether the type unifies with the type
-                    // that the impl is for, and continue if not
-                    let {substs: substs, ty: for_ty} =
-                        impl_self_ty(fcx, im.did);
-                    let im_bs = ty::lookup_item_type(tcx, im.did).bounds;
-                    alt fcx.mk_subty(ty, for_ty) {
-                      result::err(_) { cont; }
-                      result::ok(()) { }
-                    }
-
-                    // check that desired iface type unifies
-                    let of_ty = ty::subst(tcx, substs, of_ty);
-                    relate_iface_tys(fcx, sp, iface_ty, of_ty);
-
-                    // recursively process the bounds
-                    let iface_tps = iface_substs.tps;
-                    let substs_f = fixup_substs(fcx, sp, iface_id, substs);
-                    connect_iface_tps(fcx, sp, substs_f.tps,
-                                      iface_tps, im.did);
-                    let subres = lookup_vtables(fcx, isc, sp,
-                                                im_bs, substs_f, false);
-                    found += [vtable_static(im.did, substs_f.tps, subres)];
-                }
-
-                alt found.len() {
-                  0u { /* fallthrough */ }
-                  1u { ret found[0]; }
-                  _ {
-                    fcx.ccx.tcx.sess.span_err(
-                        sp, "multiple applicable methods in scope");
-                    ret found[0];
-                  }
-                }
-            }
-          }
-        }
-
-        tcx.sess.span_fatal(
-            sp, "failed to find an implementation of interface " +
-            ty_to_str(tcx, iface_ty) + " for " +
-            ty_to_str(tcx, ty));
-    }
-
-    fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t) -> ty::t {
-        let tcx = fcx.ccx.tcx;
-        alt infer::resolve_deep(fcx.infcx, ty, true) {
-          result::ok(new_type) { new_type }
-          result::err(e) {
-            tcx.sess.span_fatal(
-                sp,
-                #fmt["cannot determine a type \
-                      for this bounded type parameter: %s",
-                     infer::fixup_err_to_str(e)])
-          }
-        }
-    }
-
-    fn connect_iface_tps(fcx: @fn_ctxt, sp: span, impl_tys: [ty::t],
-                         iface_tys: [ty::t], impl_did: ast::def_id) {
-        let tcx = fcx.ccx.tcx;
-        let ity = option::get(ty::impl_iface(tcx, impl_did));
-        let iface_ty = ty::subst_tps(tcx, impl_tys, ity);
-        alt check ty::get(iface_ty).struct {
-          ty::ty_iface(_, substs) {
-            vec::iter2(substs.tps, iface_tys,
-                       {|a, b| demand::suptype(fcx, sp, a, b);});
-          }
-        }
-    }
-
-    fn resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, v: visit::vt<@fn_ctxt>) {
-        let cx = fcx.ccx;
-        alt ex.node {
-          ast::expr_path(*) {
-            alt fcx.opt_node_ty_substs(ex.id) {
-              some(substs) {
-                let did = ast_util::def_id_of_def(cx.tcx.def_map.get(ex.id));
-                let item_ty = ty::lookup_item_type(cx.tcx, did);
-                if has_iface_bounds(*item_ty.bounds) {
-                    let impls = cx.impl_map.get(ex.id);
-                    cx.vtable_map.insert(ex.id, lookup_vtables(
-                        fcx, impls, ex.span,
-                        item_ty.bounds, substs, false));
-                }
-              }
-              _ {}
-            }
-          }
-          // Must resolve bounds on methods with bounded params
-          ast::expr_field(*) | ast::expr_binary(*) |
-          ast::expr_unary(*) | ast::expr_assign_op(*) |
-          ast::expr_index(*) {
-            alt cx.method_map.find(ex.id) {
-              some(method_static(did)) {
-                let bounds = ty::lookup_item_type(cx.tcx, did).bounds;
-                if has_iface_bounds(*bounds) {
-                    let callee_id = alt ex.node {
-                      ast::expr_field(_, _, _) { ex.id }
-                      _ { ast_util::op_expr_callee_id(ex) }
-                    };
-                    let substs = fcx.node_ty_substs(callee_id);
-                    let iscs = cx.impl_map.get(ex.id);
-                    cx.vtable_map.insert(callee_id, lookup_vtables(
-                        fcx, iscs, ex.span, bounds, substs, false));
-                }
-              }
-              _ {}
-            }
-          }
-          ast::expr_cast(src, _) {
-            let target_ty = fcx.expr_ty(ex);
-            alt ty::get(target_ty).struct {
-              ty::ty_iface(*) {
-               /* Casting to an interface type.
-                  Look up all impls for the cast expr...
-               */
-                let impls = cx.impl_map.get(ex.id);
-                /*
-                  Look up vtables for the type we're casting to,
-                  passing in the source and target type
-                 */
-                let vtable = lookup_vtable(fcx, impls, ex.span,
-                                           fcx.expr_ty(src), target_ty,
-                                           true);
-                /*
-                  Map this expression to that vtable (that is: "ex has
-                  vtable <vtable>")
-                 */
-                cx.vtable_map.insert(ex.id, @[vtable]);
-              }
-              _ {}
-            }
-          }
-          _ {}
-        }
-        visit::visit_expr(ex, fcx, v);
-    }
-
-    // Detect points where an interface-bounded type parameter is
-    // instantiated, resolve the impls for the parameters.
-    fn resolve_in_block(fcx: @fn_ctxt, bl: ast::blk) {
-        visit::visit_block(bl, fcx, visit::mk_vt(@{
-            visit_expr: resolve_expr,
-            visit_item: fn@(_i: @ast::item, &&_e: @fn_ctxt,
-                            _v: visit::vt<@fn_ctxt>) {}
-            with *visit::default_visitor()
-        }));
-    }
-}
-
 fn check_crate(tcx: ty::ctxt, impl_map: resolve::impl_map,
                crate: @ast::crate) -> (method_map, vtable_map) {
     let ccx = @{impl_map: impl_map,
diff --git a/src/rustc/middle/typeck/astconv.rs b/src/rustc/middle/typeck/astconv.rs
new file mode 100644
index 00000000000..8f5eacf7708
--- /dev/null
+++ b/src/rustc/middle/typeck/astconv.rs
@@ -0,0 +1,711 @@
+#[doc = "
+
+Conversion from AST representation of types to the ty.rs representation.
+
+The main routine here is `ast_ty_to_ty()`: each use is parameterized
+by an instance of `ast_conv` and a `region_scope`.
+
+The `ast_conv` interface is the conversion context.  It has two
+implementations, one for the crate context and one for the function
+context.  The main purpose is to provide the `get_item_ty()` hook
+which looks up the type of an item by its def-id.  This can be done in
+two ways: in the initial phase, when a crate context is provided, this
+will potentially trigger a call to `ty_of_item()`.  Later, when a
+function context is used, this will simply be a lookup.
+
+The `region_scope` interface controls how region references are
+handled.  It has two methods which are used to resolve anonymous
+region references (e.g., `&T`) and named region references (e.g.,
+`&a.T`).  There are numerous region scopes that can be used, but most
+commonly you want either `empty_rscope`, which permits only the static
+region, or `type_rscope`, which permits the self region if the type in
+question is parameterized by a region.
+
+"];
+
+iface ast_conv {
+    fn tcx() -> ty::ctxt;
+    fn ccx() -> @crate_ctxt;
+    fn get_item_ty(id: ast::def_id) -> ty::ty_param_bounds_and_ty;
+
+    // what type should we use when a type is omitted?
+    fn ty_infer(span: span) -> ty::t;
+}
+
+impl of ast_conv for @crate_ctxt {
+    fn tcx() -> ty::ctxt { self.tcx }
+    fn ccx() -> @crate_ctxt { self }
+
+    fn get_item_ty(id: ast::def_id) -> ty::ty_param_bounds_and_ty {
+        if id.crate != ast::local_crate {
+            csearch::get_type(self.tcx, id)
+        } else {
+            alt self.tcx.items.find(id.node) {
+              some(ast_map::node_item(item, _)) {
+                ty_of_item(self, item)
+              }
+              some(ast_map::node_native_item(native_item, _, _)) {
+                ty_of_native_item(self, native_item)
+              }
+              x {
+                self.tcx.sess.bug(#fmt["unexpected sort of item \
+                                        in get_item_ty(): %?", x]);
+              }
+            }
+        }
+    }
+
+    fn ty_infer(span: span) -> ty::t {
+        self.tcx.sess.span_bug(span,
+                               "found `ty_infer` in unexpected place");
+    }
+}
+
+impl of ast_conv for @fn_ctxt {
+    fn tcx() -> ty::ctxt { self.ccx.tcx }
+    fn ccx() -> @crate_ctxt { self.ccx }
+
+    fn get_item_ty(id: ast::def_id) -> ty::ty_param_bounds_and_ty {
+        ty::lookup_item_type(self.tcx(), id)
+    }
+
+    fn ty_infer(_span: span) -> ty::t {
+        self.next_ty_var()
+    }
+}
+
+iface region_scope {
+    fn anon_region() -> result<ty::region, str>;
+    fn named_region(id: str) -> result<ty::region, str>;
+}
+
+enum empty_rscope { empty_rscope }
+impl of region_scope for empty_rscope {
+    fn anon_region() -> result<ty::region, str> {
+        result::err("region types are not allowed here")
+    }
+    fn named_region(id: str) -> result<ty::region, str> {
+        if id == "static" { result::ok(ty::re_static) }
+        else { result::err("only the static region is allowed here") }
+    }
+}
+
+enum type_rscope = ast::region_param;
+impl of region_scope for type_rscope {
+    fn anon_region() -> result<ty::region, str> {
+        alt *self {
+          ast::rp_self { result::ok(ty::re_bound(ty::br_self)) }
+          ast::rp_none {
+            result::err("to use region types here, the containing type \
+                         must be declared with a region bound")
+          }
+        }
+    }
+    fn named_region(id: str) -> result<ty::region, str> {
+        empty_rscope.named_region(id).chain_err { |_e|
+            if id == "self" { self.anon_region() }
+            else {
+                result::err("named regions other than `self` are not \
+                             allowed as part of a type declaration")
+            }
+        }
+    }
+}
+
+impl of region_scope for @fn_ctxt {
+    fn anon_region() -> result<ty::region, str> {
+        result::ok(self.next_region_var())
+    }
+    fn named_region(id: str) -> result<ty::region, str> {
+        empty_rscope.named_region(id).chain_err { |_e|
+            alt self.in_scope_regions.find(ty::br_named(id)) {
+              some(r) { result::ok(r) }
+              none if id == "blk" { self.block_region() }
+              none {
+                result::err(#fmt["named region `%s` not in scope here", id])
+              }
+            }
+        }
+    }
+}
+
+enum anon_rscope = {anon: ty::region, base: region_scope};
+fn in_anon_rscope<RS: region_scope copy>(self: RS, r: ty::region)
+    -> @anon_rscope {
+    @anon_rscope({anon: r, base: self as region_scope})
+}
+impl of region_scope for @anon_rscope {
+    fn anon_region() -> result<ty::region, str> {
+        result::ok(self.anon)
+    }
+    fn named_region(id: str) -> result<ty::region, str> {
+        self.base.named_region(id)
+    }
+}
+
+enum binding_rscope = {base: region_scope};
+fn in_binding_rscope<RS: region_scope copy>(self: RS) -> @binding_rscope {
+    let base = self as region_scope;
+    @binding_rscope({base: base})
+}
+impl of region_scope for @binding_rscope {
+    fn anon_region() -> result<ty::region, str> {
+        result::ok(ty::re_bound(ty::br_anon))
+    }
+    fn named_region(id: str) -> result<ty::region, str> {
+        self.base.named_region(id).chain_err {|_e|
+            result::ok(ty::re_bound(ty::br_named(id)))
+        }
+    }
+}
+
+fn ast_region_to_region<AC: ast_conv, RS: region_scope>(
+    self: AC, rscope: RS, span: span, a_r: @ast::region) -> ty::region {
+
+    let res = alt a_r.node {
+      ast::re_anon { rscope.anon_region() }
+      ast::re_named(id) { rscope.named_region(id) }
+    };
+
+    get_region_reporting_err(self.tcx(), span, res)
+}
+
+fn ast_path_to_substs_and_ty<AC: ast_conv, RS: region_scope copy>(
+    self: AC, rscope: RS, did: ast::def_id,
+    path: @ast::path) -> ty_param_substs_and_ty {
+
+    let tcx = self.tcx();
+    let {bounds: decl_bounds, rp: decl_rp, ty: decl_ty} =
+        self.get_item_ty(did);
+
+    // If the type is parameterized by the self region, then replace self
+    // region with the current anon region binding (in other words,
+    // whatever & would get replaced with).
+    let self_r = alt (decl_rp, path.rp) {
+      (ast::rp_none, none) {
+        none
+      }
+      (ast::rp_none, some(_)) {
+        tcx.sess.span_err(
+            path.span,
+            #fmt["No region bound is permitted on %s, \
+                  which is not declared as containing region pointers",
+                 ty::item_path_str(tcx, did)]);
+        none
+      }
+      (ast::rp_self, none) {
+        let res = rscope.anon_region();
+        let r = get_region_reporting_err(self.tcx(), path.span, res);
+        some(r)
+      }
+      (ast::rp_self, some(r)) {
+        some(ast_region_to_region(self, rscope, path.span, r))
+      }
+    };
+
+    // Convert the type parameters supplied by the user.
+    if !vec::same_length(*decl_bounds, path.types) {
+        self.tcx().sess.span_fatal(
+            path.span,
+            #fmt["wrong number of type arguments, expected %u but found %u",
+                 (*decl_bounds).len(), path.types.len()]);
+    }
+    let tps = path.types.map { |a_t| ast_ty_to_ty(self, rscope, a_t) };
+
+    let substs = {self_r:self_r, self_ty:none, tps:tps};
+    {substs: substs, ty: ty::subst(tcx, substs, decl_ty)}
+}
+
+fn ast_path_to_ty<AC: ast_conv, RS: region_scope copy>(
+    self: AC,
+    rscope: RS,
+    did: ast::def_id,
+    path: @ast::path,
+    path_id: ast::node_id) -> ty_param_substs_and_ty {
+
+    // Lookup the polytype of the item and then substitute the provided types
+    // for any type/region parameters.
+    let tcx = self.tcx();
+    let {substs: substs, ty: ty} =
+        ast_path_to_substs_and_ty(self, rscope, did, path);
+    write_ty_to_tcx(tcx, path_id, ty);
+    write_substs_to_tcx(tcx, path_id, substs.tps);
+    ret {substs: substs, ty: ty};
+}
+
+/*
+  Instantiates the path for the given iface reference, assuming that
+  it's bound to a valid iface type. Returns the def_id for the defining
+  iface. Fails if the type is a type other than an iface type.
+ */
+fn instantiate_iface_ref(ccx: @crate_ctxt, t: @ast::iface_ref,
+                         rp: ast::region_param)
+    -> (ast::def_id, ty_param_substs_and_ty) {
+
+    let sp = t.path.span, err = "can only implement interface types",
+        sess = ccx.tcx.sess;
+
+    let rscope = type_rscope(rp);
+
+    alt lookup_def_tcx(ccx.tcx, t.path.span, t.id) {
+      ast::def_ty(t_id) {
+        let tpt = ast_path_to_ty(ccx, rscope, t_id, t.path, t.id);
+        alt ty::get(tpt.ty).struct {
+           ty::ty_iface(*) {
+              (t_id, tpt)
+           }
+           _ { sess.span_fatal(sp, err); }
+        }
+      }
+      _ {
+          sess.span_fatal(sp, err);
+      }
+    }
+}
+
+const NO_REGIONS: uint = 1u;
+const NO_TPS: uint = 2u;
+
+// 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<AC: ast_conv, RS: region_scope copy>(
+    self: AC, rscope: RS, &&ast_ty: @ast::ty) -> ty::t {
+
+    fn ast_mt_to_mt<AC: ast_conv, RS: region_scope copy>(
+        self: AC, rscope: RS, mt: ast::mt) -> ty::mt {
+
+        ret {ty: ast_ty_to_ty(self, rscope, mt.ty), mutbl: mt.mutbl};
+    }
+
+    fn mk_vstore<AC: ast_conv, RS: region_scope copy>(
+        self: AC, rscope: RS, a_seq_ty: @ast::ty, vst: ty::vstore) -> ty::t {
+
+        let tcx = self.tcx();
+        let seq_ty = ast_ty_to_ty(self, rscope, a_seq_ty);
+
+        alt ty::get(seq_ty).struct {
+          ty::ty_vec(mt) {
+            ret ty::mk_evec(tcx, mt, vst);
+          }
+
+          ty::ty_str {
+            ret ty::mk_estr(tcx, vst);
+          }
+
+          _ {
+            tcx.sess.span_err(
+                a_seq_ty.span,
+                #fmt["Bound not allowed on a %s.",
+                     ty::ty_sort_str(tcx, seq_ty)]);
+            ret seq_ty;
+          }
+        }
+    }
+
+    fn check_path_args(tcx: ty::ctxt,
+                       path: @ast::path,
+                       flags: uint) {
+        if (flags & NO_TPS) != 0u {
+            if path.types.len() > 0u {
+                tcx.sess.span_err(
+                    path.span,
+                    "Type parameters are not allowed on this type.");
+            }
+        }
+
+        if (flags & NO_REGIONS) != 0u {
+            if path.rp.is_some() {
+                tcx.sess.span_err(
+                    path.span,
+                    "Region parameters are not allowed on this type.");
+            }
+        }
+    }
+
+    let tcx = self.tcx();
+
+    alt tcx.ast_ty_to_ty_cache.find(ast_ty) {
+      some(ty::atttce_resolved(ty)) { ret ty; }
+      some(ty::atttce_unresolved) {
+        tcx.sess.span_fatal(ast_ty.span, "illegal recursive type. \
+                                          insert a enum in the cycle, \
+                                          if this is desired)");
+      }
+      none { /* go on */ }
+    }
+
+    tcx.ast_ty_to_ty_cache.insert(ast_ty, ty::atttce_unresolved);
+    let typ = alt ast_ty.node {
+      ast::ty_nil { ty::mk_nil(tcx) }
+      ast::ty_bot { ty::mk_bot(tcx) }
+      ast::ty_box(mt) {
+        ty::mk_box(tcx, ast_mt_to_mt(self, rscope, mt))
+      }
+      ast::ty_uniq(mt) {
+        ty::mk_uniq(tcx, ast_mt_to_mt(self, rscope, mt))
+      }
+      ast::ty_vec(mt) {
+        ty::mk_vec(tcx, ast_mt_to_mt(self, rscope, mt))
+      }
+      ast::ty_ptr(mt) {
+        ty::mk_ptr(tcx, ast_mt_to_mt(self, rscope, mt))
+      }
+      ast::ty_rptr(region, mt) {
+        let r = ast_region_to_region(self, rscope, ast_ty.span, region);
+        let mt = ast_mt_to_mt(self, in_anon_rscope(rscope, r), mt);
+        ty::mk_rptr(tcx, r, mt)
+      }
+      ast::ty_tup(fields) {
+        let flds = vec::map(fields) { |t| ast_ty_to_ty(self, rscope, t) };
+        ty::mk_tup(tcx, flds)
+      }
+      ast::ty_rec(fields) {
+        let flds = fields.map {|f|
+            let tm = ast_mt_to_mt(self, rscope, f.node.mt);
+            {ident: f.node.ident, mt: tm}
+        };
+        ty::mk_rec(tcx, flds)
+      }
+      ast::ty_fn(proto, decl) {
+        ty::mk_fn(tcx, ty_of_fn_decl(self, rscope, proto, decl, none))
+      }
+      ast::ty_path(path, id) {
+        let a_def = alt tcx.def_map.find(id) {
+          none { tcx.sess.span_fatal(ast_ty.span, #fmt("unbound path %s",
+                                                       path_to_str(path))); }
+          some(d) { d }};
+        alt a_def {
+          ast::def_ty(did) | ast::def_class(did) {
+            ast_path_to_ty(self, rscope, did, path, id).ty
+          }
+          ast::def_prim_ty(nty) {
+            alt nty {
+              ast::ty_bool {
+                check_path_args(tcx, path, NO_TPS | NO_REGIONS);
+                ty::mk_bool(tcx)
+              }
+              ast::ty_int(it) {
+                check_path_args(tcx, path, NO_TPS | NO_REGIONS);
+                ty::mk_mach_int(tcx, it)
+              }
+              ast::ty_uint(uit) {
+                check_path_args(tcx, path, NO_TPS | NO_REGIONS);
+                ty::mk_mach_uint(tcx, uit)
+              }
+              ast::ty_float(ft) {
+                check_path_args(tcx, path, NO_TPS | NO_REGIONS);
+                ty::mk_mach_float(tcx, ft)
+              }
+              ast::ty_str {
+                check_path_args(tcx, path, NO_TPS);
+                // This is a bit of a hack, but basically str/& needs to be
+                // converted into a vstore:
+                alt path.rp {
+                  none {
+                    ty::mk_str(tcx)
+                  }
+                  some(ast_r) {
+                    let r = ast_region_to_region(self, rscope,
+                                                 ast_ty.span, ast_r);
+                    ty::mk_estr(tcx, ty::vstore_slice(r))
+                  }
+                }
+              }
+            }
+          }
+          ast::def_ty_param(id, n) {
+            check_path_args(tcx, path, NO_TPS | NO_REGIONS);
+            ty::mk_param(tcx, n, id)
+          }
+          ast::def_self(_) {
+            // n.b.: resolve guarantees that the self type only appears in an
+            // iface, which we rely upon in various places when creating
+            // substs
+            ty::mk_self(tcx)
+          }
+          _ {
+            tcx.sess.span_fatal(ast_ty.span,
+                                "found type name used as a variable");
+          }
+        }
+      }
+      ast::ty_vstore(a_t, ast::vstore_slice(a_r)) {
+        let r = ast_region_to_region(self, rscope, ast_ty.span, a_r);
+        mk_vstore(self, in_anon_rscope(rscope, r), a_t, ty::vstore_slice(r))
+      }
+      ast::ty_vstore(a_t, ast::vstore_uniq) {
+        mk_vstore(self, rscope, a_t, ty::vstore_uniq)
+      }
+      ast::ty_vstore(a_t, ast::vstore_box) {
+        mk_vstore(self, rscope, a_t, ty::vstore_box)
+      }
+      ast::ty_vstore(a_t, ast::vstore_fixed(some(u))) {
+        mk_vstore(self, rscope, a_t, ty::vstore_fixed(u))
+      }
+      ast::ty_vstore(_, ast::vstore_fixed(none)) {
+        tcx.sess.span_bug(
+            ast_ty.span,
+            "implied fixed length for bound");
+      }
+      ast::ty_constr(t, cs) {
+        let mut out_cs = [];
+        for cs.each {|constr|
+            out_cs += [ty::ast_constr_to_constr(tcx, constr)];
+        }
+        ty::mk_constr(tcx, ast_ty_to_ty(self, rscope, t), out_cs)
+      }
+      ast::ty_infer {
+        // ty_infer should only appear as the type of arguments or return
+        // values in a fn_expr, or as the type of local variables.  Both of
+        // these cases are handled specially and should not descend into this
+        // routine.
+        self.tcx().sess.span_bug(
+            ast_ty.span,
+            "found `ty_infer` in unexpected place");
+      }
+      ast::ty_mac(_) {
+        tcx.sess.span_bug(ast_ty.span,
+                          "found `ty_mac` in unexpected place");
+      }
+    };
+
+    tcx.ast_ty_to_ty_cache.insert(ast_ty, ty::atttce_resolved(typ));
+    ret typ;
+}
+
+fn ty_of_item(ccx: @crate_ctxt, it: @ast::item)
+    -> ty::ty_param_bounds_and_ty {
+
+    let def_id = local_def(it.id);
+    let tcx = ccx.tcx;
+    alt tcx.tcache.find(def_id) {
+      some(tpt) { ret tpt; }
+      _ {}
+    }
+    alt it.node {
+      ast::item_const(t, _) {
+        let typ = ccx.to_ty(empty_rscope, t);
+        let tpt = no_params(typ);
+        tcx.tcache.insert(local_def(it.id), tpt);
+        ret tpt;
+      }
+      ast::item_fn(decl, tps, _) {
+        let bounds = ty_param_bounds(ccx, tps);
+        let tofd = ty_of_fn_decl(ccx, empty_rscope, ast::proto_bare,
+                                 decl, none);
+        let tpt = {bounds: bounds,
+                   rp: ast::rp_none, // functions do not have a self
+                   ty: ty::mk_fn(ccx.tcx, tofd)};
+        ccx.tcx.tcache.insert(local_def(it.id), tpt);
+        ret tpt;
+      }
+      ast::item_ty(t, tps, rp) {
+        alt tcx.tcache.find(local_def(it.id)) {
+          some(tpt) { ret tpt; }
+          none { }
+        }
+
+        let tpt = {
+            let ty = {
+                let t0 = ccx.to_ty(type_rscope(rp), t);
+                // Do not associate a def id with a named, parameterized type
+                // like "foo<X>".  This is because otherwise ty_to_str will
+                // print the name as merely "foo", as it has no way to
+                // reconstruct the value of X.
+                if !vec::is_empty(tps) { t0 } else {
+                    ty::mk_with_id(tcx, t0, def_id)
+                }
+            };
+            {bounds: ty_param_bounds(ccx, tps), rp: rp, ty: ty}
+        };
+
+        check_bounds_are_used(ccx, t.span, tps, rp, tpt.ty);
+
+        tcx.tcache.insert(local_def(it.id), tpt);
+        ret tpt;
+      }
+      ast::item_res(decl, tps, _, _, _, rp) {
+        let {bounds, substs} = mk_substs(ccx, tps, rp);
+        let t_arg = ty_of_arg(ccx, type_rscope(rp),
+                              decl.inputs[0], none);
+        let t = ty::mk_res(tcx, local_def(it.id), t_arg.ty, substs);
+        let t_res = {bounds: bounds, rp: rp, ty: t};
+        tcx.tcache.insert(local_def(it.id), t_res);
+        ret t_res;
+      }
+      ast::item_enum(_, tps, rp) {
+        // Create a new generic polytype.
+        let {bounds, substs} = mk_substs(ccx, tps, rp);
+        let t = ty::mk_enum(tcx, local_def(it.id), substs);
+        let tpt = {bounds: bounds, rp: rp, ty: t};
+        tcx.tcache.insert(local_def(it.id), tpt);
+        ret tpt;
+      }
+      ast::item_iface(tps, rp, ms) {
+        let {bounds, substs} = mk_substs(ccx, tps, rp);
+        let t = ty::mk_iface(tcx, local_def(it.id), substs);
+        let tpt = {bounds: bounds, rp: rp, ty: t};
+        tcx.tcache.insert(local_def(it.id), tpt);
+        ret tpt;
+      }
+      ast::item_class(tps, _, _, _, _, rp) {
+          let {bounds,substs} = mk_substs(ccx, tps, rp);
+          let t = ty::mk_class(tcx, local_def(it.id), substs);
+          let tpt = {bounds: bounds, rp: rp, ty: t};
+          tcx.tcache.insert(local_def(it.id), tpt);
+          ret tpt;
+      }
+      ast::item_impl(*) | ast::item_mod(_) |
+      ast::item_native_mod(_) { fail; }
+    }
+}
+
+fn ty_of_native_item(ccx: @crate_ctxt, it: @ast::native_item)
+    -> ty::ty_param_bounds_and_ty {
+    alt it.node {
+      ast::native_item_fn(fn_decl, params) {
+        ret ty_of_native_fn_decl(ccx, fn_decl, params,
+                                 local_def(it.id));
+      }
+    }
+}
+
+fn ty_of_arg<AC: ast_conv, RS: region_scope copy>(
+    self: AC, rscope: RS, a: ast::arg,
+    expected_ty: option<ty::arg>) -> ty::arg {
+
+    let ty = alt a.ty.node {
+      ast::ty_infer if expected_ty.is_some() {expected_ty.get().ty}
+      ast::ty_infer {self.ty_infer(a.ty.span)}
+      _ {ast_ty_to_ty(self, rscope, a.ty)}
+    };
+
+    let mode = {
+        alt a.mode {
+          ast::infer(_) if expected_ty.is_some() {
+            result::get(ty::unify_mode(self.tcx(), a.mode,
+                                       expected_ty.get().mode))
+          }
+          ast::infer(_) {
+            alt ty::get(ty).struct {
+              // If the type is not specified, then this must be a fn expr.
+              // Leave the mode as infer(_), it will get inferred based
+              // on constraints elsewhere.
+              ty::ty_var(_) {a.mode}
+
+              // If the type is known, then use the default for that type.
+              // Here we unify m and the default.  This should update the
+              // tables in tcx but should never fail, because nothing else
+              // will have been unified with m yet:
+              _ {
+                let m1 = ast::expl(ty::default_arg_mode_for_ty(ty));
+                result::get(ty::unify_mode(self.tcx(), a.mode, m1))
+              }
+            }
+          }
+          ast::expl(_) {a.mode}
+        }
+    };
+
+    {mode: mode, ty: ty}
+}
+
+type expected_tys = option<{inputs: [ty::arg],
+                            output: ty::t}>;
+
+fn ty_of_fn_decl<AC: ast_conv, RS: region_scope copy>(
+    self: AC, rscope: RS,
+    proto: ast::proto,
+    decl: ast::fn_decl,
+    expected_tys: expected_tys) -> ty::fn_ty {
+
+    #debug["ty_of_fn_decl"];
+    indent {||
+        // new region names that appear inside of the fn decl are bound to
+        // that function type
+        let rb = in_binding_rscope(rscope);
+
+        let input_tys = decl.inputs.mapi { |i, a|
+            let expected_arg_ty = expected_tys.chain { |e|
+                // no guarantee that the correct number of expected args
+                // were supplied
+                if i < e.inputs.len() {some(e.inputs[i])} else {none}
+            };
+            ty_of_arg(self, rb, a, expected_arg_ty)
+        };
+
+        let expected_ret_ty = expected_tys.map { |e| e.output };
+        let output_ty = alt decl.output.node {
+          ast::ty_infer if expected_ret_ty.is_some() {expected_ret_ty.get()}
+          ast::ty_infer {self.ty_infer(decl.output.span)}
+          _ {ast_ty_to_ty(self, rb, decl.output)}
+        };
+
+        let out_constrs = vec::map(decl.constraints) {|constr|
+            ty::ast_constr_to_constr(self.tcx(), constr)
+        };
+        {proto: proto, inputs: input_tys,
+         output: output_ty, ret_style: decl.cf, constraints: out_constrs}
+    }
+}
+
+
+fn ty_param_bounds(ccx: @crate_ctxt,
+                   params: [ast::ty_param]) -> @[ty::param_bounds] {
+
+    fn compute_bounds(ccx: @crate_ctxt,
+                      param: ast::ty_param) -> ty::param_bounds {
+        @vec::flat_map(*param.bounds) { |b|
+            alt b {
+              ast::bound_send { [ty::bound_send] }
+              ast::bound_copy { [ty::bound_copy] }
+              ast::bound_iface(t) {
+                let ity = ast_ty_to_ty(ccx, empty_rscope, t);
+                alt ty::get(ity).struct {
+                  ty::ty_iface(*) {
+                    [ty::bound_iface(ity)]
+                  }
+                  _ {
+                    ccx.tcx.sess.span_err(
+                        t.span, "type parameter bounds must be \
+                                 interface types");
+                    []
+                  }
+                }
+              }
+            }
+        }
+    }
+
+    @params.map { |param|
+        alt ccx.tcx.ty_param_bounds.find(param.id) {
+          some(bs) { bs }
+          none {
+            let bounds = compute_bounds(ccx, param);
+            ccx.tcx.ty_param_bounds.insert(param.id, bounds);
+            bounds
+          }
+        }
+    }
+}
+
+fn ty_of_native_fn_decl(ccx: @crate_ctxt,
+                        decl: ast::fn_decl,
+                        ty_params: [ast::ty_param],
+                        def_id: ast::def_id) -> ty::ty_param_bounds_and_ty {
+
+    let bounds = ty_param_bounds(ccx, ty_params);
+    let rb = in_binding_rscope(empty_rscope);
+    let input_tys = decl.inputs.map { |a| ty_of_arg(ccx, rb, a, none) };
+    let output_ty = ast_ty_to_ty(ccx, rb, decl.output);
+
+    let t_fn = ty::mk_fn(ccx.tcx, {proto: ast::proto_bare,
+                                   inputs: input_tys,
+                                   output: output_ty,
+                                   ret_style: ast::return_val,
+                                   constraints: []});
+    let tpt = {bounds: bounds, rp: ast::rp_none, ty: t_fn};
+    ccx.tcx.tcache.insert(def_id, tpt);
+    ret tpt;
+}
diff --git a/src/rustc/middle/typeck/collect.rs b/src/rustc/middle/typeck/collect.rs
new file mode 100644
index 00000000000..d1283375b5b
--- /dev/null
+++ b/src/rustc/middle/typeck/collect.rs
@@ -0,0 +1,343 @@
+import astconv::{type_rscope, instantiate_iface_ref, ty_of_item,
+                 empty_rscope, ty_of_native_item, ast_conv};
+
+// 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 get_enum_variant_types(ccx: @crate_ctxt,
+                          enum_ty: ty::t,
+                          variants: [ast::variant],
+                          ty_params: [ast::ty_param],
+                          rp: ast::region_param) {
+    let tcx = ccx.tcx;
+
+    // Create a set of parameter types shared among all the variants.
+    for variants.each {|variant|
+        // Nullary enum constructors get turned into constants; n-ary enum
+        // constructors get turned into functions.
+        let result_ty = if vec::len(variant.node.args) == 0u {
+            enum_ty
+        } else {
+            let rs = type_rscope(rp);
+            let args = variant.node.args.map { |va|
+                let arg_ty = ccx.to_ty(rs, va.ty);
+                {mode: ast::expl(ast::by_copy), ty: arg_ty}
+            };
+            ty::mk_fn(tcx, {proto: ast::proto_box,
+                            inputs: args,
+                            output: enum_ty,
+                            ret_style: ast::return_val,
+                            constraints: []})
+        };
+        let tpt = {bounds: astconv::ty_param_bounds(ccx, ty_params),
+                   rp: rp,
+                   ty: result_ty};
+        tcx.tcache.insert(local_def(variant.node.id), tpt);
+        write_ty_to_tcx(tcx, variant.node.id, result_ty);
+    }
+}
+
+fn ensure_iface_methods(ccx: @crate_ctxt, id: ast::node_id) {
+    fn store_methods<T>(ccx: @crate_ctxt, id: ast::node_id,
+                        stuff: [T], f: fn@(T) -> ty::method) {
+        ty::store_iface_methods(ccx.tcx, id, @vec::map(stuff, f));
+    }
+
+    let tcx = ccx.tcx;
+    alt check tcx.items.get(id) {
+      ast_map::node_item(@{node: ast::item_iface(_, rp, ms), _}, _) {
+        store_methods::<ast::ty_method>(ccx, id, ms) {|m|
+            ty_of_ty_method(ccx, m, rp)
+        };
+      }
+      ast_map::node_item(@{node: ast::item_class(_,_,its,_,_,rp), _}, _) {
+        let (_,ms) = split_class_items(its);
+        // All methods need to be stored, since lookup_method
+        // relies on the same method cache for self-calls
+        store_methods::<@ast::method>(ccx, id, ms) {|m|
+            ty_of_method(ccx, m, rp)
+        };
+      }
+    }
+}
+
+fn check_methods_against_iface(ccx: @crate_ctxt,
+                               tps: [ast::ty_param],
+                               rp: ast::region_param,
+                               selfty: ty::t,
+                               a_ifacety: @ast::iface_ref,
+                               ms: [@ast::method]) {
+
+    let tcx = ccx.tcx;
+    let i_bounds = astconv::ty_param_bounds(ccx, tps);
+    let my_methods = convert_methods(ccx, ms, rp, i_bounds, selfty);
+    let (did, tpt) = instantiate_iface_ref(ccx, a_ifacety, rp);
+    if did.crate == ast::local_crate {
+        ensure_iface_methods(ccx, did.node);
+    }
+    for vec::each(*ty::iface_methods(tcx, did)) {|if_m|
+        alt vec::find(my_methods, {|m| if_m.ident == m.mty.ident}) {
+          some({mty: m, id, span}) {
+            if m.purity != if_m.purity {
+                ccx.tcx.sess.span_err(
+                    span, #fmt["method `%s`'s purity \
+                                not match the iface method's \
+                                purity", m.ident]);
+            }
+            let mt = compare_impl_method(
+                ccx.tcx, span, m, vec::len(tps),
+                if_m, tpt.substs, selfty);
+            let old = tcx.tcache.get(local_def(id));
+            if old.ty != mt {
+                tcx.tcache.insert(
+                    local_def(id),
+                    {bounds: old.bounds,
+                     rp: old.rp,
+                     ty: mt});
+                write_ty_to_tcx(tcx, id, mt);
+            }
+          }
+          none {
+            tcx.sess.span_err(
+                a_ifacety.path.span,
+                #fmt["missing method `%s`", if_m.ident]);
+          }
+        } // alt
+    } // |if_m|
+} // fn
+
+fn convert_class_item(ccx: @crate_ctxt,
+                      rp: ast::region_param,
+                      v: ast_util::ivar) {
+    /* we want to do something here, b/c within the
+    scope of the class, it's ok to refer to fields &
+    methods unqualified */
+    /* they have these types *within the scope* of the
+    class. outside the class, it's done with expr_field */
+    let tt = ccx.to_ty(type_rscope(rp), v.ty);
+    write_ty_to_tcx(ccx.tcx, v.id, tt);
+}
+
+fn convert_methods(ccx: @crate_ctxt,
+                   ms: [@ast::method],
+                   rp: ast::region_param,
+                   i_bounds: @[ty::param_bounds],
+                   self_ty: ty::t)
+    -> [{mty: ty::method, id: ast::node_id, span: span}] {
+
+    let tcx = ccx.tcx;
+    vec::map(ms) { |m|
+        write_ty_to_tcx(tcx, m.self_id, self_ty);
+        let bounds = astconv::ty_param_bounds(ccx, m.tps);
+        let mty = ty_of_method(ccx, m, rp);
+        let fty = ty::mk_fn(tcx, mty.fty);
+        tcx.tcache.insert(
+            local_def(m.id),
+            // n.b. This code is kind of sketchy (concat'ing i_bounds
+            // with bounds), but removing *i_bounds breaks other stuff
+            {bounds: @(*i_bounds + *bounds), rp: rp, ty: fty});
+        write_ty_to_tcx(tcx, m.id, fty);
+        {mty: mty, id: m.id, span: m.span}
+    }
+}
+
+fn convert(ccx: @crate_ctxt, it: @ast::item) {
+    let tcx = ccx.tcx;
+    alt it.node {
+      // These don't define types.
+      ast::item_mod(_) {}
+      ast::item_native_mod(m) {
+        if syntax::attr::native_abi(it.attrs) ==
+            either::right(ast::native_abi_rust_intrinsic) {
+            for m.items.each { |item| check_intrinsic_type(ccx, item); }
+        }
+      }
+      ast::item_enum(variants, ty_params, rp) {
+        let tpt = ty_of_item(ccx, it);
+        write_ty_to_tcx(tcx, it.id, tpt.ty);
+        get_enum_variant_types(ccx, tpt.ty, variants,
+                               ty_params, rp);
+      }
+      ast::item_impl(tps, rp, ifce, selfty, ms) {
+        let i_bounds = astconv::ty_param_bounds(ccx, tps);
+        let selfty = ccx.to_ty(type_rscope(rp), selfty);
+        write_ty_to_tcx(tcx, it.id, selfty);
+        tcx.tcache.insert(local_def(it.id),
+                          {bounds: i_bounds,
+                           rp: rp,
+                           ty: selfty});
+        alt ifce {
+          some(t) {
+            check_methods_against_iface(
+                ccx, tps, rp,
+                selfty, t, ms);
+          }
+          _ {
+            // Still have to do this to write method types
+            // into the table
+            convert_methods(
+                ccx, ms, rp,
+                i_bounds, selfty);
+          }
+        }
+      }
+      ast::item_res(decl, tps, _, dtor_id, ctor_id, rp) {
+        let {bounds, substs} = mk_substs(ccx, tps, rp);
+        let def_id = local_def(it.id);
+        let t_arg = astconv::ty_of_arg(ccx, type_rscope(rp),
+                                       decl.inputs[0], none);
+        let t_res = ty::mk_res(tcx, def_id, t_arg.ty, substs);
+
+        let t_ctor = ty::mk_fn(tcx, {
+            proto: ast::proto_box,
+            inputs: [{mode: ast::expl(ast::by_copy), ty: t_arg.ty}],
+            output: t_res,
+            ret_style: ast::return_val, constraints: []
+        });
+        let t_dtor = ty::mk_fn(tcx, {
+            proto: ast::proto_box,
+            inputs: [t_arg], output: ty::mk_nil(tcx),
+            ret_style: ast::return_val, constraints: []
+        });
+        write_ty_to_tcx(tcx, it.id, t_res);
+        write_ty_to_tcx(tcx, ctor_id, t_ctor);
+        tcx.tcache.insert(local_def(ctor_id),
+                          {bounds: bounds,
+                           rp: rp,
+                           ty: t_ctor});
+        tcx.tcache.insert(def_id, {bounds: bounds,
+                                   rp: rp,
+                                   ty: t_res});
+        write_ty_to_tcx(tcx, dtor_id, t_dtor);
+      }
+      ast::item_iface(*) {
+        let tpt = ty_of_item(ccx, it);
+        #debug["item_iface(it.id=%d, tpt.ty=%s)",
+               it.id, ty_to_str(tcx, tpt.ty)];
+        write_ty_to_tcx(tcx, it.id, tpt.ty);
+        ensure_iface_methods(ccx, it.id);
+      }
+      ast::item_class(tps, ifaces, members, ctor, m_dtor, rp) {
+        // Write the class type
+        let tpt = ty_of_item(ccx, it);
+        write_ty_to_tcx(tcx, it.id, tpt.ty);
+        // Write the ctor type
+        let t_ctor =
+            ty::mk_fn(
+                tcx,
+                astconv::ty_of_fn_decl(ccx,
+                                       empty_rscope,
+                                       ast::proto_any,
+                                       ctor.node.dec,
+                                       none));
+        write_ty_to_tcx(tcx, ctor.node.id, t_ctor);
+        tcx.tcache.insert(local_def(ctor.node.id),
+                          {bounds: tpt.bounds,
+                           rp: ast::rp_none,
+                           ty: t_ctor});
+        option::iter(m_dtor) {|dtor|
+            // Write the dtor type
+            let t_dtor = ty::mk_fn(
+                tcx,
+                // not sure about empty_rscope
+                // FIXME
+                astconv::ty_of_fn_decl(ccx,
+                                       empty_rscope,
+                                       ast::proto_any,
+                                       ast_util::dtor_dec(),
+                                       none));
+            write_ty_to_tcx(tcx, dtor.node.id, t_dtor);
+            tcx.tcache.insert(local_def(dtor.node.id),
+                              {bounds: tpt.bounds,
+                               rp: ast::rp_none,
+                               ty: t_dtor});
+        };
+        ensure_iface_methods(ccx, it.id);
+        /* FIXME: check for proper public/privateness */
+        // Write the type of each of the members
+        let (fields, methods) = split_class_items(members);
+        for fields.each {|f|
+            convert_class_item(ccx, rp, f);
+        }
+        // The selfty is just the class type
+        let {bounds:_, substs} = mk_substs(ccx, tps, rp);
+        let selfty = ty::mk_class(tcx, local_def(it.id), substs);
+        // Need to convert all methods so we can check internal
+        // references to private methods
+
+        // NDM to TJC---I think we ought to be using bounds here, not @[].
+        // But doing so causes errors later on.
+        convert_methods(ccx, methods, rp, @[], selfty);
+
+        /*
+        Finally, check that the class really implements the ifaces
+        that it claims to implement.
+        */
+        for ifaces.each { |ifce|
+            check_methods_against_iface(ccx, tps, rp, selfty,
+                                        ifce, methods);
+            let t = ty::node_id_to_type(tcx, ifce.id);
+
+            // FIXME: This assumes classes only implement
+            // non-parameterized ifaces. add a test case for
+            // a class implementing a parameterized iface.
+            // -- tjc (#1726)
+            tcx.tcache.insert(local_def(ifce.id), no_params(t));
+        }
+      }
+      _ {
+        // This call populates the type cache with the converted type
+        // of the item in passing. All we have to do here is to write
+        // it into the node type table.
+        let tpt = ty_of_item(ccx, it);
+        write_ty_to_tcx(tcx, it.id, tpt.ty);
+      }
+    }
+}
+fn convert_native(ccx: @crate_ctxt, i: @ast::native_item) {
+    // As above, this call populates the type table with the converted
+    // type of the native item. We simply write it into the node type
+    // table.
+    let tpt = ty_of_native_item(ccx, i);
+    alt i.node {
+      ast::native_item_fn(_, _) {
+        write_ty_to_tcx(ccx.tcx, i.id, tpt.ty);
+      }
+    }
+}
+fn collect_item_types(ccx: @crate_ctxt, crate: @ast::crate) {
+    visit::visit_crate(*crate, (), visit::mk_simple_visitor(@{
+        visit_item: bind convert(ccx, _),
+        visit_native_item: bind convert_native(ccx, _)
+        with *visit::default_simple_visitor()
+    }));
+}
+
+fn ty_of_method(ccx: @crate_ctxt,
+                m: @ast::method,
+                rp: ast::region_param) -> ty::method {
+    {ident: m.ident,
+     tps: astconv::ty_param_bounds(ccx, m.tps),
+     fty: astconv::ty_of_fn_decl(ccx, type_rscope(rp), ast::proto_bare,
+                                 m.decl, none),
+     purity: m.decl.purity,
+     vis: m.vis}
+}
+
+fn ty_of_ty_method(self: @crate_ctxt,
+                   m: ast::ty_method,
+                   rp: ast::region_param) -> ty::method {
+    {ident: m.ident,
+     tps: astconv::ty_param_bounds(self, m.tps),
+     fty: astconv::ty_of_fn_decl(self, type_rscope(rp), ast::proto_bare,
+                                 m.decl, none),
+     // assume public, because this is only invoked on iface methods
+     purity: m.decl.purity, vis: ast::public}
+}
diff --git a/src/rustc/middle/typeck/vtable.rs b/src/rustc/middle/typeck/vtable.rs
new file mode 100644
index 00000000000..293ea3144dc
--- /dev/null
+++ b/src/rustc/middle/typeck/vtable.rs
@@ -0,0 +1,271 @@
+fn has_iface_bounds(tps: [ty::param_bounds]) -> bool {
+    vec::any(tps, {|bs|
+        vec::any(*bs, {|b|
+            alt b { ty::bound_iface(_) { true } _ { false } }
+        })
+    })
+}
+
+fn lookup_vtables(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span,
+                  bounds: @[ty::param_bounds], substs: ty::substs,
+                  allow_unsafe: bool) -> vtable_res {
+    let tcx = fcx.ccx.tcx;
+    let mut result = [], i = 0u;
+    for substs.tps.each {|ty|
+        for vec::each(*bounds[i]) {|bound|
+            alt bound {
+              ty::bound_iface(i_ty) {
+                let i_ty = ty::subst(tcx, substs, i_ty);
+                result += [lookup_vtable(fcx, isc, sp, ty, i_ty,
+                                         allow_unsafe)];
+              }
+              _ {}
+            }
+        }
+        i += 1u;
+    }
+    @result
+}
+
+fn fixup_substs(fcx: @fn_ctxt, sp: span,
+                id: ast::def_id, substs: ty::substs) -> ty::substs {
+    let tcx = fcx.ccx.tcx;
+    // use a dummy type just to package up the substs that need fixing up
+    let t = ty::mk_iface(tcx, id, substs);
+    let t_f = fixup_ty(fcx, sp, t);
+    alt check ty::get(t_f).struct {
+      ty::ty_iface(_, substs_f) { substs_f }
+    }
+}
+
+fn relate_iface_tys(fcx: @fn_ctxt, sp: span,
+                    exp_iface_ty: ty::t, act_iface_ty: ty::t) {
+    demand::suptype(fcx, sp, exp_iface_ty, act_iface_ty)
+}
+
+/*
+Look up the vtable to use when treating an item of type <t>
+as if it has type <iface_ty>
+*/
+fn lookup_vtable(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span,
+                 ty: ty::t, iface_ty: ty::t, allow_unsafe: bool)
+    -> vtable_origin {
+
+    #debug["lookup_vtable(ty=%s, iface_ty=%s)",
+           fcx.ty_to_str(ty), fcx.ty_to_str(iface_ty)];
+    let _i = indenter();
+
+    let tcx = fcx.ccx.tcx;
+    let (iface_id, iface_substs) = alt check ty::get(iface_ty).struct {
+      ty::ty_iface(did, substs) { (did, substs) }
+    };
+    let ty = fixup_ty(fcx, sp, ty);
+    alt ty::get(ty).struct {
+      ty::ty_param(n, did) {
+        let mut n_bound = 0u;
+        for vec::each(*tcx.ty_param_bounds.get(did.node)) { |bound|
+            alt bound {
+              ty::bound_send | ty::bound_copy { /* ignore */ }
+              ty::bound_iface(ity) {
+                alt check ty::get(ity).struct {
+                  ty::ty_iface(idid, substs) {
+                    if iface_id == idid {
+                        relate_iface_tys(fcx, sp, iface_ty, ity);
+                        ret vtable_param(n, n_bound);
+                    }
+                  }
+                }
+                n_bound += 1u;
+              }
+            }
+        }
+      }
+
+      ty::ty_iface(did, substs) if iface_id == did {
+        relate_iface_tys(fcx, sp, iface_ty, ty);
+        if !allow_unsafe {
+            for vec::each(*ty::iface_methods(tcx, did)) {|m|
+                if ty::type_has_self(ty::mk_fn(tcx, m.fty)) {
+                    tcx.sess.span_err(
+                        sp, "a boxed iface with self types may not be \
+                             passed as a bounded type");
+                } else if (*m.tps).len() > 0u {
+                    tcx.sess.span_err(
+                        sp, "a boxed iface with generic methods may not \
+                             be passed as a bounded type");
+
+                }
+            }
+        }
+        ret vtable_iface(did, substs.tps);
+      }
+
+      _ {
+        let mut found = [];
+
+        for list::each(isc) {|impls|
+            /* For each impl in scope... */
+                for vec::each(*impls) {|im|
+                    // im = one specific impl
+                    // find the iface that im implements (if any)
+                    let of_ty = alt ty::impl_iface(tcx, im.did) {
+                      some(of_ty) { of_ty }
+                      _ { cont; }
+                    };
+
+                    // it must have the same id as the expected one
+                    alt ty::get(of_ty).struct {
+                      ty::ty_iface(id, _) if id != iface_id { cont; }
+                      _ { /* ok */ }
+                    }
+
+                    // check whether the type unifies with the type
+                    // that the impl is for, and continue if not
+                    let {substs: substs, ty: for_ty} =
+                        impl_self_ty(fcx, im.did);
+                    let im_bs = ty::lookup_item_type(tcx, im.did).bounds;
+                    alt fcx.mk_subty(ty, for_ty) {
+                      result::err(_) { cont; }
+                      result::ok(()) { }
+                    }
+
+                    // check that desired iface type unifies
+                    let of_ty = ty::subst(tcx, substs, of_ty);
+                    relate_iface_tys(fcx, sp, iface_ty, of_ty);
+
+                    // recursively process the bounds
+                    let iface_tps = iface_substs.tps;
+                    let substs_f = fixup_substs(fcx, sp, iface_id, substs);
+                    connect_iface_tps(fcx, sp, substs_f.tps,
+                                      iface_tps, im.did);
+                    let subres = lookup_vtables(fcx, isc, sp,
+                                                im_bs, substs_f, false);
+                    found += [vtable_static(im.did, substs_f.tps, subres)];
+                }
+
+                alt found.len() {
+                  0u { /* fallthrough */ }
+                  1u { ret found[0]; }
+                  _ {
+                    fcx.ccx.tcx.sess.span_err(
+                        sp, "multiple applicable methods in scope");
+                    ret found[0];
+                  }
+                }
+            }
+          }
+        }
+
+        tcx.sess.span_fatal(
+            sp, "failed to find an implementation of interface " +
+            ty_to_str(tcx, iface_ty) + " for " +
+            ty_to_str(tcx, ty));
+    }
+
+fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t) -> ty::t {
+    let tcx = fcx.ccx.tcx;
+    alt infer::resolve_deep(fcx.infcx, ty, true) {
+      result::ok(new_type) { new_type }
+      result::err(e) {
+        tcx.sess.span_fatal(
+            sp,
+            #fmt["cannot determine a type \
+                  for this bounded type parameter: %s",
+                 infer::fixup_err_to_str(e)])
+      }
+    }
+}
+
+fn connect_iface_tps(fcx: @fn_ctxt, sp: span, impl_tys: [ty::t],
+                     iface_tys: [ty::t], impl_did: ast::def_id) {
+    let tcx = fcx.ccx.tcx;
+    let ity = option::get(ty::impl_iface(tcx, impl_did));
+    let iface_ty = ty::subst_tps(tcx, impl_tys, ity);
+    alt check ty::get(iface_ty).struct {
+      ty::ty_iface(_, substs) {
+        vec::iter2(substs.tps, iface_tys,
+                   {|a, b| demand::suptype(fcx, sp, a, b);});
+      }
+    }
+}
+
+fn resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, v: visit::vt<@fn_ctxt>) {
+    let cx = fcx.ccx;
+    alt ex.node {
+      ast::expr_path(*) {
+        alt fcx.opt_node_ty_substs(ex.id) {
+          some(substs) {
+            let did = ast_util::def_id_of_def(cx.tcx.def_map.get(ex.id));
+            let item_ty = ty::lookup_item_type(cx.tcx, did);
+            if has_iface_bounds(*item_ty.bounds) {
+                let impls = cx.impl_map.get(ex.id);
+                cx.vtable_map.insert(ex.id, lookup_vtables(
+                    fcx, impls, ex.span,
+                    item_ty.bounds, substs, false));
+            }
+          }
+          _ {}
+        }
+      }
+      // Must resolve bounds on methods with bounded params
+      ast::expr_field(*) | ast::expr_binary(*) |
+      ast::expr_unary(*) | ast::expr_assign_op(*) |
+      ast::expr_index(*) {
+        alt cx.method_map.find(ex.id) {
+          some(method_static(did)) {
+            let bounds = ty::lookup_item_type(cx.tcx, did).bounds;
+            if has_iface_bounds(*bounds) {
+                let callee_id = alt ex.node {
+                  ast::expr_field(_, _, _) { ex.id }
+                  _ { ast_util::op_expr_callee_id(ex) }
+                };
+                let substs = fcx.node_ty_substs(callee_id);
+                let iscs = cx.impl_map.get(ex.id);
+                cx.vtable_map.insert(callee_id, lookup_vtables(
+                    fcx, iscs, ex.span, bounds, substs, false));
+            }
+          }
+          _ {}
+        }
+      }
+      ast::expr_cast(src, _) {
+        let target_ty = fcx.expr_ty(ex);
+        alt ty::get(target_ty).struct {
+          ty::ty_iface(*) {
+            /* Casting to an interface type.
+            Look up all impls for the cast expr...
+            */
+            let impls = cx.impl_map.get(ex.id);
+            /*
+            Look up vtables for the type we're casting to,
+            passing in the source and target type
+            */
+            let vtable = lookup_vtable(fcx, impls, ex.span,
+                                       fcx.expr_ty(src), target_ty,
+                                       true);
+            /*
+            Map this expression to that vtable (that is: "ex has
+            vtable <vtable>")
+            */
+            cx.vtable_map.insert(ex.id, @[vtable]);
+          }
+          _ {}
+        }
+      }
+      _ {}
+    }
+    visit::visit_expr(ex, fcx, v);
+}
+
+// Detect points where an interface-bounded type parameter is
+// instantiated, resolve the impls for the parameters.
+fn resolve_in_block(fcx: @fn_ctxt, bl: ast::blk) {
+    visit::visit_block(bl, fcx, visit::mk_vt(@{
+        visit_expr: resolve_expr,
+        visit_item: fn@(_i: @ast::item, &&_e: @fn_ctxt,
+                        _v: visit::vt<@fn_ctxt>) {}
+        with *visit::default_visitor()
+    }));
+}
+
+
diff --git a/src/rustc/middle/typeck/writeback.rs b/src/rustc/middle/typeck/writeback.rs
new file mode 100644
index 00000000000..914c4787925
--- /dev/null
+++ b/src/rustc/middle/typeck/writeback.rs
@@ -0,0 +1,184 @@
+// Type resolution: the phase that finds all the types in the AST with
+// unresolved type variables and replaces "ty_var" types with their
+// substitutions.
+export resolve_type_vars_in_fn;
+export resolve_type_vars_in_expr;
+
+fn resolve_type_vars_in_type(fcx: @fn_ctxt, sp: span, typ: ty::t) ->
+    option<ty::t> {
+    if !ty::type_needs_infer(typ) { ret some(typ); }
+    alt infer::resolve_deep(fcx.infcx, typ, true) {
+      result::ok(new_type) { ret some(new_type); }
+      result::err(e) {
+        if !fcx.ccx.tcx.sess.has_errors() {
+            fcx.ccx.tcx.sess.span_err(
+                sp,
+                #fmt["cannot determine a type \
+                      for this expression: %s",
+                     infer::fixup_err_to_str(e)])
+        }
+        ret none;
+      }
+    }
+}
+fn resolve_type_vars_for_node(wbcx: wb_ctxt, sp: span, id: ast::node_id)
+    -> option<ty::t> {
+    let fcx = wbcx.fcx, tcx = fcx.ccx.tcx;
+    let n_ty = fcx.node_ty(id);
+    alt resolve_type_vars_in_type(fcx, sp, n_ty) {
+      none {
+        wbcx.success = false;
+        ret none;
+      }
+
+      some(t) {
+        #debug["resolve_type_vars_for_node(id=%d, n_ty=%s, t=%s)",
+               id, ty_to_str(tcx, n_ty), ty_to_str(tcx, t)];
+        write_ty_to_tcx(tcx, id, t);
+        alt fcx.opt_node_ty_substs(id) {
+          some(substs) {
+            let mut new_tps = [];
+            for substs.tps.each {|subst|
+                alt resolve_type_vars_in_type(fcx, sp, subst) {
+                  some(t) { new_tps += [t]; }
+                  none { wbcx.success = false; ret none; }
+                }
+            }
+            write_substs_to_tcx(tcx, id, new_tps);
+          }
+          none {}
+        }
+        ret some(t);
+      }
+    }
+}
+
+fn maybe_resolve_type_vars_for_node(wbcx: wb_ctxt, sp: span,
+                                    id: ast::node_id)
+    -> option<ty::t> {
+    if wbcx.fcx.node_types.contains_key(id as uint) {
+        resolve_type_vars_for_node(wbcx, sp, id)
+    } else {
+        none
+    }
+}
+
+type wb_ctxt =
+    // As soon as we hit an error we have to stop resolving
+    // the entire function
+    {fcx: @fn_ctxt, mut success: bool};
+type wb_vt = visit::vt<wb_ctxt>;
+
+fn visit_stmt(s: @ast::stmt, wbcx: wb_ctxt, v: wb_vt) {
+    if !wbcx.success { ret; }
+    resolve_type_vars_for_node(wbcx, s.span, ty::stmt_node_id(s));
+    visit::visit_stmt(s, wbcx, v);
+}
+fn visit_expr(e: @ast::expr, wbcx: wb_ctxt, v: wb_vt) {
+    if !wbcx.success { ret; }
+    resolve_type_vars_for_node(wbcx, e.span, e.id);
+    alt e.node {
+      ast::expr_fn(_, decl, _, _) |
+      ast::expr_fn_block(decl, _, _) {
+        vec::iter(decl.inputs) {|input|
+            let r_ty = resolve_type_vars_for_node(wbcx, e.span, input.id);
+
+            // Just in case we never constrained the mode to anything,
+            // constrain it to the default for the type in question.
+            alt (r_ty, input.mode) {
+              (some(t), ast::infer(_)) {
+                let tcx = wbcx.fcx.ccx.tcx;
+                let m_def = ty::default_arg_mode_for_ty(t);
+                ty::set_default_mode(tcx, input.mode, m_def);
+              }
+              _ {}
+            }
+        }
+      }
+
+      ast::expr_new(_, alloc_id, _) {
+        resolve_type_vars_for_node(wbcx, e.span, alloc_id);
+      }
+
+      ast::expr_binary(_, _, _) | ast::expr_unary(_, _) |
+      ast::expr_assign_op(_, _, _) | ast::expr_index(_, _) {
+        maybe_resolve_type_vars_for_node(wbcx, e.span,
+                                         ast_util::op_expr_callee_id(e));
+      }
+
+      _ { }
+    }
+    visit::visit_expr(e, wbcx, v);
+}
+fn visit_block(b: ast::blk, wbcx: wb_ctxt, v: wb_vt) {
+    if !wbcx.success { ret; }
+    resolve_type_vars_for_node(wbcx, b.span, b.node.id);
+    visit::visit_block(b, wbcx, v);
+}
+fn visit_pat(p: @ast::pat, wbcx: wb_ctxt, v: wb_vt) {
+    if !wbcx.success { ret; }
+    resolve_type_vars_for_node(wbcx, p.span, p.id);
+    #debug["Type for pattern binding %s (id %d) resolved to %s",
+           pat_to_str(p), p.id,
+           wbcx.fcx.ty_to_str(
+               ty::node_id_to_type(wbcx.fcx.ccx.tcx,
+                                   p.id))];
+    visit::visit_pat(p, wbcx, v);
+}
+fn visit_local(l: @ast::local, wbcx: wb_ctxt, v: wb_vt) {
+    if !wbcx.success { ret; }
+    let var_id = lookup_local(wbcx.fcx, l.span, l.node.id);
+    alt infer::resolve_deep_var(wbcx.fcx.infcx, var_id, true) {
+      result::ok(lty) {
+        #debug["Type for local %s (id %d) resolved to %s",
+               pat_to_str(l.node.pat), l.node.id,
+               wbcx.fcx.ty_to_str(lty)];
+        write_ty_to_tcx(wbcx.fcx.ccx.tcx, l.node.id, lty);
+      }
+      result::err(e) {
+        wbcx.fcx.ccx.tcx.sess.span_err(
+            l.span,
+            #fmt["cannot determine a type \
+                  for this local variable: %s",
+                 infer::fixup_err_to_str(e)]);
+        wbcx.success = false;
+      }
+    }
+    visit::visit_local(l, wbcx, v);
+}
+fn visit_item(_item: @ast::item, _wbcx: wb_ctxt, _v: wb_vt) {
+    // Ignore items
+}
+
+fn resolve_type_vars_in_expr(fcx: @fn_ctxt, e: @ast::expr) -> bool {
+    let wbcx = {fcx: fcx, mut success: true};
+    let visit =
+        visit::mk_vt(@{visit_item: visit_item,
+                       visit_stmt: visit_stmt,
+                       visit_expr: visit_expr,
+                       visit_block: visit_block,
+                       visit_pat: visit_pat,
+                       visit_local: visit_local
+                       with *visit::default_visitor()});
+    visit.visit_expr(e, wbcx, visit);
+    ret wbcx.success;
+}
+
+fn resolve_type_vars_in_fn(fcx: @fn_ctxt,
+                           decl: ast::fn_decl,
+                           blk: ast::blk) -> bool {
+    let wbcx = {fcx: fcx, mut success: true};
+    let visit =
+        visit::mk_vt(@{visit_item: visit_item,
+                       visit_stmt: visit_stmt,
+                       visit_expr: visit_expr,
+                       visit_block: visit_block,
+                       visit_pat: visit_pat,
+                       visit_local: visit_local
+                       with *visit::default_visitor()});
+    visit.visit_block(blk, wbcx, visit);
+    for decl.inputs.each {|arg|
+        resolve_type_vars_for_node(wbcx, arg.ty.span, arg.id);
+    }
+    ret wbcx.success;
+}
diff --git a/src/rustc/rustc.rc b/src/rustc/rustc.rc
index 378091da2e9..a1ae3943998 100644
--- a/src/rustc/rustc.rc
+++ b/src/rustc/rustc.rc
@@ -52,7 +52,12 @@ mod middle {
     mod infer;
     mod ast_map;
     mod resolve;
-    mod typeck;
+    mod typeck {
+        mod astconv;
+        mod collect;
+        mod vtable;
+        mod writeback;
+    }
     mod check_loop;
     mod check_alt;
     mod check_const;