about summary refs log tree commit diff
path: root/src/comp
diff options
context:
space:
mode:
authorTim Chevalier <chevalier@alum.wellesley.edu>2012-01-14 16:05:07 -0800
committerTim Chevalier <chevalier@alum.wellesley.edu>2012-01-17 10:08:16 -0800
commitc3bc8fada838c15e09e76a9d5d85438667c1636c (patch)
treee0ff1cb9ea73f7ee894e8ff3ab3180260f71aeac /src/comp
parenta7bd817017c247edb0680936c6cf34ec4d8e7fc0 (diff)
downloadrust-c3bc8fada838c15e09e76a9d5d85438667c1636c.tar.gz
rust-c3bc8fada838c15e09e76a9d5d85438667c1636c.zip
Allow omission of the '.' after nullary tag patterns
This commit allows patterns like:

alt x { some(_) { ... } none { } }

without the '.' after none. The parser suspends judgment about
whether a bare ident is a tag or a new bound variable; instead,
the resolver disambiguates.

This means that any code after resolution that pattern-matches on
patterns needs to call pat_util::normalize_pat, which consults
an environment to do this disambiguation.

In addition, local variables are no longer allowed to shadow
tag names, so this required changing some code (e.g. renaming
variables named "mut", and renaming ast::sub to subtract).

The parser currently accepts patterns with and without the '.'.
Once the compiler and libraries are changed, it will no longer
accept the '.'.
Diffstat (limited to 'src/comp')
-rw-r--r--src/comp/middle/alias.rs10
-rw-r--r--src/comp/middle/ast_map.rs4
-rw-r--r--src/comp/middle/check_alt.rs18
-rw-r--r--src/comp/middle/debuginfo.rs8
-rw-r--r--src/comp/middle/pat_util.rs119
-rw-r--r--src/comp/middle/resolve.rs64
-rw-r--r--src/comp/middle/trans.rs24
-rw-r--r--src/comp/middle/trans_alt.rs58
-rw-r--r--src/comp/middle/tstate/annotate.rs4
-rw-r--r--src/comp/middle/tstate/auxiliary.rs26
-rw-r--r--src/comp/middle/tstate/collect_locals.rs10
-rw-r--r--src/comp/middle/tstate/pre_post_conditions.rs38
-rw-r--r--src/comp/middle/tstate/states.rs17
-rw-r--r--src/comp/middle/ty.rs37
-rw-r--r--src/comp/middle/typeck.rs17
-rw-r--r--src/comp/rustc.rc1
-rw-r--r--src/comp/syntax/ast.rs14
-rw-r--r--src/comp/syntax/ast_util.rs45
-rw-r--r--src/comp/syntax/fold.rs12
-rw-r--r--src/comp/syntax/parse/parser.rs29
-rw-r--r--src/comp/syntax/print/pprust.rs8
-rw-r--r--src/comp/syntax/visit.rs6
22 files changed, 387 insertions, 182 deletions
diff --git a/src/comp/middle/alias.rs b/src/comp/middle/alias.rs
index e10dec83a67..93239ec116e 100644
--- a/src/comp/middle/alias.rs
+++ b/src/comp/middle/alias.rs
@@ -9,6 +9,7 @@ import std::list;
 import option::{some, none, is_none};
 import list::list;
 import driver::session::session;
+import pat_util::*;
 
 // This is not an alias-analyser (though it would merit from becoming one, or
 // getting input from one, to be more precise). It is a pass that checks
