]> git.lizzy.rs Git - rust.git/commitdiff
Merge pull request #447 from paulstansifer/quick_error_message_fix
authorPatrick Walton <pcwalton@mimiga.net>
Fri, 10 Jun 2011 17:53:42 +0000 (10:53 -0700)
committerPatrick Walton <pcwalton@mimiga.net>
Fri, 10 Jun 2011 17:53:42 +0000 (10:53 -0700)
Error message, instead of segfault, when recursive types are used.

1  2 
src/comp/front/ast.rs
src/comp/middle/ty.rs
src/comp/middle/typeck.rs

diff --combined src/comp/front/ast.rs
index a61562b9c161b93975638c647e97189446c6b66e,32d9ee1678f00695a91896acf8ccbefae74eb5aa..d30af2a6d132219c1c4073feec054a2e78c4e3f6
@@@ -11,10 -11,6 +11,10 @@@ type ident = str
  type path_ = rec(vec[ident] idents, vec[@ty] types);
  type path = spanned[path_];
  
 +fn path_name(&path p) -> str {
 +    ret str::connect(p.node.idents, "::");
 +}
 +
  type crate_num = int;
  const crate_num local_crate = 0;
  type def_num = int;
@@@ -75,11 -71,6 +75,11 @@@ type crate = spanned[crate_]
  type crate_ = rec(vec[@crate_directive] directives,
                    _mod module);
  
 +tag meta_visibility {
 +    export_meta;
 +    local_meta;
 +}
 +
  tag crate_directive_ {
      cdir_expr(@expr);
      // FIXME: cdir_let should be eliminated
@@@ -89,7 -80,7 +89,7 @@@
      cdir_src_mod(ident, option::t[filename]);
      cdir_dir_mod(ident, option::t[filename], vec[@crate_directive]);
      cdir_view_item(@view_item);
 -    cdir_meta(vec[@meta_item]);
 +    cdir_meta(meta_visibility, vec[@meta_item]);
      cdir_syntax(path);
      cdir_auth(path, _auth);
  }
@@@ -183,6 -174,7 +183,6 @@@ fn binop_to_str(binop op) -> str 
  tag unop {
      box(mutability);
      deref;
 -    bitnot;
      not;
      neg;
  }
@@@ -194,6 -186,7 +194,6 @@@ fn unop_to_str(unop op) -> str 
              ret "@";
          }
          case (deref) {ret "*";}
 -        case (bitnot) {ret "~";}
          case (not) {ret "!";}
          case (neg) {ret "-";}
      }
  
  tag mode {
      val;
 -    alias;
 +    alias(bool);
  }
  
  type stmt = spanned[stmt_];
@@@ -245,15 -238,9 +245,15 @@@ tag spawn_dom 
      dom_thread;
  }
  
 +// FIXME: temporary
 +tag seq_kind {
 +    sk_unique;
 +    sk_rc;
 +}
 +
  type expr = spanned[expr_];
  tag expr_ {
 -    expr_vec(vec[@expr], mutability, ann);
 +    expr_vec(vec[@expr], mutability, seq_kind, ann);
      expr_tup(vec[elt], ann);
      expr_rec(vec[field], option::t[@expr], ann);
      expr_call(@expr, vec[@expr], ann);
      expr_index(@expr, @expr, ann);
      expr_path(path, ann);
      expr_ext(path, vec[@expr], option::t[str], @expr, ann);
 -    expr_fail(ann);
 +    expr_fail(ann, option::t[str]);
      expr_break(ann);
      expr_cont(ann);
      expr_ret(option::t[@expr], ann);
  
  type lit = spanned[lit_];
  tag lit_ {
 -    lit_str(str);
 +    lit_str(str, seq_kind);
      lit_char(char);
      lit_int(int);
      lit_uint(uint);
  // type structure in middle/ty.rs as well.
  
  type mt = rec(@ty ty, mutability mut);
 -type ty_field = rec(ident ident, mt mt);
 -type ty_arg = rec(mode mode, @ty ty);
 -type ty_method = rec(proto proto, ident ident,
 -                     vec[ty_arg] inputs, @ty output,
 -                     controlflow cf);
 +type ty_field_ = rec(ident ident, mt mt);
 +type ty_arg_ = rec(mode mode, @ty ty);
 +type ty_method_ = rec(proto proto, ident ident,
 +                      vec[ty_arg] inputs, @ty output,
 +                      controlflow cf, vec[@constr] constrs);
 +type ty_field = spanned[ty_field_];
 +type ty_arg = spanned[ty_arg_];
 +type ty_method = spanned[ty_method_];
 +
  type ty = spanned[ty_];
  tag ty_ {
      ty_nil;
      ty_machine(util::common::ty_mach);
      ty_char;
      ty_str;
 +    ty_istr;        // interior string
      ty_box(mt);
      ty_vec(mt);
 +    ty_ivec(mt);    // interior vector
 +    ty_ptr(mt);
      ty_task;
      ty_port(@ty);
      ty_chan(@ty);
      ty_tup(vec[mt]);
      ty_rec(vec[ty_field]);
 -    ty_fn(proto, vec[ty_arg], @ty, controlflow);
 +    ty_fn(proto, vec[ty_arg], @ty, controlflow, vec[@constr]);
      ty_obj(vec[ty_method]);
      ty_path(path, ann);
      ty_type;
  tag constr_arg_ {
      carg_base;
      carg_ident(ident);
 +    carg_lit(@lit);
  }
  type constr_arg = spanned[constr_arg_];
  type constr_ = rec(path path, vec[@constr_arg] args);
@@@ -368,8 -347,7 +368,8 @@@ type arg = rec(mode mode, @ty ty, iden
  type fn_decl = rec(vec[arg] inputs,
                     @ty output,
                     purity purity,
 -                   controlflow cf);
 +                   controlflow cf,
 +                   vec[@constr] constraints);
  tag purity {
      pure_fn;   // declared with "pred"
      impure_fn; // declared with "fn"
@@@ -525,6 -503,14 +525,14 @@@ fn is_constraint_arg(@expr e) -> bool 
      }
  }
  
+ fn eq_ty(&@ty a, &@ty b) -> bool {
+     ret std::box::ptr_eq(a,b);
+ }
+ fn hash_ty(&@ty t) -> uint {
+     ret t.span.lo << 16u + t.span.hi;
+ }
  //
  // Local Variables:
  // mode: rust
diff --combined src/comp/middle/ty.rs
index 79ac28b15904d899d8cb3c3fe2eb9b0a2b462167,4e33b0b844b4124bfaf66072da5744005ffb1d4f..61053f77091d88fe5e56967299e69659c9798adc
@@@ -9,7 -9,6 +9,7 @@@ import std::map::hashmap
  import std::option;
  import std::option::none;
  import std::option::some;
 +import std::smallintmap;
  
  import driver::session;
  import front::ast;
@@@ -41,7 -40,8 +41,7 @@@ import util::data::interner
  
  tag mode {
      mo_val;
 -    mo_alias;
 -    mo_either;
 +    mo_alias(bool);
  }
  
  type arg = rec(mode mode, t ty);
@@@ -50,8 -50,7 +50,8 @@@ type method = rec(ast::proto proto
                    ast::ident ident,
                    vec[arg] inputs,
                    t output,
 -                  controlflow cf);
 +                  controlflow cf,
 +                  vec[@ast::constr] constrs);
  
  tag any_item {
      any_item_rust(@ast::item);
@@@ -72,13 -71,14 +72,14 @@@ type ctxt = rec(@type_store ts
                  item_table items,
                  type_cache tcache,
                  creader_cache rcache,
-                 hashmap[t,str] short_names_cache);
+                 hashmap[t,str] short_names_cache,
+                 hashmap[@ast::ty,option::t[t]] ast_ty_to_ty_cache);
  type ty_ctxt = ctxt;    // Needed for disambiguation from unify::ctxt.
  
  // Convert from method type to function type.  Pretty easy; we just drop
  // 'ident'.
  fn method_ty_to_fn_ty(&ctxt cx, method m) -> t {
 -    ret mk_fn(cx, m.proto, m.inputs, m.output, m.cf);
 +    ret mk_fn(cx, m.proto, m.inputs, m.output, m.cf, m.constrs);
  }
  
  // Never construct these manually. These are interned.
@@@ -89,7 -89,9 +90,7 @@@ type raw_t = rec(sty struct
                   option::t[str] cname,
                   uint hash,
                   bool has_params,
 -                 bool has_bound_params,
 -                 bool has_vars,
 -                 bool has_locals);
 +                 bool has_vars);
  
  type t = uint;
  
@@@ -105,22 -107,21 +106,22 @@@ tag sty 
      ty_machine(util::common::ty_mach);
      ty_char;
      ty_str;
 +    ty_istr;
      ty_tag(ast::def_id, vec[t]);
      ty_box(mt);
      ty_vec(mt);
 +    ty_ivec(mt);
 +    ty_ptr(mt);
      ty_port(t);
      ty_chan(t);
      ty_task;
      ty_tup(vec[mt]);
      ty_rec(vec[field]);
 -    ty_fn(ast::proto, vec[arg], t, controlflow);
 +    ty_fn(ast::proto, vec[arg], t, controlflow, vec[@ast::constr]);
      ty_native_fn(ast::native_abi, vec[arg], t);
      ty_obj(vec[method]);
 -    ty_var(int);                                    // ephemeral type var
 -    ty_local(ast::def_id);                           // type of a local var
 +    ty_var(int);                                    // type variable
      ty_param(uint);                                 // fn/tag type param
 -    ty_bound_param(uint);                           // bound param, only paths
      ty_type;
      ty_native;
      // TODO: ty_fn_arg(t), for a possibly-aliased function argument
  
  // Data structures used in type unification
  
 -type unify_handler = obj {
 -    fn resolve_local(ast::def_id id) -> option::t[t];
 -    fn record_local(ast::def_id id, t ty);  // TODO: -> unify::result
 -    fn record_param(uint index, t binding) -> unify::result;
 -};
 -
  tag type_err {
      terr_mismatch;
      terr_controlflow_mismatch;
@@@ -164,12 -171,11 +165,12 @@@ const uint idx_f32      = 13u
  const uint idx_f64      = 14u;
  const uint idx_char     = 15u;
  const uint idx_str      = 16u;
 -const uint idx_task     = 17u;
 -const uint idx_native   = 18u;
 -const uint idx_type     = 19u;
 -const uint idx_bot      = 20u;
 -const uint idx_first_others = 21u;
 +const uint idx_istr     = 17u;
 +const uint idx_task     = 18u;
 +const uint idx_native   = 19u;
 +const uint idx_type     = 20u;
 +const uint idx_bot      = 21u;
 +const uint idx_first_others = 22u;
  
  type type_store = interner::interner[raw_t];
  
@@@ -196,7 -202,6 +197,7 @@@ fn populate_type_store(&ctxt cx) 
      intern(cx, ty_machine(ty_f64), none[str]);
      intern(cx, ty_char, none[str]);
      intern(cx, ty_str, none[str]);
 +    intern(cx, ty_istr, none[str]);
      intern(cx, ty_task, none[str]);
      intern(cx, ty_native, none[str]);
      intern(cx, ty_type, none[str]);
@@@ -241,7 -246,9 +242,9 @@@ fn mk_ctxt(session::session s, resolve:
              tcache = tcache,
              rcache = mk_rcache(),
              short_names_cache =
-             map::mk_hashmap[ty::t,str](ty::hash_ty, ty::eq_ty));
+             map::mk_hashmap[ty::t,str](ty::hash_ty, ty::eq_ty),
+             ast_ty_to_ty_cache = 
+             map::mk_hashmap[@ast::ty,option::t[t]](ast::hash_ty, ast::eq_ty));
  
      populate_type_store(cx);
      ret cx;
@@@ -254,101 -261,132 +257,101 @@@ fn mk_raw_ty(&ctxt cx, &sty st, &option
      auto h = hash_type_info(st, cname);
  
      let bool has_params = false;
 -    let bool has_bound_params = false;
      let bool has_vars = false;
 -    let bool has_locals = false;
  
      fn derive_flags_t(&ctxt cx,
                        &mutable bool has_params,
 -                      &mutable bool has_bound_params,
                        &mutable bool has_vars,
 -                      &mutable bool has_locals,
                        &t tt) {
          auto rt = interner::get[raw_t](*cx.ts, tt);
          has_params = has_params || rt.has_params;
 -        has_bound_params = has_bound_params || rt.has_bound_params;
          has_vars = has_vars || rt.has_vars;
 -        has_locals = has_locals || rt.has_locals;
      }
  
      fn derive_flags_mt(&ctxt cx,
                         &mutable bool has_params,
 -                       &mutable bool has_bound_params,
                         &mutable bool has_vars,
 -                       &mutable bool has_locals,
                         &mt m) {
 -        derive_flags_t(cx, has_params, has_bound_params,
 -                       has_vars, has_locals, m.ty);
 +        derive_flags_t(cx, has_params, has_vars, m.ty);
      }
  
  
      fn derive_flags_arg(&ctxt cx,
                          &mutable bool has_params,
 -                        &mutable bool has_bound_params,
                          &mutable bool has_vars,
 -                        &mutable bool has_locals,
                          &arg a) {
 -        derive_flags_t(cx, has_params, has_bound_params,
 -                       has_vars, has_locals, a.ty);
 +        derive_flags_t(cx, has_params, has_vars, a.ty);
      }
  
      fn derive_flags_sig(&ctxt cx,
                          &mutable bool has_params,
 -                        &mutable bool has_bound_params,
                          &mutable bool has_vars,
 -                        &mutable bool has_locals,
                          &vec[arg] args,
                          &t tt) {
          for (arg a in args) {
 -            derive_flags_arg(cx, has_params, has_bound_params,
 -                             has_vars, has_locals, a);
 +            derive_flags_arg(cx, has_params, has_vars, a);
          }
 -        derive_flags_t(cx, has_params, has_bound_params,
 -                       has_vars, has_locals, tt);
 +        derive_flags_t(cx, has_params, has_vars, tt);
      }
  
      alt (st) {
          case (ty_param(_)) {
              has_params = true;
          }
 -        case (ty_bound_param(_)) {
 -            has_bound_params = true;
 -        }
          case (ty_var(_)) { has_vars = true; }
 -        case (ty_local(_)) { has_locals = true; }
          case (ty_tag(_, ?tys)) {
              for (t tt in tys) {
 -                derive_flags_t(cx, has_params, has_bound_params,
 -                               has_vars, has_locals, tt);
 +                derive_flags_t(cx, has_params, has_vars, tt);
              }
          }
          case (ty_box(?m)) {
 -            derive_flags_mt(cx, has_params, has_bound_params,
 -                            has_vars, has_locals, m);
 +            derive_flags_mt(cx, has_params, has_vars, m);
          }
  
          case (ty_vec(?m)) {
 -            derive_flags_mt(cx, has_params, has_bound_params,
 -                            has_vars, has_locals, m);
 +            derive_flags_mt(cx, has_params, has_vars, m);
          }
  
          case (ty_port(?tt)) {
 -            derive_flags_t(cx, has_params, has_bound_params,
 -                           has_vars, has_locals, tt);
 +            derive_flags_t(cx, has_params, has_vars, tt);
          }
  
          case (ty_chan(?tt)) {
 -            derive_flags_t(cx, has_params, has_bound_params,
 -                           has_vars, has_locals, tt);
 +            derive_flags_t(cx, has_params, has_vars, tt);
          }
  
          case (ty_tup(?mts)) {
              for (mt m in mts) {
 -                derive_flags_mt(cx, has_params, has_bound_params,
 -                                has_vars, has_locals, m);
 +                derive_flags_mt(cx, has_params, has_vars, m);
              }
          }
  
          case (ty_rec(?flds)) {
              for (field f in flds) {
 -                derive_flags_mt(cx, has_params, has_bound_params,
 -                                has_vars, has_locals, f.mt);
 +                derive_flags_mt(cx, has_params, has_vars, f.mt);
              }
          }
  
 -        case (ty_fn(_, ?args, ?tt, _)) {
 -            derive_flags_sig(cx, has_params, has_bound_params,
 -                             has_vars, has_locals, args, tt);
 +        case (ty_fn(_, ?args, ?tt, _, _)) {
 +            derive_flags_sig(cx, has_params, has_vars, args, tt);
          }
  
          case (ty_native_fn(_, ?args, ?tt)) {
 -            derive_flags_sig(cx, has_params, has_bound_params,
 -                             has_vars, has_locals, args, tt);
 +            derive_flags_sig(cx, has_params, has_vars, args, tt);
          }
  
          case (ty_obj(?meths)) {
              for (method m in meths) {
 -                derive_flags_sig(cx, has_params, has_bound_params,
 -                                 has_vars, has_locals, m.inputs, m.output);
 +                derive_flags_sig(cx, has_params, has_vars, m.inputs,
 +                                 m.output);
              }
          }
          case (_) { }
      }
  
      ret rec(struct=st, cname=cname, hash=h,
 -            has_params = has_params,
 -            has_bound_params = has_bound_params,
 -            has_vars = has_vars,
 -            has_locals = has_locals);
 +            has_params=has_params,
 +            has_vars=has_vars);
  }
  
  fn intern(&ctxt cx, &sty st, &option::t[str] cname) {
@@@ -388,11 -426,11 +391,11 @@@ fn mk_mach(&ctxt cx, &util::common::ty_
          case (ty_f32) { ret idx_f32; }
          case (ty_f64) { ret idx_f64; }
      }
 -    fail;
  }
  
  fn mk_char(&ctxt cx) -> t    { ret idx_char; }
  fn mk_str(&ctxt cx) -> t     { ret idx_str; }
 +fn mk_istr(&ctxt cx) -> t    { ret idx_istr; }
  
  fn mk_tag(&ctxt cx, &ast::def_id did, &vec[t] tys) -> t {
      ret gen_ty(cx, ty_tag(did, tys));
@@@ -402,18 -440,12 +405,18 @@@ fn mk_box(&ctxt cx, &mt tm) -> t 
      ret gen_ty(cx, ty_box(tm));
  }
  
 +fn mk_ptr(&ctxt cx, &mt tm) -> t {
 +    ret gen_ty(cx, ty_ptr(tm));
 +}
 +
  fn mk_imm_box(&ctxt cx, &t ty) -> t {
      ret mk_box(cx, rec(ty=ty, mut=ast::imm));
  }
  
  fn mk_vec(&ctxt cx, &mt tm) -> t  { ret gen_ty(cx, ty_vec(tm)); }
  
 +fn mk_ivec(&ctxt cx, &mt tm) -> t { ret gen_ty(cx, ty_ivec(tm)); }
 +
  fn mk_imm_vec(&ctxt cx, &t typ) -> t {
      ret gen_ty(cx, ty_vec(rec(ty=typ, mut=ast::imm)));
  }
@@@ -436,8 -468,8 +439,8 @@@ fn mk_imm_tup(&ctxt cx, &vec[t] tys) -
  fn mk_rec(&ctxt cx, &vec[field] fs) -> t { ret gen_ty(cx, ty_rec(fs)); }
  
  fn mk_fn(&ctxt cx, &ast::proto proto, &vec[arg] args, &t ty,
 -         &controlflow cf) -> t {
 -    ret gen_ty(cx, ty_fn(proto, args, ty, cf));
 +         &controlflow cf, &vec[@ast::constr] constrs) -> t {
 +    ret gen_ty(cx, ty_fn(proto, args, ty, cf, constrs));
  }
  
  fn mk_native_fn(&ctxt cx, &ast::native_abi abi, &vec[arg] args, &t ty) -> t {
@@@ -452,10 -484,18 +455,10 @@@ fn mk_var(&ctxt cx, int v) -> t 
      ret gen_ty(cx, ty_var(v));
  }
  
 -fn mk_local(&ctxt cx, ast::def_id did) -> t {
 -    ret gen_ty(cx, ty_local(did));
 -}
 -
  fn mk_param(&ctxt cx, uint n) -> t {
      ret gen_ty(cx, ty_param(n));
  }
  
 -fn mk_bound_param(&ctxt cx, uint n) -> t {
 -    ret gen_ty(cx, ty_bound_param(n));
 -}
 -
  fn mk_type(&ctxt cx) -> t    { ret idx_type; }
  fn mk_native(&ctxt cx) -> t  { ret idx_native; }
  
@@@ -486,6 -526,173 +489,6 @@@ fn path_to_str(&ast::path pth) -> str 
      ret result;
  }
  
 -fn ty_to_str(&ctxt cx, &t typ) -> str {
 -
 -    fn fn_input_to_str(&ctxt cx, &rec(mode mode, t ty) input) -> str {
 -        auto s;
 -        alt (input.mode) {
 -            case (mo_val) { s = ""; }
 -            case (mo_alias) { s = "&"; }
 -            case (mo_either) { s = "?"; }
 -        }
 -
 -        ret s + ty_to_str(cx, input.ty);
 -    }
 -
 -    fn fn_to_str(&ctxt cx,
 -                 ast::proto proto,
 -                 option::t[ast::ident] ident,
 -                 vec[arg] inputs, t output) -> str {
 -            auto f = bind fn_input_to_str(cx, _);
 -
 -            auto s;
 -            alt (proto) {
 -                case (ast::proto_iter) {
 -                    s = "iter";
 -                }
 -                case (ast::proto_fn) {
 -                    s = "fn";
 -                }
 -            }
 -
 -            alt (ident) {
 -                case (some(?i)) {
 -                    s += " ";
 -                    s += i;
 -                }
 -                case (_) { }
 -            }
 -
 -            s += "(";
 -            s += str::connect(vec::map[arg,str](f, inputs), ", ");
 -            s += ")";
 -
 -            if (struct(cx, output) != ty_nil) {
 -                s += " -> " + ty_to_str(cx, output);
 -            }
 -            ret s;
 -    }
 -
 -    fn method_to_str(&ctxt cx, &method m) -> str {
 -        ret fn_to_str(cx, m.proto, some[ast::ident](m.ident),
 -                      m.inputs, m.output) + ";";
 -    }
 -
 -    fn field_to_str(&ctxt cx, &field f) -> str {
 -        ret mt_to_str(cx, f.mt) + " " + f.ident;
 -    }
 -
 -    fn mt_to_str(&ctxt cx, &mt m) -> str {
 -        auto mstr;
 -        alt (m.mut) {
 -            case (ast::mut)       { mstr = "mutable "; }
 -            case (ast::imm)       { mstr = "";         }
 -            case (ast::maybe_mut) { mstr = "mutable? "; }
 -        }
 -
 -        ret mstr + ty_to_str(cx, m.ty);
 -    }
 -
 -    alt (cname(cx, typ)) {
 -        case (some(?cs)) {
 -            ret cs;
 -        }
 -        case (_) { }
 -    }
 -
 -    auto s = "";
 -
 -    alt (struct(cx, typ)) {
 -        case (ty_native)       { s += "native";                         }
 -        case (ty_nil)          { s += "()";                             }
 -        case (ty_bot)          { s += "_|_";                            }
 -        case (ty_bool)         { s += "bool";                           }
 -        case (ty_int)          { s += "int";                            }
 -        case (ty_float)        { s += "float";                          }
 -        case (ty_uint)         { s += "uint";                           }
 -        case (ty_machine(?tm)) { s += common::ty_mach_to_str(tm);        }
 -        case (ty_char)         { s += "char";                           }
 -        case (ty_str)          { s += "str";                            }
 -        case (ty_box(?tm))     { s += "@" + mt_to_str(cx, tm);          }
 -        case (ty_vec(?tm))     { s += "vec[" + mt_to_str(cx, tm) + "]"; }
 -        case (ty_port(?t))     { s += "port[" + ty_to_str(cx, t) + "]"; }
 -        case (ty_chan(?t))     { s += "chan[" + ty_to_str(cx, t) + "]"; }
 -        case (ty_type)         { s += "type";                           }
 -        case (ty_task)         { s += "task";                           }
 -
 -        case (ty_tup(?elems)) {
 -            auto f = bind mt_to_str(cx, _);
 -            auto strs = vec::map[mt,str](f, elems);
 -            s += "tup(" + str::connect(strs, ",") + ")";
 -        }
 -
 -        case (ty_rec(?elems)) {
 -            auto f = bind field_to_str(cx, _);
 -            auto strs = vec::map[field,str](f, elems);
 -            s += "rec(" + str::connect(strs, ",") + ")";
 -        }
 -
 -        case (ty_tag(?id, ?tps)) {
 -            // The user should never see this if the cname is set properly!
 -            s += "<tag#" + util::common::istr(id._0) + ":" +
 -                util::common::istr(id._1) + ">";
 -            if (vec::len[t](tps) > 0u) {
 -                auto f = bind ty_to_str(cx, _);
 -                auto strs = vec::map[t,str](f, tps);
 -                s += "[" + str::connect(strs, ",") + "]";
 -            }
 -        }
 -
 -        case (ty_fn(?proto, ?inputs, ?output, _)) {
 -            s += fn_to_str(cx, proto, none[ast::ident], inputs, output);
 -        }
 -
 -        case (ty_native_fn(_, ?inputs, ?output)) {
 -            s += fn_to_str(cx, ast::proto_fn, none[ast::ident],
 -                           inputs, output);
 -        }
 -
 -        case (ty_obj(?meths)) {
 -            auto f = bind method_to_str(cx, _);
 -            auto m = vec::map[method,str](f, meths);
 -            s += "obj {\n\t" + str::connect(m, "\n\t") + "\n}";
 -        }
 -
 -        case (ty_var(?v)) {
 -            s += "<T" + util::common::istr(v) + ">";
 -        }
 -
 -        case (ty_local(?id)) {
 -            s += "<L" + util::common::istr(id._0) + ":" +
 -                util::common::istr(id._1) + ">";
 -        }
 -
 -        case (ty_param(?id)) {
 -            s += "'" + str::unsafe_from_bytes([('a' as u8) + (id as u8)]);
 -        }
 -
 -        case (ty_bound_param(?id)) {
 -            s += "''" + str::unsafe_from_bytes([('a' as u8) +
 -                                                    (id as u8)]);
 -        }
 -
 -        case (_) {
 -            s += ty_to_short_str(cx, typ);
 -        }
 -
 -    }
 -
 -    ret s;
 -}
 -
 -fn ty_to_short_str(&ctxt cx, t typ) -> str {
 -    auto f = def_to_str;
 -    auto ecx = @rec(ds=f, tcx=cx, abbrevs=metadata::ac_no_abbrevs);
 -    auto s = metadata::Encode::ty_str(ecx, typ);
 -    if (str::byte_len(s) >= 32u) { s = str::substr(s, 0u, 32u); }
 -    ret s;
 -}
 -
  // Type folds
  
  type ty_walk = fn(t);
