about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMarijn Haverbeke <marijnh@gmail.com>2012-02-22 16:57:23 +0100
committerMarijn Haverbeke <marijnh@gmail.com>2012-02-22 16:57:23 +0100
commitcb2f43cbf4332ffe90b8963f1bdbaa85febe1b92 (patch)
treea21b8f284989f9a3fc19fbbff8f971741b2b14d3
parenta3b655f8e3f1a9e84cd6dd4a1d1d2525d7fac7e7 (diff)
downloadrust-cb2f43cbf4332ffe90b8963f1bdbaa85febe1b92.tar.gz
rust-cb2f43cbf4332ffe90b8963f1bdbaa85febe1b92.zip
Stop normalizing patterns
The check for whether a pat_ident is a variant or a binding
is simple and fast. Normalizing patterns again and again is
slow and error-prone (several places were forgetting to do it).
-rw-r--r--src/comp/middle/alias.rs10
-rw-r--r--src/comp/middle/ast_map.rs22
-rw-r--r--src/comp/middle/check_alt.rs45
-rw-r--r--src/comp/middle/pat_util.rs133
-rw-r--r--src/comp/middle/resolve.rs20
-rw-r--r--src/comp/middle/trans/alt.rs173
-rw-r--r--src/comp/middle/trans/base.rs26
-rw-r--r--src/comp/middle/trans/debuginfo.rs11
-rw-r--r--src/comp/middle/tstate/annotate.rs13
-rw-r--r--src/comp/middle/tstate/auxiliary.rs2
-rw-r--r--src/comp/middle/tstate/collect_locals.rs3
-rw-r--r--src/comp/middle/tstate/pre_post_conditions.rs12
-rw-r--r--src/comp/middle/tstate/states.rs3
-rw-r--r--src/comp/middle/typeck.rs129
-rw-r--r--src/comp/syntax/ast.rs3
15 files changed, 285 insertions, 320 deletions
diff --git a/src/comp/middle/alias.rs b/src/comp/middle/alias.rs
index 34caf927662..4a04bbff6ff 100644
--- a/src/comp/middle/alias.rs
+++ b/src/comp/middle/alias.rs
@@ -332,7 +332,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 = pat_util::pat_id_map(cx.tcx, a.pats[0]);
+        let pat_id_map = pat_util::pat_id_map(cx.tcx.def_map, a.pats[0]);
         type info = {
             id: node_id,
             mutable unsafe_tys: [unsafe_ty],
@@ -596,13 +596,15 @@ fn pattern_roots(tcx: ty::ctxt, mutbl: option<unsafe_ty>, pat: @ast::pat)
     -> [pattern_root] {
     fn walk(tcx: ty::ctxt, mutbl: option<unsafe_ty>, pat: @ast::pat,
             &set: [pattern_root]) {
-        alt normalize_pat(tcx, pat).node {
-          ast::pat_wild | ast::pat_lit(_) | ast::pat_range(_, _) {}
-          ast::pat_ident(nm, sub) {
+        alt pat.node {
+          ast::pat_ident(nm, sub)
+          if !pat_util::pat_is_variant(tcx.def_map, pat) {
             set += [{id: pat.id, name: path_to_ident(nm), mutbl: mutbl,
                         span: pat.span}];
             alt sub { some(p) { walk(tcx, mutbl, p, set); } _ {} }
           }
+          ast::pat_wild | ast::pat_lit(_) | ast::pat_range(_, _) |
+          ast::pat_ident(_, _) {}
           ast::pat_enum(_, ps) | ast::pat_tup(ps) {
             for p in ps { walk(tcx, mutbl, p, set); }
           }
diff --git a/src/comp/middle/ast_map.rs b/src/comp/middle/ast_map.rs
index 1504f5f738e..aad8afe0b0c 100644
--- a/src/comp/middle/ast_map.rs
+++ b/src/comp/middle/ast_map.rs
@@ -62,19 +62,25 @@ fn map_fn(fk: visit::fn_kind, decl: fn_decl, body: blk,
     visit::visit_fn(fk, decl, body, sp, id, cx, v);
 }
 
-fn map_local(loc: @local, cx: ctx, v: vt) {
-    pat_util::pat_bindings(loc.node.pat) {|p_id, _s, _p|
-        cx.map.insert(p_id, node_local(cx.local_id));
-        cx.local_id += 1u;
+fn number_pat(cx: ctx, pat: @pat) {
+    pat_util::walk_pat(pat) {|p|
+        alt p.node {
+          pat_ident(_, _) {
+            cx.map.insert(p.id, node_local(cx.local_id));
+            cx.local_id += 1u;
+          }
+          _ {}
+        }
     };
+}
+
+fn map_local(loc: @local, cx: ctx, v: vt) {
+    number_pat(cx, loc.node.pat);
     visit::visit_local(loc, cx, v);
 }
 
 fn map_arm(arm: arm, cx: ctx, v: vt) {
-    pat_util::pat_bindings(arm.pats[0]) {|p_id, _s, _p|
-        cx.map.insert(p_id, node_local(cx.local_id));
-        cx.local_id += 1u;
-    };
+    number_pat(cx, arm.pats[0]);
     visit::visit_arm(arm, cx, v);
 }
 
diff --git a/src/comp/middle/check_alt.rs b/src/comp/middle/check_alt.rs
index e1bf34ce5e4..894aa6af371 100644
--- a/src/comp/middle/check_alt.rs
+++ b/src/comp/middle/check_alt.rs
@@ -10,11 +10,11 @@ import middle::ty;
 import middle::ty::*;
 
 fn check_crate(tcx: ty::ctxt, crate: @crate) {
-    let v =
-        @{visit_expr: bind check_expr(tcx, _, _, _),
-          visit_local: bind check_local(tcx, _, _, _)
-             with *visit::default_visitor::<()>()};
-    visit::visit_crate(*crate, (), visit::mk_vt(v));
+    visit::visit_crate(*crate, (), visit::mk_vt(@{
+        visit_expr: bind check_expr(tcx, _, _, _),
+        visit_local: bind check_local(tcx, _, _, _)
+        with *visit::default_visitor::<()>()
+    }));
     tcx.sess.abort_if_errors();
 }
 
@@ -22,7 +22,6 @@ fn check_expr(tcx: ty::ctxt, ex: @expr, &&s: (), v: visit::vt<()>) {
     visit::visit_expr(ex, s, v);
     alt ex.node {
       expr_alt(scrut, arms, mode) {
-        let arms = pat_util::normalize_arms(tcx, arms);
         check_arms(tcx, arms);
         /* Check for exhaustiveness */
         if mode == alt_exhaustive {
@@ -66,8 +65,6 @@ fn raw_pat(p: @pat) -> @pat {
     }
 }
 
-// Precondition: patterns have been normalized
-// (not checked statically yet)
 fn check_exhaustive(tcx: ty::ctxt, sp: span, pats: [@pat]) {
     if pats.len() == 0u {
         tcx.sess.span_err(sp, "non-exhaustive patterns");
@@ -216,11 +213,13 @@ fn pattern_supersedes(tcx: ty::ctxt, a: @pat, b: @pat) -> bool {
 
     alt a.node {
       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) }
-          _ { false }
+      pat_wild { true }
+      pat_ident(_, none) {
+        let opt_def_a = tcx.def_map.find(a.id);
+        alt opt_def_a {
+          some(def_variant(_, _)) { opt_def_a == tcx.def_map.find(b.id) }
+          // This is a binding
+          _ { true }
         }
       }
       pat_enum(va, suba) {
@@ -256,6 +255,12 @@ fn pattern_supersedes(tcx: ty::ctxt, a: @pat, b: @pat) -> bool {
           _ { pattern_supersedes(tcx, suba, b) }
         }
       }
+      pat_lit(la) {
+        alt b.node {
+          pat_lit(lb) { lit_expr_eq(la, lb) }
+          _ { false }
+        }
+      }
       pat_range(begina, enda) {
         alt b.node {
           pat_lit(lb) {
@@ -281,12 +286,19 @@ fn check_local(tcx: ty::ctxt, loc: @local, &&s: (), v: visit::vt<()>) {
 }
 
 fn is_refutable(tcx: ty::ctxt, pat: @pat) -> bool {
-    alt normalize_pat(tcx, pat).node {
+    alt tcx.def_map.find(pat.id) {
+      some(def_variant(enum_id, var_id)) {
+        if vec::len(*ty::enum_variants(tcx, enum_id)) != 1u { ret true; }
+      }
+      _ {}
+    }
+
+    alt pat.node {
       pat_box(sub) | pat_uniq(sub) | pat_ident(_, some(sub)) {
         is_refutable(tcx, sub)
       }
       pat_wild | pat_ident(_, none) { false }
-      pat_lit(_) { true }
+      pat_lit(_) | pat_range(_, _) { true }
       pat_rec(fields, _) {
         for it: field_pat in fields {
             if is_refutable(tcx, it.pat) { ret true; }
@@ -298,12 +310,9 @@ fn is_refutable(tcx: ty::ctxt, pat: @pat) -> bool {
         false
       }
       pat_enum(_, args) {
-        let vdef = variant_def_ids(tcx.def_map.get(pat.id));
-        if vec::len(*ty::enum_variants(tcx, vdef.enm)) != 1u { ret true; }
         for p: @pat in args { if is_refutable(tcx, p) { ret true; } }
         false
       }
-      pat_range(_, _) { true }
     }
 }
 
diff --git a/src/comp/middle/pat_util.rs b/src/comp/middle/pat_util.rs
index 8a5c2706bb1..1a6856e36bf 100644
--- a/src/comp/middle/pat_util.rs
+++ b/src/comp/middle/pat_util.rs
@@ -5,115 +5,66 @@ import syntax::fold;
 import syntax::fold::*;
 import syntax::codemap::span;
 
-export normalize_arms;
-export normalize_pat;
-export normalize_pat_def_map;
-export pat_binding_ids;
-export pat_bindings;
-export pat_id_map;
+export walk_pat;
+export pat_binding_ids, pat_bindings, pat_id_map;
+export pat_is_variant;
 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_enum(a_path, subs) {
-        @{node: pat_enum(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(enum_path, _) { @{id: p.id,
-                    node: pat_enum(enum_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)) {|p_id, _s, n|
+fn pat_id_map(dm: resolve::def_map, pat: @pat) -> pat_id_map {
+    let map = std::map::new_str_hash();
+    pat_bindings(dm, pat) {|p_id, _s, n|
       map.insert(path_to_ident(n), p_id);
     };
     ret map;
 }
 
+fn pat_is_variant(dm: resolve::def_map, pat: @pat) -> bool {
+    alt pat.node {
+      pat_enum(_, _) { true }
+      pat_ident(_, none) {
+        alt dm.find(pat.id) {
+          some(def_variant(_, _)) { true }
+          _ { false }
+        }
+      }
+      _ { false }
+    }
+}
+
 // 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: fn(node_id, span, @path)) {
-  alt pat.node {
-      pat_ident(pth, option::none) { it(pat.id, pat.span, pth); }
-      pat_ident(pth, option::some(sub)) { it(pat.id, pat.span, pth);
-        pat_bindings(sub, it); }
-      pat_enum(_, 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_bindings(dm: resolve::def_map, pat: @pat,
+                it: fn(node_id, span, @path)) {
+    walk_pat(pat) {|p|
+        alt p.node {
+          pat_ident(pth, _) if !pat_is_variant(dm, p) {
+            it(p.id, p.span, pth);
+          }
+          _ {}
+        }
     }
 }
 
-fn pat_binding_ids(pat: @pat) -> [node_id] {
+fn walk_pat(pat: @pat, it: fn(@pat)) {
+    it(pat);
+    alt pat.node {
+      pat_ident(pth, some(p)) { walk_pat(p, it); }
+      pat_rec(fields, _) { for f in fields { walk_pat(f.pat, it); } }
+      pat_enum(_, s) | pat_tup(s) { for p in s { walk_pat(p, it); } }
+      pat_box(s) | pat_uniq(s) { walk_pat(s, it); }
+      pat_wild | pat_lit(_) | pat_range(_, _) | pat_ident(_, none) {}
+    }
+}
+
+fn pat_binding_ids(dm: resolve::def_map, pat: @pat) -> [node_id] {
     let found = [];
-    pat_bindings(pat) {|b_id, _sp, _pt| found += [b_id]; };
+    pat_bindings(dm, pat) {|b_id, _sp, _pt| 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; }
-    }
-}
+fn path_to_ident(p: @path) -> ident { vec::last_total(p.node.idents) }
diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs
index 76aea3a1e3b..413821894a9 100644
--- a/src/comp/middle/resolve.rs
+++ b/src/comp/middle/resolve.rs
@@ -624,7 +624,7 @@ fn visit_local_with_scope(e: @env, loc: @local, sc:scopes, v:vt<scopes>) {
     // a single identifier unambiguous (does the pattern "foo" refer
     // to enum foo, or is it binding a new name foo?)
     alt loc.node.pat.node {
-      pat_ident(an_ident,_) {
+      pat_ident(an_ident, _) {
           // Be sure to pass ns_an_enum 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,
@@ -1139,8 +1139,7 @@ fn lookup_in_ty_params(e: env, name: ident, ty_params: [ast::ty_param])
 fn lookup_in_pat(e: env, name: ident, pat: @ast::pat) -> option<def_id> {
     let found = none;
 
-    pat_util::pat_bindings(normalize_pat_def_map(e.def_map, pat))
-     {|p_id, _sp, n|
+    pat_util::pat_bindings(e.def_map, pat) {|p_id, _sp, n|
         if str::eq(path_to_ident(n), name)
                     { found = some(local_def(p_id)); }
     };
@@ -1776,7 +1775,7 @@ fn check_item(e: @env, i: @ast::item, &&x: (), v: vt<()>) {
 }
 
 fn check_pat(e: @env, ch: checker, p: @ast::pat) {
-    pat_util::pat_bindings(normalize_pat_def_map(e.def_map, p)) {|_i, p_sp, n|
+    pat_util::pat_bindings(e.def_map, p) {|_i, p_sp, n|
        add_name(ch, p_sp, path_to_ident(n));
     };
 }
@@ -1823,13 +1822,12 @@ 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 {
-                     pat_util::pat_bindings
-                        (normalize_pat_def_map(e.def_map, loc.node.pat))
-                            {|_i, p_sp, n|
-                            let ident = path_to_ident(n);
-                            add_name(local_values, p_sp, ident);
-                            check_name(values, p_sp, ident);
-                          };
+                     pat_util::pat_bindings(e.def_map, loc.node.pat)
+                         {|_i, p_sp, n|
+                         let ident = path_to_ident(n);
+                         add_name(local_values, p_sp, ident);
+                         check_name(values, p_sp, ident);
+                     };
                 }
               }
               ast::decl_item(it) {
diff --git a/src/comp/middle/trans/alt.rs b/src/comp/middle/trans/alt.rs
index c15acf64af0..d461781e719 100644
--- a/src/comp/middle/trans/alt.rs
+++ b/src/comp/middle/trans/alt.rs
@@ -11,6 +11,7 @@ import syntax::ast::def_id;
 import syntax::codemap::span;
 import syntax::print::pprust::pat_to_str;
 import back::abi;
+import resolve::def_map;
 
 import common::*;
 
@@ -63,9 +64,9 @@ fn trans_opt(bcx: block, o: opt) -> opt_result {
     }
 }
 
-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::enum_variants(ccx.tcx, vdef.enm);
+fn variant_opt(tcx: ty::ctxt, pat_id: ast::node_id) -> opt {
+    let vdef = ast_util::variant_def_ids(tcx.def_map.get(pat_id));
+    let variants = ty::enum_variants(tcx, vdef.enm);
     for v: ty::variant_info in *variants {
         if vdef.var == v.id { ret var(v.disr_val, vdef); }
     }
@@ -116,24 +117,24 @@ fn expand_nested_bindings(m: match, col: uint, val: ValueRef) -> match {
     result
 }
 
-type enter_pat = fn@(@ast::pat) -> option<[@ast::pat]>;
+type enter_pat = fn(@ast::pat) -> option<[@ast::pat]>;
 
-fn enter_match(m: match, col: uint, val: ValueRef, e: enter_pat) -> match {
+fn enter_match(dm: def_map, m: match, col: uint, val: ValueRef,
+               e: enter_pat) -> match {
     let result = [];
     for br: match_branch in m {
         alt e(br.pats[col]) {
           some(sub) {
             let pats = sub + vec::slice(br.pats, 0u, col) +
                 vec::slice(br.pats, col + 1u, br.pats.len());
-            let new_br = @{pats: pats,
-                           bound: alt br.pats[col].node {
-                             ast::pat_ident(name, none) {
-                                 br.bound + [{ident: path_to_ident(name),
-                                              val: val}]
-                             }
-                             _ { br.bound }
-                           } with *br};
-            result += [new_br];
+            let self = br.pats[col];
+            let bound = alt self.node {
+              ast::pat_ident(name, none) if !pat_is_variant(dm, self) {
+                br.bound + [{ident: path_to_ident(name), val: val}]
+              }
+              _ { br.bound }
+            };
+            result += [@{pats: pats, bound: bound with *br}];
           }
           none { }
         }
@@ -141,48 +142,46 @@ fn enter_match(m: match, col: uint, val: ValueRef, e: enter_pat) -> match {
     ret result;
 }
 
-fn enter_default(m: match, col: uint, val: ValueRef) -> match {
-    fn matches_always(p: @ast::pat) -> bool {
+fn enter_default(dm: def_map, m: match, col: uint, val: ValueRef) -> match {
+    enter_match(dm, m, col, val) {|p|
         alt p.node {
-                ast::pat_wild | ast::pat_rec(_, _) |
-                ast::pat_ident(_, none) | ast::pat_tup(_) { true }
-                _ { false }
+          ast::pat_wild | ast::pat_rec(_, _) | ast::pat_tup(_) { some([]) }
+          ast::pat_ident(_, none) if !pat_is_variant(dm, p) {
+            some([])
+          }
+          _ { none }
         }
     }
-    fn e(p: @ast::pat) -> option<[@ast::pat]> {
-        ret if matches_always(p) { some([]) } else { none };
-    }
-    ret enter_match(m, col, val, e);
 }
 
-fn enter_opt(ccx: crate_ctxt, m: match, opt: opt, col: uint, enum_size: uint,
-             val: ValueRef) -> match {
+fn enter_opt(tcx: ty::ctxt, m: match, opt: opt, col: uint,
+             variant_size: uint, val: ValueRef) -> match {
     let dummy = @{id: 0, node: ast::pat_wild, span: dummy_sp()};
-    fn e(ccx: crate_ctxt, dummy: @ast::pat, opt: opt, size: uint,
-         p: @ast::pat) -> option<[@ast::pat]> {
+    enter_match(tcx.def_map, m, col, val) {|p|
         alt p.node {
-          ast::pat_enum(ctor, subpats) {
-            ret if opt_eq(variant_opt(ccx, p.id), opt) {
-                    some(subpats)
-                } else { none };
+          ast::pat_enum(_, subpats) {
+            if opt_eq(variant_opt(tcx, p.id), opt) { some(subpats) }
+            else { none }
+          }
+          ast::pat_ident(_, none) if pat_is_variant(tcx.def_map, p) {
+            if opt_eq(variant_opt(tcx, p.id), opt) { some([]) }
+            else { none }
           }
           ast::pat_lit(l) {
-            ret if opt_eq(lit(l), opt) { some([]) } else { none };
+            if opt_eq(lit(l), opt) { some([]) } else { none }
           }
           ast::pat_range(l1, l2) {
-            ret if opt_eq(range(l1, l2), opt) { some([]) } else { none };
+            if opt_eq(range(l1, l2), opt) { some([]) } else { none }
           }
-          _ { ret some(vec::init_elt(size, dummy)); }
+          _ { some(vec::init_elt(variant_size, dummy)) }
         }
     }
-    ret enter_match(m, col, val, bind e(ccx, dummy, opt, enum_size, _));
 }
 
-fn enter_rec(m: match, col: uint, fields: [ast::ident], val: ValueRef) ->
-   match {
+fn enter_rec(dm: def_map, m: match, col: uint, fields: [ast::ident],
+             val: ValueRef) -> match {
     let dummy = @{id: 0, node: ast::pat_wild, span: dummy_sp()};
-    fn e(dummy: @ast::pat, fields: [ast::ident], p: @ast::pat) ->
-       option<[@ast::pat]> {
+    enter_match(dm, m, col, val) {|p|
         alt p.node {
           ast::pat_rec(fpats, _) {
             let pats = [];
@@ -193,65 +192,63 @@ fn enter_rec(m: match, col: uint, fields: [ast::ident], val: ValueRef) ->
                 }
                 pats += [pat];
             }
-            ret some(pats);
+            some(pats)
           }
-          _ { ret some(vec::init_elt(fields.len(), dummy)); }
+          _ { some(vec::init_elt(fields.len(), dummy)) }
         }
     }
-    ret enter_match(m, col, val, bind e(dummy, fields, _));
 }
 
-fn enter_tup(m: match, col: uint, val: ValueRef, n_elts: uint) -> match {
+fn enter_tup(dm: def_map, m: match, col: uint, val: ValueRef,
+             n_elts: uint) -> match {
     let dummy = @{id: 0, node: ast::pat_wild, span: dummy_sp()};
-    fn e(dummy: @ast::pat, n_elts: uint, p: @ast::pat) ->
-       option<[@ast::pat]> {
+    enter_match(dm, m, col, val) {|p|
         alt p.node {
-          ast::pat_tup(elts) { ret some(elts); }
-          _ { ret some(vec::init_elt(n_elts, dummy)); }
+          ast::pat_tup(elts) { some(elts) }
+          _ { some(vec::init_elt(n_elts, dummy)) }
         }
     }
-    ret enter_match(m, col, val, bind e(dummy, n_elts, _));
 }
 
-fn enter_box(m: match, col: uint, val: ValueRef) -> match {
+fn enter_box(dm: def_map, m: match, col: uint, val: ValueRef) -> match {
     let dummy = @{id: 0, node: ast::pat_wild, span: dummy_sp()};
-    fn e(dummy: @ast::pat, p: @ast::pat) -> option<[@ast::pat]> {
+    enter_match(dm, m, col, val) {|p|
         alt p.node {
-          ast::pat_box(sub) { ret some([sub]); }
-          _ { ret some([dummy]); }
+          ast::pat_box(sub) { some([sub]) }
+          _ { some([dummy]) }
         }
     }
-    ret enter_match(m, col, val, bind e(dummy, _));
 }
 
-fn enter_uniq(m: match, col: uint, val: ValueRef) -> match {
+fn enter_uniq(dm: def_map, m: match, col: uint, val: ValueRef) -> match {
     let dummy = @{id: 0, node: ast::pat_wild, span: dummy_sp()};
-    fn e(dummy: @ast::pat, p: @ast::pat) -> option<[@ast::pat]> {
+    enter_match(dm, m, col, val) {|p|
         alt p.node {
-          ast::pat_uniq(sub) { ret some([sub]); }
-          _ { ret some([dummy]); }
+          ast::pat_uniq(sub) { some([sub]) }
+          _ { some([dummy]) }
         }
     }
-    ret enter_match(m, col, val, bind e(dummy, _));
 }
 
 fn get_options(ccx: crate_ctxt, m: match, col: uint) -> [opt] {
     fn add_to_set(&set: [opt], val: opt) {
-        for l: opt in set { if opt_eq(l, val) { ret; } }
+        for l in set { if opt_eq(l, val) { ret; } }
         set += [val];
     }
 
     let found = [];
-    for br: match_branch in m {
-        alt br.pats[col].node {
-          ast::pat_lit(l) { add_to_set(found, lit(l)); }
-          ast::pat_range(l1, l2) {
-            add_to_set(found, range(l1, l2));
-          }
-          ast::pat_enum(_, _) {
-            add_to_set(found, variant_opt(ccx, br.pats[col].id));
-          }
-          _ { }
+    for br in m {
+        let cur = br.pats[col];
+        if pat_is_variant(ccx.tcx.def_map, cur) {
+            add_to_set(found, variant_opt(ccx.tcx, br.pats[col].id));
+        } else {
+            alt cur.node {
+              ast::pat_lit(l) { add_to_set(found, lit(l)); }
+              ast::pat_range(l1, l2) {
+                add_to_set(found, range(l1, l2));
+              }
+              _ {}
+            }
         }
     }
     ret found;
@@ -362,7 +359,7 @@ fn pick_col(m: match) -> uint {
 
 fn compile_submatch(bcx: block, m: match, vals: [ValueRef], f: mk_fail,
                     &exits: [exit_node]) {
-    let bcx = bcx;
+    let bcx = bcx, tcx = bcx.tcx(), dm = tcx.def_map;
     if m.len() == 0u { Br(bcx, f()); ret; }
     if m[0].pats.len() == 0u {
         let data = m[0].data;
@@ -422,7 +419,7 @@ fn compile_submatch(bcx: block, m: match, vals: [ValueRef], f: mk_fail,
             rec_vals += [r.val];
             bcx = r.bcx;
         }
-        compile_submatch(bcx, enter_rec(m, col, rec_fields, val),
+        compile_submatch(bcx, enter_rec(dm, m, col, rec_fields, val),
                          rec_vals + vals_left, f, exits);
         ret;
     }
@@ -440,7 +437,7 @@ fn compile_submatch(bcx: block, m: match, vals: [ValueRef], f: mk_fail,
             bcx = r.bcx;
             i += 1u;
         }
-        compile_submatch(bcx, enter_tup(m, col, val, n_tup_elts),
+        compile_submatch(bcx, enter_tup(dm, m, col, val, n_tup_elts),
                          tup_vals + vals_left, f, exits);
         ret;
     }
@@ -449,14 +446,14 @@ fn compile_submatch(bcx: block, m: match, vals: [ValueRef], f: mk_fail,
     if any_box_pat(m, col) {
         let box = Load(bcx, val);
         let unboxed = GEPi(bcx, box, [0, abi::box_field_body]);
-        compile_submatch(bcx, enter_box(m, col, val), [unboxed] + vals_left,
-                         f, exits);
+        compile_submatch(bcx, enter_box(dm, m, col, val), [unboxed]
+                         + vals_left, f, exits);
         ret;
     }
 
     if any_uniq_pat(m, col) {
         let unboxed = Load(bcx, val);
-        compile_submatch(bcx, enter_uniq(m, col, val),
+        compile_submatch(bcx, enter_uniq(dm, m, col, val),
                          [unboxed] + vals_left, f, exits);
         ret;
     }
@@ -469,7 +466,7 @@ fn compile_submatch(bcx: block, m: match, vals: [ValueRef], f: mk_fail,
     if opts.len() > 0u {
         alt opts[0] {
           var(_, vdef) {
-            if (*ty::enum_variants(ccx.tcx, vdef.enm)).len() == 1u {
+            if (*ty::enum_variants(tcx, vdef.enm)).len() == 1u {
                 kind = single;
             } else {
                 let enumptr =
@@ -512,13 +509,11 @@ fn compile_submatch(bcx: block, m: match, vals: [ValueRef], f: mk_fail,
           single { Br(bcx, opt_cx.llbb); }
           switch {
             let res = trans_opt(bcx, opt);
-            alt res {
+            alt check res {
               single_result(r) {
                 llvm::LLVMAddCase(sw, r.val, opt_cx.llbb);
                 bcx = r.bcx;
               }
-              _ { bcx.tcx().sess.bug("Someone forgot to\
-                    document an invariant in compile_submatch"); }
             }
           }
           compare {
@@ -554,15 +549,15 @@ fn compile_submatch(bcx: block, m: match, vals: [ValueRef], f: mk_fail,
           }
           lit(_) | range(_, _) { }
         }
-        compile_submatch(opt_cx, enter_opt(ccx, m, opt, col, size, val),
+        compile_submatch(opt_cx, enter_opt(tcx, m, opt, col, size, val),
                          unpacked + vals_left, f, exits);
     }
 
     // Compile the fall-through case
     if kind == compare { Br(bcx, else_cx.llbb); }
     if kind != single {
-        compile_submatch(else_cx, enter_default(m, col, val), vals_left, f,
-                         exits);
+        compile_submatch(else_cx, enter_default(dm, m, col, val), vals_left,
+                         f, exits);
     }
 }
 
@@ -625,14 +620,10 @@ fn trans_alt_inner(scope_cx: block, expr: @ast::expr, arms: [ast::arm],
     let {bcx, val, _} = trans_temp_expr(bcx, expr);
     if bcx.unreachable { ret bcx; }
 
-    // n.b. nothing else in this module should need to normalize,
-    // b/c of this call
-    let arms = normalize_arms(tcx, arms);
-
     for a in arms {
         let body = scope_block(bcx, "case_body");
         body.block_span = some(a.body.span);
-        let id_map = pat_util::pat_id_map(tcx, a.pats[0]);
+        let id_map = pat_util::pat_id_map(tcx.def_map, a.pats[0]);
         bodies += [body];
         for p in a.pats {
             match += [@{pats: [p],
@@ -662,8 +653,8 @@ fn trans_alt_inner(scope_cx: block, expr: @ast::expr, arms: [ast::arm],
     let arm_cxs = [], arm_dests = [], i = 0u;
     for a in arms {
         let body_cx = bodies[i];
-        if make_phi_bindings(body_cx, exit_map,
-                             pat_util::pat_id_map(tcx, a.pats[0])) {
+        let id_map = pat_util::pat_id_map(tcx.def_map, a.pats[0]);
+        if make_phi_bindings(body_cx, exit_map, id_map) {
             let arm_dest = dup_for_join(dest);
             arm_dests += [arm_dest];
             let arm_cx = trans_block(body_cx, a.body, arm_dest);
@@ -681,8 +672,9 @@ fn bind_irrefutable_pat(bcx: block, pat: @ast::pat, val: ValueRef,
     let ccx = bcx.fcx.ccx, bcx = bcx;
 
     // Necessary since bind_irrefutable_pat is called outside trans_alt
-    alt normalize_pat(bcx.tcx(), pat).node {
+    alt pat.node {
       ast::pat_ident(_,inner) {
+        if pat_is_variant(bcx.tcx().def_map, pat) { ret bcx; }
         if make_copy || ccx.copy_map.contains_key(pat.id) {
             let ty = node_id_type(bcx, pat.id);
             let llty = type_of::type_of(ccx, ty);
@@ -698,7 +690,6 @@ fn bind_irrefutable_pat(bcx: block, pat: @ast::pat, val: ValueRef,
         }
       }
       ast::pat_enum(_, sub) {
-        if sub.len() == 0u { ret bcx; }
         let vdefs = ast_util::variant_def_ids(ccx.tcx.def_map.get(pat.id));
         let args = extract_variant_args(bcx, pat.id, vdefs, val);
         let i = 0;
diff --git a/src/comp/middle/trans/base.rs b/src/comp/middle/trans/base.rs
index d0370d992a8..a86681da8ce 100644
--- a/src/comp/middle/trans/base.rs
+++ b/src/comp/middle/trans/base.rs
@@ -3670,13 +3670,14 @@ fn alloc_ty(cx: block, t: ty::t) -> result {
 
 fn alloc_local(cx: block, local: @ast::local) -> block {
     let t = node_id_type(cx, local.node.id);
-    let p = normalize_pat(cx.tcx(), local.node.pat);
-    let is_simple = alt p.node {
-      ast::pat_ident(_, none) { true } _ { false }
+    let simple_name = alt local.node.pat.node {
+      ast::pat_ident(pth, none) { some(path_to_ident(pth)) }
+      _ { none }
     };
     // Do not allocate space for locals that can be kept immediate.
     let ccx = cx.ccx();
-    if is_simple && !ccx.mutbl_map.contains_key(local.node.pat.id) &&
+    if option::is_some(simple_name) &&
+       !ccx.mutbl_map.contains_key(local.node.pat.id) &&
        !ccx.last_uses.contains_key(local.node.pat.id) &&
        ty::type_is_immediate(t) {
         alt local.node.init {
@@ -3684,19 +3685,16 @@ fn alloc_local(cx: block, local: @ast::local) -> block {
           _ {}
         }
     }
-    let r = alloc_ty(cx, t);
-    alt p.node {
-      ast::pat_ident(pth, none) {
-        if cx.sess().opts.debuginfo {
-            let _: () = str::as_buf(path_to_ident(pth), {|buf|
-                llvm::LLVMSetValueName(r.val, buf)
+    let {bcx, val} = alloc_ty(cx, t);
+    if cx.sess().opts.debuginfo {
+        option::may(simple_name) {|name|
+            str::as_buf(name, {|buf|
+                llvm::LLVMSetValueName(val, buf)
             });
         }
-      }
-      _ { }
     }
-    cx.fcx.lllocals.insert(local.node.id, local_mem(r.val));
-    ret r.bcx;
+    cx.fcx.lllocals.insert(local.node.id, local_mem(val));
+    ret bcx;
 }
 
 fn trans_block(bcx: block, b: ast::blk, dest: dest)
diff --git a/src/comp/middle/trans/debuginfo.rs b/src/comp/middle/trans/debuginfo.rs
index 9ef09929773..39da8cf2c57 100644
--- a/src/comp/middle/trans/debuginfo.rs
+++ b/src/comp/middle/trans/debuginfo.rs
@@ -686,12 +686,11 @@ fn create_local_var(bcx: block, local: @ast::local)
       option::none {}
     }
 
-    let name = path_to_ident(alt pat_util::normalize_pat(bcx.tcx(),
-                                           local.node.pat).node {
-      ast::pat_ident(ident, _) { ident /*XXX deal w/ optional node binding*/ }
-      _ { bcx.tcx().sess.span_bug(local.span, "create_local_var: \
-             weird pattern in local"); }
-     });
+    let name = alt local.node.pat.node {
+      ast::pat_ident(pth, _) { pat_util::path_to_ident(pth) }
+      // FIXME this should be handled
+      _ { fail "no single variable name for local"; }
+    };
     let loc = codemap::lookup_char_pos(cx.sess.codemap,
                                        local.span.lo);
     let ty = node_id_type(bcx, local.node.id);
diff --git a/src/comp/middle/tstate/annotate.rs b/src/comp/middle/tstate/annotate.rs
index 1689696f207..d4271693112 100644
--- a/src/comp/middle/tstate/annotate.rs
+++ b/src/comp/middle/tstate/annotate.rs
@@ -22,17 +22,18 @@ fn collect_ids_stmt(s: @stmt, rs: @mutable [node_id]) {
     }
 }
 
-fn collect_ids_local(l: @local, rs: @mutable [node_id]) {
-    *rs += pat_binding_ids(l.node.pat);
+fn collect_ids_local(tcx: ty::ctxt, l: @local, rs: @mutable [node_id]) {
+    *rs += pat_binding_ids(tcx.def_map, l.node.pat);
 }
 
-fn node_ids_in_fn(body: blk, rs: @mutable [node_id]) {
+fn node_ids_in_fn(tcx: ty::ctxt, body: blk, rs: @mutable [node_id]) {
     let collect_ids =
         visit::mk_simple_visitor(@{visit_expr: bind collect_ids_expr(_, rs),
                                    visit_block: bind collect_ids_block(_, rs),
                                    visit_stmt: bind collect_ids_stmt(_, rs),
-                                   visit_local: bind collect_ids_local(_, rs)
-                                      with *visit::default_simple_visitor()});
+                                   visit_local:
+                                       bind collect_ids_local(tcx, _, rs)
+                                   with *visit::default_simple_visitor()});
     collect_ids.visit_block(body, (), collect_ids);
 }
 
@@ -45,7 +46,7 @@ fn init_vecs(ccx: crate_ctxt, node_ids: [node_id], len: uint) {
 
 fn visit_fn(ccx: crate_ctxt, num_constraints: uint, body: blk) {
     let node_ids: @mutable [node_id] = @mutable [];
-    node_ids_in_fn(body, node_ids);
+    node_ids_in_fn(ccx.tcx, body, node_ids);
     let node_id_vec = *node_ids;
     init_vecs(ccx, node_id_vec, num_constraints);
 }
diff --git a/src/comp/middle/tstate/auxiliary.rs b/src/comp/middle/tstate/auxiliary.rs
index cd0cd7e11c3..60d799a89de 100644
--- a/src/comp/middle/tstate/auxiliary.rs
+++ b/src/comp/middle/tstate/auxiliary.rs
@@ -1056,7 +1056,7 @@ type binding = {lhs: [inst], rhs: option<initializer>};
 
 fn local_to_bindings(tcx: ty::ctxt, loc: @local) -> binding {
     let lhs = [];
-    pat_bindings(pat_util::normalize_pat(tcx, loc.node.pat)) {|p_id, _s, name|
+    pat_bindings(tcx.def_map, loc.node.pat) {|p_id, _s, name|
         lhs += [{ident: path_to_ident(name), node: p_id}];
     };
     {lhs: lhs, rhs: loc.node.init}
diff --git a/src/comp/middle/tstate/collect_locals.rs b/src/comp/middle/tstate/collect_locals.rs
index 1abc9903d5c..9b39814e141 100644
--- a/src/comp/middle/tstate/collect_locals.rs
+++ b/src/comp/middle/tstate/collect_locals.rs
@@ -12,8 +12,7 @@ import aux::*;
 type ctxt = {cs: @mutable [sp_constr], tcx: ty::ctxt};
 
 fn collect_local(loc: @local, cx: ctxt, v: visit::vt<ctxt>) {
-    pat_bindings(pat_util::normalize_pat(cx.tcx, loc.node.pat))
-     {|p_id, _s, id|
+    pat_bindings(cx.tcx.def_map, loc.node.pat) {|p_id, _s, id|
        *cx.cs += [respan(loc.span, ninit(p_id, path_to_ident(id)))];
     };
     visit::visit_local(loc, cx, v);
diff --git a/src/comp/middle/tstate/pre_post_conditions.rs b/src/comp/middle/tstate/pre_post_conditions.rs
index 326c768cabe..f46cd39bf65 100644
--- a/src/comp/middle/tstate/pre_post_conditions.rs
+++ b/src/comp/middle/tstate/pre_post_conditions.rs
@@ -89,7 +89,7 @@ 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(normalize_pat(fcx.ccx.tcx, l.node.pat)) {|p_id, _s, n|
+    pat_bindings(fcx.ccx.tcx.def_map, l.node.pat) {|p_id, _s, n|
         let v_init = ninit(p_id, path_to_ident(n));
         relax_precond_block(fcx, bit_num(fcx, v_init) as node_id, body);
         // Hack: for-loop index variables are frequently ignored,
@@ -551,7 +551,8 @@ fn find_pre_post_stmt(fcx: fn_ctxt, s: stmt) {
                     /* LHS always becomes initialized,
                      whether or not this is a move */
                     find_pre_post_expr(fcx, an_init.expr);
-                    pat_bindings(alocal.node.pat) {|p_id, _s, _n|
+                    pat_bindings(fcx.ccx.tcx.def_map, alocal.node.pat)
+                        {|p_id, _s, _n|
                         copy_pre_post(fcx.ccx, p_id, an_init.expr);
                     };
                     /* Inherit ann from initializer, and add var being
@@ -564,7 +565,7 @@ fn find_pre_post_stmt(fcx: fn_ctxt, s: stmt) {
                       _ { }
                     }
 
-                    pat_bindings(normalize_pat(fcx.ccx.tcx, alocal.node.pat))
+                    pat_bindings(fcx.ccx.tcx.def_map, alocal.node.pat)
                         {|p_id, _s, n|
                         let ident = path_to_ident(n);
                         alt p {
@@ -592,7 +593,7 @@ 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(normalize_pat(fcx.ccx.tcx, alocal.node.pat))
+                    pat_bindings(fcx.ccx.tcx.def_map, alocal.node.pat)
                         {|pat_id, _s, n|
                             set_in_postcond(bit_num(fcx,
                                ninit(pat_id, path_to_ident(n))), prev_pp);
@@ -601,7 +602,8 @@ fn find_pre_post_stmt(fcx: fn_ctxt, s: stmt) {
                                    prev_pp.postcondition);
                   }
                   none {
-                      pat_bindings(alocal.node.pat) {|p_id, _s, _n|
+                    pat_bindings(fcx.ccx.tcx.def_map, alocal.node.pat)
+                        {|p_id, _s, _n|
                         clear_pp(node_id_to_ts_ann(fcx.ccx, p_id).conditions);
                     };
                     clear_pp(node_id_to_ts_ann(fcx.ccx, id).conditions);
diff --git a/src/comp/middle/tstate/states.rs b/src/comp/middle/tstate/states.rs
index 7a7c1cd399a..e9d73ec8e77 100644
--- a/src/comp/middle/tstate/states.rs
+++ b/src/comp/middle/tstate/states.rs
@@ -224,8 +224,7 @@ 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(pat_util::normalize_pat(fcx.ccx.tcx, l.node.pat))
-      {|p_id, _s, n|
+    pat_bindings(fcx.ccx.tcx.def_map, l.node.pat) {|p_id, _s, n|
        set_in_poststate_ident(fcx, p_id, path_to_ident(n), index_post);
     };
 
diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs
index ed9eb3c3560..75f3deb670f 100644
--- a/src/comp/middle/typeck.rs
+++ b/src/comp/middle/typeck.rs
@@ -1338,12 +1338,15 @@ fn gather_locals(ccx: @crate_ctxt,
 
     // Add pattern bindings.
     let visit_pat = fn@(p: @ast::pat, &&e: (), v: visit::vt<()>) {
-        alt normalize_pat(ccx.tcx, p).node {
-              ast::pat_ident(_, _) { assign(p.id, none); }
-              _ {/* no-op */ }
-            }
-            visit::visit_pat(p, e, v);
-        };
+        alt p.node {
+          ast::pat_ident(_, _)
+          if !pat_util::pat_is_variant(ccx.tcx.def_map, p) {
+            assign(p.id, none);
+          }
+          _ {}
+        }
+        visit::visit_pat(p, e, v);
+    };
 
     // Don't descend into fns and items
     fn visit_fn<T>(_fk: visit::fn_kind, _decl: ast::fn_decl, _body: ast::blk,
@@ -1380,12 +1383,65 @@ fn valid_range_bounds(from: @ast::expr, to: @ast::expr) -> bool {
     ast_util::compare_lit_exprs(from, to) <= 0
 }
 
+fn check_pat_variant(fcx: @fn_ctxt, map: pat_util::pat_id_map,
+                     pat: @ast::pat, path: @ast::path, subpats: [@ast::pat],
+                     expected: ty::t) {
+    // Typecheck the path.
+    let tcx = fcx.ccx.tcx;
+    let v_def = lookup_def(fcx, path.span, pat.id);
+    let v_def_ids = ast_util::variant_def_ids(v_def);
+    let ctor_tpt = ty::lookup_item_type(tcx, v_def_ids.enm);
+    instantiate_path(fcx, path, ctor_tpt, pat.span, pat.id);
+
+    // Take the enum type params out of `expected`.
+    alt structure_of(fcx, pat.span, expected) {
+      ty::ty_enum(_, expected_tps) {
+        let ctor_ty = ty::node_id_to_type(tcx, pat.id);
+        demand::with_substs(fcx, pat.span, expected, ctor_ty,
+                            expected_tps);
+        // Get the number of arguments in this enum variant.
+        let arg_types = variant_arg_types(fcx.ccx, pat.span,
+                                          v_def_ids.var, expected_tps);
+        let subpats_len = subpats.len(), arg_len = arg_types.len();
+        if arg_len > 0u {
+            // N-ary variant.
+            if arg_len != subpats_len {
+                let s = #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" }];
+                tcx.sess.span_err(pat.span, s);
+            }
+
+            vec::iter2(subpats, arg_types) {|subpat, arg_ty|
+                check_pat(fcx, map, subpat, arg_ty);
+            }
+        } else if subpats_len > 0u {
+            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" }]);
+        }
+      }
+      _ {
+        tcx.sess.span_err
+            (pat.span,
+             #fmt["mismatched types: expected enum but found `%s`",
+                  ty_to_str(tcx, expected)]);
+      }
+    }
+}
+
 // Pattern checking is top-down rather than bottom-up so that bindings get
 // their types immediately.
 fn check_pat(fcx: @fn_ctxt, map: pat_util::pat_id_map, pat: @ast::pat,
              expected: ty::t) {
     let tcx = fcx.ccx.tcx;
-    alt normalize_pat(tcx, pat).node {
+    alt pat.node {
       ast::pat_wild {
           alt structure_of(fcx, pat.span, expected) {
                   ty::ty_enum(_, expected_tps) {
@@ -1416,7 +1472,8 @@ fn check_pat(fcx: @fn_ctxt, map: pat_util::pat_id_map, pat: @ast::pat,
         }
         write_ty(tcx, pat.id, b_ty);
       }
-      ast::pat_ident(name, sub) {
+      ast::pat_ident(name, sub)
+      if !pat_util::pat_is_variant(tcx.def_map, pat) {
         let vid = lookup_local(fcx, pat.span, pat.id);
         let typ = ty::mk_var(tcx, vid);
         typ = demand::simple(fcx, pat.span, expected, typ);
@@ -1431,55 +1488,11 @@ fn check_pat(fcx: @fn_ctxt, map: pat_util::pat_id_map, pat: @ast::pat,
           _ {}
         }
       }
+      ast::pat_ident(path, _) {
+        check_pat_variant(fcx, map, pat, path, [], expected);
+      }
       ast::pat_enum(path, subpats) {
-        // Typecheck the path.
-        let v_def = lookup_def(fcx, path.span, pat.id);
-        let v_def_ids = ast_util::variant_def_ids(v_def);
-        let ctor_tpt = ty::lookup_item_type(tcx, v_def_ids.enm);
-        instantiate_path(fcx, path, ctor_tpt, pat.span, pat.id);
-
-        // Take the enum type params out of `expected`.
-        alt structure_of(fcx, pat.span, expected) {
-          ty::ty_enum(_, expected_tps) {
-            let ctor_ty = ty::node_id_to_type(tcx, pat.id);
-            demand::with_substs(fcx, pat.span, expected, ctor_ty,
-                                expected_tps);
-            // Get the number of arguments in this enum variant.
-            let arg_types = variant_arg_types(fcx.ccx, pat.span,
-                                              v_def_ids.var, expected_tps);
-            let subpats_len = subpats.len(), arg_len = arg_types.len();
-            if arg_len > 0u {
-                // N-ary variant.
-                if arg_len != subpats_len {
-                    let s = #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" }];
-                    tcx.sess.span_err(pat.span, s);
-                }
-
-                vec::iter2(subpats, arg_types) {|subpat, arg_ty|
-                    check_pat(fcx, map, subpat, arg_ty);
-                }
-            } else if subpats_len > 0u {
-                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" }]);
-            }
-          }
-          _ {
-            tcx.sess.span_err
-                (pat.span,
-                 #fmt["mismatched types: expected enum but found `%s`",
-                      ty_to_str(tcx, expected)]);
-          }
-        }
+        check_pat_variant(fcx, map, pat, path, subpats, expected);
       }
       ast::pat_rec(fields, etc) {
         let ex_fields;
@@ -2292,7 +2305,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 = pat_util::pat_id_map(tcx, arm.pats[0]);
+            let id_map = pat_util::pat_id_map(tcx.def_map, arm.pats[0]);
             for p: @ast::pat in arm.pats {
                 check_pat(fcx, id_map, p, pattern_ty);
             }
@@ -2636,7 +2649,7 @@ fn check_decl_local(fcx: @fn_ctxt, local: @ast::local) -> bool {
       }
       _ {/* fall through */ }
     }
-    let id_map = pat_util::pat_id_map(fcx.ccx.tcx, local.node.pat);
+    let id_map = pat_util::pat_id_map(fcx.ccx.tcx.def_map, local.node.pat);
     check_pat(fcx, id_map, local.node.pat, t);
     ret bot;
 }
diff --git a/src/comp/syntax/ast.rs b/src/comp/syntax/ast.rs
index a76b6f9919f..262eac3ca01 100644
--- a/src/comp/syntax/ast.rs
+++ b/src/comp/syntax/ast.rs
@@ -108,9 +108,6 @@ enum pat_ {
     // 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 enums")
-    // 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 enums into pat_enums.
     pat_ident(@path, option<@pat>),
     pat_enum(@path, [@pat]),
     pat_rec([field_pat], bool),