@@ -323,7 +324,7 @@ fn check_alt(cx: ctx, input: @ast::expr, arms: [ast::arm], sc: scope,
     for a: ast::arm in arms {
         let new_bs = sc.bs;
         let root_var = path_def_id(cx, root.ex);
-        let pat_id_map = ast_util::pat_id_map(a.pats[0]);
+        let pat_id_map = pat_util::pat_id_map(cx.tcx, a.pats[0]);
         type info = {
             id: node_id,
             mutable unsafe_tys: [unsafe_ty],
@@ -588,10 +589,11 @@ fn pattern_roots(tcx: ty::ctxt, mut: option::t<unsafe_ty>, pat: @ast::pat)
     -> [pattern_root] {
     fn walk(tcx: ty::ctxt, mut: option::t<unsafe_ty>, pat: @ast::pat,
             &set: [pattern_root]) {
-        alt pat.node {
+        alt normalize_pat(tcx, pat).node {
           ast::pat_wild. | ast::pat_lit(_) | ast::pat_range(_, _) {}
-          ast::pat_bind(nm, sub) {
-            set += [{id: pat.id, name: nm, mut: mut, span: pat.span}];
+          ast::pat_ident(nm, sub) {
+            set += [{id: pat.id, name: path_to_ident(nm), mut: mut,
+                        span: pat.span}];
             alt sub { some(p) { walk(tcx, mut, p, set); } _ {} }
           }
           ast::pat_tag(_, ps) | ast::pat_tup(ps) {
diff --git a/src/comp/middle/ast_map.rs b/src/comp/middle/ast_map.rs
index 1af44c81209..de04da49014 100644
--- a/src/comp/middle/ast_map.rs
+++ b/src/comp/middle/ast_map.rs
@@ -44,14 +44,14 @@ fn map_fn(cx: ctx, _fk: visit::fn_kind, decl: fn_decl, _body: blk,
 }
 
 fn map_local(cx: ctx, loc: @local) {
-    ast_util::pat_bindings(loc.node.pat) {|p|
+    pat_util::pat_bindings(loc.node.pat) {|p|
         cx.map.insert(p.id, node_local(cx.local_id));
         cx.local_id += 1u;
     };
 }
 
 fn map_arm(cx: ctx, arm: arm) {
-    ast_util::pat_bindings(arm.pats[0]) {|p|
+    pat_util::pat_bindings(arm.pats[0]) {|p|
         cx.map.insert(p.id, node_local(cx.local_id));
         cx.local_id += 1u;
     };
diff --git a/src/comp/middle/check_alt.rs b/src/comp/middle/check_alt.rs
index d2e3c15009d..e32866e5749 100644
--- a/src/comp/middle/check_alt.rs
+++ b/src/comp/middle/check_alt.rs
@@ -1,6 +1,7 @@
 import syntax::ast::*;
 import syntax::ast_util::{variant_def_ids, dummy_sp, compare_lit_exprs,
                           lit_expr_eq};
+import pat_util::*;
 import syntax::visit;
 import option::{some, none};
 import driver::session::session;
@@ -16,7 +17,12 @@ fn check_crate(tcx: ty::ctxt, crate: @crate) {
 
 fn check_expr(tcx: ty::ctxt, ex: @expr, &&s: (), v: visit::vt<()>) {
     visit::visit_expr(ex, s, v);
-    alt ex.node { expr_alt(_, arms) { check_arms(tcx, arms); } _ { } }
+    alt ex.node {
+        expr_alt(_, arms) {
+            check_arms(tcx, pat_util::normalize_arms(tcx, arms));
+        }
+        _ { }
+    }
 }
 
 fn check_arms(tcx: ty::ctxt, arms: [arm]) {
@@ -66,8 +72,8 @@ fn pattern_supersedes(tcx: ty::ctxt, a: @pat, b: @pat) -> bool {
     }
 
     alt a.node {
-      pat_bind(_, some(p)) { pattern_supersedes(tcx, p, b) }
-      pat_wild. | pat_bind(_, none.) { true }
+      pat_ident(_, some(p)) { pattern_supersedes(tcx, p, b) }
+      pat_wild. | pat_ident(_, none.) { true }
       pat_lit(la) {
         alt b.node {
           pat_lit(lb) { lit_expr_eq(la, lb) }
@@ -132,11 +138,11 @@ fn check_local(tcx: ty::ctxt, loc: @local, &&s: (), v: visit::vt<()>) {
 }
 
 fn is_refutable(tcx: ty::ctxt, pat: @pat) -> bool {
-    alt pat.node {
-      pat_box(sub) | pat_uniq(sub) | pat_bind(_, some(sub)) {
+    alt normalize_pat(tcx, pat).node {
+      pat_box(sub) | pat_uniq(sub) | pat_ident(_, some(sub)) {
         is_refutable(tcx, sub)
       }
-      pat_wild. | pat_bind(_, none.) { false }
+      pat_wild. | pat_ident(_, none.) { false }
       pat_lit(_) { true }
       pat_rec(fields, _) {
         for field: field_pat in fields {
diff --git a/src/comp/middle/debuginfo.rs b/src/comp/middle/debuginfo.rs
index f834fa8b58b..938fcac52d3 100644
--- a/src/comp/middle/debuginfo.rs
+++ b/src/comp/middle/debuginfo.rs
@@ -8,6 +8,7 @@ import middle::trans_build::B;
 import middle::ty;
 import syntax::{ast, codemap};
 import ast::ty;
+import pat_util::*;
 import util::ppaux::ty_to_str;
 
 export create_local_var;
@@ -629,9 +630,10 @@ fn create_local_var(bcx: @block_ctxt, local: @ast::local)
       option::none. {}
     }
 
-    let name = alt local.node.pat.node {
-      ast::pat_bind(ident, _) { ident /*XXX deal w/ optional node binding*/ }
-    };
+    let name = path_to_ident(alt pat_util::normalize_pat(bcx_tcx(bcx),
+                                           local.node.pat).node {
+      ast::pat_ident(ident, _) { ident /*XXX deal w/ optional node binding*/ }
+     });
     let loc = codemap::lookup_char_pos(cx.sess.codemap,
                                        local.span.lo);
     let ty = trans::node_id_type(cx, local.node.id);
diff --git a/src/comp/middle/pat_util.rs b/src/comp/middle/pat_util.rs
new file mode 100644
index 00000000000..f2d1b6ea6ca
--- /dev/null
+++ b/src/comp/middle/pat_util.rs
@@ -0,0 +1,119 @@
+import syntax::ast::*;
+import syntax::ast_util;
+import syntax::ast_util::respan;
+import syntax::fold;
+import syntax::fold::*;
+
+export normalize_arms;
+export normalize_pat;
+export normalize_pat_def_map;
+export pat_binding_ids;
+export pat_bindings;
+export pat_id_map;
+export path_to_ident;
+
+fn normalize_pat_def_map(dm: resolve::def_map, p: @pat) -> @pat {
+  // have to do it the hard way b/c ast fold doesn't pass around
+  // node IDs. bother.
+  alt p.node {
+      pat_wild. { p }
+      pat_ident(_, none.) { normalize_one(dm, p) }
+      pat_ident(q, some(r)) {
+        @{node: pat_ident(q, some(normalize_pat_def_map(dm, r)))
+            with *p}
+      }
+      pat_tag(a_path, subs) {
+        @{node: pat_tag(a_path,
+           vec::map(subs, {|p| normalize_pat_def_map(dm, p)})) with *p}
+      }
+      pat_rec(field_pats, b) {
+        @{node: pat_rec(vec::map(field_pats,
+           {|fp| {pat: normalize_pat_def_map(dm, fp.pat) with fp}}), b)
+            with *p}
+      }
+      pat_tup(subs) {
+        @{node: pat_tup(vec::map(subs, {|p| normalize_pat_def_map(dm, p)}))
+            with *p}
+      }
+      pat_box(q) {
+        @{node: pat_box(normalize_pat_def_map(dm, q))
+            with *p}
+      }
+      pat_uniq(q) {
+        @{node: pat_uniq(normalize_pat_def_map(dm, q))
+            with *p}
+      }
+      pat_lit(_) { p }
+      pat_range(_,_) { p }
+    }
+}
+
+fn normalize_one(dm: resolve::def_map, p: @pat) -> @pat {
+    alt dm.find(p.id) {
+        some(d) {
+          alt p.node {
+              pat_ident(tag_path, _) { @{id: p.id,
+                    node: pat_tag(tag_path, []),
+                    span: p.span} }
+              _ { p }
+          }
+        }
+        none. { p }
+    }
+}
+
+fn normalize_pat(tcx: ty::ctxt, p: @pat) -> @pat {
+  normalize_pat_def_map(tcx.def_map, p)
+}
+
+fn normalize_arms(tcx: ty::ctxt, arms:[arm]) -> [arm] {
+      vec::map(arms, {|a|
+            {pats:
+              vec::map(a.pats, {|p|
+                    pat_util::normalize_pat(tcx, p)})
+                with a}})
+}
+
+type pat_id_map = std::map::hashmap<str, node_id>;
+
+// This is used because same-named variables in alternative patterns need to
+// use the node_id of their namesake in the first pattern.
+fn pat_id_map(tcx: ty::ctxt, pat: @pat) -> pat_id_map {
+    let map = std::map::new_str_hash::<node_id>();
+    pat_bindings(normalize_pat(tcx, pat)) {|bound|
+        let name = path_to_ident(alt bound.node
+           { pat_ident(n, _) { n } });
+        map.insert(name, bound.id);
+    };
+    ret map;
+}
+
+// This does *not* normalize. The pattern should be already normalized
+// if you want to get a normalized pattern out of it.
+// Could return a constrained type in order to express that (future work)
+fn pat_bindings(pat: @pat, it: block(@pat)) {
+  alt pat.node {
+      pat_ident(_, option::none.) { it(pat); }
+      pat_ident(_, option::some(sub)) { it(pat); pat_bindings(sub, it); }
+      pat_tag(_, sub) { for p in sub { pat_bindings(p, it); } }
+      pat_rec(fields, _) { for f in fields { pat_bindings(f.pat, it); } }
+      pat_tup(elts) { for elt in elts { pat_bindings(elt, it); } }
+      pat_box(sub) { pat_bindings(sub, it); }
+      pat_uniq(sub) { pat_bindings(sub, it); }
+      pat_wild. | pat_lit(_) | pat_range(_, _) { }
+    }
+}
+
+fn pat_binding_ids(pat: @pat) -> [node_id] {
+    let found = [];
+    pat_bindings(pat) {|b| found += [b.id]; };
+    ret found;
+}
+
+fn path_to_ident(p: @path) -> ident {
+    alt vec::last(p.node.idents) {
+        none. { // sigh
+          fail "Malformed path"; }
+      some(i) { ret i; }
+    }
+}
diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs
index 5b9925c2ce5..6ccc8cc12be 100644
--- a/src/comp/middle/resolve.rs
+++ b/src/comp/middle/resolve.rs
@@ -3,6 +3,7 @@ import syntax::{ast, ast_util, codemap};
 import syntax::ast::*;
 import ast::{ident, fn_ident, def, def_id, node_id};
 import syntax::ast_util::{local_def, def_id_of_def};
+import pat_util::*;
 
 import front::attr;
 import metadata::{csearch, cstore};
@@ -181,10 +182,12 @@ fn resolve_crate(sess: session, amap: ast_map::map, crate: @ast::crate) ->
           sess: sess};
     map_crate(e, crate);
     resolve_imports(*e);
-    check_for_collisions(e, *crate);
     check_exports(e);
     resolve_names(e, crate);
     resolve_impls(e, crate);
+    // check_for_collisions must happen after resolve_names so we
+    // don't complain if a pattern uses the same nullary tag twice
+    check_for_collisions(e, *crate);
     if sess.opts.warn_unused_imports {
         check_unused_imports(e);
     }
@@ -417,6 +420,20 @@ fn resolve_names(e: @env, c: @ast::crate) {
               }
             }
           }
+          /* Here we determine whether a given pat_ident binds a new
+           variable a refers to a nullary tag. */
+          ast::pat_ident(p, none.) {
+              let fnd = lookup_in_scope(*e, sc, p.span, path_to_ident(p),
+                                    ns_val(ns_a_tag));
+              alt fnd {
+                some(ast::def_variant(did, vid)) {
+                    e.def_map.insert(pat.id, ast::def_variant(did, vid));
+                }
+                _ {
+                    // Binds a var -- nothing needs to be done
+                }
+              }
+          }
           _ { }
         }
     }
@@ -539,30 +556,32 @@ fn visit_expr_with_scope(x: @ast::expr, sc: scopes, v: vt<scopes>) {
     }
 }
 
+// This is only for irrefutable patterns (e.g. ones that appear in a let)
+// So if x occurs, and x is already known to be a tag, that's always an error.
 fn visit_local_with_scope(e: @env, loc: @local, sc:scopes, v:vt<scopes>) {
-    // Checks whether the given local has the same name as a tag that's
+    // Check whether the given local has the same name as a tag that's
     // in scope
     // We disallow this, in order to make alt patterns consisting of
     // a single identifier unambiguous (does the pattern "foo" refer
     // to tag foo, or is it binding a new name foo?)
     alt loc.node.pat.node {
-      pat_bind(an_ident,_) {
+      pat_ident(an_ident,_) {
           // Be sure to pass ns_a_tag to lookup_in_scope so that
           // if this is a name that's being shadowed, we don't die
-          alt lookup_in_scope(*e, sc, loc.span, an_ident, ns_val(ns_a_tag)) {
+          alt lookup_in_scope(*e, sc, loc.span,
+                 path_to_ident(an_ident), ns_val(ns_a_tag)) {
               some(ast::def_variant(tag_id,variant_id)) {
                   // Declaration shadows a tag that's in scope.
                   // That's an error.
                   e.sess.span_err(loc.span,
                     #fmt("Declaration of %s shadows a tag that's in scope",
-                         an_ident));
+                         path_to_ident(an_ident)));
                   }
               _ {}
           }
       }
       _ {}
     }
-
     visit::visit_local(loc, sc, v);
 }
 
@@ -907,7 +926,7 @@ fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace)
           }
           scope_loop(local) {
             if ns == ns_val(ns_any_value) {
-                alt lookup_in_pat(name, local.node.pat) {
+                alt lookup_in_pat(e, name, local.node.pat) {
                   some(did) { ret some(ast::def_binding(did)); }
                   _ { }
                 }
@@ -918,7 +937,7 @@ fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace)
           }
           scope_arm(a) {
             if ns == ns_val(ns_any_value) {
-                alt lookup_in_pat(name, a.pats[0]) {
+                alt lookup_in_pat(e, name, a.pats[0]) {
                   some(did) { ret some(ast::def_binding(did)); }
                   _ { ret none; }
                 }
@@ -997,11 +1016,13 @@ fn lookup_in_ty_params(e: env, name: ident, ty_params: [ast::ty_param])
     ret none::<def>;
 }
 
-fn lookup_in_pat(name: ident, pat: @ast::pat) -> option::t<def_id> {
+fn lookup_in_pat(e: env, name: ident, pat: @ast::pat) -> option::t<def_id> {
     let found = none;
-    ast_util::pat_bindings(pat) {|bound|
-        let p_name = alt bound.node { ast::pat_bind(n, _) { n } };
-        if str::eq(p_name, name) { found = some(local_def(bound.id)); }
+
+    pat_util::pat_bindings(normalize_pat_def_map(e.def_map, pat)) {|bound|
+        let p_name = alt bound.node { ast::pat_ident(n, _) { n } };
+        if str::eq(path_to_ident(p_name), name)
+                    { found = some(local_def(bound.id)); }
     };
     ret found;
 }
@@ -1041,7 +1062,7 @@ fn lookup_in_block(e: env, name: ident, sp: span, b: ast::blk_, pos: uint,
                         let (style, loc) = locs[j];
                         if ns == ns_val(ns_any_value)
                                      && (i < pos || j < loc_pos) {
-                            alt lookup_in_pat(name, loc.node.pat) {
+                            alt lookup_in_pat(e, name, loc.node.pat) {
                               some(did) {
                                 ret some(ast::def_local(did, style));
                               }
@@ -1571,9 +1592,9 @@ fn check_item(e: @env, i: @ast::item, &&x: (), v: vt<()>) {
     }
 }
 
-fn check_pat(ch: checker, p: @ast::pat) {
-    ast_util::pat_bindings(p) {|p|
-        let ident = alt p.node { pat_bind(n, _) { n } };
+fn check_pat(e: @env, ch: checker, p: @ast::pat) {
+    pat_util::pat_bindings(normalize_pat_def_map(e.def_map, p)) {|p|
+        let ident = path_to_ident(alt p.node { pat_ident(n, _) { n } });
         add_name(ch, p.span, ident);
     };
 }
@@ -1581,13 +1602,13 @@ fn check_pat(ch: checker, p: @ast::pat) {
 fn check_arm(e: @env, a: ast::arm, &&x: (), v: vt<()>) {
     visit::visit_arm(a, x, v);
     let ch0 = checker(*e, "binding");
-    check_pat(ch0, a.pats[0]);
+    check_pat(e, ch0, a.pats[0]);
     let seen0 = ch0.seen;
     let i = vec::len(a.pats);
     while i > 1u {
         i -= 1u;
         let ch = checker(*e, "binding");
-        check_pat(ch, a.pats[i]);
+        check_pat(e, ch, a.pats[i]);
 
         // Ensure the bindings introduced in this pattern are the same as in
         // the first pattern.
@@ -1620,8 +1641,11 @@ fn check_block(e: @env, b: ast::blk, &&x: (), v: vt<()>) {
               ast::decl_local(locs) {
                 let local_values = checker(*e, "value");
                 for (_, loc) in locs {
-                    ast_util::pat_bindings(loc.node.pat) {|p|
-                        let ident = alt p.node { pat_bind(n, _) { n } };
+                        pat_util::pat_bindings
+                            (normalize_pat_def_map(e.def_map, loc.node.pat))
+                            {|p|
+                            let ident = path_to_ident(alt p.node
+                                 { pat_ident(n, _) { n } });
                         add_name(local_values, p.span, ident);
                         check_name(values, p.span, ident);
                     };
diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs
index 2a029c22b3d..314385a9f25 100644
--- a/src/comp/middle/trans.rs
+++ b/src/comp/middle/trans.rs
@@ -26,7 +26,8 @@ import back::{link, abi, upcall};
 import syntax::{ast, ast_util, codemap};
 import syntax::visit;
 import syntax::codemap::span;
-import syntax::print::pprust::{expr_to_str, stmt_to_str};
+import syntax::print::pprust::{expr_to_str, stmt_to_str, path_to_str};
+import pat_util::*;
 import visit::vt;
 import util::common::*;
 import lib::llvm::{llvm, mk_target_data, mk_type_names};
@@ -775,6 +776,8 @@ fn GEP_tag(cx: @block_ctxt, llblobptr: ValueRef, tag_id: ast::def_id,
 
     let true_arg_tys: [ty::t] = [];
     for aty: ty::t in arg_tys {
+            // Would be nice to have a way of stating the invariant
+            // that ty_substs is valid for aty
         let arg_ty = ty::substitute_type_params(bcx_tcx(cx), ty_substs, aty);
         true_arg_tys += [arg_ty];
     }
@@ -2203,7 +2206,7 @@ fn trans_eager_binop(cx: @block_ctxt, op: ast::binop, lhs: ValueRef,
         if is_float { FAdd(cx, lhs, rhs) }
         else { Add(cx, lhs, rhs) }
       }
-      ast::sub. {
+      ast::subtract. {
         if is_float { FSub(cx, lhs, rhs) }
         else { Sub(cx, lhs, rhs) }
       }
@@ -2641,6 +2644,7 @@ fn trans_local_var(cx: @block_ctxt, def: ast::def) -> local_var_result {
         alt table.find(id) {
           some(local_mem(v)) { {val: v, kind: owned} }
           some(local_imm(v)) { {val: v, kind: owned_imm} }
+          r { fail("take_local: internal error"); }
         }
     }
     alt def {
@@ -2649,9 +2653,11 @@ fn trans_local_var(cx: @block_ctxt, def: ast::def) -> local_var_result {
         ret { val: cx.fcx.llupvars.get(did.node), kind: owned };
       }
       ast::def_arg(did, _) {
+        assert (cx.fcx.llargs.contains_key(did.node));
         ret take_local(cx.fcx.llargs, did.node);
       }
       ast::def_local(did, _) | ast::def_binding(did) {
+        assert (cx.fcx.lllocals.contains_key(did.node));
         ret take_local(cx.fcx.lllocals, did.node);
       }
       ast::def_self(did) {
@@ -3502,6 +3508,7 @@ fn trans_expr(bcx: @block_ctxt, e: @ast::expr, dest: dest) -> @block_ctxt {
         ret trans_expr(bcx, ast_util::ternary_to_if(e), dest);
       }
       ast::expr_alt(expr, arms) {
+          //          tcx.sess.span_note(e.span, "about to call trans_alt");
         ret trans_alt::trans_alt(bcx, expr, arms, dest);
       }
       ast::expr_block(blk) {
@@ -4205,8 +4212,9 @@ fn alloc_ty(cx: @block_ctxt, t: ty::t) -> result {
 
 fn alloc_local(cx: @block_ctxt, local: @ast::local) -> @block_ctxt {
     let t = node_id_type(bcx_ccx(cx), local.node.id);
-    let is_simple = alt local.node.pat.node {
-      ast::pat_bind(_, none.) { true } _ { false }
+    let p = normalize_pat(bcx_tcx(cx), local.node.pat);
+    let is_simple = alt p.node {
+      ast::pat_ident(_, none.) { true } _ { false }
     };
     // Do not allocate space for locals that can be kept immediate.
     let ccx = bcx_ccx(cx);
@@ -4219,10 +4227,10 @@ fn alloc_local(cx: @block_ctxt, local: @ast::local) -> @block_ctxt {
         }
     }
     let r = alloc_ty(cx, t);
-    alt local.node.pat.node {
-      ast::pat_bind(ident, none.) {
+    alt p.node {
+      ast::pat_ident(pth, none.) {
         if bcx_ccx(cx).sess.opts.debuginfo {
-            let _: () = str::as_buf(ident, {|buf|
+            let _: () = str::as_buf(path_to_ident(pth), {|buf|
                 llvm::LLVMSetValueName(r.val, buf)
             });
         }
@@ -4635,7 +4643,7 @@ fn trans_const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
             if is_float { llvm::LLVMConstFAdd(te1, te2) }
             else        { llvm::LLVMConstAdd(te1, te2) }
           }
-          ast::sub.    {
+          ast::subtract. {
             if is_float { llvm::LLVMConstFSub(te1, te2) }
             else        { llvm::LLVMConstSub(te1, te2) }
           }
diff --git a/src/comp/middle/trans_alt.rs b/src/comp/middle/trans_alt.rs
index a7eb94b9288..5addbebddc1 100644
--- a/src/comp/middle/trans_alt.rs
+++ b/src/comp/middle/trans_alt.rs
@@ -1,8 +1,10 @@
 import core::{str, vec, option};
 import option::{some, none};
 
+import driver::session::session;
 import lib::llvm::llvm;
 import lib::llvm::llvm::{ValueRef, BasicBlockRef};
+import pat_util::*;
 import trans_build::*;
 import trans::{new_sub_block_ctxt, new_scope_block_ctxt, load_if_immediate};
 import syntax::ast;
@@ -10,6 +12,7 @@ import syntax::ast_util;
 import syntax::ast_util::{dummy_sp};
 import syntax::ast::def_id;
 import syntax::codemap::span;
+import syntax::print::pprust::pat_to_str;
 
 import trans_common::*;
 
@@ -61,6 +64,7 @@ fn trans_opt(bcx: @block_ctxt, o: opt) -> opt_result {
     }
 }
 
+// FIXME: invariant -- pat_id is bound in the def_map?
 fn variant_opt(ccx: @crate_ctxt, pat_id: ast::node_id) -> opt {
     let vdef = ast_util::variant_def_ids(ccx.tcx.def_map.get(pat_id));
     let variants = ty::tag_variants(ccx.tcx, vdef.tg);
@@ -83,13 +87,13 @@ type match_branch =
       bound: bind_map,
       data: @{body: BasicBlockRef,
               guard: option::t<@ast::expr>,
-              id_map: ast_util::pat_id_map}};
+              id_map: pat_id_map}};
 type match = [match_branch];
 
 fn has_nested_bindings(m: match, col: uint) -> bool {
     for br in m {
         alt br.pats[col].node {
-          ast::pat_bind(_, some(_)) { ret true; }
+          ast::pat_ident(_, some(_)) { ret true; }
           _ {}
         }
     }
@@ -99,12 +103,13 @@ fn has_nested_bindings(m: match, col: uint) -> bool {
 fn expand_nested_bindings(m: match, col: uint, val: ValueRef) -> match {
     let result = [];
     for br in m {
-        alt br.pats[col].node {
-          ast::pat_bind(name, some(inner)) {
+      alt br.pats[col].node {
+          ast::pat_ident(name, some(inner)) {
             let pats = vec::slice(br.pats, 0u, col) + [inner] +
                 vec::slice(br.pats, col + 1u, vec::len(br.pats));
             result += [@{pats: pats,
-                         bound: br.bound + [{ident: name, val: val}]
+                        bound: br.bound + [{ident: path_to_ident(name),
+                                val: val}]
                          with *br}];
           }
           _ { result += [br]; }
@@ -124,8 +129,9 @@ fn enter_match(m: match, col: uint, val: ValueRef, e: enter_pat) -> match {
                 vec::slice(br.pats, col + 1u, vec::len(br.pats));
             let new_br = @{pats: pats,
                            bound: alt br.pats[col].node {
-                             ast::pat_bind(name, none.) {
-                               br.bound + [{ident: name, val: val}]
+                             ast::pat_ident(name, none.) {
+                                 br.bound + [{ident: path_to_ident(name),
+                                              val: val}]
                              }
                              _ { br.bound }
                            } with *br};
@@ -139,11 +145,11 @@ fn enter_match(m: match, col: uint, val: ValueRef, e: enter_pat) -> match {
 
 fn enter_default(m: match, col: uint, val: ValueRef) -> match {
     fn matches_always(p: @ast::pat) -> bool {
-        ret alt p.node {
-          ast::pat_wild. | ast::pat_bind(_, none.) | ast::pat_rec(_, _) |
-          ast::pat_tup(_) { true }
-          _ { false }
-        };
+        alt p.node {
+                ast::pat_wild. | ast::pat_rec(_, _) |
+                ast::pat_ident(_, none.) | ast::pat_tup(_) { true }
+                _ { false }
+        }
     }
     fn e(p: @ast::pat) -> option::t<[@ast::pat]> {
         ret if matches_always(p) { some([]) } else { none };
@@ -257,6 +263,8 @@ fn extract_variant_args(bcx: @block_ctxt, pat_id: ast::node_id,
                         vdefs: {tg: def_id, var: def_id}, val: ValueRef) ->
    {vals: [ValueRef], bcx: @block_ctxt} {
     let ccx = bcx.fcx.lcx.ccx, bcx = bcx;
+    // invariant:
+    // pat_id must have the same length ty_param_substs as vdefs?
     let ty_param_substs = ty::node_id_to_type_params(ccx.tcx, pat_id);
     let blobptr = val;
     let variants = ty::tag_variants(ccx.tcx, vdefs.tg);
@@ -274,6 +282,9 @@ fn extract_variant_args(bcx: @block_ctxt, pat_id: ast::node_id,
     while i < size {
         check (valid_variant_index(i, bcx, vdefs_tg, vdefs_var));
         let r =
+            // invariant needed:
+            // how do we know it even makes sense to pass in ty_param_substs
+            // here? What if it's [] and the tag type has variables in it?
             trans::GEP_tag(bcx, blobptr, vdefs_tg, vdefs_var, ty_param_substs,
                            i);
         bcx = r.bcx;
@@ -328,7 +339,7 @@ fn pick_col(m: match) -> uint {
     fn score(p: @ast::pat) -> uint {
         alt p.node {
           ast::pat_lit(_) | ast::pat_tag(_, _) | ast::pat_range(_, _) { 1u }
-          ast::pat_bind(_, some(p)) { score(p) }
+          ast::pat_ident(_, some(p)) { score(p) }
           _ { 0u }
         }
     }
@@ -582,7 +593,7 @@ fn compile_submatch(bcx: @block_ctxt, m: match, vals: [ValueRef], f: mk_fail,
 
 // Returns false for unreachable blocks
 fn make_phi_bindings(bcx: @block_ctxt, map: [exit_node],
-                     ids: ast_util::pat_id_map) -> bool {
+                     ids: pat_util::pat_id_map) -> bool {
     let our_block = bcx.llbb as uint;
     let success = true, bcx = bcx;
     ids.items {|name, node_id|
@@ -623,7 +634,7 @@ fn make_phi_bindings(bcx: @block_ctxt, map: [exit_node],
     ret success;
 }
 
-fn trans_alt(cx: @block_ctxt, expr: @ast::expr, arms: [ast::arm],
+fn trans_alt(cx: @block_ctxt, expr: @ast::expr, arms_: [ast::arm],
              dest: trans::dest) -> @block_ctxt {
     let bodies = [];
     let match: match = [];
@@ -633,9 +644,15 @@ fn trans_alt(cx: @block_ctxt, expr: @ast::expr, arms: [ast::arm],
     let er = trans::trans_temp_expr(alt_cx, expr);
     if er.bcx.unreachable { ret er.bcx; }
 
+    /*
+      n.b. nothing else in this module should need to normalize,
+      b/c of this call
+     */
+    let arms = normalize_arms(bcx_tcx(cx), arms_);
+
     for a: ast::arm in arms {
         let body = new_scope_block_ctxt(er.bcx, "case_body");
-        let id_map = ast_util::pat_id_map(a.pats[0]);
+        let id_map = pat_util::pat_id_map(bcx_tcx(cx), a.pats[0]);
         bodies += [body];
         for p: @ast::pat in a.pats {
             match +=
@@ -666,7 +683,8 @@ fn trans_alt(cx: @block_ctxt, expr: @ast::expr, arms: [ast::arm],
     for a: ast::arm in arms {
         let body_cx = bodies[i];
         if make_phi_bindings(body_cx, exit_map,
-                             ast_util::pat_id_map(a.pats[0])) {
+                             pat_util::pat_id_map(bcx_tcx(cx),
+                                                  a.pats[0])) {
             let arm_dest = trans::dup_for_join(dest);
             arm_dests += [arm_dest];
             arm_cxs += [trans::trans_block_dps(body_cx, a.body, arm_dest)];
@@ -684,8 +702,10 @@ fn trans_alt(cx: @block_ctxt, expr: @ast::expr, arms: [ast::arm],
 fn bind_irrefutable_pat(bcx: @block_ctxt, pat: @ast::pat, val: ValueRef,
                         make_copy: bool) -> @block_ctxt {
     let ccx = bcx.fcx.lcx.ccx, bcx = bcx;
-    alt pat.node {
-      ast::pat_bind(_, inner) {
+
+    // Necessary since bind_irrefutable_pat is called outside trans_alt
+    alt normalize_pat(bcx_tcx(bcx), pat).node {
+      ast::pat_ident(_,inner) {
         if make_copy || ccx.copy_map.contains_key(pat.id) {
             let ty = ty::node_id_to_monotype(ccx.tcx, pat.id);
             // FIXME: Could constrain pat_bind to make this
diff --git a/src/comp/middle/tstate/annotate.rs b/src/comp/middle/tstate/annotate.rs
index d4dbf830a5f..f44ac06e4c1 100644
--- a/src/comp/middle/tstate/annotate.rs
+++ b/src/comp/middle/tstate/annotate.rs
@@ -1,12 +1,12 @@
 
 import core::{int, uint};
 import syntax::ast::*;
-import syntax::ast_util::pat_binding_ids;
 import syntax::visit;
 import syntax::codemap::span;
 import util::common::{log_stmt};
 import aux::{num_constraints, get_fn_info, crate_ctxt, add_node};
-import middle::tstate::ann::empty_ann;
+import ann::empty_ann;
+import pat_util::pat_binding_ids;
 
 fn collect_ids_expr(e: @expr, rs: @mutable [node_id]) { *rs += [e.id]; }
 
diff --git a/src/comp/middle/tstate/auxiliary.rs b/src/comp/middle/tstate/auxiliary.rs
index 5c0c6c78b3a..cb592be250c 100644
--- a/src/comp/middle/tstate/auxiliary.rs
+++ b/src/comp/middle/tstate/auxiliary.rs
@@ -1,5 +1,6 @@
 import core::{vec, int, uint, option};
 import option::*;
+import pat_util::*;
 import syntax::ast::*;
 import syntax::ast_util::*;
 import syntax::{visit, codemap};
@@ -69,10 +70,10 @@ fn tritv_to_str(fcx: fn_ctxt, v: tritv::t) -> str {
     for p: norm_constraint in constraints(fcx) {
         alt tritv_get(v, p.bit_num) {
           dont_care. { }
-          t {
+          tt {
             s +=
                 if comma { ", " } else { comma = true; "" } +
-                    if t == tfalse { "!" } else { "" } +
+                    if tt == tfalse { "!" } else { "" } +
                     constraint_to_str(fcx.ccx.tcx, p.c);
           }
         }
@@ -313,7 +314,7 @@ fn node_id_to_ts_ann(ccx: crate_ctxt, id: node_id) -> ts_ann {
         #error("node_id_to_ts_ann: no ts_ann for node_id %d", id);
         fail;
       }
-      some(t) { ret t; }
+      some(tt) { ret tt; }
     }
 }
 
@@ -779,13 +780,6 @@ fn replace(subst: subst, d: pred_args) -> [constr_arg_general_<inst>] {
     ret rslt;
 }
 
-fn path_to_ident(cx: ty::ctxt, p: @path) -> ident {
-    alt vec::last(p.node.idents) {
-      none. { cx.sess.span_fatal(p.span, "Malformed path"); }
-      some(i) { ret i; }
-    }
-}
-
 tag if_ty { if_check; plain_if; }
 
 fn local_node_id_to_def_id_strict(fcx: fn_ctxt, sp: span, i: node_id) ->
@@ -1059,18 +1053,20 @@ fn ast_constr_to_sp_constr(tcx: ty::ctxt, args: [arg], c: @constr) ->
 
 type binding = {lhs: [inst], rhs: option::t<initializer>};
 
-fn local_to_bindings(loc: @local) -> binding {
+fn local_to_bindings(tcx: ty::ctxt, loc: @local) -> binding {
     let lhs = [];
-    pat_bindings(loc.node.pat) {|p|
-        let ident = alt p.node { pat_bind(name, _) { name } };
+    pat_bindings(pat_util::normalize_pat(tcx, loc.node.pat)) {|p|
+            let ident = alt p.node
+               { pat_ident(name, _) { path_to_ident(name) } };
         lhs += [{ident: ident, node: p.id}];
     };
     {lhs: lhs, rhs: loc.node.init}
 }
 
-fn locals_to_bindings(locals: [(let_style, @local)]) -> [binding] {
+fn locals_to_bindings(tcx: ty::ctxt,
+                      locals: [(let_style, @local)]) -> [binding] {
     let rslt = [];
-    for (_, loc) in locals { rslt += [local_to_bindings(loc)]; }
+    for (_, loc) in locals { rslt += [local_to_bindings(tcx, loc)]; }
     ret rslt;
 }
 
diff --git a/src/comp/middle/tstate/collect_locals.rs b/src/comp/middle/tstate/collect_locals.rs
index f303e790189..102451a6985 100644
--- a/src/comp/middle/tstate/collect_locals.rs
+++ b/src/comp/middle/tstate/collect_locals.rs
@@ -1,18 +1,20 @@
+import option::*;
+import pat_util::*;
 import syntax::ast::*;
 import syntax::ast_util::*;
-import option::*;
 import syntax::visit;
-import aux::*;
 import util::common::new_def_hash;
 import syntax::codemap::span;
 import syntax::ast_util::respan;
 import driver::session::session;
+import aux::*;
 
 type ctxt = {cs: @mutable [sp_constr], tcx: ty::ctxt};
 
 fn collect_local(loc: @local, cx: ctxt, v: visit::vt<ctxt>) {
-    pat_bindings(loc.node.pat) {|p|
-        let ident = alt p.node { pat_bind(id, _) { id } };
+    pat_bindings(pat_util::normalize_pat(cx.tcx, loc.node.pat)) {|p|
+            let ident = alt p.node
+                 { pat_ident(id, _) { path_to_ident(id) } };
         log(debug, "collect_local: pushing " + ident);;
         *cx.cs += [respan(loc.span, ninit(p.id, ident))];
     };
diff --git a/src/comp/middle/tstate/pre_post_conditions.rs b/src/comp/middle/tstate/pre_post_conditions.rs
index 7d52c5882a3..57bb0379779 100644
--- a/src/comp/middle/tstate/pre_post_conditions.rs
+++ b/src/comp/middle/tstate/pre_post_conditions.rs
@@ -8,6 +8,8 @@ import bitvectors::{bit_num, seq_preconds, seq_postconds,
                     intersect_states,
                     relax_precond_block, gen};
 import tritv::*;
+
+import pat_util::*;
 import syntax::ast::*;
 import syntax::ast_util::*;
 import syntax::visit;
@@ -16,6 +18,7 @@ import util::common::{new_def_hash, log_expr, field_exprs,
 import syntax::codemap::span;
 import driver::session::session;
 
+
 fn find_pre_post_mod(_m: _mod) -> _mod {
     #debug("implement find_pre_post_mod!");
     fail;
@@ -103,8 +106,9 @@ fn find_pre_post_loop(fcx: fn_ctxt, l: @local, index: @expr, body: blk,
                       id: node_id) {
     find_pre_post_expr(fcx, index);
     find_pre_post_block(fcx, body);
-    pat_bindings(l.node.pat) {|p|
-        let ident = alt p.node { pat_bind(id, _) { id } };
+    pat_bindings(normalize_pat(fcx.ccx.tcx, l.node.pat)) {|p|
+        let ident = alt p.node
+             { pat_ident(id, _) { path_to_ident(id) } };
         let v_init = ninit(p.id, ident);
         relax_precond_block(fcx, bit_num(fcx, v_init) as node_id, body);
         // Hack: for-loop index variables are frequently ignored,
@@ -197,7 +201,7 @@ fn gen_if_local(fcx: fn_ctxt, lhs: @expr, rhs: @expr, larger_id: node_id,
             set_pre_and_post(fcx.ccx, larger_id, p.precondition,
                              p.postcondition);
             gen(fcx, larger_id,
-                ninit(d_id.node, path_to_ident(fcx.ccx.tcx, pth)));
+                ninit(d_id.node, path_to_ident(pth)));
           }
           _ { find_pre_post_exprs(fcx, [lhs, rhs], larger_id); }
         }
@@ -232,7 +236,7 @@ fn handle_update(fcx: fn_ctxt, parent: @expr, lhs: @expr, rhs: @expr,
               def_local(d_id, _) {
                 let i =
                     bit_num(fcx,
-                            ninit(d_id.node, path_to_ident(fcx.ccx.tcx, p)));
+                            ninit(d_id.node, path_to_ident(p)));
                 require_and_preserve(i, expr_pp(fcx.ccx, lhs));
               }
               _ { }
@@ -250,9 +254,9 @@ fn handle_update(fcx: fn_ctxt, parent: @expr, lhs: @expr, rhs: @expr,
                 alt d1 {
                   some(id1) {
                     let instlhs =
-                        {ident: path_to_ident(fcx.ccx.tcx, p), node: id};
+                        {ident: path_to_ident(p), node: id};
                     let instrhs =
-                        {ident: path_to_ident(fcx.ccx.tcx, p1), node: id1};
+                        {ident: path_to_ident(p1), node: id1};
                     copy_in_poststate_two(fcx, tmp, post, instlhs, instrhs,
                                           ty);
                   }
@@ -340,7 +344,7 @@ fn find_pre_post_expr(fcx: fn_ctxt, e: @expr) {
       expr_path(p) {
         let rslt = expr_pp(fcx.ccx, e);
         clear_pp(rslt);
-        handle_var(fcx, rslt, e.id, path_to_ident(fcx.ccx.tcx, p));
+        handle_var(fcx, rslt, e.id, path_to_ident(p));
       }
       expr_log(_, lvl, arg) {
         find_pre_post_exprs(fcx, [lvl, arg], e.id);
@@ -581,18 +585,19 @@ fn find_pre_post_stmt(fcx: fn_ctxt, s: stmt) {
                       _ { }
                     }
 
-                    pat_bindings(alocal.node.pat) {|pat|
+                    pat_bindings(normalize_pat(fcx.ccx.tcx, alocal.node.pat))
+                        {|pat|
                         /* FIXME: This won't be necessary when typestate
                         works well enough for pat_bindings to return a
                         refinement-typed thing. */
-                        let ident = alt pat.node { pat_bind(n, _) { n } };
+                            let ident = alt pat.node
+                                     { pat_ident(n, _) { path_to_ident(n) } };
                         alt p {
                           some(p) {
                             copy_in_postcond(fcx, id,
                                              {ident: ident, node: pat.id},
                                              {ident:
-                                                  path_to_ident(fcx.ccx.tcx,
-                                                                p),
+                                                  path_to_ident(p),
                                               node: an_init.expr.id},
                                              op_to_oper_ty(an_init.op));
                           }
@@ -612,11 +617,14 @@ fn find_pre_post_stmt(fcx: fn_ctxt, s: stmt) {
                                seq_preconds(fcx, [prev_pp, e_pp]));
                     /* Include the LHSs too, since those aren't in the
                      postconds of the RHSs themselves */
-                    pat_bindings(alocal.node.pat) {|pat|
+                    pat_bindings(normalize_pat(fcx.ccx.tcx, alocal.node.pat))
+                        {|pat|
+                            // FIXME
+                            // Generalize this pattern? map_if_ident...
                         alt pat.node {
-                          pat_bind(n, _) {
-                            set_in_postcond(bit_num(fcx, ninit(pat.id, n)),
-                                            prev_pp);
+                          pat_ident(n, _) {
+                            set_in_postcond(bit_num(fcx,
+                               ninit(pat.id, path_to_ident(n))), prev_pp);
                           }
                         }
                     };
diff --git a/src/comp/middle/tstate/states.rs b/src/comp/middle/tstate/states.rs
index adfc4411adb..50208b9dacf 100644
--- a/src/comp/middle/tstate/states.rs
+++ b/src/comp/middle/tstate/states.rs
@@ -5,6 +5,7 @@ import aux::*;
 import tritv::{tritv_clone, tritv_set, ttrue};
 
 import bitvectors::*;
+import pat_util::*;
 import syntax::ast::*;
 import syntax::ast_util::*;
 import syntax::codemap::span;
@@ -37,7 +38,7 @@ fn handle_move_or_copy(fcx: fn_ctxt, post: poststate, rhs_path: @path,
       some(rhsid) {
         // RHS is a local var
         let instrhs =
-            {ident: path_to_ident(fcx.ccx.tcx, rhs_path), node: rhsid.node};
+            {ident: path_to_ident(rhs_path), node: rhsid.node};
         copy_in_poststate(fcx, post, instlhs, instrhs,
                           op_to_oper_ty(init_op));
       }
@@ -143,9 +144,9 @@ fn find_pre_post_state_two(fcx: fn_ctxt, pres: prestate, lhs: @expr,
                 alt d1 {
                   some(id1) {
                     let instlhs =
-                        {ident: path_to_ident(fcx.ccx.tcx, p), node: id};
+                        {ident: path_to_ident(p), node: id};
                     let instrhs =
-                        {ident: path_to_ident(fcx.ccx.tcx, p1), node: id1};
+                        {ident: path_to_ident(p1), node: id1};
                     copy_in_poststate_two(fcx, tmp, post, instlhs, instrhs,
                                           ty);
                   }
@@ -206,8 +207,9 @@ fn find_pre_post_state_loop(fcx: fn_ctxt, pres: prestate, l: @local,
     // Make sure the index vars are considered initialized
     // in the body
     let index_post = tritv_clone(expr_poststate(fcx.ccx, index));
-    pat_bindings(l.node.pat) {|p|
-        let ident = alt p.node { pat_bind(name, _) { name } };
+    pat_bindings(pat_util::normalize_pat(fcx.ccx.tcx, l.node.pat)) {|p|
+        let ident = alt p.node
+           { pat_ident(name, _) { path_to_ident(name) } };
         set_in_poststate_ident(fcx, p.id, ident, index_post);
     };
 
@@ -231,7 +233,7 @@ fn gen_if_local(fcx: fn_ctxt, p: poststate, e: @expr) -> bool {
         alt fcx.ccx.tcx.def_map.find(e.id) {
           some(def_local(loc, _)) {
             ret set_in_poststate_ident(fcx, loc.node,
-                                       path_to_ident(fcx.ccx.tcx, pth), p);
+                                       path_to_ident(pth), p);
           }
           _ { ret false; }
         }
@@ -632,7 +634,8 @@ fn find_pre_post_state_stmt(fcx: fn_ctxt, pres: prestate, s: @stmt) -> bool {
         alt adecl.node {
           decl_local(alocals) {
             set_prestate(stmt_ann, pres);
-            let c_and_p = seq_states(fcx, pres, locals_to_bindings(alocals));
+            let c_and_p = seq_states(fcx, pres,
+                  locals_to_bindings(fcx.ccx.tcx, alocals));
             /* important to do this in one step to ensure
             termination (don't want to set changed to true
             for intermediate changes) */
diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs
index d44b0ee0072..59fd826791a 100644
--- a/src/comp/middle/ty.rs
+++ b/src/comp/middle/ty.rs
@@ -371,6 +371,8 @@ const idx_first_others: uint = 20u;
 
 type type_store = interner::interner<@raw_t>;
 
+// substs is a list of actuals that correspond to ty's
+// formal parameters
 type ty_param_substs_opt_and_ty = {substs: option::t<[ty::t]>, ty: ty::t};
 
 type node_type_table =
@@ -2215,7 +2217,7 @@ mod unify {
           ty::ty_box(expected_mt) {
             alt struct(cx.tcx, actual) {
               ty::ty_box(actual_mt) {
-                let (mut, var) = alt unify_mut(
+                let (mutt, var) = alt unify_mut(
                     expected_mt.mut, actual_mt.mut, variance) {
                   none. { ret ures_err(terr_box_mutability); }
                   some(mv) { mv }
@@ -2224,7 +2226,7 @@ mod unify {
                     cx, expected_mt.ty, actual_mt.ty, var);
                 alt result {
                   ures_ok(result_sub) {
-                    let mt = {ty: result_sub, mut: mut};
+                    let mt = {ty: result_sub, mut: mutt};
                     ret ures_ok(mk_box(cx.tcx, mt));
                   }
                   _ { ret result; }
@@ -2236,7 +2238,7 @@ mod unify {
           ty::ty_uniq(expected_mt) {
             alt struct(cx.tcx, actual) {
               ty::ty_uniq(actual_mt) {
-                let (mut, var) = alt unify_mut(
+                let (mutt, var) = alt unify_mut(
                     expected_mt.mut, actual_mt.mut, variance) {
                   none. { ret ures_err(terr_box_mutability); }
                   some(mv) { mv }
@@ -2245,7 +2247,7 @@ mod unify {
                     cx, expected_mt.ty, actual_mt.ty, var);
                 alt result {
                   ures_ok(result_mt) {
-                    let mt = {ty: result_mt, mut: mut};
+                    let mt = {ty: result_mt, mut: mutt};
                     ret ures_ok(mk_uniq(cx.tcx, mt));
                   }
                   _ { ret result; }
@@ -2257,7 +2259,7 @@ mod unify {
           ty::ty_vec(expected_mt) {
             alt struct(cx.tcx, actual) {
               ty::ty_vec(actual_mt) {
-                let (mut, var) = alt unify_mut(
+                let (mutt, var) = alt unify_mut(
                     expected_mt.mut, actual_mt.mut, variance) {
                   none. { ret ures_err(terr_vec_mutability); }
                   some(mv) { mv }
@@ -2266,7 +2268,7 @@ mod unify {
                     cx, expected_mt.ty, actual_mt.ty, var);
                 alt result {
                   ures_ok(result_sub) {
-                    let mt = {ty: result_sub, mut: mut};
+                    let mt = {ty: result_sub, mut: mutt};
                     ret ures_ok(mk_vec(cx.tcx, mt));
                   }
                   _ { ret result; }
@@ -2278,7 +2280,7 @@ mod unify {
           ty::ty_ptr(expected_mt) {
             alt struct(cx.tcx, actual) {
               ty::ty_ptr(actual_mt) {
-                let (mut, var) = alt unify_mut(
+                let (mutt, var) = alt unify_mut(
                     expected_mt.mut, actual_mt.mut, variance) {
                   none. { ret ures_err(terr_vec_mutability); }
                   some(mv) { mv }
@@ -2287,7 +2289,7 @@ mod unify {
                     cx, expected_mt.ty, actual_mt.ty, var);
                 alt result {
                   ures_ok(result_sub) {
-                    let mt = {ty: result_sub, mut: mut};
+                    let mt = {ty: result_sub, mut: mutt};
                     ret ures_ok(mk_ptr(cx.tcx, mt));
                   }
                   _ { ret result; }
@@ -2342,7 +2344,7 @@ mod unify {
                 while i < expected_len {
                     let expected_field = expected_fields[i];
                     let actual_field = actual_fields[i];
-                    let (mut, var) = alt unify_mut(
+                    let (mutt, var) = alt unify_mut(
                         expected_field.mt.mut, actual_field.mt.mut, variance)
                         {
                       none. { ret ures_err(terr_record_mutability); }
@@ -2359,7 +2361,7 @@ mod unify {
                                    actual_field.mt.ty, var);
                     alt result {
                       ures_ok(rty) {
-                        let mt = {ty: rty, mut: mut};
+                        let mt = {ty: rty, mut: mutt};
                         result_fields += [{mt: mt with expected_field}];
                       }
                       _ { ret result; }
@@ -2579,11 +2581,18 @@ fn type_err_to_str(err: ty::type_err) -> str {
 // Replaces type parameters in the given type using the given list of
 // substitions.
 fn substitute_type_params(cx: ctxt, substs: [ty::t], typ: t) -> t {
-    if !type_contains_params(cx, typ) { ret typ; }
+
+   if !type_contains_params(cx, typ) { ret typ; }
+    // Precondition? idx < vec::len(substs)
     fn substituter(_cx: ctxt, substs: @[ty::t], idx: uint, _did: def_id)
         -> t {
-        // FIXME: bounds check can fail
-        ret substs[idx];
+        if idx < vec::len(*substs) {
+            ret substs[idx];
+        }
+        else {
+            fail #fmt("Internal error in substituter (substitute_type_params)\
+             %u %u", vec::len(*substs), idx);
+        }
     }
     ret fold_ty(cx, fm_param(bind substituter(cx, @substs, _, _)), typ);
 }
@@ -2729,7 +2738,7 @@ fn is_binopable(cx: ctxt, ty: t, op: ast::binop) -> bool {
     fn opcat(op: ast::binop) -> int {
         alt op {
           ast::add. { opcat_add }
-          ast::sub. { opcat_sub }
+          ast::subtract. { opcat_sub }
           ast::mul. { opcat_mult }
           ast::div. { opcat_mult }
           ast::rem. { opcat_mult }
diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs
index c7736056a50..d447dfcfce4 100644
--- a/src/comp/middle/typeck.rs
+++ b/src/comp/middle/typeck.rs
@@ -6,6 +6,7 @@ import metadata::csearch;
 import driver::session::session;
 import util::common::*;
 import syntax::codemap::span;
+import pat_util::*;
 import middle::ty;
 import middle::ty::{node_id_to_type, arg, block_ty,
                     expr_ty, field, node_type_table, mk_nil,
@@ -1137,8 +1138,8 @@ fn gather_locals(ccx: @crate_ctxt,
 
     // Add pattern bindings.
     let visit_pat = fn@(p: @ast::pat, &&e: (), v: visit::vt<()>) {
-            alt p.node {
-              ast::pat_bind(_, _) { assign(p.id, none); }
+        alt normalize_pat(ccx.tcx, p).node {
+              ast::pat_ident(_, _) { assign(p.id, none); }
               _ {/* no-op */ }
             }
             visit::visit_pat(p, e, v);
@@ -1181,9 +1182,9 @@ fn valid_range_bounds(from: @ast::expr, to: @ast::expr) -> bool {
 
 // Pattern checking is top-down rather than bottom-up so that bindings get
 // their types immediately.
-fn check_pat(fcx: @fn_ctxt, map: ast_util::pat_id_map, pat: @ast::pat,
+fn check_pat(fcx: @fn_ctxt, map: pat_util::pat_id_map, pat: @ast::pat,
              expected: ty::t) {
-    alt pat.node {
+    alt normalize_pat(fcx.ccx.tcx, pat).node {
       ast::pat_wild. {
           alt structure_of(fcx, pat.span, expected) {
                   ty::ty_tag(_, expected_tps) {
@@ -1218,11 +1219,11 @@ fn check_pat(fcx: @fn_ctxt, map: ast_util::pat_id_map, pat: @ast::pat,
         }
         write::ty_only_fixup(fcx, pat.id, b_ty);
       }
-      ast::pat_bind(name, sub) {
+      ast::pat_ident(name, sub) {
         let vid = lookup_local(fcx, pat.span, pat.id);
         let typ = ty::mk_var(fcx.ccx.tcx, vid);
         typ = demand::simple(fcx, pat.span, expected, typ);
-        let canon_id = map.get(name);
+        let canon_id = map.get(path_to_ident(name));
         if canon_id != pat.id {
             let ct =
                 ty::mk_var(fcx.ccx.tcx,
@@ -2014,7 +2015,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
         // bindings.
         let pattern_ty = ty::expr_ty(tcx, expr);
         for arm: ast::arm in arms {
-            let id_map = ast_util::pat_id_map(arm.pats[0]);
+            let id_map = pat_util::pat_id_map(tcx, arm.pats[0]);
             for p: @ast::pat in arm.pats {
                 check_pat(fcx, id_map, p, pattern_ty);
             }
@@ -2360,7 +2361,7 @@ fn check_decl_local(fcx: @fn_ctxt, local: @ast::local) -> bool {
           }
           _ {/* fall through */ }
         }
-        let id_map = ast_util::pat_id_map(local.node.pat);
+        let id_map = pat_util::pat_id_map(fcx.ccx.tcx, local.node.pat);
         check_pat(fcx, id_map, local.node.pat, t);
       }
     }
diff --git a/src/comp/rustc.rc b/src/comp/rustc.rc
index f688be64374..db36e6adc96 100644
--- a/src/comp/rustc.rc
+++ b/src/comp/rustc.rc
@@ -39,6 +39,7 @@ mod middle {
     mod gc;
     mod debuginfo;
     mod capture;
+    mod pat_util;
 
     mod tstate {
         mod ck;
diff --git a/src/comp/syntax/ast.rs b/src/comp/syntax/ast.rs
index b953362b1be..6419e690c44 100644
--- a/src/comp/syntax/ast.rs
+++ b/src/comp/syntax/ast.rs
@@ -97,7 +97,17 @@ type field_pat = {ident: ident, pat: @pat};
 
 tag pat_ {
     pat_wild;
-    pat_bind(ident, option::t<@pat>);
+    // A pat_ident may either be a new bound variable,
+    // or a nullary tag (in which case the second field
+    // is none).
+    // In the nullary tag case, the parser can't determine
+    // which it is. The resolver determines this, and
+    // records this pattern's node_id in an auxiliary
+    // set (of "pat_idents that refer to nullary tags")
+    // After the resolution phase, code should never pattern-
+    // match on a pat directly! Always call pat_util::normalize_pat --
+    // it turns any pat_idents that refer to nullary tags into pat_tags.
+    pat_ident(@path, option::t<@pat>);
     pat_tag(@path, [@pat]);
     pat_rec([field_pat], bool);
     pat_tup([@pat]);
@@ -126,7 +136,7 @@ pure fn is_blockish(p: ast::proto) -> bool {
 
 tag binop {
     add;
-    sub;
+    subtract;
     mul;
     div;
     rem;
diff --git a/src/comp/syntax/ast_util.rs b/src/comp/syntax/ast_util.rs
index eaac7fa9596..ed65aa11df9 100644
--- a/src/comp/syntax/ast_util.rs
+++ b/src/comp/syntax/ast_util.rs
@@ -33,43 +33,10 @@ fn def_id_of_def(d: def) -> def_id {
     }
 }
 
-type pat_id_map = std::map::hashmap<str, node_id>;
-
-// This is used because same-named variables in alternative patterns need to
-// use the node_id of their namesake in the first pattern.
-fn pat_id_map(pat: @pat) -> pat_id_map {
-    let map = std::map::new_str_hash::<node_id>();
-    pat_bindings(pat) {|bound|
-        let name = alt bound.node { pat_bind(n, _) { n } };
-        map.insert(name, bound.id);
-    };
-    ret map;
-}
-
-// FIXME: could return a constrained type
-fn pat_bindings(pat: @pat, it: block(@pat)) {
-    alt pat.node {
-      pat_bind(_, option::none.) { it(pat); }
-      pat_bind(_, option::some(sub)) { it(pat); pat_bindings(sub, it); }
-      pat_tag(_, sub) { for p in sub { pat_bindings(p, it); } }
-      pat_rec(fields, _) { for f in fields { pat_bindings(f.pat, it); } }
-      pat_tup(elts) { for elt in elts { pat_bindings(elt, it); } }
-      pat_box(sub) { pat_bindings(sub, it); }
-      pat_uniq(sub) { pat_bindings(sub, it); }
-      pat_wild. | pat_lit(_) | pat_range(_, _) { }
-    }
-}
-
-fn pat_binding_ids(pat: @pat) -> [node_id] {
-    let found = [];
-    pat_bindings(pat) {|b| found += [b.id]; };
-    ret found;
-}
-
 fn binop_to_str(op: binop) -> str {
     alt op {
       add. { ret "+"; }
-      sub. { ret "-"; }
+      subtract. { ret "-"; }
       mul. { ret "*"; }
       div. { ret "/"; }
       rem. { ret "%"; }
@@ -262,7 +229,7 @@ fn eval_const_expr(e: @expr) -> const_val {
         alt (eval_const_expr(a), eval_const_expr(b)) {
           (const_float(a), const_float(b)) {
             alt op {
-              add. { const_float(a + b) } sub. { const_float(a - b) }
+              add. { const_float(a + b) } subtract. { const_float(a - b) }
               mul. { const_float(a * b) } div. { const_float(a / b) }
               rem. { const_float(a % b) } eq. { fromb(a == b) }
               lt. { fromb(a < b) } le. { fromb(a <= b) } ne. { fromb(a != b) }
@@ -271,7 +238,7 @@ fn eval_const_expr(e: @expr) -> const_val {
           }
           (const_int(a), const_int(b)) {
             alt op {
-              add. { const_int(a + b) } sub. { const_int(a - b) }
+              add. { const_int(a + b) } subtract. { const_int(a - b) }
               mul. { const_int(a * b) } div. { const_int(a / b) }
               rem. { const_int(a % b) } and. | bitand. { const_int(a & b) }
               or. | bitor. { const_int(a | b) } bitxor. { const_int(a ^ b) }
@@ -282,7 +249,7 @@ fn eval_const_expr(e: @expr) -> const_val {
           }
           (const_uint(a), const_uint(b)) {
             alt op {
-              add. { const_uint(a + b) } sub. { const_uint(a - b) }
+              add. { const_uint(a + b) } subtract. { const_uint(a - b) }
               mul. { const_uint(a * b) } div. { const_uint(a / b) }
               rem. { const_uint(a % b) } and. | bitand. { const_uint(a & b) }
               or. | bitor. { const_uint(a | b) } bitxor. { const_uint(a ^ b) }
@@ -327,6 +294,10 @@ fn lit_eq(a: @lit, b: @lit) -> bool {
     compare_const_vals(lit_to_const(a), lit_to_const(b)) == 0
 }
 
+fn ident_to_path(s: span, i: ident) -> @path {
+    @respan(s, {global: false, idents: [i], types: []})
+}
+
 // Local Variables:
 // mode: rust
 // fill-column: 78;
diff --git a/src/comp/syntax/fold.rs b/src/comp/syntax/fold.rs
index e74e1cd9ba7..a5ce71f8cd5 100644
--- a/src/comp/syntax/fold.rs
+++ b/src/comp/syntax/fold.rs
@@ -10,6 +10,7 @@ export make_fold;
 export noop_fold_crate;
 export noop_fold_item;
 export noop_fold_expr;
+export noop_fold_pat;
 export noop_fold_mod;
 export noop_fold_ty;
 
@@ -273,8 +274,8 @@ fn noop_fold_arm(a: arm, fld: ast_fold) -> arm {
 fn noop_fold_pat(p: pat_, fld: ast_fold) -> pat_ {
     ret alt p {
           pat_wild. { p }
-          pat_bind(ident, sub) {
-            pat_bind(fld.fold_ident(ident), option::map(sub, fld.fold_pat))
+          pat_ident(pth, sub) {
+            pat_ident(fld.fold_path(pth), option::map(sub, fld.fold_pat))
           }
           pat_lit(_) { p }
           pat_tag(pth, pats) {
@@ -317,8 +318,8 @@ fn noop_fold_expr(e: expr_, fld: ast_fold) -> expr_ {
     let fold_mac = bind fold_mac_(_, fld);
 
     ret alt e {
-          expr_vec(exprs, mut) {
-            expr_vec(fld.map_exprs(fld.fold_expr, exprs), mut)
+            expr_vec(exprs, mutt) {
+            expr_vec(fld.map_exprs(fld.fold_expr, exprs), mutt)
           }
           expr_rec(fields, maybe_expr) {
             expr_rec(vec::map(fields, fold_field),
@@ -390,8 +391,7 @@ fn noop_fold_expr(e: expr_, fld: ast_fold) -> expr_ {
           }
           expr_path(pth) { expr_path(fld.fold_path(pth)) }
           expr_fail(e) { expr_fail(option::map(e, fld.fold_expr)) }
-          expr_break. { e }
-          expr_cont. { e }
+          expr_break. | expr_cont. { e }
           expr_ret(e) { expr_ret(option::map(e, fld.fold_expr)) }
           expr_be(e) { expr_be(fld.fold_expr(e)) }
           expr_log(i, lv, e) { expr_log(i, fld.fold_expr(lv),
diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs
index ef65a0bd897..b8f55dcba96 100644
--- a/src/comp/syntax/parse/parser.rs
+++ b/src/comp/syntax/parse/parser.rs
@@ -8,6 +8,7 @@ import token::can_begin_expr;
 import codemap::span;
 import util::interner;
 import ast::{node_id, spanned};
+import ast_util::{mk_sp, ident_to_path};
 import front::attr;
 import lexer::reader;
 import driver::diagnostic;
@@ -1097,7 +1098,7 @@ fn prec_table() -> @[op_spec] {
           {tok: token::BINOP(token::SLASH), op: ast::div, prec: 11},
           {tok: token::BINOP(token::PERCENT), op: ast::rem, prec: 11},
           {tok: token::BINOP(token::PLUS), op: ast::add, prec: 10},
-          {tok: token::BINOP(token::MINUS), op: ast::sub, prec: 10},
+          {tok: token::BINOP(token::MINUS), op: ast::subtract, prec: 10},
           {tok: token::BINOP(token::LSL), op: ast::lsl, prec: 9},
           {tok: token::BINOP(token::LSR), op: ast::lsr, prec: 9},
           {tok: token::BINOP(token::ASR), op: ast::asr, prec: 9},
@@ -1165,7 +1166,7 @@ fn parse_assign_expr(p: parser) -> @ast::expr {
         let aop = ast::add;
         alt op {
           token::PLUS. { aop = ast::add; }
-          token::MINUS. { aop = ast::sub; }
+          token::MINUS. { aop = ast::subtract; }
           token::STAR. { aop = ast::mul; }
           token::SLASH. { aop = ast::div; }
           token::PERCENT. { aop = ast::rem; }
@@ -1426,7 +1427,11 @@ fn parse_pat(p: parser) -> @ast::pat {
                 break;
             }
 
+            let lo1 = p.last_span.lo;
             let fieldname = parse_ident(p);
+            let hi1 = p.last_span.lo;
+            let fieldpath = ast_util::ident_to_path(ast_util::mk_sp(lo1, hi1),
+                                          fieldname);
             let subpat;
             if p.token == token::COLON {
                 p.bump();
@@ -1436,7 +1441,7 @@ fn parse_pat(p: parser) -> @ast::pat {
                     p.fatal("found " + fieldname + " in binding position");
                 }
                 subpat = @{id: p.get_id(),
-                           node: ast::pat_bind(fieldname, none),
+                           node: ast::pat_ident(fieldpath, none),
                            span: ast_util::mk_sp(lo, hi)};
             }
             fields += [{ident: fieldname, pat: subpat}];
@@ -1478,7 +1483,10 @@ fn parse_pat(p: parser) -> @ast::pat {
             }
         } else if is_plain_ident(p) &&
                       alt p.look_ahead(1u) {
-                        token::DOT. | token::LPAREN. | token::LBRACKET. {
+                    // Take this out once the libraries change
+                        token::DOT. |
+                        token::LPAREN. | token::LBRACKET. |
+                            token::LT. {
                           false
                         }
                         _ { true }
@@ -1486,7 +1494,7 @@ fn parse_pat(p: parser) -> @ast::pat {
             hi = p.span.hi;
             let name = parse_value_ident(p);
             let sub = eat(p, token::AT) ? some(parse_pat(p)) : none;
-            pat = ast::pat_bind(name, sub);
+            pat = ast::pat_ident(ident_to_path(mk_sp(lo, hi), name), sub);
         } else {
             let tag_path = parse_path_and_ty_param_substs(p, true);
             hi = tag_path.span.hi;
@@ -1499,10 +1507,19 @@ fn parse_pat(p: parser) -> @ast::pat {
                 args = a.node;
                 hi = a.span.hi;
               }
+              token::LBRACE. { args = []; }
+              // take this out once the libraries change
               token::DOT. { args = []; p.bump(); }
               _ { expect(p, token::LPAREN); fail; }
             }
-            pat = ast::pat_tag(tag_path, args);
+            // at this point, we're not sure whether it's a tag or a bind
+            if vec::len(args) == 0u &&
+               vec::len(tag_path.node.idents) == 1u {
+                pat = ast::pat_ident(tag_path, none);
+            }
+            else {
+                pat = ast::pat_tag(tag_path, args);
+            }
         }
       }
     }
diff --git a/src/comp/syntax/print/pprust.rs b/src/comp/syntax/print/pprust.rs
index 092fb5780b1..258012c4f49 100644
--- a/src/comp/syntax/print/pprust.rs
+++ b/src/comp/syntax/print/pprust.rs
@@ -1055,10 +1055,12 @@ fn print_pat(s: ps, &&pat: @ast::pat) {
     maybe_print_comment(s, pat.span.lo);
     let ann_node = node_pat(s, pat);
     s.ann.pre(ann_node);
+    /* Pat isn't normalized, but the beauty of it
+     is that it doesn't matter */
     alt pat.node {
       ast::pat_wild. { word(s.s, "_"); }
-      ast::pat_bind(id, sub) {
-        word(s.s, id);
+      ast::pat_ident(path, sub) {
+        print_path(s, path, true);
         alt sub {
           some(p) { word(s.s, "@"); print_pat(s, p); }
           _ {}
@@ -1070,7 +1072,7 @@ fn print_pat(s: ps, &&pat: @ast::pat) {
             popen(s);
             commasep(s, inconsistent, args, print_pat);
             pclose(s);
-        } else { word(s.s, "."); }
+        } else { word(s.s, "."); }  // FIXME
       }
       ast::pat_rec(fields, etc) {
         word(s.s, "{");
diff --git a/src/comp/syntax/visit.rs b/src/comp/syntax/visit.rs
index d4cd956d2f1..a48b949bad1 100644
--- a/src/comp/syntax/visit.rs
+++ b/src/comp/syntax/visit.rs
@@ -195,9 +195,13 @@ fn visit_pat<E>(p: @pat, e: E, v: vt<E>) {
         for f: field_pat in fields { v.visit_pat(f.pat, e, v); }
       }
       pat_tup(elts) { for elt in elts { v.visit_pat(elt, e, v); } }
-      pat_box(inner) | pat_uniq(inner) | pat_bind(_, some(inner)) {
+      pat_box(inner) | pat_uniq(inner) {
         v.visit_pat(inner, e, v);
       }
+      pat_ident(path, inner) {
+          visit_path(path, e, v);
+          option::may(inner, {|subpat| v.visit_pat(subpat, e, v)});
+      }
       _ { }
     }
 }