@@@ -501,12 -708,10 +504,12 @@@ fn walk_ty(&ctxt cx, ty_walk walker, t 
          case (ty_machine(_))    { /* no-op */ }
          case (ty_char)          { /* no-op */ }
          case (ty_str)           { /* no-op */ }
 +        case (ty_istr)          { /* no-op */ }
          case (ty_type)          { /* no-op */ }
          case (ty_native)        { /* no-op */ }
          case (ty_box(?tm))      { walk_ty(cx, walker, tm.ty); }
          case (ty_vec(?tm))      { walk_ty(cx, walker, tm.ty); }
 +        case (ty_ivec(?tm))     { walk_ty(cx, walker, tm.ty); }
          case (ty_port(?subty))  { walk_ty(cx, walker, subty); }
          case (ty_chan(?subty))  { walk_ty(cx, walker, subty); }
          case (ty_tag(?tid, ?subtys)) {
                  walk_ty(cx, walker, fl.mt.ty);
              }
          }
 -        case (ty_fn(?proto, ?args, ?ret_ty, _)) {
 +        case (ty_fn(?proto, ?args, ?ret_ty, _, _)) {
              for (arg a in args) {
                  walk_ty(cx, walker, a.ty);
              }
              }
          }
          case (ty_var(_))         { /* no-op */ }
 -        case (ty_local(_))       { /* no-op */ }
          case (ty_param(_))       { /* no-op */ }
 -        case (ty_bound_param(_)) { /* no-op */ }
      }
  
      walker(ty);
  }
  
 -type ty_fold = fn(t) -> t;
 +tag fold_mode {
 +    fm_var(fn(int)->t);
 +    fm_param(fn(uint)->t);
 +    fm_general(fn(t)->t);
 +}
  
 -fn fold_ty(&ctxt cx, ty_fold fld, t ty_0) -> t {
 +fn fold_ty(&ctxt cx, fold_mode fld, t ty_0) -> t {
      auto ty = ty_0;
 +
 +    // Fast paths.
 +    alt (fld) {
 +        case (fm_var(_)) { if (!type_contains_vars(cx, ty)) { ret ty; } }
 +        case (fm_param(_)) { if (!type_contains_params(cx, ty)) { ret ty; } }
 +        case (fm_general(_)) { /* no fast path */ }
 +    }
 +
      alt (struct(cx, ty)) {
          case (ty_nil)           { /* no-op */ }
          case (ty_bot)           { /* no-op */ }
              ty = copy_cname(cx, mk_box(cx, rec(ty=fold_ty(cx, fld, tm.ty),
                                                 mut=tm.mut)), ty);
          }
 +        case (ty_ptr(?tm)) {
 +            ty = copy_cname(cx, mk_ptr(cx, rec(ty=fold_ty(cx, fld, tm.ty),
 +                                               mut=tm.mut)), ty);
 +        }
          case (ty_vec(?tm)) {
              ty = copy_cname(cx, mk_vec(cx, rec(ty=fold_ty(cx, fld, tm.ty),
                                                            mut=tm.mut)), ty);
              }
              ty = copy_cname(cx, mk_rec(cx, new_fields), ty);
          }
 -        case (ty_fn(?proto, ?args, ?ret_ty, ?cf)) {
 +        case (ty_fn(?proto, ?args, ?ret_ty, ?cf, ?constrs)) {
              let vec[arg] new_args = [];
              for (arg a in args) {
                  auto new_ty = fold_ty(cx, fld, a.ty);
                  new_args += [rec(mode=a.mode, ty=new_ty)];
              }
              ty = copy_cname(cx, mk_fn(cx, proto, new_args,
 -                                      fold_ty(cx, fld, ret_ty), cf), ty);
 +                                  fold_ty(cx, fld, ret_ty), cf, constrs), ty);
          }
          case (ty_native_fn(?abi, ?args, ?ret_ty)) {
              let vec[arg] new_args = [];
                  }
                  new_methods += [rec(proto=m.proto, ident=m.ident,
                                 inputs=new_args,
 -                               output=fold_ty(cx, fld, m.output), cf=m.cf)];
 +                               output=fold_ty(cx, fld, m.output),
 +                               cf=m.cf, constrs=m.constrs)];
              }
              ty = copy_cname(cx, mk_obj(cx, new_methods), ty);
          }
 -        case (ty_var(_))         { /* no-op */ }
 -        case (ty_local(_))       { /* no-op */ }
 -        case (ty_param(_))       { /* no-op */ }
 -        case (ty_bound_param(_)) { /* no-op */ }
 +        case (ty_var(?id)) {
 +            alt (fld) {
 +                case (fm_var(?folder)) { ty = folder(id); }
 +                case (_) { /* no-op */ }
 +            }
 +        }
 +        case (ty_param(?id)) {
 +            alt (fld) {
 +                case (fm_param(?folder)) { ty = folder(id); }
 +                case (_) { /* no-op */ }
 +            }
 +        }
      }
  
 -    ret fld(ty);
 +    // If this is a general type fold, then we need to run it now.
 +    alt (fld) {
 +        case (fm_general(?folder)) { ret folder(ty); }
 +        case (_) { ret ty; }
 +    }
  }
  
  // Type utilities
@@@ -716,10 -894,11 +719,10 @@@ fn type_is_structural(&ctxt cx, &t ty) 
          case (ty_tup(_))    { ret true; }
          case (ty_rec(_))    { ret true; }
          case (ty_tag(_,_))  { ret true; }
 -        case (ty_fn(_,_,_,_)) { ret true; }
 +        case (ty_fn(_,_,_,_,_)) { ret true; }
          case (ty_obj(_))    { ret true; }
          case (_)            { ret false; }
      }
 -    fail;
  }
  
  fn type_is_sequence(&ctxt cx, &t ty) -> bool {
          case (ty_vec(_))    { ret true; }
          case (_)            { ret false; }
      }
 -    fail;
  }
  
  fn sequence_element_type(&ctxt cx, &t ty) -> t {
      alt (struct(cx, ty)) {
          case (ty_str)      { ret mk_mach(cx, common::ty_u8); }
          case (ty_vec(?mt)) { ret mt.ty; }
 +        // NB: This is not exhaustive.
      }
 -    fail;
 +
 +    cx.sess.bug("sequence_element_type called on non-sequence value");
  }
  
  fn type_is_tup_like(&ctxt cx, &t ty) -> bool {
          case (ty_tag(_,_))  { ret true; }
          case (_)            { ret false; }
      }
 -    fail;
  }
  
  fn get_element_type(&ctxt cx, &t ty, uint i) -> t {
          case (ty_rec(?flds)) {
              ret flds.(i).mt.ty;
          }
 +        // NB: This is not exhaustive -- struct(cx, ty) could be a box or a
 +        // tag.
      }
 -    fail;
 +
 +    cx.sess.bug("get_element_type called on a value other than a "
 +                + "tuple or record");
  }
  
  fn type_is_box(&ctxt cx, &t ty) -> bool {
          case (ty_box(_)) { ret true; }
          case (_) { ret false; }
      }
 -    fail;
  }
  
  fn type_is_boxed(&ctxt cx, &t ty) -> bool {
          case (ty_task) { ret true; }
          case (_) { ret false; }
      }
 -    fail;
  }
  
  fn type_is_scalar(&ctxt cx, &t ty) -> bool {
          case (ty_char) { ret true; }
          case (ty_type) { ret true; }
          case (ty_native) { ret true; }
 +        case (ty_ptr(_)) { ret true; }
          case (_) { ret false; }
      }
 -    fail;
  }
  
 -
  fn type_has_pointers(&ctxt cx, &t ty) -> bool {
      alt (struct(cx, ty)) {
          // scalar types
          case (ty_nil) { ret false; }
 +        case (ty_bot) { ret false; }
          case (ty_bool) { ret false; }
          case (ty_int) { ret false; }
          case (ty_float) { ret false; }
              for (variant_info variant in variants) {
                  auto tup_ty = mk_imm_tup(cx, variant.args);
                  // Perform any type parameter substitutions.
 -                tup_ty = bind_params_in_type(cx, tup_ty);
                  tup_ty = substitute_type_params(cx, tps, tup_ty);
                  if (type_has_pointers(cx, tup_ty)) { ret true; }
              }
          }
          case (_) { ret true; }
      }
 -    fail;
  }
  
  
@@@ -851,6 -1030,7 +854,6 @@@ fn type_is_native(&ctxt cx, &t ty) -> b
          case (ty_native) { ret true; }
          case (_) { ret false; }
      }
 -    fail;
  }
  
  fn type_has_dynamic_size(&ctxt cx, &t ty) -> bool {
@@@ -905,6 -1085,7 +908,6 @@@ fn type_is_integral(&ctxt cx, &t ty) -
          case (ty_char) { ret true; }
          case (_) { ret false; }
      }
 -    fail;
  }
  
  fn type_is_fp(&ctxt cx, &t ty) -> bool {
          }
          case (_) { ret false; }
      }
 -    fail;
  }
  
  fn type_is_signed(&ctxt cx, &t ty) -> bool {
          }
          case (_) { ret false; }
      }
 -    fail;
  }
  
  fn type_param(&ctxt cx, &t ty) -> option::t[uint] {
@@@ -1007,50 -1190,48 +1010,50 @@@ fn hash_type_structure(&sty st) -> uin
          }
          case (ty_char) { ret 15u; }
          case (ty_str) { ret 16u; }
 +        case (ty_istr) { ret 17u; }
          case (ty_tag(?did, ?tys)) {
 -            auto h = hash_def(17u, did);
 +            auto h = hash_def(18u, did);
              for (t typ in tys) {
                  h += h << 5u + hash_ty(typ);
              }
              ret h;
          }
 -        case (ty_box(?mt)) { ret hash_subty(18u, mt.ty); }
 -        case (ty_vec(?mt)) { ret hash_subty(19u, mt.ty); }
 -        case (ty_port(?typ)) { ret hash_subty(20u, typ); }
 -        case (ty_chan(?typ)) { ret hash_subty(21u, typ); }
 -        case (ty_task) { ret 22u; }
 +        case (ty_box(?mt)) { ret hash_subty(19u, mt.ty); }
 +        case (ty_vec(?mt)) { ret hash_subty(20u, mt.ty); }
 +        case (ty_ivec(?mt)) { ret hash_subty(21u, mt.ty); }
 +        case (ty_port(?typ)) { ret hash_subty(22u, typ); }
 +        case (ty_chan(?typ)) { ret hash_subty(23u, typ); }
 +        case (ty_task) { ret 24u; }
          case (ty_tup(?mts)) {
 -            auto h = 23u;
 +            auto h = 25u;
              for (mt tm in mts) {
                  h += h << 5u + hash_ty(tm.ty);
              }
              ret h;
          }
          case (ty_rec(?fields)) {
 -            auto h = 24u;
 +            auto h = 26u;
              for (field f in fields) {
                  h += h << 5u + hash_ty(f.mt.ty);
              }
              ret h;
          }
 -        case (ty_fn(_, ?args, ?rty, _)) { ret hash_fn(25u, args, rty); }
 -        case (ty_native_fn(_, ?args, ?rty)) { ret hash_fn(26u, args, rty); }
 +        // ???
 +        case (ty_fn(_, ?args, ?rty, _, _)) { ret hash_fn(27u, args, rty); } 
 +        case (ty_native_fn(_, ?args, ?rty)) { ret hash_fn(28u, args, rty); }
          case (ty_obj(?methods)) {
 -            auto h = 27u;
 +            auto h = 29u;
              for (method m in methods) {
                  h += h << 5u + str::hash(m.ident);
              }
              ret h;
          }
 -        case (ty_var(?v)) { ret hash_uint(28u, v as uint); }
 -        case (ty_local(?did)) { ret hash_def(29u, did); }
 -        case (ty_param(?pid)) { ret hash_uint(30u, pid); }
 -        case (ty_bound_param(?pid)) { ret hash_uint(31u, pid); }
 +        case (ty_var(?v)) { ret hash_uint(30u, v as uint); }
 +        case (ty_param(?pid)) { ret hash_uint(31u, pid); }
          case (ty_type) { ret 32u; }
          case (ty_native) { ret 33u; }
          case (ty_bot) { ret 34u; }
 +        case (ty_ptr(?mt)) { ret hash_subty(35u, mt.ty); }
      }
  }
  
@@@ -1070,72 -1251,6 +1073,72 @@@ fn hash_ty(&t typ) -> uint { ret typ; 
  
  // Type equality. This function is private to this module (and slow); external
  // users should use `eq_ty()` instead.
 +
 +fn arg_eq(@ast::constr_arg a, @ast::constr_arg b) -> bool {
 +    alt (a.node) {
 +        case (ast::carg_base) {
 +            alt (b.node) {
 +                case (ast::carg_base) {
 +                    ret true;
 +                }
 +                case (_) {
 +                    ret false;
 +                }
 +            }
 +        }
 +        case (ast::carg_ident(?s)) {
 +            alt (b.node) {
 +                case (ast::carg_ident(?t)) {
 +                    ret (s == t);
 +                }
 +                case (_) {
 +                    ret false;
 +                }
 +            }
 +        }
 +        case (ast::carg_lit(?l)) {
 +            alt (b.node) {
 +                case (ast::carg_lit(?m)) {
 +                    ret util::common::lit_eq(l, m);
 +                }
 +                case (_) {
 +                    ret false;
 +                }
 +            }
 +        }
 +    }
 +}
 +fn args_eq(vec[@ast::constr_arg] a, vec[@ast::constr_arg] b) -> bool {
 +    let uint i = 0u;
 +    for (@ast::constr_arg arg in a) {
 +        if (!arg_eq(arg, b.(i))) {
 +            ret false;
 +        }
 +        i += 1u;
 +    }
 +    ret true;
 +}
 +
 +
 +fn constr_eq(&@ast::constr c, &@ast::constr d) -> bool {
 +    ret path_to_str(c.node.path) == path_to_str(d.node.path) // FIXME: hack
 +        && args_eq(c.node.args, d.node.args);
 +}
 +
 +fn constrs_eq(&vec[@ast::constr] cs, &vec[@ast::constr] ds) -> bool {
 +    if (vec::len(cs) != vec::len(ds)) {
 +        ret false;
 +    }
 +    auto i = 0;
 +    for (@ast::constr c in cs) {
 +        if (!constr_eq(c, ds.(i))) {
 +            ret false;
 +        }
 +        i += 1;
 +    }
 +    ret true;
 +}
 +
  fn equal_type_structures(&sty a, &sty b) -> bool {
      fn equal_mt(&mt a, &mt b) -> bool {
          ret a.mut == b.mut && eq_ty(a.ty, b.ty);
                  case (_) { ret false; }
              }
          }
 +        case (ty_ptr(?mt_a)) {
 +            alt (b) {
 +                case (ty_ptr(?mt_b)) { ret equal_mt(mt_a, mt_b); }
 +                case (_) { ret false; }
 +            }
 +        }
          case (ty_port(?t_a)) {
              alt (b) {
                  case (ty_port(?t_b)) { ret eq_ty(t_a, t_b); }
                  case (_) { ret false; }
              }
          }
 -        case (ty_fn(?p_a, ?args_a, ?rty_a, ?cf_a)) {
 +        case (ty_fn(?p_a, ?args_a, ?rty_a, ?cf_a, ?constrs_a)) {
              alt (b) {
 -                case (ty_fn(?p_b, ?args_b, ?rty_b, ?cf_b)) {
 +                case (ty_fn(?p_b, ?args_b, ?rty_b, ?cf_b, ?constrs_b)) {
                      ret p_a == p_b &&
                          cf_a == cf_b &&
 +                        constrs_eq(constrs_a, constrs_b) &&
                          equal_fn(args_a, rty_a, args_b, rty_b);
                  }
                  case (_) { ret false; }
                  case (_) { ret false; }
              }
          }
 -        case (ty_local(?did_a)) {
 -            alt (b) {
 -                case (ty_local(?did_b)) { ret equal_def(did_a, did_b); }
 -                case (_) { ret false; }
 -            }
 -        }
          case (ty_param(?pid_a)) {
              alt (b) {
                  case (ty_param(?pid_b)) { ret pid_a == pid_b; }
                  case (_) { ret false; }
              }
          }
 -        case (ty_bound_param(?pid_a)) {
 -            alt (b) {
 -                case (ty_bound_param(?pid_b)) { ret pid_a == pid_b; }
 -                case (_) { ret false; }
 -            }
 -        }
          case (ty_type) {
              alt (b) {
                  case (ty_type) { ret true; }
@@@ -1413,25 -1533,24 +1416,25 @@@ fn eq_ty(&t a, &t b) -> bool { ret a =
  
  // Type lookups
  
 -fn ann_to_ty_param_substs_opt_and_ty(&node_type_table ntt, &ast::ann ann)
 -        -> ty_param_substs_opt_and_ty {
 -    alt (ntt.(ann.id)) {
 +fn ann_to_ty_param_substs_opt_and_ty(&ctxt cx, &ast::ann ann)
 +    -> ty_param_substs_opt_and_ty {
 +
 +    // Pull out the node type table.
 +    alt ({cx.node_types.(ann.id)}) {
          case (none) {
 -            log_err "ann_to_ty_param_substs_opt_and_ty() called on an " +
 -                "untyped node";
 -            fail;
 +            cx.sess.bug("ann_to_ty_param_substs_opt_and_ty() called on an " +
 +                        "untyped node");
          }
          case (some(?tpot)) { ret tpot; }
      }
  }
  
 -fn ann_to_type(&node_type_table ntt, &ast::ann ann) -> t {
 -    ret ann_to_ty_param_substs_opt_and_ty(ntt, ann)._1;
 +fn ann_to_type(&ctxt cx, &ast::ann ann) -> t {
 +    ret ann_to_ty_param_substs_opt_and_ty(cx, ann)._1;
  }
  
 -fn ann_to_type_params(&node_type_table ntt, &ast::ann ann) -> vec[t] {
 -    alt (ann_to_ty_param_substs_opt_and_ty(ntt, ann)._0) {
 +fn ann_to_type_params(&ctxt cx, &ast::ann ann) -> vec[t] {
 +    alt (ann_to_ty_param_substs_opt_and_ty(cx, ann)._0) {
          case (none) {
              let vec[t] result = [];
              ret result;
      }
  }
  
 -fn ann_has_type_params(&node_type_table ntt, &ast::ann ann) -> bool {
 -    auto tpt = ann_to_ty_param_substs_opt_and_ty(ntt, ann);
 +fn ann_has_type_params(&ctxt cx, &ast::ann ann) -> bool {
 +    auto tpt = ann_to_ty_param_substs_opt_and_ty(cx, ann);
      ret !option::is_none[vec[t]](tpt._0);
  }
  
  
 -// Returns the type of an annotation, with type parameter substitutions
 -// performed if applicable.
 -fn ann_to_monotype(&ctxt cx, ast::ann a) -> t {
 -    auto tpot = ann_to_ty_param_substs_opt_and_ty(cx.node_types, a);
 +// Returns a type with type parameter substitutions performed if applicable.
 +fn ty_param_substs_opt_and_ty_to_monotype(&ctxt cx,
 +                                          &ty_param_substs_opt_and_ty tpot)
 +        -> t {
      alt (tpot._0) {
          case (none) { ret tpot._1; }
 -        case (some(?tps)) {
 -            ret substitute_type_params(cx, tps, tpot._1);
 -        }
 +        case (some(?tps)) { ret substitute_type_params(cx, tps, tpot._1); }
      }
  }
  
 +// Returns the type of an annotation, with type parameter substitutions
 +// performed if applicable.
 +fn ann_to_monotype(&ctxt cx, ast::ann a) -> t {
 +    auto tpot = ann_to_ty_param_substs_opt_and_ty(cx, a);
 +    ret ty_param_substs_opt_and_ty_to_monotype(cx, tpot);
 +}
 +
  
  // Returns the number of distinct type parameters in the given type.
  fn count_ty_params(&ctxt cx, t ty) -> uint {
@@@ -1494,62 -1608,122 +1497,62 @@@ fn type_contains_vars(&ctxt cx, &t typ
      ret interner::get[raw_t](*cx.ts, typ).has_vars;
  }
  
 -fn type_contains_locals(&ctxt cx, &t typ) -> bool {
 -    ret interner::get[raw_t](*cx.ts, typ).has_locals;
 -}
 -
  fn type_contains_params(&ctxt cx, &t typ) -> bool {
      ret interner::get[raw_t](*cx.ts, typ).has_params;
  }
  
 -fn type_contains_bound_params(&ctxt cx, &t typ) -> bool {
 -    ret interner::get[raw_t](*cx.ts, typ).has_bound_params;
 -}
 -
  // Type accessors for substructures of types
  
  fn ty_fn_args(&ctxt cx, &t fty) -> vec[arg] {
      alt (struct(cx, fty)) {
 -        case (ty::ty_fn(_, ?a, _, _)) { ret a; }
 +        case (ty::ty_fn(_, ?a, _, _, _)) { ret a; }
          case (ty::ty_native_fn(_, ?a, _)) { ret a; }
      }
 -    fail;
 +    cx.sess.bug("ty_fn_args() called on non-fn type");
  }
  
  fn ty_fn_proto(&ctxt cx, &t fty) -> ast::proto {
      alt (struct(cx, fty)) {
 -        case (ty::ty_fn(?p, _, _, _)) { ret p; }
 +        case (ty::ty_fn(?p, _, _, _, _)) { ret p; }
      }
 -    fail;
 +    cx.sess.bug("ty_fn_proto() called on non-fn type");
  }
  
  fn ty_fn_abi(&ctxt cx, &t fty) -> ast::native_abi {
      alt (struct(cx, fty)) {
          case (ty::ty_native_fn(?a, _, _)) { ret a; }
      }
 -    fail;
 +    cx.sess.bug("ty_fn_abi() called on non-native-fn type");
  }
  
  fn ty_fn_ret(&ctxt cx, &t fty) -> t {
      alt (struct(cx, fty)) {
 -        case (ty::ty_fn(_, _, ?r, _)) { ret r; }
 +        case (ty::ty_fn(_, _, ?r, _, _)) { ret r; }
          case (ty::ty_native_fn(_, _, ?r)) { ret r; }
      }
 -    fail;
 +    cx.sess.bug("ty_fn_ret() called on non-fn type");
  }
  
  fn is_fn_ty(&ctxt cx, &t fty) -> bool {
      alt (struct(cx, fty)) {
 -        case (ty::ty_fn(_, _, _, _)) { ret true; }
 +        case (ty::ty_fn(_, _, _, _, _)) { ret true; }
          case (ty::ty_native_fn(_, _, _)) { ret true; }
          case (_) { ret false; }
      }
 -    ret false;
  }
  
 -
 -// Type accessors for AST nodes
 -
 -// Given an item, returns the associated type as well as the number of type
 -// parameters it has.
 -fn native_item_ty(&node_type_table ntt, &@ast::native_item it)
 -        -> ty_param_count_and_ty {
 -    auto ty_param_count;
 -    auto result_ty;
 -    alt (it.node) {
 -        case (ast::native_item_fn(_, _, _, ?tps, _, ?ann)) {
 -            ty_param_count = vec::len[ast::ty_param](tps);
 -            result_ty = ann_to_type(ntt, ann);
 -        }
 +fn ty_var_id(&ctxt cx, t typ) -> int {
 +    alt (struct(cx, typ)) {
 +        case (ty::ty_var(?vid)) { ret vid; }
 +        case (_) { log_err "ty_var_id called on non-var ty"; fail; }
      }
 -    ret tup(ty_param_count, result_ty);
  }
  
 -fn item_ty(&node_type_table ntt, &@ast::item it) -> ty_param_count_and_ty {
 -    auto ty_param_count;
 -    auto result_ty;
 -    alt (it.node) {
 -        case (ast::item_const(_, _, _, _, ?ann)) {
 -            ty_param_count = 0u;
 -            result_ty = ann_to_type(ntt, ann);
 -        }
 -        case (ast::item_fn(_, _, ?tps, _, ?ann)) {
 -            ty_param_count = vec::len[ast::ty_param](tps);
 -            result_ty = ann_to_type(ntt, ann);
 -        }
 -        case (ast::item_mod(_, _, _)) {
 -            fail;   // modules are typeless
 -        }
 -        case (ast::item_ty(_, _, ?tps, _, ?ann)) {
 -            ty_param_count = vec::len[ast::ty_param](tps);
 -            result_ty = ann_to_type(ntt, ann);
 -        }
 -        case (ast::item_tag(_, _, ?tps, ?did, ?ann)) {
 -            ty_param_count = vec::len[ast::ty_param](tps);
 -            result_ty = ann_to_type(ntt, ann);
 -        }
 -        case (ast::item_obj(_, _, ?tps, _, ?ann)) {
 -            ty_param_count = vec::len[ast::ty_param](tps);
 -            result_ty = ann_to_type(ntt, ann);
 -        }
 -    }
 -
 -    ret tup(ty_param_count, result_ty);
 -}
  
 -fn stmt_ty(&ctxt cx, &@ast::stmt s) -> t {
 -    alt (s.node) {
 -        case (ast::stmt_expr(?e,_)) {
 -            ret expr_ty(cx, e);
 -        }
 -        case (_) {
 -            ret mk_nil(cx);
 -        }
 -    }
 -}
 +// Type accessors for AST nodes
  
  fn block_ty(&ctxt cx, &ast::block b) -> t {
 -    ret ann_to_type(cx.node_types, b.node.a);
 +    ret ann_to_type(cx, b.node.a);
  }
  
  // Returns the type of a pattern as a monotype. Like @expr_ty, this function
@@@ -1580,7 -1754,7 +1583,7 @@@ fn item_ann(&@ast::item it) -> ast::an
  
  fn expr_ann(&@ast::expr e) -> ast::ann {
      alt (e.node) {
 -        case (ast::expr_vec(_,_,?a)) { ret a; }
 +        case (ast::expr_vec(_,_,_,?a)) { ret a; }
          case (ast::expr_tup(_,?a)) { ret a; }
          case (ast::expr_rec(_,_,?a)) { ret a; }
          case (ast::expr_call(_,_,?a)) { ret a; }
          case (ast::expr_index(_,_,?a)) { ret a; }
          case (ast::expr_path(_,?a)) { ret a; }
          case (ast::expr_ext(_,_,_,_,?a)) { ret a; }
 -        case (ast::expr_fail(?a)) { ret a; }
 +        case (ast::expr_fail(?a,_)) { ret a; }
          case (ast::expr_ret(_,?a)) { ret a; }
          case (ast::expr_put(_,?a)) { ret a; }
          case (ast::expr_be(_,?a)) { ret a; }
@@@ -1636,18 -1810,18 +1639,18 @@@ fn expr_ty_params_and_ty(&ctxt cx, &@as
          -> tup(vec[t], t) {
      auto a = expr_ann(expr);
  
 -    ret tup(ann_to_type_params(cx.node_types, a),
 -            ann_to_type(cx.node_types, a));
 +    ret tup(ann_to_type_params(cx, a),
 +            ann_to_type(cx, a));
  }
  
 -fn expr_has_ty_params(&node_type_table ntt, &@ast::expr expr) -> bool {
 -    ret ann_has_type_params(ntt, expr_ann(expr));
 +fn expr_has_ty_params(&ctxt cx, &@ast::expr expr) -> bool {
 +    ret ann_has_type_params(cx, expr_ann(expr));
  }
  
  fn decl_local_ty(&ctxt cx, &@ast::decl d) -> t {
      alt (d.node) {
          case (ast::decl_local(?l)) {
 -            ret ann_to_type(cx.node_types, l.ann);
 +            ret ann_to_type(cx, l.ann);
          }
          case (_) {
              cx.sess.bug("decl_local_ty called on an item decl");
@@@ -1717,6 -1891,7 +1720,6 @@@ fn field_idx(&session::session sess, &s
          i += 1u;
      }
      sess.span_err(sp, "unknown field '" + id + "' of record");
 -    fail;
  }
  
  fn method_idx(&session::session sess, &span sp,
          i += 1u;
      }
      sess.span_err(sp, "unknown method '" + id + "' of obj");
 -    fail;
  }
  
  fn sort_methods(&vec[method] meths) -> vec[method] {
@@@ -1757,89 -1933,55 +1760,89 @@@ fn is_lval(&@ast::expr expr) -> bool 
  mod unify {
      tag result {
          ures_ok(t);
 -        ures_err(type_err, t, t);
 +        ures_err(type_err);
      }
  
 -    tag set_result {
 -        usr_ok(vec[t]);
 -        usr_err(type_err, t, t);
 +    tag union_result {
 +        unres_ok;
 +        unres_err(type_err);
      }
  
 -    type bindings[T] = rec(ufind::ufind sets,
 -                           hashmap[T,uint] ids,
 -                           mutable vec[mutable option::t[t]] types);
 +    tag fixup_result {
 +        fix_ok(t);      // fixup succeeded
 +        fix_err(int);   // fixup failed because a type variable was unresolved
 +    }
  
 -    fn mk_bindings[T](map::hashfn[T] hasher, map::eqfn[T] eqer)
 -            -> @bindings[T] {
 -        let vec[mutable option::t[t]] types = [mutable];
 -        ret @rec(sets=ufind::make(),
 -                 ids=map::mk_hashmap[T,uint](hasher, eqer),
 -                 mutable types=types);
 +    type var_bindings = rec(ufind::ufind sets,
 +                            smallintmap::smallintmap[t] types);
 +
 +    type ctxt = rec(@var_bindings vb, ty_ctxt tcx);
 +
 +    fn mk_var_bindings() -> @var_bindings {
 +        ret @rec(sets=ufind::make(), types=smallintmap::mk[t]());
      }
  
 -    fn record_binding[T](&@ctxt cx, &@bindings[T] bindings, &T key, t typ)
 -            -> result {
 -        auto n = get_or_create_set[T](bindings, key);
 +    // Unifies two sets.
 +    fn union(&@ctxt cx, uint set_a, uint set_b) -> union_result {
 +        ufind::grow(cx.vb.sets, uint::max(set_a, set_b) + 1u);
  
 -        auto result_type = typ;
 -        if (n < vec::len[option::t[t]](bindings.types)) {
 -            alt (bindings.types.(n)) {
 -                case (some(?old_type)) {
 -                    alt (unify_step(cx, old_type, typ)) {
 -                        case (ures_ok(?unified_type)) {
 -                            result_type = unified_type;
 +        auto root_a = ufind::find(cx.vb.sets, set_a);
 +        auto root_b = ufind::find(cx.vb.sets, set_b);
 +        ufind::union(cx.vb.sets, set_a, set_b);
 +        auto root_c = ufind::find(cx.vb.sets, set_a);
 +
 +        alt (smallintmap::find[t](cx.vb.types, root_a)) {
 +            case (none[t]) {
 +                alt (smallintmap::find[t](cx.vb.types, root_b)) {
 +                    case (none[t]) { ret unres_ok; }
 +                    case (some[t](?t_b)) {
 +                        smallintmap::insert[t](cx.vb.types, root_c, t_b);
 +                        ret unres_ok;
 +                    }
 +                }
 +            }
 +            case (some[t](?t_a)) {
 +                alt (smallintmap::find[t](cx.vb.types, root_b)) {
 +                    case (none[t]) {
 +                        smallintmap::insert[t](cx.vb.types, root_c, t_a);
 +                        ret unres_ok;
 +                    }
 +                    case (some[t](?t_b)) {
 +                        alt (unify_step(cx, t_a, t_b)) {
 +                            case (ures_ok(?t_c)) {
 +                                smallintmap::insert[t](cx.vb.types, root_c,
 +                                                       t_c);
 +                                ret unres_ok;
 +                            }
 +                            case (ures_err(?terr)) { ret unres_err(terr); }
                          }
 -                        case (?res) { ret res; }
                      }
                  }
 -                case (none) { /* fall through */ }
              }
          }
 +    }
  
 -        vec::grow_set[option::t[t]](bindings.types, n, none[t],
 -                                    some[t](result_type));
 +    fn record_var_binding(&@ctxt cx, int key, t typ) -> result {
 +        ufind::grow(cx.vb.sets, (key as uint) + 1u);
 +        auto root = ufind::find(cx.vb.sets, key as uint);
  
 +        auto result_type = typ;
 +        alt (smallintmap::find[t](cx.vb.types, root)) {
 +            case (some(?old_type)) {
 +                alt (unify_step(cx, old_type, typ)) {
 +                    case (ures_ok(?unified_type)) {
 +                        result_type = unified_type;
 +                    }
 +                    case (?res) { ret res; }
 +                }
 +            }
 +            case (none) { /* fall through */ }
 +        }
 +
 +        smallintmap::insert[t](cx.vb.types, root, result_type);
          ret ures_ok(typ);
      }
  
 -    type ctxt = rec(@bindings[int] bindings,
 -                    unify_handler handler,
 -                    ty_ctxt tcx);
 -
      // Wraps the given type in an appropriate cname.
      //
      // TODO: This doesn't do anything yet. We should carry the cname up from
              ret ures_ok(expected);
          }
  
 -        ret ures_err(terr_mismatch, expected, actual);
 +        ret ures_err(terr_mismatch);
      }
  
      // Unifies two mutability flags.
          auto expected_len = vec::len[arg](expected_inputs);
          auto actual_len = vec::len[arg](actual_inputs);
          if (expected_len != actual_len) {
 -            ret fn_common_res_err(ures_err(terr_arg_count,
 -                                           expected, actual));
 +            ret fn_common_res_err(ures_err(terr_arg_count));
          }
  
          // TODO: as above, we should have an iter2 iterator.
              auto expected_input = expected_inputs.(i);
              auto actual_input = actual_inputs.(i);
  
 -            // Unify the result modes. "mo_either" unifies with both modes.
 +            // Unify the result modes.
              auto result_mode;
 -            if (expected_input.mode == mo_either) {
 -                result_mode = actual_input.mode;
 -            } else if (actual_input.mode == mo_either) {
 -                result_mode = expected_input.mode;
 -            } else if (expected_input.mode != actual_input.mode) {
 +            if (expected_input.mode != actual_input.mode) {
                  // FIXME this is the wrong error
 -                ret fn_common_res_err(ures_err(terr_arg_count,
 -                                               expected, actual));
 +                ret fn_common_res_err(ures_err(terr_arg_count));
              } else {
                  result_mode = expected_input.mode;
              }
  
 -            auto result = unify_step(cx, actual_input.ty, expected_input.ty);
 +            auto result = unify_step(cx, expected_input.ty, actual_input.ty);
  
              alt (result) {
                  case (ures_ok(?rty)) {
                  &t actual,
                  &vec[arg] expected_inputs, &t expected_output,
                  &vec[arg] actual_inputs, &t actual_output,
 -                &controlflow expected_cf, &controlflow actual_cf)
 +                &controlflow expected_cf, &controlflow actual_cf,
 +                &vec[@ast::constr] expected_constrs,
 +                &vec[@ast::constr] actual_constrs)
          -> result {
  
          if (e_proto != a_proto) {
 -            ret ures_err(terr_mismatch, expected, actual);
 +            ret ures_err(terr_mismatch);
          }
          alt (expected_cf) {
              case (ast::return) { } // ok
                             this check is necessary to ensure that the
                             annotation in an object method matches the
                             declared object type */
 -                        ret ures_err(terr_controlflow_mismatch,
 -                                     expected, actual);
 +                        ret ures_err(terr_controlflow_mismatch);
                      }
                  }
              }
              }
              case (fn_common_res_ok(?result_ins, ?result_out)) {
                  auto t2 = mk_fn(cx.tcx, e_proto, result_ins, result_out,
 -                                actual_cf);
 +                                actual_cf, actual_constrs);
                  ret ures_ok(t2);
              }
          }
                         &vec[arg] expected_inputs, &t expected_output,
                         &vec[arg] actual_inputs, &t actual_output)
          -> result {
 -        if (e_abi != a_abi) {
 -            ret ures_err(terr_mismatch, expected, actual);
 -        }
 +        if (e_abi != a_abi) { ret ures_err(terr_mismatch); }
  
          auto t = unify_fn_common(cx, expected, actual,
                                   expected_inputs, expected_output,
        let uint expected_len = vec::len[method](expected_meths);
        let uint actual_len = vec::len[method](actual_meths);
  
 -      if (expected_len != actual_len) {
 -        ret ures_err(terr_meth_count, expected, actual);
 -      }
 +      if (expected_len != actual_len) { ret ures_err(terr_meth_count); }
  
        while (i < expected_len) {
          auto e_meth = expected_meths.(i);
          auto a_meth = actual_meths.(i);
          if (! str::eq(e_meth.ident, a_meth.ident)) {
 -          ret ures_err(terr_obj_meths(e_meth.ident, a_meth.ident),
 -                       expected, actual);
 +          ret ures_err(terr_obj_meths(e_meth.ident, a_meth.ident));
          }
          auto r = unify_fn(cx,
                            e_meth.proto, a_meth.proto,
                            expected, actual,
                            e_meth.inputs, e_meth.output,
                            a_meth.inputs, a_meth.output,
 -                          e_meth.cf, a_meth.cf);
 +                          e_meth.cf, a_meth.cf,
 +                          e_meth.constrs, a_meth.constrs);
          alt (r) {
              case (ures_ok(?tfn)) {
                  alt (struct(cx.tcx, tfn)) {
 -                    case (ty_fn(?proto, ?ins, ?out, ?cf)) {
 +                    case (ty_fn(?proto, ?ins, ?out, ?cf, ?constrs)) {
                          result_meths += [rec(inputs = ins,
                                               output = out,
 -                                             cf     = cf
 +                                             cf     = cf,
 +                                             constrs = constrs
                                               with e_meth)];
                      }
                  }
        ret ures_ok(t);
      }
  
 -    fn get_or_create_set[T](&@bindings[T] bindings, &T key) -> uint {
 -        auto set_num;
 -        alt (bindings.ids.find(key)) {
 -            case (none) {
 -                set_num = ufind::make_set(bindings.sets);
 -                bindings.ids.insert(key, set_num);
 +    // If the given type is a variable, returns the structure of that type.
 +    fn resolve_type_structure(&ty_ctxt tcx, &@var_bindings vb, t typ)
 +            -> fixup_result {
 +        alt (struct(tcx, typ)) {
 +            case (ty_var(?vid)) {
 +                if ((vid as uint) >= ufind::set_count(vb.sets)) {
 +                    ret fix_err(vid);
 +                }
 +
 +                auto root_id = ufind::find(vb.sets, vid as uint);
 +                alt (smallintmap::find[t](vb.types, root_id)) {
 +                    case (none[t]) { ret fix_err(vid); }
 +                    case (some[t](?rt)) { ret fix_ok(rt); }
 +                }
              }
 -            case (some(?n)) { set_num = n; }
 +            case (_) { ret fix_ok(typ); }
          }
 -        ret set_num;
      }
  
      fn unify_step(&@ctxt cx, &t expected, &t actual) -> result {
          // Fast path.
          if (eq_ty(expected, actual)) { ret ures_ok(expected); }
  
 -        alt (struct(cx.tcx, actual)) {
 +        // Stage 1: Handle the cases in which one side or another is a type
 +        // variable.
  
 -            // a _|_ type can be used anywhere
 -            case (ty::ty_bot) {
 -                ret ures_ok(expected);
 -            }
 -       
 +        alt (struct(cx.tcx, actual)) {
              // If the RHS is a variable type, then just do the appropriate
              // binding.
              case (ty::ty_var(?actual_id)) {
 -                auto actual_n = get_or_create_set[int](cx.bindings,
 -                                                       actual_id);
 +                auto actual_n = actual_id as uint;
                  alt (struct(cx.tcx, expected)) {
                      case (ty::ty_var(?expected_id)) {
 -                        auto expected_n = get_or_create_set[int](cx.bindings,
 -                                                                 expected_id);
 -                        ufind::union(cx.bindings.sets, expected_n, actual_n);
 +                        auto expected_n = expected_id as uint;
 +                        union(cx, expected_n, actual_n);
                      }
 -
                      case (_) {
                          // Just bind the type variable to the expected type.
 -                        alt (record_binding[int](cx, cx.bindings, actual_id,
 -                                                 expected)) {
 +                        alt (record_var_binding(cx, actual_id, expected)) {
                              case (ures_ok(_)) { /* fall through */ }
                              case (?res) { ret res; }
                          }
                      }
                  }
 -                ret ures_ok(actual);
 -            }
 -            case (ty::ty_local(?actual_id)) {
 -                auto result_ty;
 -                alt (cx.handler.resolve_local(actual_id)) {
 -                    case (none) { result_ty = expected; }
 -                    case (some(?actual_ty)) {
 -                        auto result = unify_step(cx, expected, actual_ty);
 -                        alt (result) {
 -                            case (ures_ok(?rty)) { result_ty = rty; }
 -                            case (_) { ret result; }
 -                        }
 -                    }
 -                }
 -
 -                cx.handler.record_local(actual_id, result_ty);
 -                ret ures_ok(result_ty);
 +                ret ures_ok(mk_var(cx.tcx, actual_id));
              }
 -            case (ty::ty_bound_param(?actual_id)) {
 -                alt (struct(cx.tcx, expected)) {
 -                    case (ty::ty_local(_)) {
 -                        log_err "TODO: bound param unifying with local";
 -                        fail;
 -                    }
  
 -                    case (_) {
 -                        ret cx.handler.record_param(actual_id, expected);
 -                    }
 +            case (_) { /* empty */ }
 +        }
 +
 +        alt (struct(cx.tcx, expected)) {
 +            case (ty::ty_var(?expected_id)) {
 +                // Add a binding. (`actual` can't actually be a var here.)
 +                alt (record_var_binding(cx, expected_id, actual)) {
 +                    case (ures_ok(_)) { /* fall through */ }
 +                    case (?res) { ret res; }
                  }
 +                ret ures_ok(mk_var(cx.tcx, expected_id));
              }
 -            case (_) { /* empty */ }
 +
 +            case (_) { /* fall through */ }
 +        }
 +
 +        // Stage 2: Handle all other cases.
 +
 +        alt (struct(cx.tcx, actual)) {
 +            case (ty::ty_bot)        { ret ures_ok(expected);                }
 +            case (_)                 { /* fall through */                    }
          }
  
          alt (struct(cx.tcx, expected)) {
              case (ty::ty_nil)        { ret struct_cmp(cx, expected, actual); }
              // _|_ unifies with anything
 -            case (ty::ty_bot)        { ret ures_ok(expected);                }
 +            case (ty::ty_bot)        { ret ures_ok(actual);                  }
              case (ty::ty_bool)       { ret struct_cmp(cx, expected, actual); }
              case (ty::ty_int)        { ret struct_cmp(cx, expected, actual); }
              case (ty::ty_uint)       { ret struct_cmp(cx, expected, actual); }
              case (ty::ty_float)      { ret struct_cmp(cx, expected, actual); }
              case (ty::ty_char)       { ret struct_cmp(cx, expected, actual); }
              case (ty::ty_str)        { ret struct_cmp(cx, expected, actual); }
 +            case (ty::ty_istr)       { ret struct_cmp(cx, expected, actual); }
              case (ty::ty_type)       { ret struct_cmp(cx, expected, actual); }
              case (ty::ty_native)     { ret struct_cmp(cx, expected, actual); }
              case (ty::ty_param(_))   { ret struct_cmp(cx, expected, actual); }
                      case (ty::ty_tag(?actual_id, ?actual_tps)) {
                          if (expected_id._0 != actual_id._0 ||
                                  expected_id._1 != actual_id._1) {
 -                            ret ures_err(terr_mismatch, expected, actual);
 +                            ret ures_err(terr_mismatch);
                          }
  
                          // TODO: factor this cruft out, see the TODO in the
                      case (_) { /* fall through */ }
                  }
  
 -                ret ures_err(terr_mismatch, expected, actual);
 +                ret ures_err(terr_mismatch);
              }
  
              case (ty::ty_box(?expected_mt)) {
                      case (ty::ty_box(?actual_mt)) {
                          auto mut;
                          alt (unify_mut(expected_mt.mut, actual_mt.mut)) {
 -                            case (none) {
 -                                ret ures_err(terr_box_mutability, expected,
 -                                             actual);
 -                            }
 +                            case (none) { ret ures_err(terr_box_mutability); }
                              case (some(?m)) { mut = m; }
                          }
  
                          }
                      }
  
 -                    case (_) {
 -                        ret ures_err(terr_mismatch, expected, actual);
 -                    }
 +                    case (_) { ret ures_err(terr_mismatch); }
                  }
              }
  
                      case (ty::ty_vec(?actual_mt)) {
                          auto mut;
                          alt (unify_mut(expected_mt.mut, actual_mt.mut)) {
 -                            case (none) {
 -                                ret ures_err(terr_vec_mutability, expected,
 -                                             actual);
 -                            }
 +                            case (none) { ret ures_err(terr_vec_mutability); }
                              case (some(?m)) { mut = m; }
                          }
  
                          }
                      }
  
 -                    case (_) {
 -                        ret ures_err(terr_mismatch, expected, actual);
 -                   }
 +                    case (_) { ret ures_err(terr_mismatch); }
 +                }
 +            }
 +
 +            case (ty::ty_ivec(?expected_mt)) {
 +                alt (struct(cx.tcx, actual)) {
 +                    case (ty::ty_ivec(?actual_mt)) {
 +                        auto mut;
 +                        alt (unify_mut(expected_mt.mut, actual_mt.mut)) {
 +                            case (none) { ret ures_err(terr_vec_mutability); }
 +                            case (some(?m)) { mut = m; }
 +                        }
 +
 +                        auto result = unify_step(cx,
 +                                                 expected_mt.ty,
 +                                                 actual_mt.ty);
 +                        alt (result) {
 +                            case (ures_ok(?result_sub)) {
 +                                auto mt = rec(ty=result_sub, mut=mut);
 +                                ret ures_ok(mk_ivec(cx.tcx, mt));
 +                            }
 +                            case (_) {
 +                                ret result;
 +                            }
 +                        }
 +                    }
 +
 +                    case (_) { ret ures_err(terr_mismatch); }
                  }
              }
  
                          }
                      }
  
 -                    case (_) {
 -                        ret ures_err(terr_mismatch, expected, actual);
 -                    }
 +                    case (_) { ret ures_err(terr_mismatch); }
                  }
              }
  
                          }
                      }
  
 -                    case (_) {
 -                        ret ures_err(terr_mismatch, expected, actual);
 -                    }
 +                    case (_) { ret ures_err(terr_mismatch); }
                  }
              }
  
                          if (expected_len != actual_len) {
                              auto err = terr_tuple_size(expected_len,
                                                         actual_len);
 -                            ret ures_err(err, expected, actual);
 +                            ret ures_err(err);
                          }
  
                          // TODO: implement an iterator that can iterate over
                                             actual_elem.mut)) {
                                  case (none) {
                                      auto err = terr_tuple_mutability;
 -                                    ret ures_err(err, expected, actual);
 +                                    ret ures_err(err);
                                  }
                                  case (some(?m)) { mut = m; }
                              }
                          ret ures_ok(mk_tup(cx.tcx, result_elems));
                      }
  
 -                    case (_) {
 -                        ret ures_err(terr_mismatch, expected, actual);
 -                    }
 +                    case (_) { ret ures_err(terr_mismatch); }
                  }
              }
  
                          if (expected_len != actual_len) {
                              auto err = terr_record_size(expected_len,
                                                          actual_len);
 -                            ret ures_err(err, expected, actual);
 +                            ret ures_err(err);
                          }
  
                          // TODO: implement an iterator that can iterate over
                              alt (unify_mut(expected_field.mt.mut,
                                             actual_field.mt.mut)) {
                                  case (none) {
 -                                    ret ures_err(terr_record_mutability,
 -                                                 expected, actual);
 +                                    ret ures_err(terr_record_mutability);
                                  }
                                  case (some(?m)) { mut = m; }
                              }
                                  auto err =
                                      terr_record_fields(expected_field.ident,
                                                         actual_field.ident);
 -                                ret ures_err(err, expected, actual);
 +                                ret ures_err(err);
                              }
  
                              auto result = unify_step(cx,
                          ret ures_ok(mk_rec(cx.tcx, result_fields));
                      }
  
 -                    case (_) {
 -                        ret ures_err(terr_mismatch, expected, actual);
 -                    }
 +                    case (_) { ret ures_err(terr_mismatch); }
                  }
              }
  
              case (ty::ty_fn(?ep, ?expected_inputs,
 -                            ?expected_output, ?expected_cf)) {
 +                            ?expected_output, ?expected_cf,
 +                            ?expected_constrs)) {
                  alt (struct(cx.tcx, actual)) {
                      case (ty::ty_fn(?ap, ?actual_inputs,
 -                                    ?actual_output, ?actual_cf)) {
 +                                    ?actual_output, ?actual_cf,
 +                                    ?actual_constrs)) {
                          ret unify_fn(cx, ep, ap,
                                       expected, actual,
                                       expected_inputs, expected_output,
                                       actual_inputs, actual_output,
 -                                     expected_cf, actual_cf);
 +                                     expected_cf, actual_cf,
 +                                     expected_constrs, actual_constrs);
                      }
  
 -                    case (_) {
 -                        ret ures_err(terr_mismatch, expected, actual);
 -                    }
 +                    case (_) { ret ures_err(terr_mismatch); }
                  }
              }
  
                                              expected_inputs, expected_output,
                                              actual_inputs, actual_output);
                      }
 -                    case (_) {
 -                        ret ures_err(terr_mismatch, expected, actual);
 -                    }
 +                    case (_) { ret ures_err(terr_mismatch); }
                  }
              }
  
                          ret unify_obj(cx, expected, actual,
                                        expected_meths, actual_meths);
                      }
 -                    case (_) {
 -                        ret ures_err(terr_mismatch, expected, actual);
 -                    }
 -                }
 -            }
 -
 -            case (ty::ty_var(?expected_id)) {
 -                // Add a binding. (`actual` can't actually be a var here.)
 -                alt (record_binding[int](cx, cx.bindings, expected_id,
 -                                         actual)) {
 -                    case (ures_ok(_)) { /* fall through */ }
 -                    case (?res) { ret res; }
 +                    case (_) { ret ures_err(terr_mismatch); }
                  }
 -                ret ures_ok(expected);
 -            }
 -
 -            case (ty::ty_local(?expected_id)) {
 -                auto result_ty;
 -                alt (cx.handler.resolve_local(expected_id)) {
 -                    case (none) { result_ty = actual; }
 -                    case (some(?expected_ty)) {
 -                        auto result = unify_step(cx, expected_ty, actual);
 -                        alt (result) {
 -                            case (ures_ok(?rty)) { result_ty = rty; }
 -                            case (_) { ret result; }
 -                        }
 -                    }
 -                }
 -
 -                cx.handler.record_local(expected_id, result_ty);
 -                ret ures_ok(result_ty);
 -            }
 -
 -            case (ty::ty_bound_param(?expected_id)) {
 -                ret cx.handler.record_param(expected_id, actual);
              }
          }
 -
 -        // TODO: remove me once match-exhaustiveness checking works
 -        fail;
      }
  
 -    // Performs type binding substitution.
 -    fn substitute(&ty_ctxt tcx,
 -                  &@bindings[int] bindings,
 -                  &vec[t] set_types,
 -                  &t typ) -> t {
 -        if (!type_contains_vars(tcx, typ)) {
 -            ret typ;
 -        }
 +    fn unify(&t expected,
 +             &t actual,
 +             &@var_bindings vb,
 +             &ty_ctxt tcx) -> result {
 +        auto cx = @rec(vb=vb, tcx=tcx);
 +        ret unify_step(cx, expected, actual);
 +    }
  
 -        fn substituter(ty_ctxt tcx,
 -                       @bindings[int] bindings,
 -                       vec[t] types,
 -                       t typ) -> t {
 -            alt (struct(tcx, typ)) {
 -                case (ty_var(?id)) {
 -                    alt (bindings.ids.find(id)) {
 -                        case (some(?n)) {
 -                            auto root = ufind::find(bindings.sets, n);
 -                            ret types.(root);
 -                        }
 -                        case (none) { ret typ; }
 -                    }
 +    fn dump_var_bindings(ty_ctxt tcx, @var_bindings vb) {
 +        auto i = 0u;
 +        while (i < vec::len[ufind::node](vb.sets.nodes)) {
 +            auto sets = "";
 +            auto j = 0u;
 +            while (j < vec::len[option::t[uint]](vb.sets.nodes)) {
 +                if (ufind::find(vb.sets, j) == i) { sets += #fmt(" %u", j); }
 +                j += 1u;
 +            }
 +
 +            auto typespec;
 +            alt (smallintmap::find[t](vb.types, i)) {
 +                case (none[t]) { typespec = ""; }
 +                case (some[t](?typ)) {
 +                    typespec = " =" + pretty::ppaux::ty_to_str(tcx, typ);
                  }
 -                case (_) { ret typ; }
              }
 -        }
  
 -        auto f = bind substituter(tcx, bindings, set_types, _);
 -        ret fold_ty(tcx, f, typ);
 -    }
 -
 -    fn unify_sets[T](&ty_ctxt tcx, &@bindings[T] bindings) -> set_result {
 -        obj handler() {
 -            fn resolve_local(ast::def_id id) -> option::t[t] {
 -                log_err "resolve_local in unify_sets";
 -                fail;
 -            }
 -            fn record_local(ast::def_id id, t ty) {
 -                log_err "record_local in unify_sets";
 -                fail;
 -            }
 -            fn record_param(uint index, t binding) -> unify::result {
 -                log_err "record_param in unify_sets";
 -                fail;
 -            }
 +            log_err #fmt("set %u:%s%s", i, typespec, sets);
 +            i += 1u;
          }
 +    }
  
 -        auto node_count = vec::len[option::t[t]](bindings.types);
 +    // Fixups and substitutions
  
 -        let vec[option::t[t]] results =
 -            vec::init_elt[option::t[t]](none[t], node_count);
 +    fn fixup_vars(ty_ctxt tcx, @var_bindings vb, t typ) -> fixup_result {
 +        fn subst_vars(ty_ctxt tcx, @var_bindings vb,
 +                      @mutable option::t[int] unresolved, int vid) -> t {
 +            if ((vid as uint) >= ufind::set_count(vb.sets)) {
 +                *unresolved = some[int](vid);
 +                ret ty::mk_var(tcx, vid);
 +            }
  
 -        auto i = 0u;
 -        while (i < node_count) {
 -            auto root = ufind::find(bindings.sets, i);
 -            alt (bindings.types.(i)) {
 -                case (none) { /* nothing to do */ }
 -                case (some(?actual)) {
 -                    alt (results.(root)) {
 -                        case (none) { results.(root) = some[t](actual); }
 -                        case (some(?expected)) {
 -                            // FIXME: Is this right?
 -                            auto bindings = mk_bindings[int](int::hash,
 -                                                             int::eq_alias);
 -                            alt (unify(expected, actual, handler(), bindings,
 -                                    tcx)) {
 -                                case (ures_ok(?result_ty)) {
 -                                    results.(i) = some[t](result_ty);
 -                                }
 -                                case (ures_err(?e, ?t_a, ?t_b)) {
 -                                    ret usr_err(e, t_a, t_b);
 -                                }
 -                            }
 -                        }
 -                    }
 +            auto root_id = ufind::find(vb.sets, vid as uint);
 +            alt (smallintmap::find[t](vb.types, root_id)) {
 +                case (none[t]) {
 +                    *unresolved = some[int](vid);
 +                    ret ty::mk_var(tcx, vid);
 +                }
 +                case (some[t](?rt)) {
 +                    ret fold_ty(tcx,
 +                        fm_var(bind subst_vars(tcx, vb, unresolved, _)), rt);
                  }
              }
 -            i += 1u;
          }
  
 -        // FIXME: This is equivalent to map(option::get, results) but it
 -        // causes an assertion in typeck at the moment.
 -        let vec[t] real_results = [];
 -        for (option::t[t] typ in results) {
 -            real_results += [option::get[t](typ)];
 -        }
 +        auto unresolved = @mutable none[int];
 +        auto rty = fold_ty(tcx,
 +                           fm_var(bind subst_vars(tcx, vb, unresolved, _)),
 +                           typ);
  
 -        ret usr_ok(real_results);
 +        auto ur = *unresolved;
 +        alt (ur) {
 +            case (none[int]) { ret fix_ok(rty); }
 +            case (some[int](?var_id)) { ret fix_err(var_id); }
 +        }
      }
  
 -    fn unify(&t expected,
 -             &t actual,
 -             &unify_handler handler,
 -             &@bindings[int] bindings,
 -             &ty_ctxt tcx) -> result {
 -        auto cx = @rec(bindings=bindings, handler=handler, tcx=tcx);
 -        ret unify_step(cx, expected, actual);
 -    }
 +    fn resolve_type_var(&ty_ctxt tcx, &@var_bindings vb, int vid)
 +            -> fixup_result {
 +        if ((vid as uint) >= ufind::set_count(vb.sets)) { ret fix_err(vid); }
  
 -    fn fixup(&ty_ctxt tcx, &@bindings[int] bindings, t typ) -> result {
 -        alt (unify_sets[int](tcx, bindings)) {
 -            case (usr_ok(?set_types)) {
 -                ret ures_ok(substitute(tcx, bindings, set_types, typ));
 -            }
 -            case (usr_err(?terr, ?t0, ?t1)) { ret ures_err(terr, t0, t1); }
 +        auto root_id = ufind::find(vb.sets, vid as uint);
 +        alt (smallintmap::find[t](vb.types, root_id)) {
 +            case (none[t]) { ret fix_err(vid); }
 +            case (some[t](?rt)) { ret fixup_vars(tcx, vb, rt); }
          }
      }
  }
@@@ -2592,39 -2808,47 +2595,39 @@@ fn type_err_to_str(&ty::type_err err) -
      }
  }
  
 -// Performs bound type parameter replacement using the supplied mapping from
 -// parameter IDs to types.
 -fn substitute_type_params(&ctxt cx, &vec[t] bindings, &t typ) -> t {
 -    if (!type_contains_bound_params(cx, typ)) {
 -        ret typ;
 +// Converts type parameters in a type to type variables and returns the
 +// resulting type along with a list of type variable IDs.
 +fn bind_params_in_type(&ctxt cx, fn()->int next_ty_var, t typ,
 +                       uint ty_param_count) -> tup(vec[int], t) {
 +    let vec[int] param_var_ids = [];
 +    auto i = 0u;
 +    while (i < ty_param_count) {
 +        param_var_ids += [next_ty_var()];
 +        i += 1u;
      }
 -    fn replacer(&ctxt cx, vec[t] bindings, t typ) -> t {
 -        alt (struct(cx, typ)) {
 -            case (ty_bound_param(?param_index)) {
 -                ret bindings.(param_index);
 -            }
 -            case (_) { ret typ; }
 -        }
 +
 +    fn binder(ctxt cx, vec[int] param_var_ids, fn()->int next_ty_var,
 +              uint index) -> t {
 +        ret mk_var(cx, param_var_ids.(index));
      }
  
 -    auto f = bind replacer(cx, bindings, _);
 -    ret fold_ty(cx, f, typ);
 +    auto new_typ = fold_ty(cx,
 +        fm_param(bind binder(cx, param_var_ids, next_ty_var, _)), typ);
 +    ret tup(param_var_ids, new_typ);
  }
  
 -// Converts type parameters in a type to bound type parameters.
 -fn bind_params_in_type(&ctxt cx, &t typ) -> t {
 -    if (!type_contains_params(cx, typ)) {
 -        ret typ;
 -    }
 -    fn binder(&ctxt cx, t typ) -> t {
 -        alt (struct(cx, typ)) {
 -            case (ty_bound_param(?index)) {
 -                log_err "bind_params_in_type() called on type that already " +
 -                    "has bound params in it";
 -                fail;
 -            }
 -            case (ty_param(?index)) { ret mk_bound_param(cx, index); }
 -            case (_) { ret typ; }
 -        }
 +// Replaces type parameters in the given type using the given list of
 +// substitions.
 +fn substitute_type_params(&ctxt cx, vec[ty::t] substs, t typ) -> t {
 +    if (!type_contains_params(cx, typ)) { ret typ; }
 +
 +    fn substituter(ctxt cx, vec[ty::t] substs, uint idx) -> t {
 +        ret substs.(idx);
      }
  
 -    auto f = bind binder(cx, _);
 -    ret fold_ty(cx, f, typ);
 +    ret fold_ty(cx, fm_param(bind substituter(cx, substs, _)), typ);
  }
  
 -
  fn def_has_ty_params(&ast::def def) -> bool {
      alt (def) {
          case (ast::def_fn(_))            { ret true;  }
@@@ -2679,6 -2903,7 +2682,6 @@@ fn tag_variants(&ctxt cx, &ast::def_id 
              }
          }
      }
 -    fail;   // not reached
  }
  
  // Returns information about the tag variant with the given ID:
@@@ -2695,9 -2920,9 +2698,9 @@@ fn tag_variant_with_id(&ctxt cx
          }
          i += 1u;
      }
 +            
 +    cx.sess.bug("tag_variant_with_id(): no variant exists with that ID");
  
 -    log_err "tag_variant_with_id(): no variant exists with that ID";
 -    fail;
  }
  
  // If the given item is in an external crate, looks up its type and adds it to
@@@ -2719,40 -2944,19 +2722,40 @@@ fn lookup_item_type(ctxt cx, ast::def_i
      }
  }
  
 -fn ret_ty_of_fn_ty(ty_ctxt tcx, t a_ty) -> t {
 -    alt (ty::struct(tcx, a_ty)) {
 -        case (ty::ty_fn(_, _, ?ret_ty, _)) {
 +fn ret_ty_of_fn_ty(ctxt cx, t a_ty) -> t {
 +    alt (ty::struct(cx, a_ty)) {
 +        case (ty::ty_fn(_, _, ?ret_ty, _, _)) {
              ret ret_ty;
          }
          case (_) {
 -            fail;
 +            cx.sess.bug("ret_ty_of_fn_ty() called on non-function type");
          }
      }
  }
  
 -fn ret_ty_of_fn(ty_ctxt tcx, ast::ann ann) -> t {
 -    ret ret_ty_of_fn_ty(tcx, ann_to_type(tcx.node_types, ann));
 +fn ret_ty_of_fn(ctxt cx, ast::ann ann) -> t {
 +    ret ret_ty_of_fn_ty(cx, ann_to_type(cx, ann));
 +}
 +
 +fn lookup_fn_decl(ty_ctxt tcx, ast::ann ann)
 +    -> option::t[tup(ast::fn_decl, ast::def_id)] {
 +    auto nada = none[tup(ast::fn_decl, ast::def_id)];
 +    alt (tcx.def_map.find(ann.id)) {
 +        case (some(ast::def_fn(?d))) {
 +            alt (tcx.items.find(d)) {
 +                case (some(any_item_rust(?it))) {
 +                    alt (it.node) {
 +                        case (ast::item_fn(_,?f,_,_,_)) {
 +                            ret some(tup(f.decl, d));
 +                        }
 +                        case (_) { ret nada; }
 +                    }
 +                }
 +                case (_) { ret nada; }
 +            }
 +        }
 +        case (_) { ret nada; }
 +    }
  }
  
  // Local Variables:
index 289691444b0b5c7baa6928986a07642a10d35482,0c21090d6e48acfe8cb7b4d168db3ca01a885f40..cf99f1ab4b18360689d49596ef9739b8574e3833
@@@ -18,22 -18,22 +18,22 @@@ import middle::ty::field
  import middle::ty::method;
  import middle::ty::mo_val;
  import middle::ty::mo_alias;
 -import middle::ty::mo_either;
  import middle::ty::node_type_table;
  import middle::ty::pat_ty;
  import middle::ty::path_to_str;
 -import middle::ty::struct;
  import middle::ty::ty_param_substs_opt_and_ty;
 -import middle::ty::ty_to_str;
 -import middle::ty::type_is_integral;
 -import middle::ty::type_is_scalar;
 +import pretty::ppaux::ty_to_str;
  import middle::ty::ty_param_count_and_ty;
  import middle::ty::ty_nil;
  import middle::ty::unify::ures_ok;
  import middle::ty::unify::ures_err;
 +import middle::ty::unify::fixup_result;
 +import middle::ty::unify::fix_ok;
 +import middle::ty::unify::fix_err;
  
  import std::int;
  import std::str;
 +import std::ufind;
  import std::uint;
  import std::vec;
  import std::map;
@@@ -48,43 -48,91 +48,43 @@@ import middle::tstate::ann::ts_ann
  type ty_table = hashmap[ast::def_id, ty::t];
  type fn_purity_table = hashmap[ast::def_id, ast::purity];
  
 -type unify_cache_entry = tup(ty::t,ty::t,vec[mutable ty::t]);
 -type unify_cache = hashmap[unify_cache_entry,ty::unify::result];
 -
  type obj_info = rec(vec[ast::obj_field] obj_fields, ast::def_id this_obj);
  
  type crate_ctxt = rec(mutable vec[obj_info] obj_infos,
                        @fn_purity_table fn_purity_table,
 -                      unify_cache unify_cache,
 -                      mutable uint cache_hits,
 -                      mutable uint cache_misses,
                        ty::ctxt tcx);
  
  type fn_ctxt = rec(ty::t ret_ty,
                     ast::purity purity,
 -                   @ty_table locals,
 +                   @ty::unify::var_bindings var_bindings,
 +                   hashmap[ast::def_id,int] locals,
 +                   hashmap[ast::def_id,ast::ident] local_names,
 +                   mutable int next_var_id,
 +                   mutable vec[uint] fixups,
                     @crate_ctxt ccx);
  
 -type stmt_ctxt = rec(@fn_ctxt fcx,
 -                     mutable int next_var_id,
 -                     mutable vec[uint] fixups);
 -
  // Used for ast_ty_to_ty() below.
  type ty_getter = fn(&ast::def_id) -> ty::ty_param_count_and_ty;
  
 -// Creates a statement context and passes it to the given thunk, then runs
 -// fixups. This function has the signature it does so that the caller can
 -// never forget to run fixups!
 -fn with_stmt_ctxt(&@fn_ctxt fcx, fn(&@stmt_ctxt) f) {
 -    let vec[uint] fixups = [];
 -    auto scx = @rec(fcx=fcx, mutable next_var_id=0, mutable fixups=fixups);
 -    f(scx);
 -    // TODO: run fixups
 -}
 -
 -// Substitutes the user's explicit types for the parameters in a path
 -// expression.
 -fn substitute_ty_params(&@crate_ctxt ccx,
 -                        &ty::t typ,
 -                        uint ty_param_count,
 -                        &vec[ty::t] supplied,
 -                        &span sp) -> ty::t {
 -    fn substituter(@crate_ctxt ccx, vec[ty::t] supplied, ty::t typ) -> ty::t {
 -        alt (struct(ccx.tcx, typ)) {
 -            case (ty::ty_bound_param(?pid)) { ret supplied.(pid); }
 -            case (_) { ret typ; }
 -        }
 -    }
 -
 -    auto supplied_len = vec::len[ty::t](supplied);
 -    if (ty_param_count != supplied_len) {
 -        ccx.tcx.sess.span_err(sp, "expected " +
 -                          uint::to_str(ty_param_count, 10u) +
 -                          " type parameter(s) but found " +
 -                          uint::to_str(supplied_len, 10u) + " parameter(s)");
 -        fail;
 -    }
 -
 -    if (!ty::type_contains_bound_params(ccx.tcx, typ)) {
 -        ret typ;
 -    }
 -
 -    auto f = bind substituter(ccx, supplied, _);
 -    ret ty::fold_ty(ccx.tcx, f, typ);
 -}
 -
  
  // Returns the type parameter count and the type for the given definition.
  fn ty_param_count_and_ty_for_def(&@fn_ctxt fcx, &span sp, &ast::def defn)
          -> ty_param_count_and_ty {
      alt (defn) {
          case (ast::def_arg(?id)) {
 -            // assert (fcx.locals.contains_key(id));
 -            ret tup(0u, fcx.locals.get(id));
 +            assert (fcx.locals.contains_key(id));
 +            auto typ = ty::mk_var(fcx.ccx.tcx, fcx.locals.get(id));
 +            ret tup(0u, typ);
          }
          case (ast::def_local(?id)) {
 -            auto t;
 -            alt (fcx.locals.find(id)) {
 -                case (some(?t1)) { t = t1; }
 -                case (none) { t = ty::mk_local(fcx.ccx.tcx, id); }
 -            }
 -            ret tup(0u, t);
 +            assert (fcx.locals.contains_key(id));
 +            auto typ = ty::mk_var(fcx.ccx.tcx, fcx.locals.get(id));
 +            ret tup(0u, typ);
          }
          case (ast::def_obj_field(?id)) {
 -            // assert (fcx.locals.contains_key(id));
 -            ret tup(0u, fcx.locals.get(id));
 +            assert (fcx.locals.contains_key(id));
 +            auto typ = ty::mk_var(fcx.ccx.tcx, fcx.locals.get(id));
 +            ret tup(0u, typ);
          }
          case (ast::def_fn(?id)) {
              ret ty::lookup_item_type(fcx.ccx.tcx, id);
              ret ty::lookup_item_type(fcx.ccx.tcx, vid);
          }
          case (ast::def_binding(?id)) {
 -            // assert (fcx.locals.contains_key(id));
 -            ret tup(0u, fcx.locals.get(id));
 +            assert (fcx.locals.contains_key(id));
 +            auto typ = ty::mk_var(fcx.ccx.tcx, fcx.locals.get(id));
 +            ret tup(0u, typ);
          }
          case (ast::def_obj(?id)) {
              ret ty::lookup_item_type(fcx.ccx.tcx, id);
  
          case (ast::def_ty(_)) {
              fcx.ccx.tcx.sess.span_err(sp, "expected value but found type");
 -            fail;
          }
  
          case (_) {
              // FIXME: handle other names.
              fcx.ccx.tcx.sess.unimpl("definition variant");
 -            fail;
          }
      }
  }
  
  // Instantiates the given path, which must refer to an item with the given
  // number of type parameters and type.
 -fn instantiate_path(&@stmt_ctxt scx,
 +fn instantiate_path(&@fn_ctxt fcx,
                      &ast::path pth,
                      &ty_param_count_and_ty tpt,
                      &span sp) -> ty_param_substs_opt_and_ty {
      auto ty_param_count = tpt._0;
 -    auto t = bind_params_in_type(scx.fcx.ccx.tcx, tpt._1);
 +
 +    auto bind_result = bind_params_in_type(fcx.ccx.tcx,
 +                                           bind next_ty_var_id(fcx),
 +                                           tpt._1,
 +                                           ty_param_count);
 +    auto ty_param_vars = bind_result._0;
 +    auto t = bind_result._1;
  
      auto ty_substs_opt;
      auto ty_substs_len = vec::len[@ast::ty](pth.node.types);
          let vec[ty::t] ty_substs = [];
          auto i = 0u;
          while (i < ty_substs_len) {
 -            ty_substs += [ast_ty_to_ty_crate(scx.fcx.ccx,
 -                                             pth.node.types.(i))];
 +            // TODO: Report an error if the number of type params in the item
 +            // and the supplied number of type params don't match.
 +            auto ty_var = ty::mk_var(fcx.ccx.tcx, ty_param_vars.(i));
 +            auto ty_subst = ast_ty_to_ty_crate(fcx.ccx,
 +                                               pth.node.types.(i));
 +            auto res_ty = demand::simple(fcx, pth.span, ty_var, ty_subst);
 +            ty_substs += [res_ty];
              i += 1u;
          }
          ty_substs_opt = some[vec[ty::t]](ty_substs);
  
          if (ty_param_count == 0u) {
 -            scx.fcx.ccx.tcx.sess.span_err(sp,
 -                                          "this item does not take type " +
 -                                          "parameters");
 +            fcx.ccx.tcx.sess.span_err(sp, "this item does not take type " +
 +                                      "parameters");
              fail;
          }
      } else {
          let vec[ty::t] ty_substs = [];
          auto i = 0u;
          while (i < ty_param_count) {
 -            ty_substs += [next_ty_var(scx)];
 +            ty_substs += [ty::mk_var(fcx.ccx.tcx, ty_param_vars.(i))];
              i += 1u;
          }
          ty_substs_opt = some[vec[ty::t]](ty_substs);
      }
  
 -    ret tup(ty_substs_opt, t);
 +    ret tup(ty_substs_opt, tpt._1);
  }
  
  fn ast_mode_to_mode(ast::mode mode) -> ty::mode {
      auto ty_mode;
      alt (mode) {
          case (ast::val) { ty_mode = mo_val; }
 -        case (ast::alias) { ty_mode = mo_alias; }
 +        case (ast::alias(?mut)) { ty_mode = mo_alias(mut); }
      }
      ret ty_mode;
  }
  
 +
 +// Type tests
 +
 +fn structurally_resolved_type(&@fn_ctxt fcx, &span sp, ty::t typ) -> ty::t {
 +    auto r = ty::unify::resolve_type_structure(fcx.ccx.tcx, fcx.var_bindings,
 +                                               typ);
 +    alt (r) {
 +        case (fix_ok(?typ_s)) { ret typ_s; }
 +        case (fix_err(_)) {
 +            fcx.ccx.tcx.sess.span_err(sp, "the type of this value must be " +
 +                "known in this context");
 +        }
 +    }
 +}
 +
 +// Returns the one-level-deep structure of the given type.
 +fn structure_of(&@fn_ctxt fcx, &span sp, ty::t typ) -> ty::sty {
 +    ret ty::struct(fcx.ccx.tcx, structurally_resolved_type(fcx, sp, typ));
 +}
 +
 +fn type_is_integral(&@fn_ctxt fcx, &span sp, ty::t typ) -> bool {
 +    auto typ_s = structurally_resolved_type(fcx, sp, typ);
 +    ret ty::type_is_integral(fcx.ccx.tcx, typ_s);
 +}
 +
 +fn type_is_scalar(&@fn_ctxt fcx, &span sp, ty::t typ) -> bool {
 +    auto typ_s = structurally_resolved_type(fcx, sp, typ);
 +    ret ty::type_is_scalar(fcx.ccx.tcx, typ_s);
 +}
 +
 +
  // Parses the programmer's textual representation of a type into our internal
  // notion of a type. `getter` is a function that returns the type
  // corresponding to a definition ID:
  fn ast_ty_to_ty(&ty::ctxt tcx, &ty_getter getter, &@ast::ty ast_ty) -> ty::t {
+     alt (tcx.ast_ty_to_ty_cache.find(ast_ty)) {
+         case (some[option::t[ty::t]](some[ty::t](?ty))) { ret ty; } 
+         case (some[option::t[ty::t]](none)) {
+             tcx.sess.span_err(ast_ty.span, "illegal recursive type "
+                 + "(insert a tag in the cycle, if this is desired)");
+         }
+         case (none[option::t[ty::t]]) { } /* go on */
+     }
+     tcx.ast_ty_to_ty_cache.insert(ast_ty, none[ty::t]);
+     
      fn ast_arg_to_arg(&ty::ctxt tcx,
                        &ty_getter getter,
 -                      &rec(ast::mode mode, @ast::ty ty) arg)
 +                      &ast::ty_arg arg)
              -> rec(ty::mode mode, ty::t ty) {
 -        auto ty_mode = ast_mode_to_mode(arg.mode);
 -        ret rec(mode=ty_mode, ty=ast_ty_to_ty(tcx, getter, arg.ty));
 +        auto ty_mode = ast_mode_to_mode(arg.node.mode);
 +        ret rec(mode=ty_mode, ty=ast_ty_to_ty(tcx, getter, arg.node.ty));
      }
  
      fn ast_mt_to_mt(&ty::ctxt tcx,
      }
  
      fn instantiate(&ty::ctxt tcx,
 +                   &span sp,
                     &ty_getter getter,
                     &ast::def_id id,
                     &vec[@ast::ty] args) -> ty::t {
          // TODO: maybe record cname chains so we can do
          // "foo = int" like OCaml?
          auto params_opt_and_ty = getter(id);
 -
          if (params_opt_and_ty._0 == 0u) {
              ret params_opt_and_ty._1;
          }
  
          // The typedef is type-parametric. Do the type substitution.
          //
 -        // TODO: Make sure the number of supplied bindings matches the number
 -        // of type parameters in the typedef. Emit a friendly error otherwise.
 -        auto bound_ty = bind_params_in_type(tcx, params_opt_and_ty._1);
          let vec[ty::t] param_bindings = [];
          for (@ast::ty ast_ty in args) {
              param_bindings += [ast_ty_to_ty(tcx, getter, ast_ty)];
          }
 -        ret ty::substitute_type_params(tcx, param_bindings, bound_ty);
 +
 +        if (vec::len(param_bindings) !=
 +            ty::count_ty_params(tcx, params_opt_and_ty._1)) {
 +            tcx.sess.span_err(sp, "Wrong number of type arguments for a"
 +                            + " polymorphic tag");
 +        }
 +
 +
 +        auto typ = ty::substitute_type_params(tcx, param_bindings,
 +                                       params_opt_and_ty._1);
 +        ret typ;
      }
  
      auto mut = ast::imm;
          case (ast::ty_machine(?tm)) { typ = ty::mk_mach(tcx, tm); }
          case (ast::ty_char)         { typ = ty::mk_char(tcx); }
          case (ast::ty_str)          { typ = ty::mk_str(tcx); }
 +        case (ast::ty_istr)         { typ = ty::mk_istr(tcx); }
          case (ast::ty_box(?mt)) {
              typ = ty::mk_box(tcx, ast_mt_to_mt(tcx, getter, mt));
          }
          case (ast::ty_vec(?mt)) {
              typ = ty::mk_vec(tcx, ast_mt_to_mt(tcx, getter, mt));
          }
 +        case (ast::ty_ivec(?mt)) {
 +            typ = ty::mk_ivec(tcx, ast_mt_to_mt(tcx, getter, mt));
 +        }
 +        case (ast::ty_ptr(?mt)) {
 +            typ = ty::mk_ptr(tcx, ast_mt_to_mt(tcx, getter, mt));
 +        }
          case (ast::ty_task) { typ = ty::mk_task(tcx); }
          case (ast::ty_port(?t)) {
              typ = ty::mk_port(tcx, ast_ty_to_ty(tcx, getter, t));
          case (ast::ty_rec(?fields)) {
              let vec[field] flds = [];
              for (ast::ty_field f in fields) {
 -                auto tm = ast_mt_to_mt(tcx, getter, f.mt);
 -                vec::push[field](flds, rec(ident=f.ident, mt=tm));
 +                auto tm = ast_mt_to_mt(tcx, getter, f.node.mt);
 +                vec::push[field](flds, rec(ident=f.node.ident, mt=tm));
              }
              typ = ty::mk_rec(tcx, flds);
          }
  
 -        case (ast::ty_fn(?proto, ?inputs, ?output, ?cf)) {
 +        case (ast::ty_fn(?proto, ?inputs, ?output, ?cf, ?constrs)) {
              auto f = bind ast_arg_to_arg(tcx, getter, _);
              auto i = vec::map[ast::ty_arg, arg](f, inputs);
              auto out_ty = ast_ty_to_ty(tcx, getter, output);
 -            typ = ty::mk_fn(tcx, proto, i, out_ty, cf);
 +            typ = ty::mk_fn(tcx, proto, i, out_ty, cf, constrs);
          }
  
          case (ast::ty_path(?path, ?ann)) {
              alt (tcx.def_map.get(ann.id)) {
                  case (ast::def_ty(?id)) {
 -                    typ = instantiate(tcx, getter, id, path.node.types);
 +                    typ = instantiate(tcx, ast_ty.span, getter, id,
 +                                      path.node.types);
                  }
                  case (ast::def_native_ty(?id)) { typ = getter(id)._1; }
                  case (ast::def_obj(?id)) {
 -                    typ = instantiate(tcx, getter, id, path.node.types);
 +                    typ = instantiate(tcx, ast_ty.span, getter, id,
 +                                      path.node.types);
                  }
                  case (ast::def_ty_arg(?id)) { typ = ty::mk_param(tcx, id); }
                  case (_)                   {
              let vec[ty::method] tmeths = [];
              auto f = bind ast_arg_to_arg(tcx, getter, _);
              for (ast::ty_method m in meths) {
 -                auto ins = vec::map[ast::ty_arg, arg](f, m.inputs);
 -                auto out = ast_ty_to_ty(tcx, getter, m.output);
 +                auto ins = vec::map[ast::ty_arg, arg](f, m.node.inputs);
 +                auto out = ast_ty_to_ty(tcx, getter, m.node.output);
                  let ty::method new_m =
 -                                  rec(proto=m.proto,
 -                                      ident=m.ident,
 +                                  rec(proto=m.node.proto,
 +                                      ident=m.node.ident,
                                        inputs=ins,
                                        output=out,
 -                                      cf=m.cf);
 +                                      cf=m.node.cf,
 +                                      constrs=m.node.constrs);
                  vec::push[ty::method](tmeths, new_m);
              }
  
              typ = ty::rename(tcx, typ, cname_str);
          }
      }
+     tcx.ast_ty_to_ty_cache.insert(ast_ty, some(typ));
      ret typ;
  }
  
@@@ -388,13 -391,11 +400,13 @@@ fn ast_ty_to_ty_crate(@crate_ctxt ccx, 
  mod write {
      fn inner(&node_type_table ntt, uint node_id,
               &ty_param_substs_opt_and_ty tpot) {
 +        auto ntt_ = *ntt;
          vec::grow_set[option::t[ty::ty_param_substs_opt_and_ty]]
 -            (*ntt,
 +            (ntt_,
               node_id,
               none[ty_param_substs_opt_and_ty],
               some[ty_param_substs_opt_and_ty](tpot));
 +        *ntt = ntt_;
      }
  
      // Writes a type parameter count and type pair into the node type table.
      // Writes a type parameter count and type pair into the node type table.
      // This function allows for the possibility of type variables, which will
      // be rewritten later during the fixup phase.
 -    fn ty_fixup(&@stmt_ctxt scx, uint node_id,
 +    fn ty_fixup(@fn_ctxt fcx, uint node_id,
                  &ty_param_substs_opt_and_ty tpot) {
 -        inner(scx.fcx.ccx.tcx.node_types, node_id, tpot);
 -        if (ty::type_contains_vars(scx.fcx.ccx.tcx, tpot._1)) {
 -            scx.fixups += [node_id];
 +        inner(fcx.ccx.tcx.node_types, node_id, tpot);
 +        if (ty::type_contains_vars(fcx.ccx.tcx, tpot._1)) {
 +            fcx.fixups += [node_id];
          }
      }
  
  
      // Writes a type with no type parameters into the node type table. This
      // function allows for the possibility of type variables.
 -    fn ty_only_fixup(&@stmt_ctxt scx, uint node_id, ty::t typ) {
 -        be ty_fixup(scx, node_id, tup(none[vec[ty::t]], typ));
 +    fn ty_only_fixup(@fn_ctxt fcx, uint node_id, ty::t typ) {
 +        be ty_fixup(fcx, node_id, tup(none[vec[ty::t]], typ));
      }
  
      // Writes a nil type into the node type table.
@@@ -465,8 -466,7 +477,8 @@@ mod collect 
                       &ast::def_id def_id) -> ty::ty_param_count_and_ty {
          auto input_tys = vec::map[ast::arg,arg](ty_of_arg, decl.inputs);
          auto output_ty = convert(decl.output);
 -        auto t_fn = ty::mk_fn(cx.tcx, proto, input_tys, output_ty, decl.cf);
 +        auto t_fn = ty::mk_fn(cx.tcx, proto, input_tys, output_ty,
 +                              decl.cf, decl.constraints);
          auto ty_param_count = vec::len[ast::ty_param](ty_params);
          auto tpt = tup(ty_param_count, t_fn);
          cx.tcx.tcache.insert(def_id, tpt);
          auto inputs = vec::map[ast::arg,arg](f, m.node.meth.decl.inputs);
          auto output = convert(m.node.meth.decl.output);
          ret rec(proto=m.node.meth.proto, ident=m.node.ident,
 -                inputs=inputs, output=output, cf=m.node.meth.decl.cf);
 +                inputs=inputs, output=output, cf=m.node.meth.decl.cf,
 +                constrs=m.node.meth.decl.constraints);
      }
  
      fn ty_of_obj(@ctxt cx,
          for (ast::obj_field f in obj_info.fields) {
              auto g = bind getter(cx, _);
              auto t_field = ast_ty_to_ty(cx.tcx, g, f.ty);
 -            vec::push[arg](t_inputs, rec(mode=ty::mo_alias, ty=t_field));
 +            vec::push(t_inputs, rec(mode=ty::mo_alias(false), ty=t_field));
          }
  
 +        let vec[@ast::constr] constrs = [];
          auto t_fn = ty::mk_fn(cx.tcx, ast::proto_fn, t_inputs, t_obj._1,
 -                              ast::return);
 +                              ast::return, constrs);
  
          auto tpt = tup(t_obj._0, t_fn);
          cx.tcx.tcache.insert(ctor_id, tpt);
                  let vec[arg] args = [];
                  for (ast::variant_arg va in variant.node.args) {
                      auto arg_ty = ast_ty_to_ty(cx.tcx, f, va.ty);
 -                    args += [rec(mode=ty::mo_alias, ty=arg_ty)];
 +                    args += [rec(mode=ty::mo_alias(false), ty=arg_ty)];
                  }
                  auto tag_t = ty::mk_tag(cx.tcx, tag_id, ty_param_tys);
 +                // FIXME: this will be different for constrained types
 +                let vec[@ast::constr] res_constrs = [];
                  result_ty = ty::mk_fn(cx.tcx, ast::proto_fn, args, tag_t,
 -                                      ast::return);
 +                                      ast::return, res_constrs);
              }
  
              auto tpt = tup(ty_param_count, result_ty);
                  alt (object.dtor) {
                      case (none) { /* nothing to do */ }
                      case (some(?m)) {
 -                        // TODO: typechecker botch
 -                        let vec[arg] no_args = [];
 -                        auto t = ty::mk_fn(cx.tcx, ast::proto_fn, no_args,
 -                                           ty::mk_nil(cx.tcx), ast::return);
 +                        let vec[@ast::constr] constrs = [];
 +                        let vec[arg] res_inputs  = [];
 +                        auto t = ty::mk_fn(cx.tcx, ast::proto_fn, res_inputs,
 +                                   ty::mk_nil(cx.tcx), ast::return, constrs);
                          write::ty_only(cx.tcx, m.node.ann.id, t);
                      }
                  }
          // type of the native item. We simply write it into the node type
          // table.
          auto tpt = ty_of_native_item(cx, i,
 -                                     option::get[ast::native_abi](*abi));
 +                                     option::get[ast::native_abi]({*abi}));
  
          alt (i.node) {
              case (ast::native_item_ty(_,_)) {
  
  // Type unification
  
 +// TODO: rename to just "unify"
  mod unify {
 -    fn simple(&@stmt_ctxt scx, &ty::t expected, &ty::t actual)
 +    fn simple(&@fn_ctxt fcx, &ty::t expected, &ty::t actual)
              -> ty::unify::result {
 -        // FIXME: horrid botch
 -        let vec[mutable ty::t] param_substs =
 -            [mutable ty::mk_nil(scx.fcx.ccx.tcx)];
 -        vec::pop(param_substs);
 -        ret with_params(scx, expected, actual, param_substs);
 -    }
 -
 -    fn with_params(&@stmt_ctxt scx,
 -                   &ty::t expected,
 -                   &ty::t actual,
 -                   &vec[mutable ty::t] param_substs) -> ty::unify::result {
 -        auto cache_key = tup(expected, actual, param_substs);
 -        alt (scx.fcx.ccx.unify_cache.find(cache_key)) {
 -            case (some(?r)) {
 -                scx.fcx.ccx.cache_hits += 1u;
 -                ret r;
 -            }
 -            case (none) {
 -                scx.fcx.ccx.cache_misses += 1u;
 -            }
 -        }
 -
 -        obj unify_handler(@stmt_ctxt scx, vec[mutable ty::t] param_substs) {
 -            fn resolve_local(ast::def_id id) -> option::t[ty::t] {
 -                alt (scx.fcx.locals.find(id)) {
 -                    case (none) { ret none[ty::t]; }
 -                    case (some(?existing_type)) {
 -                        if (ty::type_contains_vars(scx.fcx.ccx.tcx,
 -                                                   existing_type)) {
 -                            // Not fully resolved yet. The writeback phase
 -                            // will mop up.
 -                            ret none[ty::t];
 -                        }
 -                        ret some[ty::t](existing_type);
 -                    }
 -                }
 -            }
 -            fn record_local(ast::def_id id, ty::t new_type) {
 -                auto unified_type;
 -                alt (scx.fcx.locals.find(id)) {
 -                    case (none) { unified_type = new_type; }
 -                    case (some(?old_type)) {
 -                        alt (with_params(scx, old_type, new_type,
 -                                         param_substs)) {
 -                            case (ures_ok(?ut)) { unified_type = ut; }
 -                            case (_) { fail; /* FIXME */ }
 -                        }
 -                    }
 -                }
 -
 -                // TODO: "freeze"
 -                let vec[ty::t] param_substs_1 = [];
 -                for (ty::t subst in param_substs) {
 -                    param_substs_1 += [subst];
 -                }
 -
 -                unified_type = ty::substitute_type_params(scx.fcx.ccx.tcx,
 -                                                          param_substs_1,
 -                                                          unified_type);
 -                scx.fcx.locals.insert(id, unified_type);
 -            }
 -            fn record_param(uint index, ty::t binding) -> ty::unify::result {
 -                // Unify with the appropriate type in the parameter
 -                // substitution list:
 -                auto old_subst = param_substs.(index);
 -
 -                auto result = with_params(scx, old_subst, binding,
 -                                          param_substs);
 -                alt (result) {
 -                    case (ures_ok(?new_subst)) {
 -                        param_substs.(index) = new_subst;
 -                        ret ures_ok(ty::mk_bound_param(scx.fcx.ccx.tcx,
 -                                                       index));
 -                    }
 -                    case (_) { ret result; }
 -                }
 -            }
 -        }
 -
 -
 -        auto handler = unify_handler(scx, param_substs);
 -
 -        auto bindings = ty::unify::mk_bindings[int](int::hash, int::eq_alias);
 -        auto result = ty::unify::unify(expected, actual, handler, bindings,
 -                                       scx.fcx.ccx.tcx);
 -
 -        alt (result) {
 -            case (ures_ok(?rty)) {
 -                if (ty::type_contains_vars(scx.fcx.ccx.tcx, rty)) {
 -                    result = ty::unify::fixup(scx.fcx.ccx.tcx, bindings, rty);
 -                }
 -            }
 -            case (_) { /* nothing */ }
 -        }
 -
 -        scx.fcx.ccx.unify_cache.insert(cache_key, result);
 -        ret result;
 +        ret ty::unify::unify(expected, actual, fcx.var_bindings, fcx.ccx.tcx);
      }
  }
  
@@@ -862,10 -953,10 +874,10 @@@ tag autoderef_kind 
      NO_AUTODEREF;
  }
  
 -fn strip_boxes(&ty::ctxt tcx, &ty::t t) -> ty::t {
 +fn strip_boxes(&@fn_ctxt fcx, &span sp, &ty::t t) -> ty::t {
      auto t1 = t;
      while (true) {
 -        alt (struct(tcx, t1)) {
 +        alt (structure_of(fcx, sp, t1)) {
              case (ty::ty_box(?inner)) { t1 = inner.ty; }
              case (_) { ret t1; }
          }
@@@ -883,11 -974,11 +895,11 @@@ fn add_boxes(&@crate_ctxt ccx, uint n, 
  }
  
  
 -fn count_boxes(&ty::ctxt tcx, &ty::t t) -> uint {
 +fn count_boxes(&@fn_ctxt fcx, &span sp, &ty::t t) -> uint {
      auto n = 0u;
      auto t1 = t;
      while (true) {
 -        alt (struct(tcx, t1)) {
 +        alt (structure_of(fcx, sp, t1)) {
              case (ty::ty_box(?inner)) { n += 1u; t1 = inner.ty; }
              case (_) { ret n; }
          }
  }
  
  
 +fn resolve_type_vars_if_possible(&@fn_ctxt fcx, ty::t typ) -> ty::t {
 +    alt (ty::unify::fixup_vars(fcx.ccx.tcx, fcx.var_bindings, typ)) {
 +        case (fix_ok(?new_type)) { ret new_type; }
 +        case (fix_err(_)) { ret typ; }
 +    }
 +}
 +
 +
  // Demands - procedures that require that two types unify and emit an error
  // message if they don't.
  
  type ty_param_substs_and_ty = tup(vec[ty::t], ty::t);
  
  mod demand {
 -    fn simple(&@stmt_ctxt scx, &span sp, &ty::t expected, &ty::t actual)
 +    fn simple(&@fn_ctxt fcx, &span sp, &ty::t expected, &ty::t actual)
              -> ty::t {
          let vec[ty::t] tps = [];
 -        ret full(scx, sp, expected, actual, tps, NO_AUTODEREF)._1;
 +        ret full(fcx, sp, expected, actual, tps, NO_AUTODEREF)._1;
      }
  
 -    fn autoderef(&@stmt_ctxt scx, &span sp, &ty::t expected, &ty::t actual,
 +    fn autoderef(&@fn_ctxt fcx, &span sp, &ty::t expected, &ty::t actual,
                   autoderef_kind adk) -> ty::t {
          let vec[ty::t] tps = [];
 -        ret full(scx, sp, expected, actual, tps, adk)._1;
 +        ret full(fcx, sp, expected, actual, tps, adk)._1;
      }
  
      // Requires that the two types unify, and prints an error message if they
      // don't. Returns the unified type and the type parameter substitutions.
  
 -    fn full(&@stmt_ctxt scx, &span sp, &ty::t expected, &ty::t actual,
 +    fn full(&@fn_ctxt fcx, &span sp, &ty::t expected, &ty::t actual,
              &vec[ty::t] ty_param_substs_0, autoderef_kind adk)
              -> ty_param_substs_and_ty {
  
          auto implicit_boxes = 0u;
  
          if (adk == AUTODEREF_OK) {
 -            expected_1 = strip_boxes(scx.fcx.ccx.tcx, expected_1);
 -            actual_1 = strip_boxes(scx.fcx.ccx.tcx, actual_1);
 -            implicit_boxes = count_boxes(scx.fcx.ccx.tcx, actual);
 +            expected_1 = strip_boxes(fcx, sp, expected_1);
 +            actual_1 = strip_boxes(fcx, sp, actual_1);
 +            implicit_boxes = count_boxes(fcx, sp, actual);
          }
  
 -        let vec[mutable ty::t] ty_param_substs =
 -            [mutable ty::mk_nil(scx.fcx.ccx.tcx)];
 -        vec::pop(ty_param_substs);   // FIXME: horrid botch
 +        let vec[mutable ty::t] ty_param_substs = [mutable];
 +        let vec[int] ty_param_subst_var_ids = [];
          for (ty::t ty_param_subst in ty_param_substs_0) {
 -            ty_param_substs += [mutable ty_param_subst];
 +            // Generate a type variable and unify it with the type parameter
 +            // substitution. We will then pull out these type variables.
 +            auto t_0 = next_ty_var(fcx);
 +            ty_param_substs += [mutable t_0];
 +            ty_param_subst_var_ids += [ty::ty_var_id(fcx.ccx.tcx, t_0)];
 +
 +            simple(fcx, sp, ty_param_subst, t_0);
          }
  
 -        alt (unify::with_params(scx, expected_1, actual_1, ty_param_substs)) {
 +        alt (unify::simple(fcx, expected_1, actual_1)) {
              case (ures_ok(?t)) {
 -                // TODO: Use "freeze", when we have it.
                  let vec[ty::t] result_ty_param_substs = [];
 -                for (ty::t ty_param_subst in ty_param_substs) {
 -                    result_ty_param_substs += [ty_param_subst];
 +                for (int var_id in ty_param_subst_var_ids) {
 +                    auto tp_subst = ty::mk_var(fcx.ccx.tcx, var_id);
 +                    result_ty_param_substs += [tp_subst];
                  }
  
                  ret tup(result_ty_param_substs,
 -                        add_boxes(scx.fcx.ccx, implicit_boxes, t));
 +                        add_boxes(fcx.ccx, implicit_boxes, t));
              }
  
 -            case (ures_err(?err, ?expected, ?actual)) {
 -                scx.fcx.ccx.tcx.sess.span_err
 +            case (ures_err(?err)) {
 +                auto e_err = resolve_type_vars_if_possible(fcx, expected_1);
 +                auto a_err = resolve_type_vars_if_possible(fcx, actual_1);
 +
 +                fcx.ccx.tcx.sess.span_err
                      (sp, "mismatched types: expected "
 -                     + ty_to_str(scx.fcx.ccx.tcx, expected) + " but found "
 -                     + ty_to_str(scx.fcx.ccx.tcx, actual) + " ("
 +                     + ty_to_str(fcx.ccx.tcx, e_err) + " but found "
 +                     + ty_to_str(fcx.ccx.tcx, a_err) + " ("
                       + ty::type_err_to_str(err) + ")");
  
                  // TODO: In the future, try returning "expected", reporting
                  // the error, and continue.
 -                fail;
              }
          }
      }
  
  
  // Returns true if the two types unify and false if they don't.
 -fn are_compatible(&@stmt_ctxt scx, &ty::t expected, &ty::t actual) -> bool {
 -    alt (unify::simple(scx, expected, actual)) {
 -        case (ures_ok(_))        { ret true;  }
 -        case (ures_err(_, _, _)) { ret false; }
 +fn are_compatible(&@fn_ctxt fcx, &ty::t expected, &ty::t actual) -> bool {
 +    alt (unify::simple(fcx, expected, actual)) {
 +        case (ures_ok(_))   { ret true;  }
 +        case (ures_err(_))  { ret false; }
      }
  }
  
@@@ -997,12 -1073,13 +1009,12 @@@ fn variant_arg_types(&@crate_ctxt ccx, 
      let vec[ty::t] result = [];
  
      auto tpt = ty::lookup_item_type(ccx.tcx, vid);
 -    alt (struct(ccx.tcx, tpt._1)) {
 -        case (ty::ty_fn(_, ?ins, _, _)) {
 +    alt (ty::struct(ccx.tcx, tpt._1)) {
 +        case (ty::ty_fn(_, ?ins, _, _, _)) {
              // N-ary variant.
              for (ty::arg arg in ins) {
 -                auto arg_ty = bind_params_in_type(ccx.tcx, arg.ty);
 -                arg_ty = substitute_ty_params(ccx, arg_ty, ty_param_count,
 -                                              tag_ty_params, sp);
 +                auto arg_ty = ty::substitute_type_params(ccx.tcx,
 +                    tag_ty_params, arg.ty);
                  result += [arg_ty];
              }
          }
  }
  
  
 -// The "push-down" phase, which takes a typed grammar production and pushes
 -// its type down into its constituent parts.
 -//
 -// For example, consider "auto x; x = 352;". check_expr() doesn't know the
 -// type of "x" at the time it sees it, so that function will simply store a
 -// type variable for the type of "x". However, after checking the entire
 -// assignment expression, check_expr() will assign the type of int to the
 -// expression "x = 352" as a whole. In this case, then, the job of these
 -// functions is to clean up by assigning the type of int to both sides of the
 -// assignment expression.
 +// Type resolution: the phase that finds all the types in the AST with
 +// unresolved type variables and replaces "ty_var" types with their
 +// substitutions.
  //
 -// TODO: We only need to do this once per statement: check_expr() bubbles the
 -// types up, and pushdown_expr() pushes the types down. However, in many cases
 -// we're more eager than we need to be, calling pushdown_expr() and friends
 -// directly inside check_expr(). This results in a quadratic algorithm.
 -
 -mod pushdown {
 -    // Push-down over typed patterns. Note that the pattern that you pass to
 -    // this function must have been passed to check_pat() first.
 -    //
 -    // TODO: enforce this via a predicate.
 -
 -    fn pushdown_pat(&@stmt_ctxt scx, &ty::t expected, &@ast::pat pat) {
 -        alt (pat.node) {
 -            case (ast::pat_wild(?ann)) {
 -                auto t = demand::simple(scx, pat.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann));
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::pat_lit(?lit, ?ann)) {
 -                auto t = demand::simple(scx, pat.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann));
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::pat_bind(?id, ?did, ?ann)) {
 -                auto t = demand::simple(scx, pat.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann));
 -                scx.fcx.locals.insert(did, t);
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::pat_tag(?id, ?subpats, ?ann)) {
 -                // Take the variant's type parameters out of the expected
 -                // type.
 -                auto tag_tps;
 -                alt (struct(scx.fcx.ccx.tcx, expected)) {
 -                    case (ty::ty_tag(_, ?tps)) { tag_tps = tps; }
 -                    case (_) {
 -                        log_err "tag pattern type not actually a tag?!";
 -                        fail;
 -                    }
 -                }
 -
 -                // Get the types of the arguments of the variant.
 -
 -                let vec[ty::t] tparams = [];
 -                auto j = 0u;
 -                auto actual_ty_params =
 -                  ty::ann_to_type_params(scx.fcx.ccx.tcx.node_types, ann);
 -
 -                for (ty::t some_ty in tag_tps) {
 -                    let ty::t t1 = some_ty;
 -                    let ty::t t2 = actual_ty_params.(j);
 -
 -                    let ty::t res = demand::simple(scx, pat.span, t1, t2);
 -
 -                    vec::push(tparams, res);
 -                    j += 1u;
 -                }
 -
 -                auto arg_tys;
 -                alt (scx.fcx.ccx.tcx.def_map.get(ann.id)) {
 -                    case (ast::def_variant(_, ?vdefid)) {
 -                        arg_tys = variant_arg_types(scx.fcx.ccx, pat.span,
 -                                                    vdefid, tparams);
 -                    }
 -                }
 -
 -                auto i = 0u;
 -                for (@ast::pat subpat in subpats) {
 -                    pushdown_pat(scx, arg_tys.(i), subpat);
 -                    i += 1u;
 -                }
 -
 -                auto tps =
 -                    ty::ann_to_type_params(scx.fcx.ccx.tcx.node_types, ann);
 -                auto tt = ann_to_type(scx.fcx.ccx.tcx.node_types, ann);
 -
 -                let ty_param_substs_and_ty res_t = demand::full(scx, pat.span,
 -                      expected, tt, tps, NO_AUTODEREF);
 +// TODO: inefficient since not all types have vars in them. It would be better
 +// to maintain a list of fixups.
  
 -                auto ty_params_subst = ty::ann_to_ty_param_substs_opt_and_ty
 -                    (scx.fcx.ccx.tcx.node_types, ann);
 -
 -                auto ty_params_opt;
 -                alt (ty_params_subst._0) {
 -                    case (none) {
 -                        ty_params_opt = none[vec[ty::t]];
 -                    }
 -                    case (some(?tps)) {
 -                        ty_params_opt = some[vec[ty::t]](tag_tps);
 -                    }
 -                }
 +mod writeback {
 +    fn resolve_type_vars_in_type(&@fn_ctxt fcx, &span sp, ty::t typ)
 +            -> ty::t {
 +        if (!ty::type_contains_vars(fcx.ccx.tcx, typ)) { ret typ; }
  
 -                write::ty_fixup(scx, ann.id, tup(ty_params_opt, tt));
 +        alt (ty::unify::fixup_vars(fcx.ccx.tcx, fcx.var_bindings, typ)) {
 +            case (fix_ok(?new_type)) { ret new_type; }
 +            case (fix_err(?vid)) {
 +                fcx.ccx.tcx.sess.span_err(sp,
 +                    "cannot determine a type for this expression");
              }
          }
      }
  
 -    // Push-down over typed expressions. Note that the expression that you
 -    // pass to this function must have been passed to check_expr() first.
 -    //
 -    // TODO: enforce this via a predicate.
 -    // TODO: This function is incomplete.
 -
 -    fn pushdown_expr(&@stmt_ctxt scx, &ty::t expected, &@ast::expr e) {
 -        be pushdown_expr_full(scx, expected, e, NO_AUTODEREF);
 -    }
 -
 -    fn pushdown_expr_full(&@stmt_ctxt scx, &ty::t expected, &@ast::expr e,
 -                          autoderef_kind adk) {
 -        alt (e.node) {
 -            case (ast::expr_vec(?es_0, ?mut, ?ann)) {
 -                // TODO: enforce mutability
 -
 -                auto t = demand::simple(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann));
 -                alt (struct(scx.fcx.ccx.tcx, t)) {
 -                    case (ty::ty_vec(?mt)) {
 -                        for (@ast::expr e_0 in es_0) {
 -                            pushdown_expr(scx, mt.ty, e_0);
 -                        }
 -                    }
 -                    case (_) {
 -                        log_err "vec expr doesn't have a vec type!";
 -                        fail;
 -                    }
 -                }
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::expr_tup(?es_0, ?ann)) {
 -                auto t = demand::simple(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann));
 -                alt (struct(scx.fcx.ccx.tcx, t)) {
 -                    case (ty::ty_tup(?mts)) {
 -                        auto i = 0u;
 -                        for (ast::elt elt_0 in es_0) {
 -                            pushdown_expr(scx, mts.(i).ty, elt_0.expr);
 -                            i += 1u;
 -                        }
 -                    }
 -                    case (_) {
 -                        log_err "tup expr doesn't have a tup type!";
 -                        fail;
 -                    }
 -                }
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::expr_rec(?fields_0, ?base_0, ?ann)) {
 -
 -                auto t = demand::simple(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann));
 -                alt (struct(scx.fcx.ccx.tcx, t)) {
 -                    case (ty::ty_rec(?field_mts)) {
 -                        alt (base_0) {
 -                            case (none) {
 -                                auto i = 0u;
 -                                for (ast::field field_0 in fields_0) {
 -                                    assert (str::eq(field_0.node.ident,
 -                                                    field_mts.(i).ident));
 -                                    pushdown_expr(scx,
 -                                                  field_mts.(i).mt.ty,
 -                                                  field_0.node.expr);
 -                                    i += 1u;
 -                                }
 -                            }
 -                            case (some(?bx)) {
 -
 -                                let vec[field] base_fields = [];
 -
 -                                for (ast::field field_0 in fields_0) {
 -
 -                                    for (ty::field ft in field_mts) {
 -                                        if (str::eq(field_0.node.ident,
 -                                                    ft.ident)) {
 -                                            pushdown_expr(scx, ft.mt.ty,
 -                                                          field_0.node.expr);
 -                                        }
 -                                    }
 -                                }
 -                            }
 -                        }
 -                    }
 -                    case (_) {
 -                        log_err "rec expr doesn't have a rec type!";
 -                        fail;
 -                    }
 -                }
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::expr_bind(?sube, ?es, ?ann)) {
 -                auto t = demand::simple(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann));
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::expr_call(?sube, ?es, ?ann)) {
 -                // NB: we call 'demand::autoderef' and pass in adk only in
 -                // cases where e is an expression that could *possibly*
 -                // produce a box; things like expr_binary or expr_bind can't,
 -                // so there's no need.
 -                auto t = demand::autoderef(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann), adk);
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::expr_self_method(?id, ?ann)) {
 -                auto t = demand::simple(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann));
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::expr_binary(?bop, ?lhs, ?rhs, ?ann)) {
 -                auto t = demand::simple(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann));
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::expr_unary(?uop, ?sube, ?ann)) {
 -                // See note in expr_unary for why we're calling
 -                // demand::autoderef.
 -                auto t = demand::autoderef(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann), adk);
 -                write::ty_only_fixup(scx, ann.id, t);
 -
 -                /* The following is a bit special-cased, but takes care of
 -                   the case where we say let @vec[whatever] v = @[]; */
 -                auto inner_ty = t;
 -                alt (uop) {
 -                    case (ast::box(?mut)) {
 -                        alt (struct(scx.fcx.ccx.tcx, t)) {
 -                            case (ty::ty_box(?inner)) { inner_ty = inner.ty; }
 -                            case (_) { 
 -                                scx.fcx.ccx.tcx.sess.span_err(e.span,
 -                                  "Expecting an application of box"
 -                                   + " to have a box type");
 -                            }
 -                        }
 -                    }
 -                    case (ast::deref) {
 -                        inner_ty = ty::mk_box(scx.fcx.ccx.tcx,
 -                        // maybe_mut should work because it'll unify with
 -                        // the existing type?
 -                                   rec(ty=t, mut=ast::maybe_mut));
 -                    }
 -                    case (_) { inner_ty = strip_boxes(scx.fcx.ccx.tcx, t); }
 -                }
 -
 -                pushdown_expr(scx, inner_ty, sube);
 -            }
 -            case (ast::expr_lit(?lit, ?ann)) {
 -                auto t = demand::simple(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann));
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::expr_cast(?sube, ?ast_ty, ?ann)) {
 -                auto t = demand::simple(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann));
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::expr_if(?cond, ?then_0, ?else_0, ?ann)) {
 -                auto t = demand::autoderef(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann), adk);
 -
 -                auto then_t = ty::block_ty(scx.fcx.ccx.tcx, then_0);
 -                pushdown_block(scx, expected, then_0);
 +    fn resolve_type_vars_for_node(&@fn_ctxt fcx, &span sp, &ast::ann ann) {
 +        auto tpot = ty::ann_to_ty_param_substs_opt_and_ty(fcx.ccx.tcx, ann);
 +        auto new_ty = resolve_type_vars_in_type(fcx, sp, tpot._1);
  
 -                alt (else_0) {
 -                    case (none) { /* no-op */ }
 -                    case (some(?e_0)) {
 -                        auto else_t = ty::expr_ty(scx.fcx.ccx.tcx, e_0);
 -                        pushdown_expr(scx, expected, e_0);
 -                    }
 +        auto new_substs_opt;
 +        alt (tpot._0) {
 +            case (none[vec[ty::t]]) { new_substs_opt = none[vec[ty::t]]; }
 +            case (some[vec[ty::t]](?substs)) {
 +                let vec[ty::t] new_substs = [];
 +                for (ty::t subst in substs) {
 +                    new_substs += [resolve_type_vars_in_type(fcx, sp, subst)];
                  }
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::expr_for(?decl, ?seq, ?bloc, ?ann)) {
 -                auto t = demand::simple(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann));
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::expr_for_each(?decl, ?seq, ?bloc, ?ann)) {
 -                auto t = demand::simple(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann));
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::expr_while(?cond, ?bloc, ?ann)) {
 -                auto t = demand::simple(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann));
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::expr_do_while(?bloc, ?cond, ?ann)) {
 -                auto t = demand::simple(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann));
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::expr_block(?bloc, ?ann)) {
 -                auto t = demand::autoderef(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann), adk);
 -                write::ty_only_fixup(scx, ann.id, t);
 -                pushdown_block(scx, t, bloc);
 -            }
 -            case (ast::expr_move(?lhs_0, ?rhs_0, ?ann)) {
 -                auto t = demand::autoderef(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann), adk);
 -                pushdown_expr(scx, expected, lhs_0);
 -                pushdown_expr(scx, expected, rhs_0);
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::expr_assign(?lhs_0, ?rhs_0, ?ann)) {
 -                auto t = demand::autoderef(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann), adk);
 -                pushdown_expr(scx, expected, lhs_0);
 -                pushdown_expr(scx, expected, rhs_0);
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::expr_assign_op(?op, ?lhs_0, ?rhs_0, ?ann)) {
 -                auto t = demand::autoderef(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann), adk);
 -                pushdown_expr(scx, expected, lhs_0);
 -                pushdown_expr(scx, expected, rhs_0);
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::expr_field(?lhs, ?rhs, ?ann)) {
 -                auto t = demand::autoderef(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann), adk);
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::expr_index(?base, ?index, ?ann)) {
 -                auto t = demand::autoderef(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann), adk);
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            case (ast::expr_path(?pth, ?ann)) {
 -                auto tp_substs_0 =
 -                    ty::ann_to_type_params(scx.fcx.ccx.tcx.node_types, ann);
 -                auto t_0 = ann_to_type(scx.fcx.ccx.tcx.node_types, ann);
 -
 -                auto result_0 = demand::full(scx, e.span, expected, t_0,
 -                                             tp_substs_0, adk);
 -                auto t = result_0._1;
 -
 -                // Fill in the type parameter substitutions if they weren't
 -                // provided by the programmer.
 -                auto ty_params_opt;
 -                alt (ty::ann_to_ty_param_substs_opt_and_ty
 -                        (scx.fcx.ccx.tcx.node_types, ann)._0) {
 -                    case (none) {
 -                        ty_params_opt = none[vec[ty::t]];
 -                    }
 -                    case (some(?tps)) {
 -                        ty_params_opt = some[vec[ty::t]](tps);
 -                    }
 -                }
 -
 -                write::ty_fixup(scx, ann.id, tup(ty_params_opt, t));
 -            }
 -            case (ast::expr_ext(?p, ?args, ?body, ?expanded, ?ann)) {
 -                auto t = demand::autoderef(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann), adk);
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -            /* FIXME: should this check the type annotations? */
 -            case (ast::expr_fail(_))  { /* no-op */ }
 -            case (ast::expr_log(_,_,_)) { /* no-op */ }
 -            case (ast::expr_break(_)) { /* no-op */ }
 -            case (ast::expr_cont(_))  { /* no-op */ }
 -            case (ast::expr_ret(_,_)) { /* no-op */ }
 -            case (ast::expr_put(_,_)) { /* no-op */ }
 -            case (ast::expr_be(_,_))  { /* no-op */ }
 -            case (ast::expr_check(_,_)) { /* no-op */ }
 -            case (ast::expr_assert(_,_)) { /* no-op */ }
 -
 -            case (ast::expr_port(?ann)) {
 -                auto t = demand::simple(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann));
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -
 -            case (ast::expr_chan(?es, ?ann)) {
 -                auto t = demand::simple(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann));
 -                alt (struct(scx.fcx.ccx.tcx, t)) {
 -                    case (ty::ty_chan(?subty)) {
 -                        auto pt = ty::mk_port(scx.fcx.ccx.tcx, subty);
 -                        pushdown_expr(scx, pt, es);
 -                    }
 -                    case (_) {
 -                        log "chan expr doesn't have a chan type!";
 -                        fail;
 -                    }
 -                }
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -
 -            case (ast::expr_alt(?discrim, ?arms_0, ?ann)) {
 -                auto t = expected;
 -                for (ast::arm arm_0 in arms_0) {
 -                    pushdown_block(scx, expected, arm_0.block);
 -                    auto bty = block_ty(scx.fcx.ccx.tcx, arm_0.block);
 -                    t = demand::simple(scx, e.span, t, bty);
 -                }
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -
 -            case (ast::expr_recv(?lval, ?expr, ?ann)) {
 -                pushdown_expr(scx, next_ty_var(scx), lval);
 -                auto t = expr_ty(scx.fcx.ccx.tcx, lval);
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -
 -            case (ast::expr_send(?lval, ?expr, ?ann)) {
 -                pushdown_expr(scx, next_ty_var(scx), expr);
 -                auto t = expr_ty(scx.fcx.ccx.tcx, expr);
 -                pushdown_expr(scx, ty::mk_chan(scx.fcx.ccx.tcx, t), lval);
 -            }
 -
 -            case (ast::expr_spawn(?dom, ?name, ?func, ?args, ?ann)) {
 -                // NB: we call 'demand::autoderef' and pass in adk only in
 -                // cases where e is an expression that could *possibly*
 -                // produce a box; things like expr_binary or expr_bind can't,
 -                // so there's no need.
 -                auto t = demand::autoderef(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann), adk);
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -
 -            case (ast::expr_anon_obj(?anon_obj, ?tps, ?odid, ?ann)) {
 -                // NB: Not sure if this is correct, but not worrying too much
 -                // about it since pushdown is going away anyway.
 -                auto t = demand::autoderef(scx, e.span, expected,
 -                    ann_to_type(scx.fcx.ccx.tcx.node_types, ann), adk);
 -                write::ty_only_fixup(scx, ann.id, t);
 -            }
 -
 -            case (_) {
 -                scx.fcx.ccx.tcx.sess.span_unimpl(e.span,
 -                    #fmt("type unification for expression variant: %s",
 -                         pretty::pprust::expr_to_str(e)));
 -                fail;
 +                new_substs_opt = some[vec[ty::t]](new_substs);
              }
          }
 -    }
  
 -    // Push-down over typed blocks.
 -    fn pushdown_block(&@stmt_ctxt scx, &ty::t expected, &ast::block bloc) {
 -        alt (bloc.node.expr) {
 -            case (some(?e_0)) {
 -                pushdown_expr(scx, expected, e_0);
 -            }
 -            case (none) {
 -                /* empty */
 -            }
 -        }
 -        demand::simple(scx, bloc.span, expected,
 -          ann_to_type(scx.fcx.ccx.tcx.node_types, bloc.node.a));
 -    }
 -}
 -
 -
 -// Local variable resolution: the phase that finds all the types in the AST
 -// and replaces opaque "ty_local" types with the resolved local types.
 -
 -mod writeback {
 -    fn wb_local(&@fn_ctxt fcx, &span sp, &@ast::local local) {
 -        auto local_ty;
 -        alt (fcx.locals.find(local.id)) {
 -            case (none) {
 -                fcx.ccx.tcx.sess.span_err(sp,
 -                    "unable to determine type of local: " + local.ident);
 -                fail;
 -            }
 -            case (some(?lt)) {
 -                local_ty = lt;
 -            }
 -        }
 -
 -        if (ty::type_contains_vars(fcx.ccx.tcx, local_ty)) {
 -            fcx.ccx.tcx.sess.span_err(sp,
 -              "Ambiguous type " + ty_to_str(fcx.ccx.tcx, local_ty)
 -             + "\n(Try adding more type annotations.)");
 -        }
 -        write::ty_only(fcx.ccx.tcx, local.ann.id, local_ty);
 -    }
 -
 -    fn resolve_local_types(&@fn_ctxt fcx, &ast::ann ann) {
 -        fn resolver(@fn_ctxt fcx, ty::t typ) -> ty::t {
 -            alt (struct(fcx.ccx.tcx, typ)) {
 -                case (ty::ty_local(?lid))   { ret fcx.locals.get(lid); }
 -                case (_)                    { ret typ; }
 -            }
 -        }
 -
 -        auto tpot =
 -            ty::ann_to_ty_param_substs_opt_and_ty(fcx.ccx.tcx.node_types,
 -                                                  ann);
 -        auto tt = tpot._1;
 -        if (!ty::type_contains_locals(fcx.ccx.tcx, tt)) { ret; }
 -
 -        auto f = bind resolver(fcx, _);
 -        auto new_type = ty::fold_ty(fcx.ccx.tcx, f, tt);
 -        write::ty(fcx.ccx.tcx, ann.id, tup(tpot._0, new_type));
 +        write::ty(fcx.ccx.tcx, ann.id, tup(new_substs_opt, new_ty));
      }
  
      fn visit_stmt_pre(@fn_ctxt fcx, &@ast::stmt s) {
 -        resolve_local_types(fcx, ty::stmt_ann(s));
 +        resolve_type_vars_for_node(fcx, s.span, ty::stmt_ann(s));
      }
  
      fn visit_expr_pre(@fn_ctxt fcx, &@ast::expr e) {
 -        resolve_local_types(fcx, ty::expr_ann(e));
 +        resolve_type_vars_for_node(fcx, e.span, ty::expr_ann(e));
      }
  
      fn visit_block_pre(@fn_ctxt fcx, &ast::block b) {
 -        resolve_local_types(fcx, b.node.a);
 +        resolve_type_vars_for_node(fcx, b.span, b.node.a);
      }
  
 -    fn visit_arm_pre(@fn_ctxt fcx, &ast::arm a) {
 -        // FIXME: Need a visit_pat_pre
 -        resolve_local_types(fcx, ty::pat_ann(a.pat));
 +    fn visit_pat_pre(@fn_ctxt fcx, &@ast::pat p) {
 +        resolve_type_vars_for_node(fcx, p.span, ty::pat_ann(p));
      }
  
      fn visit_decl_pre(@fn_ctxt fcx, &@ast::decl d) {
          alt (d.node) {
 -            case (ast::decl_local(?l)) { wb_local(fcx, d.span, l); }
 -            case (ast::decl_item(_)) { /* no annotation */ }
 +            case (ast::decl_local(?l)) {
 +                auto var_id = fcx.locals.get(l.id);
 +                auto fix_rslt = ty::unify::resolve_type_var(fcx.ccx.tcx,
 +                    fcx.var_bindings, var_id);
 +                alt (fix_rslt) {
 +                    case (fix_ok(?lty)) {
 +                        write::ty_only(fcx.ccx.tcx, l.ann.id, lty);
 +                    }
 +                    case (fix_err(_)) {
 +                        fcx.ccx.tcx.sess.span_err(d.span,
 +                            "cannot determine a type for this local " +
 +                            "variable");
 +                    }
 +                }
 +            }
 +            case (_) { /* no-op */ }
          }
      }
  
 -    fn resolve_local_types_in_block(&@fn_ctxt fcx, &ast::block block) {
 +    fn resolve_type_vars_in_block(&@fn_ctxt fcx, &ast::block block) {
          // A trick to ignore any contained items.
          auto ignore = @mutable false;
          fn visit_item_pre(@mutable bool ignore, &@ast::item item) {
                           visit_stmt_pre=bind visit_stmt_pre(fcx, _),
                           visit_expr_pre=bind visit_expr_pre(fcx, _),
                           visit_block_pre=bind visit_block_pre(fcx, _),
 -                         visit_arm_pre=bind visit_arm_pre(fcx, _),
 +                         visit_pat_pre=bind visit_pat_pre(fcx, _),
                           visit_decl_pre=bind visit_decl_pre(fcx, _)
                           with walk::default_visitor());
          walk::walk_block(visit, block);
  }
  
  
 +// Local variable gathering. We gather up all locals and create variable IDs
 +// for them before typechecking the function.
 +
 +type gather_result = rec(
 +    @ty::unify::var_bindings var_bindings,
 +    hashmap[ast::def_id,int] locals,
 +    hashmap[ast::def_id,ast::ident] local_names,
 +    int next_var_id
 +);
 +
 +fn gather_locals(&@crate_ctxt ccx, &ast::fn_decl decl, &ast::block body,
 +                 &ast::ann ann) -> gather_result {
 +    fn next_var_id(@mutable int nvi) -> int {
 +        auto rv = *nvi;
 +        *nvi += 1;
 +        ret rv;
 +    }
 +
 +    fn assign(&ty::ctxt tcx,
 +              &@ty::unify::var_bindings var_bindings,
 +              &hashmap[ast::def_id,int] locals,
 +              &hashmap[ast::def_id,ast::ident] local_names,
 +              @mutable int nvi,
 +              ast::def_id lid,
 +              &ast::ident ident,
 +              option::t[ty::t] ty_opt) {
 +        auto var_id = next_var_id(nvi);
 +        locals.insert(lid, var_id);
 +        local_names.insert(lid, ident);
 +
 +        alt (ty_opt) {
 +            case (none[ty::t]) { /* nothing to do */ }
 +            case (some[ty::t](?typ)) {
 +                ty::unify::unify(ty::mk_var(tcx, var_id), typ, var_bindings,
 +                                 tcx);
 +            }
 +        }
 +    }
 +
 +    auto vb = ty::unify::mk_var_bindings();
 +    auto locals = new_def_hash[int]();
 +    auto local_names = new_def_hash[ast::ident]();
 +    auto nvi = @mutable 0;
 +
 +    // Add object fields, if any.
 +    alt (get_obj_info(ccx)) {
 +        case (option::some(?oinfo)) {
 +            for (ast::obj_field f in oinfo.obj_fields) {
 +                auto field_ty = ty::ann_to_type(ccx.tcx, f.ann);
 +                assign(ccx.tcx, vb, locals, local_names, nvi, f.id, f.ident,
 +                       some[ty::t](field_ty));
 +            }
 +        }
 +        case (option::none) { /* no fields */ }
 +    }
 +
 +    // Add formal parameters.
 +    auto args = ty::ty_fn_args(ccx.tcx, ty::ann_to_type(ccx.tcx, ann));
 +    auto i = 0u;
 +    for (ty::arg arg in args) {
 +        assign(ccx.tcx, vb, locals, local_names, nvi, decl.inputs.(i).id,
 +               decl.inputs.(i).ident, some[ty::t](arg.ty));
 +        i += 1u;
 +    }
 +
 +    // Add explicitly-declared locals.
 +    fn visit_decl_pre(@crate_ctxt ccx,
 +                      @ty::unify::var_bindings vb,
 +                      hashmap[ast::def_id,int] locals,
 +                      hashmap[ast::def_id,ast::ident] local_names,
 +                      @mutable int nvi,
 +                      &@ast::decl d) {
 +        alt (d.node) {
 +            case (ast::decl_local(?local)) {
 +                alt (local.ty) {
 +                    case (none) {
 +                        // Auto slot.
 +                        assign(ccx.tcx, vb, locals, local_names, nvi,
 +                               local.id, local.ident, none[ty::t]);
 +                    }
 +                    case (some(?ast_ty)) {
 +                        // Explicitly typed slot.
 +                        auto local_ty = ast_ty_to_ty_crate(ccx, ast_ty);
 +                        assign(ccx.tcx, vb, locals, local_names, nvi,
 +                               local.id, local.ident, some[ty::t](local_ty));
 +                    }
 +                }
 +            }
 +            case (_) { /* no-op */ }
 +        }
 +    }
 +
 +    // Add pattern bindings.
 +    fn visit_pat_pre(@crate_ctxt ccx,
 +                     @ty::unify::var_bindings vb,
 +                     hashmap[ast::def_id,int] locals,
 +                     hashmap[ast::def_id,ast::ident] local_names,
 +                     @mutable int nvi,
 +                     &@ast::pat p) {
 +        alt (p.node) {
 +            case (ast::pat_bind(?ident, ?did, _)) {
 +                assign(ccx.tcx, vb, locals, local_names, nvi, did, ident,
 +                       none[ty::t]);
 +            }
 +            case (_) { /* no-op */ }
 +        }
 +    }
 +
 +    auto visit =
 +        rec(visit_decl_pre=bind visit_decl_pre(ccx, vb, locals, local_names,
 +                                               nvi, _),
 +            visit_pat_pre=bind visit_pat_pre(ccx, vb, locals, local_names,
 +                                             nvi, _)
 +            with walk::default_visitor());
 +    walk::walk_block(visit, body);
 +
 +    ret rec(
 +        var_bindings=vb,
 +        locals=locals,
 +        local_names=local_names,
 +        next_var_id=*nvi
 +    );
 +}
 +
 +
  // AST fragment utilities
  
 -fn replace_expr_type(&@stmt_ctxt scx,
 +fn replace_expr_type(&@fn_ctxt fcx,
                       &@ast::expr expr,
                       &tup(vec[ty::t], ty::t) new_tyt) {
      auto new_tps;
 -    if (ty::expr_has_ty_params(scx.fcx.ccx.tcx.node_types, expr)) {
 +    if (ty::expr_has_ty_params(fcx.ccx.tcx, expr)) {
          new_tps = some[vec[ty::t]](new_tyt._0);
      } else {
          new_tps = none[vec[ty::t]];
      }
  
 -    write::ty_fixup(scx, ty::expr_ann(expr).id, tup(new_tps, new_tyt._1));
 +    write::ty_fixup(fcx, ty::expr_ann(expr).id, tup(new_tps, new_tyt._1));
 +}
 +
 +fn replace_node_type_only(&ty::ctxt tcx, uint fixup, ty::t new_t) {
 +    auto fixup_opt = tcx.node_types.(fixup);
 +    auto tps = option::get[ty::ty_param_substs_opt_and_ty](fixup_opt)._0;
 +    tcx.node_types.(fixup) =
 +        some[ty::ty_param_substs_opt_and_ty](tup(tps, new_t));
  }
  
  
  
  fn check_lit(@crate_ctxt ccx, &@ast::lit lit) -> ty::t {
      alt (lit.node) {
 -        case (ast::lit_str(_))              { ret ty::mk_str(ccx.tcx); }
 +        case (ast::lit_str(_, ast::sk_rc))     { ret ty::mk_str(ccx.tcx); }
 +        case (ast::lit_str(_, ast::sk_unique)) { ret ty::mk_istr(ccx.tcx); }
          case (ast::lit_char(_))             { ret ty::mk_char(ccx.tcx); }
          case (ast::lit_int(_))              { ret ty::mk_int(ccx.tcx);  }
          case (ast::lit_float(_))            { ret ty::mk_float(ccx.tcx);  }
          case (ast::lit_nil)                 { ret ty::mk_nil(ccx.tcx);  }
          case (ast::lit_bool(_))             { ret ty::mk_bool(ccx.tcx); }
      }
 -
 -    fail; // not reached
  }
  
 -fn check_pat(&@stmt_ctxt scx, &@ast::pat pat) {
 +// Pattern checking is top-down rather than bottom-up so that bindings get
 +// their types immediately.
 +fn check_pat(&@fn_ctxt fcx, &@ast::pat pat, ty::t expected) {
      alt (pat.node) {
          case (ast::pat_wild(?ann)) {
 -            auto typ = next_ty_var(scx);
 -            write::ty_only_fixup(scx, ann.id, typ);
 +            write::ty_only_fixup(fcx, ann.id, expected);
          }
          case (ast::pat_lit(?lt, ?ann)) {
 -            auto typ = check_lit(scx.fcx.ccx, lt);
 -            write::ty_only_fixup(scx, ann.id, typ);
 -        }
 -        case (ast::pat_bind(?id, ?def_id, ?a)) {
 -            auto typ = next_ty_var(scx);
 -            write::ty_only_fixup(scx, a.id, typ);
 -        }
 -        case (ast::pat_tag(?p, ?subpats, ?old_ann)) {
 -            auto vdef = ast::variant_def_ids
 -                (scx.fcx.ccx.tcx.def_map.get(old_ann.id));
 -            auto t = ty::lookup_item_type(scx.fcx.ccx.tcx,
 -                                          vdef._1)._1;
 -            auto len = vec::len[ast::ident](p.node.idents);
 -            auto last_id = p.node.idents.(len - 1u);
 -
 -            auto tpt = ty::lookup_item_type(scx.fcx.ccx.tcx,
 -                                            vdef._0);
 -
 -            auto path_tpot = instantiate_path(scx, p, tpt, pat.span);
 -
 -            alt (struct(scx.fcx.ccx.tcx, t)) {
 -                // N-ary variants have function types.
 -                case (ty::ty_fn(_, ?args, ?tag_ty, _)) {
 -                    auto arg_len = vec::len[arg](args);
 -                    auto subpats_len = vec::len[@ast::pat](subpats);
 -                    if (arg_len != subpats_len) {
 -                        // TODO: pluralize properly
 -                        auto err_msg = "tag type " + last_id + " has " +
 -                                       uint::to_str(arg_len, 10u) +
 -                                       " field(s), but this pattern has " +
 -                                       uint::to_str(subpats_len, 10u) +
 -                                       " field(s)";
 -
 -                        scx.fcx.ccx.tcx.sess.span_err(pat.span, err_msg);
 -                        fail;   // TODO: recover
 -                    }
 -
 -                    for (@ast::pat subpat in subpats) {
 -                        check_pat(scx, subpat);
 -                    }
 -
 -                    write::ty_fixup(scx, old_ann.id, path_tpot);
 -                }
 -
 -                // Nullary variants have tag types.
 -                case (ty::ty_tag(?tid, _)) {
 -                    auto subpats_len = vec::len[@ast::pat](subpats);
 -                    if (subpats_len > 0u) {
 -                        // TODO: pluralize properly
 -                        auto err_msg = "tag type " + last_id +
 -                                       " has no field(s)," +
 -                                       " but this pattern has " +
 -                                       uint::to_str(subpats_len, 10u) +
 -                                       " field(s)";
 -
 -                        scx.fcx.ccx.tcx.sess.span_err(pat.span, err_msg);
 -                        fail;   // TODO: recover
 -                    }
 -
 -                    write::ty_fixup(scx, old_ann.id, path_tpot);
 +            auto typ = check_lit(fcx.ccx, lt);
 +            typ = demand::simple(fcx, pat.span, expected, typ);
 +            write::ty_only_fixup(fcx, ann.id, typ);
 +        }
 +        case (ast::pat_bind(?id, ?def_id, ?ann)) {
 +            auto vid = fcx.locals.get(def_id);
 +            auto typ = ty::mk_var(fcx.ccx.tcx, vid);
 +            typ = demand::simple(fcx, pat.span, expected, typ);
 +            write::ty_only_fixup(fcx, ann.id, typ);
 +        }
 +        case (ast::pat_tag(?path, ?subpats, ?ann)) {
 +            // Typecheck the path.
 +            auto v_def = fcx.ccx.tcx.def_map.get(ann.id);
 +            auto v_def_ids = ast::variant_def_ids(v_def);
 +
 +            auto tag_tpt = ty::lookup_item_type(fcx.ccx.tcx,
 +                                                v_def_ids._0);
 +            auto path_tpot = instantiate_path(fcx, path, tag_tpt, pat.span);
 +
 +            // Take the tag type params out of `expected`.
 +            auto expected_tps;
 +            alt (structure_of(fcx, pat.span, expected)) {
 +                case (ty::ty_tag(_, ?tps)) { expected_tps = tps; }
 +                case (_) {
 +                    // FIXME: Switch expected and actual in this message? I
 +                    // can never tell.
 +                    fcx.ccx.tcx.sess.span_err(pat.span,
 +                        #fmt("mismatched types: expected tag but found %s",
 +                             ty_to_str(fcx.ccx.tcx, expected)));
 +                }
 +            }
 +
 +            // Unify with the expected tag type.
 +            auto ctor_ty = ty::ty_param_substs_opt_and_ty_to_monotype(
 +                fcx.ccx.tcx, path_tpot);
 +            auto path_tpt = demand::full(fcx, pat.span, expected, ctor_ty,
 +                                         expected_tps, NO_AUTODEREF);
 +            path_tpot = tup(some[vec[ty::t]](path_tpt._0), path_tpt._1);
 +
 +            // Get the number of arguments in this tag variant.
 +            auto arg_types = variant_arg_types(fcx.ccx, pat.span,
 +                                               v_def_ids._1, expected_tps);
 +
 +            auto subpats_len = vec::len[@ast::pat](subpats);
 +
 +            if (vec::len[ty::t](arg_types) > 0u) {
 +                // N-ary variant.
 +                auto arg_len = vec::len[ty::t](arg_types);
 +                if (arg_len != subpats_len) {
 +                    // TODO: note definition of tag variant
 +                    // TODO (issue #448): Wrap a #fmt string over multiple
 +                    // lines...
 +                    fcx.ccx.tcx.sess.span_err(pat.span, #fmt(
 +  "this pattern has %u field%s, but the corresponding variant has %u field%s",
 +                        subpats_len,
 +                        if (subpats_len == 1u) { "" } else { "s" },
 +                        arg_len,
 +                        if (arg_len == 1u) { "" } else { "s" }));
 +                }
 +
 +                // TODO: vec::iter2
 +                auto i = 0u;
 +                for (@ast::pat subpat in subpats) {
 +                    check_pat(fcx, subpat, arg_types.(i));
 +                    i += 1u;
                  }
 +            } else if (subpats_len > 0u) {
 +                // TODO: note definition of tag variant
 +                // TODO (issue #448): Wrap a #fmt string over multiple
 +                // lines...
 +                fcx.ccx.tcx.sess.span_err(pat.span, #fmt(
 +"this pattern has %u field%s, but the corresponding variant has no fields",
 +                    subpats_len,
 +                    if (subpats_len == 1u) { "" } else { "s" }));
              }
 +
 +            write::ty_fixup(fcx, ann.id, path_tpot);
          }
      }
  }
@@@ -1434,247 -1820,243 +1446,247 @@@ fn require_pure_function(@crate_ctxt cc
      }
  }
  
 -fn check_expr(&@stmt_ctxt scx, &@ast::expr expr) {
 -    // scx.fcx.ccx.tcx.sess.span_warn(expr.span, "typechecking expr " +
 +fn check_expr(&@fn_ctxt fcx, &@ast::expr expr) {
 +    // fcx.ccx.tcx.sess.span_warn(expr.span, "typechecking expr " +
      //                                pretty::pprust::expr_to_str(expr));
  
      // A generic function to factor out common logic from call and bind
      // expressions.
 -    fn check_call_or_bind(&@stmt_ctxt scx, &@ast::expr f,
 +    fn check_call_or_bind(&@fn_ctxt fcx, &span sp, &@ast::expr f,
                            &vec[option::t[@ast::expr]] args) {
          // Check the function.
 -        check_expr(scx, f);
 +        check_expr(fcx, f);
  
 -        // Check the arguments and generate the argument signature.
 -        let vec[option::t[@ast::expr]] args_0 = [];
 -        let vec[arg] arg_tys_0 = [];
 -        for (option::t[@ast::expr] a_opt in args) {
 -            alt (a_opt) {
 -                case (some(?a)) {
 -                    check_expr(scx, a);
 -                    auto typ = expr_ty(scx.fcx.ccx.tcx, a);
 -                    vec::push[arg](arg_tys_0, rec(mode=mo_either, ty=typ));
 -                }
 -                case (none) {
 -                    auto typ = next_ty_var(scx);
 -                    vec::push[arg](arg_tys_0, rec(mode=mo_either, ty=typ));
 -                }
 -            }
 -        }
 +        // Get the function type.
 +        auto fty = expr_ty(fcx.ccx.tcx, f);
  
 -        auto rt_0 = next_ty_var(scx);
 -        auto t_0;
 -        alt (struct(scx.fcx.ccx.tcx, expr_ty(scx.fcx.ccx.tcx, f))) {
 -            case (ty::ty_fn(?proto, _, _, ?cf))   {
 -                t_0 = ty::mk_fn(scx.fcx.ccx.tcx, proto, arg_tys_0, rt_0, cf);
 +        // Grab the argument types and the return type.
 +        auto arg_tys;
 +        alt (structure_of(fcx, sp, fty)) {
 +            case (ty::ty_fn(_, ?arg_tys_0, _, _, _)) {
 +                arg_tys = arg_tys_0;
              }
 -            case (ty::ty_native_fn(?abi, _, _))   {
 -                t_0 = ty::mk_native_fn(scx.fcx.ccx.tcx, abi, arg_tys_0, rt_0);
 +            case (ty::ty_native_fn(_, ?arg_tys_0, _)) {
 +                arg_tys = arg_tys_0;
              }
 -            case (?u) {
 -                scx.fcx.ccx.tcx.sess.span_err(f.span,
 -                    "check_call_or_bind(): fn expr doesn't have fn type,"
 -                    + " instead having: " +
 -                    ty_to_str(scx.fcx.ccx.tcx,
 -                              expr_ty(scx.fcx.ccx.tcx, f)));
 -                fail;
 +            case (_) {
 +                fcx.ccx.tcx.sess.span_err(f.span, "mismatched types: " +
 +                    "expected function or native function but found " +
 +                    ty_to_str(fcx.ccx.tcx, fty));
              }
          }
  
 -        // Unify the callee and arguments.
 -        auto tpt_0 = ty::expr_ty_params_and_ty(scx.fcx.ccx.tcx, f);
 -        auto tpt_1 = demand::full(scx, f.span, tpt_0._1, t_0, tpt_0._0,
 -                                  NO_AUTODEREF);
 -        replace_expr_type(scx, f, tpt_1);
 +        // Check that the correct number of arguments were supplied.
 +        auto expected_arg_count = vec::len[ty::arg](arg_tys);
 +        auto supplied_arg_count = vec::len[option::t[@ast::expr]](args);
 +        if (expected_arg_count != supplied_arg_count) {
 +            fcx.ccx.tcx.sess.span_err(sp,
 +                #fmt("this function takes %u parameter%s but %u parameter%s \
 +                     supplied",
 +                     expected_arg_count,
 +                     if (expected_arg_count == 1u) { "" } else { "s" },
 +                     supplied_arg_count,
 +                     if (supplied_arg_count == 1u) { " was" }
 +                        else { "s were" }));
 +        }
 +
 +        // Check the arguments.
 +        // TODO: iter2
 +        auto i = 0u;
 +        for (option::t[@ast::expr] a_opt in args) {
 +            alt (a_opt) {
 +                case (some(?a)) {
 +                    check_expr(fcx, a);
 +                    demand::simple(fcx, a.span, arg_tys.(i).ty,
 +                                   expr_ty(fcx.ccx.tcx, a));
 +                }
 +                case (none) { /* no-op */ }
 +            }
 +            i += 1u;
 +        }
      }
  
      // A generic function for checking assignment expressions
 -    fn check_assignment(&@stmt_ctxt scx, &@ast::expr lhs, &@ast::expr rhs,
 -                        &ast::ann a) {
 -        check_expr(scx, lhs);
 -        check_expr(scx, rhs);
 -        auto lhs_t0 = expr_ty(scx.fcx.ccx.tcx, lhs);
 -        auto rhs_t0 = expr_ty(scx.fcx.ccx.tcx, rhs);
 -
 -        pushdown::pushdown_expr(scx, rhs_t0, lhs);
 -        auto lhs_t1 = expr_ty(scx.fcx.ccx.tcx, lhs);
 -        pushdown::pushdown_expr(scx, lhs_t1, rhs);
 -        auto rhs_t1 = expr_ty(scx.fcx.ccx.tcx, rhs);
 -
 -        write::ty_only_fixup(scx, a.id, rhs_t1);
 +    fn check_assignment(&@fn_ctxt fcx, &span sp, &@ast::expr lhs,
 +                        &@ast::expr rhs, &ast::ann a) {
 +        check_expr(fcx, lhs);
 +        check_expr(fcx, rhs);
 +        auto typ = demand::simple(fcx, sp,
 +                                  expr_ty(fcx.ccx.tcx, lhs),
 +                                  expr_ty(fcx.ccx.tcx, rhs));
 +        write::ty_only_fixup(fcx, a.id, typ);
      }
  
      // A generic function for checking call expressions
 -    fn check_call(&@stmt_ctxt scx, &@ast::expr f, &vec[@ast::expr] args) {
 +    fn check_call(&@fn_ctxt fcx, &span sp, &@ast::expr f,
 +                  &vec[@ast::expr] args) {
          let vec[option::t[@ast::expr]] args_opt_0 = [];
          for (@ast::expr arg in args) {
              args_opt_0 += [some[@ast::expr](arg)];
          }
  
          // Call the generic checker.
 -        check_call_or_bind(scx, f, args_opt_0);
 +        check_call_or_bind(fcx, sp, f, args_opt_0);
      }
  
      // A generic function for checking for or for-each loops
 -    fn check_for_or_for_each(&@stmt_ctxt scx, &@ast::decl decl,
 +    fn check_for_or_for_each(&@fn_ctxt fcx, &@ast::decl decl,
                               &ty::t element_ty, &ast::block body,
                               uint node_id) {
 -        check_decl_local(scx.fcx, decl);
 -        check_block(scx, body);
 +        check_decl_local(fcx, decl);
 +        check_block(fcx, body);
  
          // Unify type of decl with element type of the seq
 -        demand::simple(scx, decl.span, ty::decl_local_ty(scx.fcx.ccx.tcx,
 +        demand::simple(fcx, decl.span, ty::decl_local_ty(fcx.ccx.tcx,
                                                           decl),
                         element_ty);
          
 -        auto typ = ty::mk_nil(scx.fcx.ccx.tcx);
 -        write::ty_only_fixup(scx, node_id, typ);
 +        auto typ = ty::mk_nil(fcx.ccx.tcx);
 +        write::ty_only_fixup(fcx, node_id, typ);
      }
  
      alt (expr.node) {
          case (ast::expr_lit(?lit, ?a)) {
 -            auto typ = check_lit(scx.fcx.ccx, lit);
 -            write::ty_only_fixup(scx, a.id, typ);
 +            auto typ = check_lit(fcx.ccx, lit);
 +            write::ty_only_fixup(fcx, a.id, typ);
          }
  
          case (ast::expr_binary(?binop, ?lhs, ?rhs, ?a)) {
 -            check_expr(scx, lhs);
 -            check_expr(scx, rhs);
 -            auto lhs_t0 = expr_ty(scx.fcx.ccx.tcx, lhs);
 -            auto rhs_t0 = expr_ty(scx.fcx.ccx.tcx, rhs);
 +            check_expr(fcx, lhs);
 +            check_expr(fcx, rhs);
  
 -            // FIXME: Binops have a bit more subtlety than this.
 -            pushdown::pushdown_expr_full(scx, rhs_t0, lhs, AUTODEREF_OK);
 -            auto lhs_t1 = expr_ty(scx.fcx.ccx.tcx, lhs);
 -            pushdown::pushdown_expr_full(scx, lhs_t1, rhs, AUTODEREF_OK);
 +            auto lhs_t = expr_ty(fcx.ccx.tcx, lhs);
  
 -            auto t = strip_boxes(scx.fcx.ccx.tcx, lhs_t0);
 +            // FIXME: Binops have a bit more subtlety than this.
 +            auto t = strip_boxes(fcx, expr.span, lhs_t);
              alt (binop) {
 -                case (ast::eq) { t = ty::mk_bool(scx.fcx.ccx.tcx); }
 -                case (ast::lt) { t = ty::mk_bool(scx.fcx.ccx.tcx); }
 -                case (ast::le) { t = ty::mk_bool(scx.fcx.ccx.tcx); }
 -                case (ast::ne) { t = ty::mk_bool(scx.fcx.ccx.tcx); }
 -                case (ast::ge) { t = ty::mk_bool(scx.fcx.ccx.tcx); }
 -                case (ast::gt) { t = ty::mk_bool(scx.fcx.ccx.tcx); }
 +                case (ast::eq) { t = ty::mk_bool(fcx.ccx.tcx); }
 +                case (ast::lt) { t = ty::mk_bool(fcx.ccx.tcx); }
 +                case (ast::le) { t = ty::mk_bool(fcx.ccx.tcx); }
 +                case (ast::ne) { t = ty::mk_bool(fcx.ccx.tcx); }
 +                case (ast::ge) { t = ty::mk_bool(fcx.ccx.tcx); }
 +                case (ast::gt) { t = ty::mk_bool(fcx.ccx.tcx); }
                  case (_) { /* fall through */ }
              }
  
 -            write::ty_only_fixup(scx, a.id, t);
 +            write::ty_only_fixup(fcx, a.id, t);
          }
  
          case (ast::expr_unary(?unop, ?oper, ?a)) {
 -            check_expr(scx, oper);
 +            check_expr(fcx, oper);
  
 -            auto oper_t = expr_ty(scx.fcx.ccx.tcx, oper);
 +            auto oper_t = expr_ty(fcx.ccx.tcx, oper);
              alt (unop) {
                  case (ast::box(?mut)) {
 -                    oper_t = ty::mk_box(scx.fcx.ccx.tcx,
 +                    oper_t = ty::mk_box(fcx.ccx.tcx,
                                          rec(ty=oper_t, mut=mut));
                  }
                  case (ast::deref) {
 -                    alt (struct(scx.fcx.ccx.tcx, oper_t)) {
 +                    alt (structure_of(fcx, expr.span, oper_t)) {
                          case (ty::ty_box(?inner)) { oper_t = inner.ty; }
                          case (_) {
 -                            scx.fcx.ccx.tcx.sess.span_err
 +                            fcx.ccx.tcx.sess.span_err
                                  (expr.span,
                                   "dereferencing non-box type: "
 -                                 + ty_to_str(scx.fcx.ccx.tcx, oper_t));
 +                                 + ty_to_str(fcx.ccx.tcx, oper_t));
                          }
                      }
                  }
 -                case (_) { oper_t = strip_boxes(scx.fcx.ccx.tcx, oper_t); }
 +                case (ast::not) {
 +                    if (!type_is_integral(fcx, oper.span, oper_t) &&
 +                            structure_of(fcx, oper.span, oper_t)
 +                                != ty::ty_bool) {
 +                        fcx.ccx.tcx.sess.span_err(expr.span,
 +                            #fmt("mismatched types: expected bool or \
 +                            integer but found %s",
 +                            ty_to_str(fcx.ccx.tcx, oper_t)));
 +                    }
 +                }
 +                case (_) { oper_t = strip_boxes(fcx, expr.span, oper_t); }
              }
  
 -            write::ty_only_fixup(scx, a.id, oper_t);
 +            write::ty_only_fixup(fcx, a.id, oper_t);
          }
  
          case (ast::expr_path(?pth, ?old_ann)) {
 -            auto t = ty::mk_nil(scx.fcx.ccx.tcx);
 -            auto defn = scx.fcx.ccx.tcx.def_map.get(old_ann.id);
 +            auto t = ty::mk_nil(fcx.ccx.tcx);
 +            auto defn = fcx.ccx.tcx.def_map.get(old_ann.id);
  
 -            auto tpt = ty_param_count_and_ty_for_def(scx.fcx, expr.span,
 +            auto tpt = ty_param_count_and_ty_for_def(fcx, expr.span,
                                                       defn);
  
              if (ty::def_has_ty_params(defn)) {
 -                auto path_tpot = instantiate_path(scx, pth, tpt, expr.span);
 -                write::ty_fixup(scx, old_ann.id, path_tpot);
 +                auto path_tpot = instantiate_path(fcx, pth, tpt, expr.span);
 +                write::ty_fixup(fcx, old_ann.id, path_tpot);
                  ret;
              }
  
              // The definition doesn't take type parameters. If the programmer
              // supplied some, that's an error.
              if (vec::len[@ast::ty](pth.node.types) > 0u) {
 -                scx.fcx.ccx.tcx.sess.span_err(expr.span,
 +                fcx.ccx.tcx.sess.span_err(expr.span,
                      "this kind of value does not take type parameters");
 -                fail;
              }
  
 -            write::ty_only_fixup(scx, old_ann.id, tpt._1);
 +            write::ty_only_fixup(fcx, old_ann.id, tpt._1);
          }
  
          case (ast::expr_ext(?p, ?args, ?body, ?expanded, ?a)) {
 -            check_expr(scx, expanded);
 -            auto t = expr_ty(scx.fcx.ccx.tcx, expanded);
 -            write::ty_only_fixup(scx, a.id, t);
 +            check_expr(fcx, expanded);
 +            auto t = expr_ty(fcx.ccx.tcx, expanded);
 +            write::ty_only_fixup(fcx, a.id, t);
          }
  
 -        case (ast::expr_fail(?a)) {
 -            write::bot_ty(scx.fcx.ccx.tcx, a.id);
 +        case (ast::expr_fail(?a, _)) {
 +            write::bot_ty(fcx.ccx.tcx, a.id);
          }
  
          case (ast::expr_break(?a)) {
 -            write::bot_ty(scx.fcx.ccx.tcx, a.id);
 +            write::bot_ty(fcx.ccx.tcx, a.id);
          }
  
          case (ast::expr_cont(?a)) {
 -            write::bot_ty(scx.fcx.ccx.tcx, a.id);
 +            write::bot_ty(fcx.ccx.tcx, a.id);
          }
  
          case (ast::expr_ret(?expr_opt, ?a)) {
              alt (expr_opt) {
                  case (none) {
 -                    auto nil = ty::mk_nil(scx.fcx.ccx.tcx);
 -                    if (!are_compatible(scx, scx.fcx.ret_ty, nil)) {
 -                        // TODO: span_err
 -                        scx.fcx.ccx.tcx.sess.err("ret; in function " +
 -                            "returning non-nil");
 +                    auto nil = ty::mk_nil(fcx.ccx.tcx);
 +                    if (!are_compatible(fcx, fcx.ret_ty, nil)) {
 +                        fcx.ccx.tcx.sess.span_err(expr.span,
 +                          "ret; in function returning non-nil");
                      }
  
 -                    write::bot_ty(scx.fcx.ccx.tcx, a.id);
 +                    write::bot_ty(fcx.ccx.tcx, a.id);
                  }
  
                  case (some(?e)) {
 -                    check_expr(scx, e);
 -                    pushdown::pushdown_expr(scx, scx.fcx.ret_ty, e);
 -
 -                    write::bot_ty(scx.fcx.ccx.tcx, a.id);
 +                    check_expr(fcx, e);
 +                    demand::simple(fcx, expr.span, fcx.ret_ty,
 +                                   expr_ty(fcx.ccx.tcx, e));
 +                    write::bot_ty(fcx.ccx.tcx, a.id);
                  }
              }
          }
  
          case (ast::expr_put(?expr_opt, ?a)) {
 -            require_impure(scx.fcx.ccx.tcx.sess, scx.fcx.purity, expr.span);
 +            require_impure(fcx.ccx.tcx.sess, fcx.purity, expr.span);
  
              alt (expr_opt) {
                  case (none) {
 -                    auto nil = ty::mk_nil(scx.fcx.ccx.tcx);
 -                    if (!are_compatible(scx, scx.fcx.ret_ty, nil)) {
 -                         scx.fcx.ccx.tcx.sess.span_err(expr.span,
 +                    auto nil = ty::mk_nil(fcx.ccx.tcx);
 +                    if (!are_compatible(fcx, fcx.ret_ty, nil)) {
 +                         fcx.ccx.tcx.sess.span_err(expr.span,
                              "put; in iterator yielding non-nil");
                      }
  
 -                    write::nil_ty(scx.fcx.ccx.tcx, a.id);
 +                    write::nil_ty(fcx.ccx.tcx, a.id);
                  }
  
                  case (some(?e)) {
 -                    check_expr(scx, e);
 -                    pushdown::pushdown_expr(scx, scx.fcx.ret_ty, e);
 -
 -                    write::nil_ty(scx.fcx.ccx.tcx, a.id);
 +                    check_expr(fcx, e);
 +                    write::nil_ty(fcx.ccx.tcx, a.id);
                  }
              }
          }
              // FIXME: prove instead of assert
              assert (ast::is_call_expr(e));
  
 -            check_expr(scx, e);
 -            pushdown::pushdown_expr(scx, scx.fcx.ret_ty, e);
 +            check_expr(fcx, e);
 +            demand::simple(fcx, e.span, fcx.ret_ty, expr_ty(fcx.ccx.tcx, e));
  
 -            write::nil_ty(scx.fcx.ccx.tcx, a.id);
 +            write::nil_ty(fcx.ccx.tcx, a.id);
          }
  
          case (ast::expr_log(?l, ?e, ?a)) {
 -            auto expr_t = check_expr(scx, e);
 -            write::nil_ty(scx.fcx.ccx.tcx, a.id);
 +            auto expr_t = check_expr(fcx, e);
 +            write::nil_ty(fcx.ccx.tcx, a.id);
          }
  
          case (ast::expr_check(?e, ?a)) {
 -            check_expr(scx, e);
 -            demand::simple(scx, expr.span, ty::mk_bool(scx.fcx.ccx.tcx),
 -                expr_ty(scx.fcx.ccx.tcx, e));
 +            check_expr(fcx, e);
 +            demand::simple(fcx, expr.span, ty::mk_bool(fcx.ccx.tcx),
 +                expr_ty(fcx.ccx.tcx, e));
              /* e must be a call expr where all arguments are either
               literals or slots */
              alt (e.node) {
                      alt (operator.node) {
                          case (ast::expr_path(?oper_name, ?ann)) {
                              auto d_id;
 -                            alt (scx.fcx.ccx.tcx.def_map.get(ann.id)) {
 +                            alt (fcx.ccx.tcx.def_map.get(ann.id)) {
                                  case (ast::def_fn(?_d_id)) { d_id = _d_id; }
                              }
                              for (@ast::expr operand in operands) {
                                  if (! ast::is_constraint_arg(operand)) {
 -                                    scx.fcx.ccx.tcx.sess.span_err(expr.span,
 +                                    fcx.ccx.tcx.sess.span_err(expr.span,
                                         "Constraint args must be "
                                       + "slot variables or literals");
                                  }
                              }
  
 -                            require_pure_function(scx.fcx.ccx, d_id,
 +                            require_pure_function(fcx.ccx, d_id,
                                                    expr.span);
  
 -                            write::nil_ty(scx.fcx.ccx.tcx, a.id);
 +                            write::nil_ty(fcx.ccx.tcx, a.id);
                          }
                          case (_) {
 -                           scx.fcx.ccx.tcx.sess.span_err(expr.span,
 +                           fcx.ccx.tcx.sess.span_err(expr.span,
                               "In a constraint, expected the constraint name "
                             + "to be an explicit name");
                          }
                      }
                  }
                  case (_) {
 -                    scx.fcx.ccx.tcx.sess.span_err(expr.span,
 +                    fcx.ccx.tcx.sess.span_err(expr.span,
                          "check on non-predicate");
                  }
              }
          }
  
          case (ast::expr_assert(?e, ?a)) {
 -            check_expr(scx, e);
 -            auto ety = expr_ty(scx.fcx.ccx.tcx, e);
 -            demand::simple(scx, expr.span, ty::mk_bool(scx.fcx.ccx.tcx), ety);
 +            check_expr(fcx, e);
 +            auto ety = expr_ty(fcx.ccx.tcx, e);
 +            demand::simple(fcx, expr.span, ty::mk_bool(fcx.ccx.tcx), ety);
  
 -            write::nil_ty(scx.fcx.ccx.tcx, a.id);
 +            write::nil_ty(fcx.ccx.tcx, a.id);
          }
  
          case (ast::expr_move(?lhs, ?rhs, ?a)) {
 -            require_impure(scx.fcx.ccx.tcx.sess, scx.fcx.purity, expr.span);
 -            check_assignment(scx, lhs, rhs, a);
 +            require_impure(fcx.ccx.tcx.sess, fcx.purity, expr.span);
 +            check_assignment(fcx, expr.span, lhs, rhs, a);
          }
  
          case (ast::expr_assign(?lhs, ?rhs, ?a)) {
 -            require_impure(scx.fcx.ccx.tcx.sess, scx.fcx.purity, expr.span);
 -            check_assignment(scx, lhs, rhs, a);
 +            require_impure(fcx.ccx.tcx.sess, fcx.purity, expr.span);
 +            check_assignment(fcx, expr.span, lhs, rhs, a);
          }
  
          case (ast::expr_assign_op(?op, ?lhs, ?rhs, ?a)) {
 -            require_impure(scx.fcx.ccx.tcx.sess, scx.fcx.purity, expr.span);
 -            check_assignment(scx, lhs, rhs, a);
 +            require_impure(fcx.ccx.tcx.sess, fcx.purity, expr.span);
 +            check_assignment(fcx, expr.span, lhs, rhs, a);
          }
  
          case (ast::expr_send(?lhs, ?rhs, ?a)) {
 -            require_impure(scx.fcx.ccx.tcx.sess, scx.fcx.purity, expr.span);
 +            require_impure(fcx.ccx.tcx.sess, fcx.purity, expr.span);
  
 -            check_expr(scx, lhs);
 -            check_expr(scx, rhs);
 -            auto rhs_t = expr_ty(scx.fcx.ccx.tcx, rhs);
 +            check_expr(fcx, lhs);
 +            check_expr(fcx, rhs);
 +            auto rhs_t = expr_ty(fcx.ccx.tcx, rhs);
 +
 +            auto chan_t = ty::mk_chan(fcx.ccx.tcx, rhs_t);
  
 -            auto chan_t = ty::mk_chan(scx.fcx.ccx.tcx, rhs_t);
 -            pushdown::pushdown_expr(scx, chan_t, lhs);
              auto item_t;
 -            auto lhs_t = expr_ty(scx.fcx.ccx.tcx, lhs);
 -            alt (struct(scx.fcx.ccx.tcx, lhs_t)) {
 +            auto lhs_t = expr_ty(fcx.ccx.tcx, lhs);
 +            alt (structure_of(fcx, expr.span, lhs_t)) {
                  case (ty::ty_chan(?it)) { item_t = it; }
 -                case (_) { fail; }
 +                case (_) {
 +                    fcx.ccx.tcx.sess.span_err(expr.span,
 +                        #fmt("mismatched types: expected chan but found %s",
 +                             ty_to_str(fcx.ccx.tcx, lhs_t)));
 +                }
              }
 -            pushdown::pushdown_expr(scx, item_t, rhs);
  
 -            write::ty_only_fixup(scx, a.id, chan_t);
 +            write::ty_only_fixup(fcx, a.id, chan_t);
          }
  
          case (ast::expr_recv(?lhs, ?rhs, ?a)) {
 -            require_impure(scx.fcx.ccx.tcx.sess, scx.fcx.purity, expr.span);
 +            require_impure(fcx.ccx.tcx.sess, fcx.purity, expr.span);
  
 -            check_expr(scx, lhs);
 -            check_expr(scx, rhs);
 -            auto lhs_t1 = expr_ty(scx.fcx.ccx.tcx, lhs);
 +            check_expr(fcx, lhs);
 +            check_expr(fcx, rhs);
  
 -            auto port_t = ty::mk_port(scx.fcx.ccx.tcx, lhs_t1);
 -            pushdown::pushdown_expr(scx, port_t, rhs);
 -            auto item_t;
 -            auto rhs_t = expr_ty(scx.fcx.ccx.tcx, rhs);
 -            alt (struct(scx.fcx.ccx.tcx, rhs_t)) {
 -                case (ty::ty_port(?it)) { item_t = it; }
 -                case (_) { fail; }
 -            }
 -            pushdown::pushdown_expr(scx, item_t, lhs);
 +            auto item_t = expr_ty(fcx.ccx.tcx, lhs);
 +            auto port_t = ty::mk_port(fcx.ccx.tcx, item_t);
 +            demand::simple(fcx, expr.span, port_t, expr_ty(fcx.ccx.tcx, rhs));
  
 -            write::ty_only_fixup(scx, a.id, item_t);
 +            write::ty_only_fixup(fcx, a.id, item_t);
          }
  
          case (ast::expr_if(?cond, ?thn, ?elsopt, ?a)) {
 -            check_expr(scx, cond);
 -            pushdown::pushdown_expr(scx, ty::mk_bool(scx.fcx.ccx.tcx),
 -                                    cond);
 -
 -            check_block(scx, thn);
 +            check_expr(fcx, cond);
 +            check_block(fcx, thn);
  
              auto if_t = alt (elsopt) {
                  case (some(?els)) {
 -                    check_expr(scx, els);
 +                    check_expr(fcx, els);
  
 -                    auto thn_t = block_ty(scx.fcx.ccx.tcx, thn);
 -                    auto elsopt_t = expr_ty(scx.fcx.ccx.tcx, els);
 -                    if (!ty::type_is_bot(scx.fcx.ccx.tcx, elsopt_t)) {
 +                    auto thn_t = block_ty(fcx.ccx.tcx, thn);
 +                    auto elsopt_t = expr_ty(fcx.ccx.tcx, els);
 +
 +                    demand::simple(fcx, expr.span, thn_t, elsopt_t);
 +
 +                    if (!ty::type_is_bot(fcx.ccx.tcx, elsopt_t)) {
                          elsopt_t
                      } else {
                          thn_t
                      }
                  }
                  case (none) {
 -                    ty::mk_nil(scx.fcx.ccx.tcx)
 +                    ty::mk_nil(fcx.ccx.tcx)
                  }
              };
  
 -            write::ty_only_fixup(scx, a.id, if_t);
 +            write::ty_only_fixup(fcx, a.id, if_t);
          }
  
          case (ast::expr_for(?decl, ?seq, ?body, ?a)) {
 -            check_expr(scx, seq);
 -            alt (struct (scx.fcx.ccx.tcx,
 -                         expr_ty(scx.fcx.ccx.tcx, seq))) {
 +            check_expr(fcx, seq);
 +            alt (structure_of(fcx, expr.span, expr_ty(fcx.ccx.tcx, seq))) {
                  // FIXME: I include the check_for_or_each call in 
                  // each case because of a bug in typestate.
                  // The bug is fixed; once there's a new snapshot,
                  // the call can be moved out of the alt expression
                  case (ty::ty_vec(?vec_elt_ty)) {
                      auto elt_ty = vec_elt_ty.ty;
 -                    check_for_or_for_each(scx, decl, elt_ty, body, a.id);
 +                    check_for_or_for_each(fcx, decl, elt_ty, body, a.id);
                  }
                  case (ty::ty_str) {
 -                    auto elt_ty = ty::mk_mach(scx.fcx.ccx.tcx, 
 +                    auto elt_ty = ty::mk_mach(fcx.ccx.tcx, 
                                           util::common::ty_u8);
 -                    check_for_or_for_each(scx, decl, elt_ty, body, a.id);
 +                    check_for_or_for_each(fcx, decl, elt_ty, body, a.id);
                  }
                  case (_) {
 -                    scx.fcx.ccx.tcx.sess.span_err(expr.span,
 +                    fcx.ccx.tcx.sess.span_err(expr.span,
                        "type of for loop iterator is not a vector or string");
                  }
              }
          }
  
          case (ast::expr_for_each(?decl, ?seq, ?body, ?a)) {
 -            check_expr(scx, seq);
 -            check_for_or_for_each(scx, decl, expr_ty(scx.fcx.ccx.tcx, seq),
 +            check_expr(fcx, seq);
 +            check_for_or_for_each(fcx, decl, expr_ty(fcx.ccx.tcx, seq),
                                    body, a.id);
          }
  
          case (ast::expr_while(?cond, ?body, ?a)) {
 -            check_expr(scx, cond);
 -            pushdown::pushdown_expr(scx, ty::mk_bool(scx.fcx.ccx.tcx), cond);
 -            check_block(scx, body);
 +            check_expr(fcx, cond);
 +            check_block(fcx, body);
 +
 +            demand::simple(fcx, cond.span, ty::mk_bool(fcx.ccx.tcx),
 +                           expr_ty(fcx.ccx.tcx, cond));
  
 -            auto typ = ty::mk_nil(scx.fcx.ccx.tcx);
 -            write::ty_only_fixup(scx, a.id, typ);
 +            auto typ = ty::mk_nil(fcx.ccx.tcx);
 +            write::ty_only_fixup(fcx, a.id, typ);
          }
  
          case (ast::expr_do_while(?body, ?cond, ?a)) {
 -            check_expr(scx, cond);
 -            pushdown::pushdown_expr(scx, ty::mk_bool(scx.fcx.ccx.tcx), cond);
 -            check_block(scx, body);
 +            check_expr(fcx, cond);
 +            check_block(fcx, body);
  
 -            auto typ = block_ty(scx.fcx.ccx.tcx, body);
 -            write::ty_only_fixup(scx, a.id, typ);
 +            auto typ = block_ty(fcx.ccx.tcx, body);
 +            write::ty_only_fixup(fcx, a.id, typ);
          }
  
          case (ast::expr_alt(?expr, ?arms, ?a)) {
 -            check_expr(scx, expr);
 +            check_expr(fcx, expr);
  
              // Typecheck the patterns first, so that we get types for all the
              // bindings.
 -            auto pattern_ty = expr_ty(scx.fcx.ccx.tcx, expr);
 +            auto pattern_ty = ty::expr_ty(fcx.ccx.tcx, expr);
  
              let vec[@ast::pat] pats = [];
              for (ast::arm arm in arms) {
 -                check_pat(scx, arm.pat);
 -                pattern_ty = demand::simple(scx, arm.pat.span, pattern_ty,
 -                    pat_ty(scx.fcx.ccx.tcx, arm.pat));
 +                check_pat(fcx, arm.pat, pattern_ty);
                  pats += [arm.pat];
              }
  
 -            for (@ast::pat pat in pats) {
 -                pushdown::pushdown_pat(scx, pattern_ty, pat);
 -            }
 -
              // Now typecheck the blocks.
 -            auto result_ty = next_ty_var(scx);
 +            auto result_ty = next_ty_var(fcx);
  
              let vec[ast::block] blocks = [];
              for (ast::arm arm in arms) {
 -                check_block(scx, arm.block);
 +                check_block(fcx, arm.block);
  
 -                auto bty = block_ty(scx.fcx.ccx.tcx, arm.block);
 +                auto bty = block_ty(fcx.ccx.tcx, arm.block);
                  // Failing alt arms don't need to have a matching type
 -                if (!ty::type_is_bot(scx.fcx.ccx.tcx, bty)) {
 -                    result_ty = demand::simple(scx, arm.block.span,
 +                if (!ty::type_is_bot(fcx.ccx.tcx, bty)) {
 +                    result_ty = demand::simple(fcx, arm.block.span,
                                                 result_ty, bty);
                  }
              }
  
 -            auto i = 0u;
 -            for (ast::block bloc in blocks) {
 -                pushdown::pushdown_block(scx, result_ty, bloc);
 -            }
 -
 -            pushdown::pushdown_expr(scx, pattern_ty, expr);
 -
 -            write::ty_only_fixup(scx, a.id, result_ty);
 +            write::ty_only_fixup(fcx, a.id, result_ty);
          }
  
          case (ast::expr_block(?b, ?a)) {
 -            check_block(scx, b);
 +            check_block(fcx, b);
              alt (b.node.expr) {
                  case (some(?expr)) {
 -                    auto typ = expr_ty(scx.fcx.ccx.tcx, expr);
 -                    write::ty_only_fixup(scx, a.id, typ);
 +                    auto typ = expr_ty(fcx.ccx.tcx, expr);
 +                    write::ty_only_fixup(fcx, a.id, typ);
                  }
                  case (none) {
 -                    auto typ = ty::mk_nil(scx.fcx.ccx.tcx);
 -                    write::ty_only_fixup(scx, a.id, typ);
 +                    auto typ = ty::mk_nil(fcx.ccx.tcx);
 +                    write::ty_only_fixup(fcx, a.id, typ);
                  }
              }
          }
  
          case (ast::expr_bind(?f, ?args, ?a)) {
              // Call the generic checker.
 -            check_call_or_bind(scx, f, args);
 +            check_call_or_bind(fcx, expr.span, f, args);
  
              // Pull the argument and return types out.
              auto proto_1;
              let vec[ty::arg] arg_tys_1 = [];
              auto rt_1;
 -            auto fty = expr_ty(scx.fcx.ccx.tcx, f);
 +            auto fty = expr_ty(fcx.ccx.tcx, f);
              auto t_1;
 -            alt (struct(scx.fcx.ccx.tcx, fty)) {
 -                case (ty::ty_fn(?proto, ?arg_tys, ?rt, ?cf)) {
 +            alt (structure_of(fcx, expr.span, fty)) {
 +                case (ty::ty_fn(?proto, ?arg_tys, ?rt, ?cf, ?constrs)) {
                      proto_1 = proto;
                      rt_1 = rt;
  
 +                    // FIXME:
 +                    // probably need to munge the constrs to drop constraints
 +                    // for any bound args
 +
                      // For each blank argument, add the type of that argument
                      // to the resulting function type.
                      auto i = 0u;
                          }
                          i += 1u;
                      }
 -                    t_1 = ty::mk_fn(scx.fcx.ccx.tcx, proto_1, arg_tys_1, rt_1,
 -                                    cf);
 +                    t_1 = ty::mk_fn(fcx.ccx.tcx, proto_1, arg_tys_1, rt_1,
 +                                    cf, constrs);
                  }
                  case (_) {
                      log_err "LHS of bind expr didn't have a function type?!";
                      fail;
                  }
              }
 -            write::ty_only_fixup(scx, a.id, t_1);
 +            write::ty_only_fixup(fcx, a.id, t_1);
          }
  
          case (ast::expr_call(?f, ?args, ?a)) {
               need to restrict it to being an explicit expr_path if we're
              inside a pure function, and need an environment mapping from
              function name onto purity-designation */
 -            require_pure_call(scx.fcx.ccx, scx.fcx.purity, f, expr.span);
 +            require_pure_call(fcx.ccx, fcx.purity, f, expr.span);
  
 -            check_call(scx, f, args);
 +            check_call(fcx, expr.span, f, args);
  
              // Pull the return type out of the type of the function.
 -            auto rt_1 = ty::mk_nil(scx.fcx.ccx.tcx); // FIXME: typestate botch
 -            auto fty = expr_ty(scx.fcx.ccx.tcx, f);
 -            alt (struct(scx.fcx.ccx.tcx, fty)) {
 -                case (ty::ty_fn(_,_,?rt,_))         { rt_1 = rt; }
 +            auto rt_1;
 +            auto fty = ty::expr_ty(fcx.ccx.tcx, f);
 +            alt (structure_of(fcx, expr.span, fty)) {
 +                case (ty::ty_fn(_,_,?rt,_, _))         { rt_1 = rt; }
                  case (ty::ty_native_fn(_, _, ?rt))  { rt_1 = rt; }
                  case (_) {
                      log_err "LHS of call expr didn't have a function type?!";
                  }
              }
  
 -            write::ty_only_fixup(scx, a.id, rt_1);
 +            write::ty_only_fixup(fcx, a.id, rt_1);
          }
  
          case (ast::expr_self_method(?id, ?a)) {
 -            auto t = ty::mk_nil(scx.fcx.ccx.tcx);
 +            auto t = ty::mk_nil(fcx.ccx.tcx);
              let ty::t this_obj_ty;
  
 -            let option::t[obj_info] this_obj_info = get_obj_info(scx.fcx.ccx);
 +            let option::t[obj_info] this_obj_info = get_obj_info(fcx.ccx);
  
              alt (this_obj_info) {
                  // If we're inside a current object, grab its type.
                      // FIXME: In the case of anonymous objects with methods
                      // containing self-calls, this lookup fails because
                      // obj_info.this_obj is not in the type cache
 -                    this_obj_ty = ty::lookup_item_type(scx.fcx.ccx.tcx, 
 +                    this_obj_ty = ty::lookup_item_type(fcx.ccx.tcx, 
                                                         obj_info.this_obj)._1;
                  }
  
              }
  
              // Grab this method's type out of the current object type.
 -            alt (struct(scx.fcx.ccx.tcx, this_obj_ty)) {
 +            alt (structure_of(fcx, expr.span, this_obj_ty)) {
                  case (ty::ty_obj(?methods)) {
                      for (ty::method method in methods) {
                          if (method.ident == id) {
 -                            t = ty::method_ty_to_fn_ty(scx.fcx.ccx.tcx,
 +                            t = ty::method_ty_to_fn_ty(fcx.ccx.tcx,
                                                         method);
                          }
                      }
                  case (_) { fail; }
              }
  
 -            write::ty_only_fixup(scx, a.id, t);
 +            write::ty_only_fixup(fcx, a.id, t);
  
 -            require_impure(scx.fcx.ccx.tcx.sess, scx.fcx.purity, expr.span);
 +            require_impure(fcx.ccx.tcx.sess, fcx.purity, expr.span);
          }
  
          case (ast::expr_spawn(_, _, ?f, ?args, ?a)) {
 -            check_call(scx, f, args);
 +            check_call(fcx, expr.span, f, args);
  
 -            auto fty = expr_ty(scx.fcx.ccx.tcx, f);
 -            auto ret_ty = ty::ret_ty_of_fn_ty(scx.fcx.ccx.tcx, fty);
 +            auto fty = expr_ty(fcx.ccx.tcx, f);
 +            auto ret_ty = ty::ret_ty_of_fn_ty(fcx.ccx.tcx, fty);
  
 -            demand::simple(scx, f.span, ty::mk_nil(scx.fcx.ccx.tcx), ret_ty);
 +            demand::simple(fcx, f.span, ty::mk_nil(fcx.ccx.tcx), ret_ty);
  
              // FIXME: Other typechecks needed
  
 -            auto typ = ty::mk_task(scx.fcx.ccx.tcx);
 -            write::ty_only_fixup(scx, a.id, typ);
 +            auto typ = ty::mk_task(fcx.ccx.tcx);
 +            write::ty_only_fixup(fcx, a.id, typ);
          }
  
          case (ast::expr_cast(?e, ?t, ?a)) {
 -            check_expr(scx, e);
 -            auto t_1 = ast_ty_to_ty_crate(scx.fcx.ccx, t);
 +            check_expr(fcx, e);
 +            auto t_1 = ast_ty_to_ty_crate(fcx.ccx, t);
              // FIXME: there are more forms of cast to support, eventually.
 -            if (! (type_is_scalar(scx.fcx.ccx.tcx,
 -                    expr_ty(scx.fcx.ccx.tcx, e)) &&
 -                    type_is_scalar(scx.fcx.ccx.tcx, t_1))) {
 -                scx.fcx.ccx.tcx.sess.span_err(expr.span,
 +            if (! (type_is_scalar(fcx, expr.span, expr_ty(fcx.ccx.tcx, e)) &&
 +                   type_is_scalar(fcx, expr.span, t_1))) {
 +                fcx.ccx.tcx.sess.span_err(expr.span,
                      "non-scalar cast: " +
 -                    ty_to_str(scx.fcx.ccx.tcx,
 -                        expr_ty(scx.fcx.ccx.tcx, e)) +
 -                    " as " + ty_to_str(scx.fcx.ccx.tcx, t_1));
 +                    ty_to_str(fcx.ccx.tcx,
 +                        expr_ty(fcx.ccx.tcx, e)) +
 +                    " as " + ty_to_str(fcx.ccx.tcx, t_1));
              }
  
 -            write::ty_only_fixup(scx, a.id, t_1);
 +            write::ty_only_fixup(fcx, a.id, t_1);
          }
  
 -        case (ast::expr_vec(?args, ?mut, ?a)) {
 +        case (ast::expr_vec(?args, ?mut, ?kind, ?a)) {
              let ty::t t;
              if (vec::len[@ast::expr](args) == 0u) {
 -                t = next_ty_var(scx);
 +                t = next_ty_var(fcx);
              } else {
 -                check_expr(scx, args.(0));
 -                t = expr_ty(scx.fcx.ccx.tcx, args.(0));
 +                check_expr(fcx, args.(0));
 +                t = expr_ty(fcx.ccx.tcx, args.(0));
              }
  
              for (@ast::expr e in args) {
 -                check_expr(scx, e);
 -                auto expr_t = expr_ty(scx.fcx.ccx.tcx, e);
 -                demand::simple(scx, expr.span, t, expr_t);
 +                check_expr(fcx, e);
 +                auto expr_t = expr_ty(fcx.ccx.tcx, e);
 +                demand::simple(fcx, expr.span, t, expr_t);
 +            }
 +
 +            auto typ;
 +            alt (kind) {
 +                case (ast::sk_rc) {
 +                    typ = ty::mk_vec(fcx.ccx.tcx, rec(ty=t, mut=mut));
 +                }
 +                case (ast::sk_unique) {
 +                    typ = ty::mk_ivec(fcx.ccx.tcx, rec(ty=t, mut=mut));
 +                }
              }
  
 -            auto typ = ty::mk_vec(scx.fcx.ccx.tcx, rec(ty=t, mut=mut));
 -            write::ty_only_fixup(scx, a.id, typ);
 +            write::ty_only_fixup(fcx, a.id, typ);
          }
  
          case (ast::expr_tup(?elts, ?a)) {
              let vec[ty::mt] elts_mt = [];
  
              for (ast::elt e in elts) {
 -                check_expr(scx, e.expr);
 -                auto ety = expr_ty(scx.fcx.ccx.tcx, e.expr);
 +                check_expr(fcx, e.expr);
 +                auto ety = expr_ty(fcx.ccx.tcx, e.expr);
                  elts_mt += [rec(ty=ety, mut=e.mut)];
              }
  
 -            auto typ = ty::mk_tup(scx.fcx.ccx.tcx, elts_mt);
 -            write::ty_only_fixup(scx, a.id, typ);
 +            auto typ = ty::mk_tup(fcx.ccx.tcx, elts_mt);
 +            write::ty_only_fixup(fcx, a.id, typ);
          }
  
          case (ast::expr_rec(?fields, ?base, ?a)) {
  
              alt (base) {
                  case (none) { /* no-op */}
 -                case (some(?b_0)) { check_expr(scx, b_0); }
 +                case (some(?b_0)) { check_expr(fcx, b_0); }
              }
  
              let vec[field] fields_t = [];
  
              for (ast::field f in fields) {
 -                check_expr(scx, f.node.expr);
 -                auto expr_t = expr_ty(scx.fcx.ccx.tcx, f.node.expr);
 +                check_expr(fcx, f.node.expr);
 +                auto expr_t = expr_ty(fcx.ccx.tcx, f.node.expr);
  
                  auto expr_mt = rec(ty=expr_t, mut=f.node.mut);
                  vec::push[field](fields_t, rec(ident=f.node.ident,
  
              alt (base) {
                  case (none) {
 -                    auto typ = ty::mk_rec(scx.fcx.ccx.tcx, fields_t);
 -                    write::ty_only_fixup(scx, a.id, typ);
 +                    auto typ = ty::mk_rec(fcx.ccx.tcx, fields_t);
 +                    write::ty_only_fixup(fcx, a.id, typ);
                  }
  
                  case (some(?bexpr)) {
 -                    check_expr(scx, bexpr);
 -                    auto bexpr_t = expr_ty(scx.fcx.ccx.tcx, bexpr);
 +                    check_expr(fcx, bexpr);
 +                    auto bexpr_t = expr_ty(fcx.ccx.tcx, bexpr);
  
                      let vec[field] base_fields = [];
  
 -                    alt (struct(scx.fcx.ccx.tcx, bexpr_t)) {
 +                    alt (structure_of(fcx, expr.span, bexpr_t)) {
                          case (ty::ty_rec(?flds)) { base_fields = flds; }
                          case (_) {
 -                            scx.fcx.ccx.tcx.sess.span_err
 +                            fcx.ccx.tcx.sess.span_err
                                  (expr.span,
                                   "record update non-record base");
                          }
                      }
  
 -                    write::ty_only_fixup(scx, a.id, bexpr_t);
 +                    write::ty_only_fixup(fcx, a.id, bexpr_t);
  
                      for (ty::field f in fields_t) {
                          auto found = false;
                          for (ty::field bf in base_fields) {
                              if (str::eq(f.ident, bf.ident)) {
 -                                demand::simple(scx, expr.span, f.mt.ty,
 +                                demand::simple(fcx, expr.span, f.mt.ty,
                                                 bf.mt.ty);
                                  found = true;
                              }
                          }
                          if (!found) {
 -                            scx.fcx.ccx.tcx.sess.span_err
 +                            fcx.ccx.tcx.sess.span_err
                                  (expr.span,
                                   "unknown field in record update: "
                                   + f.ident);
          }
  
          case (ast::expr_field(?base, ?field, ?a)) {
 -            check_expr(scx, base);
 -            auto base_t = expr_ty(scx.fcx.ccx.tcx, base);
 -            base_t = strip_boxes(scx.fcx.ccx.tcx, base_t);
 -            alt (struct(scx.fcx.ccx.tcx, base_t)) {
 +            check_expr(fcx, base);
 +            auto base_t = expr_ty(fcx.ccx.tcx, base);
 +            base_t = strip_boxes(fcx, expr.span, base_t);
 +            alt (structure_of(fcx, expr.span, base_t)) {
                  case (ty::ty_tup(?args)) {
 -                    let uint ix = ty::field_num(scx.fcx.ccx.tcx.sess,
 +                    let uint ix = ty::field_num(fcx.ccx.tcx.sess,
                                                  expr.span, field);
                      if (ix >= vec::len[ty::mt](args)) {
 -                        scx.fcx.ccx.tcx.sess.span_err(expr.span,
 +                        fcx.ccx.tcx.sess.span_err(expr.span,
                                                    "bad index on tuple");
                      }
 -                    write::ty_only_fixup(scx, a.id, args.(ix).ty);
 +                    write::ty_only_fixup(fcx, a.id, args.(ix).ty);
                  }
  
                  case (ty::ty_rec(?fields)) {
 -                    let uint ix = ty::field_idx(scx.fcx.ccx.tcx.sess,
 +                    let uint ix = ty::field_idx(fcx.ccx.tcx.sess,
                                                  expr.span, field, fields);
                      if (ix >= vec::len[ty::field](fields)) {
 -                        scx.fcx.ccx.tcx.sess.span_err(expr.span,
 +                        fcx.ccx.tcx.sess.span_err(expr.span,
                                                "bad index on record");
                      }
 -                    write::ty_only_fixup(scx, a.id, fields.(ix).mt.ty);
 +                    write::ty_only_fixup(fcx, a.id, fields.(ix).mt.ty);
                  }
  
                  case (ty::ty_obj(?methods)) {
 -                    let uint ix = ty::method_idx(scx.fcx.ccx.tcx.sess,
 +                    let uint ix = ty::method_idx(fcx.ccx.tcx.sess,
                                                   expr.span, field, methods);
  
                      if (ix >= vec::len[ty::method](methods)) {
 -                        scx.fcx.ccx.tcx.sess.span_err(expr.span,
 +                        fcx.ccx.tcx.sess.span_err(expr.span,
                                                    "bad index on obj");
                      }
                      auto meth = methods.(ix);
 -                    auto t = ty::mk_fn(scx.fcx.ccx.tcx, meth.proto,
 -                                       meth.inputs, meth.output, meth.cf);
 -                    write::ty_only_fixup(scx, a.id, t);
 +                    auto t = ty::mk_fn(fcx.ccx.tcx, meth.proto,
 +                                       meth.inputs, meth.output, meth.cf,
 +                                       meth.constrs);
 +                    write::ty_only_fixup(fcx, a.id, t);
                  }
  
                  case (_) {
 -                    scx.fcx.ccx.tcx.sess.span_unimpl(expr.span,
 +                    fcx.ccx.tcx.sess.span_unimpl(expr.span,
                          "base type for expr_field in typeck::check_expr: " +
 -                        ty_to_str(scx.fcx.ccx.tcx, base_t));
 +                        ty_to_str(fcx.ccx.tcx, base_t));
                  }
              }
          }
  
          case (ast::expr_index(?base, ?idx, ?a)) {
 -            check_expr(scx, base);
 -            auto base_t = expr_ty(scx.fcx.ccx.tcx, base);
 -            base_t = strip_boxes(scx.fcx.ccx.tcx, base_t);
 +            check_expr(fcx, base);
 +            auto base_t = expr_ty(fcx.ccx.tcx, base);
 +            base_t = strip_boxes(fcx, expr.span, base_t);
  
 -            check_expr(scx, idx);
 -            auto idx_t = expr_ty(scx.fcx.ccx.tcx, idx);
 -            alt (struct(scx.fcx.ccx.tcx, base_t)) {
 +            check_expr(fcx, idx);
 +            auto idx_t = expr_ty(fcx.ccx.tcx, idx);
 +            alt (structure_of(fcx, expr.span, base_t)) {
                  case (ty::ty_vec(?mt)) {
 -                    if (! type_is_integral(scx.fcx.ccx.tcx, idx_t)) {
 -                        scx.fcx.ccx.tcx.sess.span_err
 +                    if (! type_is_integral(fcx, idx.span, idx_t)) {
 +                        fcx.ccx.tcx.sess.span_err
                              (idx.span,
                               "non-integral type of vec index: "
 -                             + ty_to_str(scx.fcx.ccx.tcx, idx_t));
 +                             + ty_to_str(fcx.ccx.tcx, idx_t));
                      }
 -                    write::ty_only_fixup(scx, a.id, mt.ty);
 +                    write::ty_only_fixup(fcx, a.id, mt.ty);
                  }
                  case (ty::ty_str) {
 -                    if (! type_is_integral(scx.fcx.ccx.tcx, idx_t)) {
 -                        scx.fcx.ccx.tcx.sess.span_err
 +                    if (! type_is_integral(fcx, idx.span, idx_t)) {
 +                        fcx.ccx.tcx.sess.span_err
                              (idx.span,
                               "non-integral type of str index: "
 -                             + ty_to_str(scx.fcx.ccx.tcx, idx_t));
 +                             + ty_to_str(fcx.ccx.tcx, idx_t));
                      }
 -                    auto typ = ty::mk_mach(scx.fcx.ccx.tcx, common::ty_u8);
 -                    write::ty_only_fixup(scx, a.id, typ);
 +                    auto typ = ty::mk_mach(fcx.ccx.tcx, common::ty_u8);
 +                    write::ty_only_fixup(fcx, a.id, typ);
                  }
                  case (_) {
 -                    scx.fcx.ccx.tcx.sess.span_err
 +                    fcx.ccx.tcx.sess.span_err
                          (expr.span,
                           "vector-indexing bad type: "
 -                         + ty_to_str(scx.fcx.ccx.tcx, base_t));
 +                         + ty_to_str(fcx.ccx.tcx, base_t));
                  }
              }
          }
  
          case (ast::expr_port(?a)) {
 -            auto t = next_ty_var(scx);
 -            auto pt = ty::mk_port(scx.fcx.ccx.tcx, t);
 -            write::ty_only_fixup(scx, a.id, pt);
 +            auto t = next_ty_var(fcx);
 +            auto pt = ty::mk_port(fcx.ccx.tcx, t);
 +            write::ty_only_fixup(fcx, a.id, pt);
          }
  
          case (ast::expr_chan(?x, ?a)) {
 -            check_expr(scx, x);
 -            auto port_t = expr_ty(scx.fcx.ccx.tcx, x);
 -            alt (struct(scx.fcx.ccx.tcx, port_t)) {
 +            check_expr(fcx, x);
 +            auto port_t = expr_ty(fcx.ccx.tcx, x);
 +            alt (structure_of(fcx, expr.span, port_t)) {
                  case (ty::ty_port(?subtype)) {
 -                    auto ct = ty::mk_chan(scx.fcx.ccx.tcx, subtype);
 -                    write::ty_only_fixup(scx, a.id, ct);
 +                    auto ct = ty::mk_chan(fcx.ccx.tcx, subtype);
 +                    write::ty_only_fixup(fcx, a.id, ct);
                  }
                  case (_) {
 -                    scx.fcx.ccx.tcx.sess.span_err(expr.span,
 +                    fcx.ccx.tcx.sess.span_err(expr.span,
                          "bad port type: " +
 -                        ty_to_str(scx.fcx.ccx.tcx, port_t));
 +                        ty_to_str(fcx.ccx.tcx, port_t));
                  }
              }
          }
              }
              let ast::def_id di = obj_def_ids.ty;
  
 -            vec::push[obj_info](scx.fcx.ccx.obj_infos,
 +            vec::push[obj_info](fcx.ccx.obj_infos,
                                  rec(obj_fields=fields, this_obj=di));
  
              // Typecheck 'with_obj', if it exists.
                      // This had better have object type.  TOOD: report an
                      // error if the user is trying to extend a non-object
                      // with_obj.
 -                    check_expr(scx, e);
 +                    check_expr(fcx, e);
                  }
              }
  
              // Typecheck the methods.
              for (@ast::method method in anon_obj.methods) {
 -                check_method(scx.fcx.ccx, method);
 +                check_method(fcx.ccx, method);
              }
  
 -            auto t = next_ty_var(scx);
 +            auto t = next_ty_var(fcx);
  
  
              // FIXME: These next three functions are largely ripped off from
                                                       m.node.meth.decl.inputs);
                  auto output = convert(m.node.meth.decl.output);
                  ret rec(proto=m.node.meth.proto, ident=m.node.ident,
 -                        inputs=inputs, output=output, cf=m.node.meth.decl.cf);
 +                        inputs=inputs, output=output, cf=m.node.meth.decl.cf,
 +                        constrs=m.node.meth.decl.constraints);
              }
  
              fn get_anon_obj_method_types(@crate_ctxt ccx,
                                                    anon_obj.methods);
              }
  
 -            auto methods = get_anon_obj_method_types(scx.fcx.ccx, anon_obj);
 -            auto ot = ty::mk_obj(scx.fcx.ccx.tcx,
 +            auto methods = get_anon_obj_method_types(fcx.ccx, anon_obj);
 +            auto ot = ty::mk_obj(fcx.ccx.tcx,
                                   ty::sort_methods(methods));
 -            write::ty_only_fixup(scx, a.id, ot);
 +            write::ty_only_fixup(fcx, a.id, ot);
  
              // Now remove the info from the stack.
 -            vec::pop[obj_info](scx.fcx.ccx.obj_infos);
 +            vec::pop[obj_info](fcx.ccx.obj_infos);
          }
  
          case (_) {
 -            scx.fcx.ccx.tcx.sess.unimpl("expr type in typeck::check_expr");
 +            fcx.ccx.tcx.sess.unimpl("expr type in typeck::check_expr");
          }
      }
  }
  
 -fn next_ty_var(&@stmt_ctxt scx) -> ty::t {
 -    auto t = ty::mk_var(scx.fcx.ccx.tcx, scx.next_var_id);
 -    scx.next_var_id += 1;
 -    ret t;
 +fn next_ty_var_id(@fn_ctxt fcx) -> int {
 +    auto id = fcx.next_var_id;
 +    fcx.next_var_id += 1;
 +    ret id;
 +}
 +
 +fn next_ty_var(&@fn_ctxt fcx) -> ty::t {
 +    ret ty::mk_var(fcx.ccx.tcx, next_ty_var_id(fcx));
  }
  
  fn get_obj_info(&@crate_ctxt ccx) -> option::t[obj_info] {
      ret vec::last[obj_info](ccx.obj_infos);
  }
  
 -fn check_decl_initializer(&@stmt_ctxt scx, &ast::def_id lid,
 +fn check_decl_initializer(&@fn_ctxt fcx, &ast::def_id lid,
                            &ast::initializer init) {
 -    check_expr(scx, init.expr);
 +    check_expr(fcx, init.expr);
  
 -    auto lty = ty::mk_local(scx.fcx.ccx.tcx, lid);
 +    auto lty = ty::mk_var(fcx.ccx.tcx, fcx.locals.get(lid));
      alt (init.op) {
          case (ast::init_assign) {
 -            pushdown::pushdown_expr(scx, lty, init.expr);
 +            demand::simple(fcx, init.expr.span, lty,
 +                           expr_ty(fcx.ccx.tcx, init.expr));
          }
          case (ast::init_move) {
 -            pushdown::pushdown_expr(scx, lty, init.expr);
 +            demand::simple(fcx, init.expr.span, lty,
 +                           expr_ty(fcx.ccx.tcx, init.expr));
          }
          case (ast::init_recv) {
 -            auto port_ty = ty::mk_port(scx.fcx.ccx.tcx, lty);
 -            pushdown::pushdown_expr(scx, port_ty, init.expr);
 +            auto port_ty = ty::mk_port(fcx.ccx.tcx, lty);
 +            demand::simple(fcx, init.expr.span, port_ty,
 +                           expr_ty(fcx.ccx.tcx, init.expr));
          }
      }
  }
  fn check_decl_local(&@fn_ctxt fcx, &@ast::decl decl) -> @ast::decl {
      alt (decl.node) {
          case (ast::decl_local(?local)) {
 -            auto t = ty::mk_nil(fcx.ccx.tcx);
 -
 -            alt (local.ty) {
 -                case (none) {
 -                    // Auto slot. Do nothing for now.
 -                }
 -
 -                case (some(?ast_ty)) {
 -                    auto local_ty = ast_ty_to_ty_crate(fcx.ccx, ast_ty);
 -                    fcx.locals.insert(local.id, local_ty);
 -                    t = local_ty;
 -                }
 -            }
 -
              auto a_res = local.ann;
 -            write::ty_only(fcx.ccx.tcx, a_res.id, t);
 +            auto t = ty::mk_var(fcx.ccx.tcx, fcx.locals.get(local.id));
 +            write::ty_only_fixup(fcx, a_res.id, t);
  
              auto initopt = local.init;
              alt (local.init) {
                  case (some(?init)) {
 -                    with_stmt_ctxt(fcx,
 -                        bind check_decl_initializer(_, local.id, init));
 +                    check_decl_initializer(fcx, local.id, init);
                  }
                  case (_) { /* fall through */  }
              }
 -            auto local_1 = @rec(init = initopt, ann = a_res with *local);
 +            auto local_1 = @rec(init=initopt, ann=a_res with *local);
              ret @rec(node=ast::decl_local(local_1) with *decl);
          }
      }
  }
  
 -fn check_and_pushdown_expr(&@stmt_ctxt scx, &@ast::expr expr) {
 -    check_expr(scx, expr);
 -    auto ety = expr_ty(scx.fcx.ccx.tcx, expr);
 -    pushdown::pushdown_expr(scx, ety, expr);
 -}
 -
  fn check_stmt(&@fn_ctxt fcx, &@ast::stmt stmt) {
      auto node_id;
      alt (stmt.node) {
          }
          case (ast::stmt_expr(?expr,?a)) {
              node_id = a.id;
 -            with_stmt_ctxt(fcx, bind check_and_pushdown_expr(_, expr));
 +            check_expr(fcx, expr);
          }
      }
  
      write::nil_ty(fcx.ccx.tcx, node_id);
  }
  
 -fn check_block(&@stmt_ctxt scx, &ast::block block) {
 -    for (@ast::stmt s in block.node.stmts) { check_stmt(scx.fcx, s); }
 +fn check_block(&@fn_ctxt fcx, &ast::block block) {
 +    for (@ast::stmt s in block.node.stmts) { check_stmt(fcx, s); }
  
      alt (block.node.expr) {
 -        case (none) {
 -            write::nil_ty(scx.fcx.ccx.tcx, block.node.a.id);
 -        }
 +        case (none) { write::nil_ty(fcx.ccx.tcx, block.node.a.id); }
          case (some(?e)) {
 -            check_expr(scx, e);
 -            auto ety = expr_ty(scx.fcx.ccx.tcx, e);
 -            pushdown::pushdown_expr(scx, ety, e);
 -            write::ty_only_fixup(scx, block.node.a.id, ety);
 +            check_expr(fcx, e);
 +            auto ety = expr_ty(fcx.ccx.tcx, e);
 +            write::ty_only_fixup(fcx, block.node.a.id, ety);
          }
      }
  
  fn check_const(&@crate_ctxt ccx, &span sp, &@ast::expr e, &ast::ann ann) {
      // FIXME: this is kinda a kludge; we manufacture a fake function context
      // and statement context for checking the initializer expression.
 -    auto rty = ann_to_type(ccx.tcx.node_types, ann);
 -    let @fn_ctxt fcx = @rec(ret_ty = rty,
 -                            purity = ast::pure_fn,
 -                            locals = @common::new_def_hash[ty::t](),
 -                            ccx = ccx);
 -
 -    with_stmt_ctxt(fcx, bind check_and_pushdown_expr(_, e));
 +    auto rty = ann_to_type(ccx.tcx, ann);
 +    let vec[uint] fixups = [];
 +    let @fn_ctxt fcx = @rec(ret_ty=rty,
 +                            purity=ast::pure_fn,
 +                            var_bindings=ty::unify::mk_var_bindings(),
 +                            locals=new_def_hash[int](),
 +                            local_names=new_def_hash[ast::ident](),
 +                            mutable next_var_id=0,
 +                            mutable fixups=fixups,
 +                            ccx=ccx);
 +
 +    check_expr(fcx, e);
  }
  
  fn check_fn(&@crate_ctxt ccx, &ast::fn_decl decl, ast::proto proto,
 -            &ast::block body) {
 -    auto local_ty_table = @common::new_def_hash[ty::t]();
 -
 -    // FIXME: duplicate work: the item annotation already has the arg types
 -    // and return type translated to typeck::ty values. We don't need do to it
 -    // again here, we can extract them.
 -
 -    alt (get_obj_info(ccx)) {
 -        case (option::some(?oinfo)) {
 -            for (ast::obj_field f in oinfo.obj_fields) {
 -                auto field_ty = ty::ann_to_type(ccx.tcx.node_types, f.ann);
 -                local_ty_table.insert(f.id, field_ty);
 -            }
 -        }
 -        case (option::none) { /* no fields */ }
 -    }
 +            &ast::block body, &ast::ann ann) {
 +    auto gather_result = gather_locals(ccx, decl, body, ann);
  
 -    // Store the type of each argument in the table.
 -    for (ast::arg arg in decl.inputs) {
 -        auto input_ty = ast_ty_to_ty_crate(ccx, arg.ty);
 -        local_ty_table.insert(arg.id, input_ty);
 -    }
 -
 -    let @fn_ctxt fcx = @rec(ret_ty = ast_ty_to_ty_crate(ccx, decl.output),
 -                            purity = decl.purity,
 -                            locals = local_ty_table,
 -                            ccx = ccx);
 +    let vec[uint] fixups = [];
 +    let @fn_ctxt fcx = @rec(ret_ty=ast_ty_to_ty_crate(ccx, decl.output),
 +                            purity=decl.purity,
 +                            var_bindings=gather_result.var_bindings,
 +                            locals=gather_result.locals,
 +                            local_names=gather_result.local_names,
 +                            mutable next_var_id=gather_result.next_var_id,
 +                            mutable fixups=fixups,
 +                            ccx=ccx);
  
      // TODO: Make sure the type of the block agrees with the function type.
 -    with_stmt_ctxt(fcx, bind check_block(_, body));
 +    check_block(fcx, body);
  
      alt (decl.purity) {
          case (ast::pure_fn) {
          case (_) {}
      }
  
 -    writeback::resolve_local_types_in_block(fcx, body);
 +    writeback::resolve_type_vars_in_block(fcx, body);
  }
  
  fn check_method(&@crate_ctxt ccx, &@ast::method method) {
 -    check_fn(ccx, method.node.meth.decl, method.node.meth.proto,
 -             method.node.meth.body);
 +    auto m = method.node.meth;
 +    check_fn(ccx, m.decl, m.proto, m.body, method.node.ann);
  }
  
  fn check_item(@crate_ctxt ccx, &@ast::item it) {
          case (ast::item_const(_, _, ?e, _, ?a)) {
              check_const(ccx, it.span, e, a);
          }
 -        case (ast::item_fn(_, ?f, _, _, _)) {
 -            check_fn(ccx, f.decl, f.proto, f.body);
 +        case (ast::item_fn(_, ?f, _, _, ?a)) {
 +            check_fn(ccx, f.decl, f.proto, f.body, a);
          }
          case (ast::item_obj(_, ?ob, _, ?obj_def_ids, _)) {
              // We're entering an object, so gather up the info we need.
      }
  }
  
 -// Utilities for the unification cache
 -
 -fn hash_unify_cache_entry(&unify_cache_entry uce) -> uint {
 -    auto h = ty::hash_ty(uce._0);
 -    h += h << 5u + ty::hash_ty(uce._1);
 -
 -    auto i = 0u;
 -    auto tys_len = vec::len(uce._2);
 -    while (i < tys_len) {
 -        h += h << 5u + ty::hash_ty(uce._2.(i));
 -        i += 1u;
 -    }
 -
 -    ret h;
 -}
 -
 -fn eq_unify_cache_entry(&unify_cache_entry a, &unify_cache_entry b) -> bool {
 -    if (!ty::eq_ty(a._0, b._0) || !ty::eq_ty(a._1, b._1)) { ret false; }
 -
 -    auto i = 0u;
 -    auto tys_len = vec::len(a._2);
 -    if (vec::len(b._2) != tys_len) { ret false; }
 -
 -    while (i < tys_len) {
 -        if (!ty::eq_ty(a._2.(i), b._2.(i))) { ret false; }
 -        i += 1u;
 -    }
 -
 -    ret true;
 -}
 -
  fn mk_fn_purity_table(&@ast::crate crate) -> @fn_purity_table {
      auto res = @new_def_hash[ast::purity]();
  
@@@ -2528,16 -2970,26 +2540,16 @@@ fn check_crate(&ty::ctxt tcx, &@ast::cr
  
      let vec[obj_info] obj_infos = [];
  
 -    auto hasher = hash_unify_cache_entry;
 -    auto eqer = eq_unify_cache_entry;
 -    auto unify_cache =
 -        map::mk_hashmap[unify_cache_entry,ty::unify::result](hasher, eqer);
      auto fpt = mk_fn_purity_table(crate); // use a variation on collect
  
      auto ccx = @rec(mutable obj_infos=obj_infos,
                      fn_purity_table=fpt,
 -                    unify_cache=unify_cache,
 -                    mutable cache_hits=0u,
 -                    mutable cache_misses=0u,
                      tcx=tcx);
  
      auto visit = rec(visit_item_pre = bind check_item(ccx, _)
                       with walk::default_visitor());
  
      walk::walk_crate(visit, *crate);
 -
 -    log #fmt("cache hit rate: %u/%u", ccx.cache_hits,
 -             ccx.cache_hits + ccx.cache_misses);
  }
  
  //