about summary refs log tree commit diff
path: root/src/comp
diff options
context:
space:
mode:
authorMarijn Haverbeke <marijnh@gmail.com>2011-12-08 11:56:16 +0100
committerMarijn Haverbeke <marijnh@gmail.com>2011-12-08 12:03:48 +0100
commit9a269a3aa8fe8140ad3f2fc2cfdfd68d6b40ec86 (patch)
treeefb59785d520476c50204eadce27bfb9128ca512 /src/comp
parent8c966b7b18a5529c33dd9766460880bd681ab102 (diff)
downloadrust-9a269a3aa8fe8140ad3f2fc2cfdfd68d6b40ec86.tar.gz
rust-9a269a3aa8fe8140ad3f2fc2cfdfd68d6b40ec86.zip
Allow binding of nested patterns
See src/test/run-pass/nested-patterns.rs for some examples. The syntax is

    boundvar@subpattern

Which will match the subpattern as usual, but also bind boundvar to the
whole matched value.

Closes #838
Diffstat (limited to 'src/comp')
-rw-r--r--src/comp/middle/alias.rs3
-rw-r--r--src/comp/middle/check_alt.rs55
-rw-r--r--src/comp/middle/resolve.rs6
-rw-r--r--src/comp/middle/trans.rs4
-rw-r--r--src/comp/middle/trans_alt.rs68
-rw-r--r--src/comp/middle/tstate/auxiliary.rs2
-rw-r--r--src/comp/middle/tstate/collect_locals.rs2
-rw-r--r--src/comp/middle/tstate/pre_post_conditions.rs14
-rw-r--r--src/comp/middle/tstate/states.rs2
-rw-r--r--src/comp/middle/typeck.rs8
-rw-r--r--src/comp/syntax/ast.rs2
-rw-r--r--src/comp/syntax/ast_util.rs5
-rw-r--r--src/comp/syntax/fold.rs4
-rw-r--r--src/comp/syntax/parse/parser.rs11
-rw-r--r--src/comp/syntax/print/pprust.rs8
-rw-r--r--src/comp/syntax/visit.rs5
16 files changed, 120 insertions, 79 deletions
diff --git a/src/comp/middle/alias.rs b/src/comp/middle/alias.rs
index 909e90fd0c6..3d081c143f3 100644
--- a/src/comp/middle/alias.rs
+++ b/src/comp/middle/alias.rs
@@ -585,8 +585,9 @@ fn pattern_roots(tcx: ty::ctxt, mut: option::t<unsafe_ty>, pat: @ast::pat)
             &set: [pattern_root]) {
         alt pat.node {
           ast::pat_wild. | ast::pat_lit(_) | ast::pat_range(_, _) {}
-          ast::pat_bind(nm) {
+          ast::pat_bind(nm, sub) {
             set += [{id: pat.id, name: nm, mut: mut, span: pat.span}];
+            alt sub { some(p) { walk(tcx, mut, p, set); } _ {} }
           }
           ast::pat_tag(_, ps) | ast::pat_tup(ps) {
             for p in ps { walk(tcx, mut, p, set); }
diff --git a/src/comp/middle/check_alt.rs b/src/comp/middle/check_alt.rs
index 59410cd3ecb..8e83105199a 100644
--- a/src/comp/middle/check_alt.rs
+++ b/src/comp/middle/check_alt.rs
@@ -2,6 +2,7 @@ import syntax::ast::*;
 import syntax::ast_util::{variant_def_ids, dummy_sp, compare_lit_exprs,
                           lit_expr_eq};
 import syntax::visit;
+import std::option::{some, none};
 
 fn check_crate(tcx: ty::ctxt, crate: @crate) {
     let v =
@@ -64,57 +65,58 @@ fn pattern_supersedes(tcx: ty::ctxt, a: @pat, b: @pat) -> bool {
     }
 
     alt a.node {
-      pat_wild. | pat_bind(_) { ret true; }
+      pat_bind(_, some(p)) { pattern_supersedes(tcx, p, b) }
+      pat_wild. | pat_bind(_, none.) { true }
       pat_lit(la) {
         alt b.node {
-          pat_lit(lb) { ret lit_expr_eq(la, lb); }
-          _ { ret false; }
+          pat_lit(lb) { lit_expr_eq(la, lb) }
+          _ { false }
         }
       }
       pat_tag(va, suba) {
         alt b.node {
           pat_tag(vb, subb) {
-            ret tcx.def_map.get(a.id) == tcx.def_map.get(b.id) &&
-                    patterns_supersede(tcx, suba, subb);
+            tcx.def_map.get(a.id) == tcx.def_map.get(b.id) &&
+                patterns_supersede(tcx, suba, subb)
           }
-          _ { ret false; }
+          _ { false }
         }
       }
       pat_rec(suba, _) {
         alt b.node {
-          pat_rec(subb, _) { ret field_patterns_supersede(tcx, suba, subb); }
-          _ { ret false; }
+          pat_rec(subb, _) { field_patterns_supersede(tcx, suba, subb) }
+          _ { false }
         }
       }
       pat_tup(suba) {
         alt b.node {
-          pat_tup(subb) { ret patterns_supersede(tcx, suba, subb); }
-          _ { ret false; }
+          pat_tup(subb) { patterns_supersede(tcx, suba, subb) }
+          _ { false }
         }
       }
       pat_box(suba) {
         alt b.node {
-          pat_box(subb) { ret pattern_supersedes(tcx, suba, subb); }
-          _ { ret pattern_supersedes(tcx, suba, b); }
+          pat_box(subb) { pattern_supersedes(tcx, suba, subb) }
+          _ { pattern_supersedes(tcx, suba, b) }
         }
       }
       pat_uniq(suba) {
         alt b.node {
-          pat_uniq(subb) { ret pattern_supersedes(tcx, suba, subb); }
-          _ { ret pattern_supersedes(tcx, suba, b); }
+          pat_uniq(subb) { pattern_supersedes(tcx, suba, subb) }
+          _ { pattern_supersedes(tcx, suba, b) }
         }
       }
       pat_range(begina, enda) {
         alt b.node {
           pat_lit(lb) {
-            ret compare_lit_exprs(begina, lb) <= 0 &&
-                compare_lit_exprs(enda, lb) >= 0;
+            compare_lit_exprs(begina, lb) <= 0 &&
+            compare_lit_exprs(enda, lb) >= 0
           }
           pat_range(beginb, endb) {
-            ret compare_lit_exprs(begina, beginb) <= 0 &&
-                compare_lit_exprs(enda, endb) >= 0;
+            compare_lit_exprs(begina, beginb) <= 0 &&
+            compare_lit_exprs(enda, endb) >= 0
           }
-          _ { ret false; }
+          _ { false }
         }
       }
     }
@@ -130,25 +132,26 @@ 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_wild. | pat_bind(_) { ret false; }
-      pat_lit(_) { ret true; }
-      pat_box(sub) { ret is_refutable(tcx, sub); }
-      pat_uniq(sub) { ret is_refutable(tcx, sub); }
+      pat_box(sub) | pat_uniq(sub) | pat_bind(_, some(sub)) {
+        is_refutable(tcx, sub)
+      }
+      pat_wild. | pat_bind(_, none.) { false }
+      pat_lit(_) { true }
       pat_rec(fields, _) {
         for field: field_pat in fields {
             if is_refutable(tcx, field.pat) { ret true; }
         }
-        ret false;
+        false
       }
       pat_tup(elts) {
         for elt in elts { if is_refutable(tcx, elt) { ret true; } }
-        ret false;
+        false
       }
       pat_tag(_, args) {
         let vdef = variant_def_ids(tcx.def_map.get(pat.id));
         if std::vec::len(ty::tag_variants(tcx, vdef.tg)) != 1u { ret true; }
         for p: @pat in args { if is_refutable(tcx, p) { ret true; } }
-        ret false;
+        false
       }
     }
 }
diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs
index 9fdbaaaba79..7ed27f2502d 100644
--- a/src/comp/middle/resolve.rs
+++ b/src/comp/middle/resolve.rs
@@ -819,7 +819,7 @@ fn lookup_in_ty_params(name: ident, ty_params: [ast::ty_param]) ->
 fn lookup_in_pat(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 } };
+        let p_name = alt bound.node { ast::pat_bind(n, _) { n } };
         if str::eq(p_name, name) { found = some(local_def(bound.id)); }
     };
     ret found;
@@ -1376,7 +1376,7 @@ 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 } };
+        let ident = alt p.node { pat_bind(n, _) { n } };
         add_name(ch, p.span, ident);
     };
 }
@@ -1424,7 +1424,7 @@ fn check_block(e: @env, b: ast::blk, &&x: (), v: vt<()>) {
                 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 } };
+                        let ident = alt p.node { pat_bind(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 458266298d4..104085579b5 100644
--- a/src/comp/middle/trans.rs
+++ b/src/comp/middle/trans.rs
@@ -4702,7 +4702,7 @@ 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(_) { true } _ { false }
+      ast::pat_bind(_, none.) { true } _ { false }
     };
     // Do not allocate space for locals that can be kept immediate.
     let ccx = bcx_ccx(cx);
@@ -4716,7 +4716,7 @@ 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) {
+      ast::pat_bind(ident, none.) {
         if bcx_ccx(cx).sess.get_opts().debuginfo {
             let _: () = str::as_buf(ident, {|buf|
                 llvm::LLVMSetValueName(r.val, buf)
diff --git a/src/comp/middle/trans_alt.rs b/src/comp/middle/trans_alt.rs
index efbad8d212a..3b729a819e3 100644
--- a/src/comp/middle/trans_alt.rs
+++ b/src/comp/middle/trans_alt.rs
@@ -88,14 +88,31 @@ type match_branch =
               id_map: ast_util::pat_id_map}};
 type match = [match_branch];
 
-fn matches_always(p: @ast::pat) -> bool {
-    ret alt p.node {
-          ast::pat_wild. { true }
-          ast::pat_bind(_) { true }
-          ast::pat_rec(_, _) { true }
-          ast::pat_tup(_) { true }
-          _ { false }
-        };
+fn has_nested_bindings(m: match, col: uint) -> bool {
+    for br in m {
+        alt br.pats[col].node {
+          ast::pat_bind(_, some(_)) { ret true; }
+          _ {}
+        }
+    }
+    ret false;
+}
+
+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)) {
+            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}]
+                         with *br}];
+          }
+          _ { result += [br]; }
+        }
+    }
+    result
 }
 
 type enter_pat = fn@(@ast::pat) -> option::t<[@ast::pat]>;
@@ -109,7 +126,7 @@ 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) {
+                             ast::pat_bind(name, none.) {
                                br.bound + [{ident: name, val: val}]
                              }
                              _ { br.bound }
@@ -123,6 +140,13 @@ 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 }
+        };
+    }
     fn e(p: @ast::pat) -> option::t<[@ast::pat]> {
         ret if matches_always(p) { some([]) } else { none };
     }
@@ -303,18 +327,17 @@ type exit_node = {bound: bind_map, from: BasicBlockRef, to: BasicBlockRef};
 type mk_fail = fn@() -> BasicBlockRef;
 
 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) }
+          _ { 0u }
+        }
+    }
     let scores = vec::init_elt_mut(0u, vec::len(m[0].pats));
     for br: match_branch in m {
         let i = 0u;
-        for p: @ast::pat in br.pats {
-            alt p.node {
-              ast::pat_lit(_) | ast::pat_tag(_, _) | ast::pat_range(_, _) {
-                scores[i] += 1u;
-              }
-              _ { }
-            }
-            i += 1u;
-        }
+        for p: @ast::pat in br.pats { scores[i] += score(p); i += 1u; }
     }
     let max_score = 0u;
     let best_col = 0u;
@@ -368,6 +391,9 @@ fn compile_submatch(bcx: @block_ctxt, m: match, vals: [ValueRef], f: mk_fail,
 
     let col = pick_col(m);
     let val = vals[col];
+    let m = has_nested_bindings(m, col) ?
+        expand_nested_bindings(m, col, val) : m;
+
     let vals_left =
         vec::slice(vals, 0u, col) +
             vec::slice(vals, col + 1u, vec::len(vals));
@@ -662,7 +688,7 @@ 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(_) {
+      ast::pat_bind(_, 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
@@ -676,6 +702,10 @@ fn bind_irrefutable_pat(bcx: @block_ctxt, pat: @ast::pat, val: ValueRef,
             bcx.fcx.lllocals.insert(pat.id, local_mem(alloc));
             trans_common::add_clean(bcx, alloc, ty);
         } else { bcx.fcx.lllocals.insert(pat.id, local_mem(val)); }
+        alt inner {
+          some(pat) { bcx = bind_irrefutable_pat(bcx, pat, val, true); }
+          _ {}
+        }
       }
       ast::pat_tag(_, sub) {
         if vec::len(sub) == 0u { ret bcx; }
diff --git a/src/comp/middle/tstate/auxiliary.rs b/src/comp/middle/tstate/auxiliary.rs
index 315eac89147..c156bb739ff 100644
--- a/src/comp/middle/tstate/auxiliary.rs
+++ b/src/comp/middle/tstate/auxiliary.rs
@@ -1048,7 +1048,7 @@ type binding = {lhs: [inst], rhs: option::t<initializer>};
 fn local_to_bindings(loc: @local) -> binding {
     let lhs = [];
     pat_bindings(loc.node.pat) {|p|
-        let ident = alt p.node { pat_bind(name) { name } };
+        let ident = alt p.node { pat_bind(name, _) { name } };
         lhs += [{ident: ident, 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 f94ef6d220a..e5166a5e6ba 100644
--- a/src/comp/middle/tstate/collect_locals.rs
+++ b/src/comp/middle/tstate/collect_locals.rs
@@ -12,7 +12,7 @@ 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 } };
+        let ident = alt p.node { pat_bind(id, _) { id } };
         log "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 d8e57c90986..4e54b405542 100644
--- a/src/comp/middle/tstate/pre_post_conditions.rs
+++ b/src/comp/middle/tstate/pre_post_conditions.rs
@@ -108,7 +108,7 @@ fn find_pre_post_loop(fcx: fn_ctxt, l: @local, index: @expr, body: blk,
     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 } };
+        let ident = alt p.node { pat_bind(id, _) { 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,
@@ -579,11 +579,7 @@ fn find_pre_post_stmt(fcx: fn_ctxt, s: stmt) {
                         /* 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 }
-                          _ { fcx.ccx.tcx.sess.span_bug(pat.span,
-                                                        "Impossible LHS"); }
-                        };
+                        let ident = alt pat.node { pat_bind(n, _) { n } };
                         alt p {
                           some(p) {
                             copy_in_postcond(fcx, id,
@@ -612,14 +608,10 @@ fn find_pre_post_stmt(fcx: fn_ctxt, s: stmt) {
                      postconds of the RHSs themselves */
                     pat_bindings(alocal.node.pat) {|pat|
                         alt pat.node {
-                          pat_bind(n) {
+                          pat_bind(n, _) {
                             set_in_postcond(bit_num(fcx, ninit(pat.id, n)),
                                             prev_pp);
                           }
-                          _ {
-                            fcx.ccx.tcx.sess.span_bug(pat.span,
-                                                      "Impossible LHS");
-                          }
                         }
                     };
                     copy_pre_post_(fcx.ccx, id, prev_pp.precondition,
diff --git a/src/comp/middle/tstate/states.rs b/src/comp/middle/tstate/states.rs
index aa7977d142d..2b47baed9ee 100644
--- a/src/comp/middle/tstate/states.rs
+++ b/src/comp/middle/tstate/states.rs
@@ -206,7 +206,7 @@ fn find_pre_post_state_loop(fcx: fn_ctxt, pres: prestate, l: @local,
     // 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 } };
+        let ident = alt p.node { pat_bind(name, _) { name } };
         set_in_poststate_ident(fcx, p.id, ident, index_post);
     };
 
diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs
index 3a37072e509..34fc1816c3f 100644
--- a/src/comp/middle/typeck.rs
+++ b/src/comp/middle/typeck.rs
@@ -1178,7 +1178,7 @@ fn gather_locals(ccx: @crate_ctxt, f: ast::_fn, id: ast::node_id,
     let visit_pat =
         lambda (p: @ast::pat, &&e: (), v: visit::vt<()>) {
             alt p.node {
-              ast::pat_bind(_) { assign(p.id, none); }
+              ast::pat_bind(_, _) { assign(p.id, none); }
               _ {/* no-op */ }
             }
             visit::visit_pat(p, e, v);
@@ -1248,7 +1248,7 @@ 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) {
+      ast::pat_bind(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);
@@ -1260,6 +1260,10 @@ fn check_pat(fcx: @fn_ctxt, map: ast_util::pat_id_map, pat: @ast::pat,
             typ = demand::simple(fcx, pat.span, ct, typ);
         }
         write::ty_only_fixup(fcx, pat.id, typ);
+        alt sub {
+          some(p) { check_pat(fcx, map, p, expected); }
+          _ {}
+        }
       }
       ast::pat_tag(path, subpats) {
         // Typecheck the path.
diff --git a/src/comp/syntax/ast.rs b/src/comp/syntax/ast.rs
index be3bf5b5ee0..30acee6dc68 100644
--- a/src/comp/syntax/ast.rs
+++ b/src/comp/syntax/ast.rs
@@ -92,7 +92,7 @@ type field_pat = {ident: ident, pat: @pat};
 
 tag pat_ {
     pat_wild;
-    pat_bind(ident);
+    pat_bind(ident, option::t<@pat>);
     pat_tag(@path, [@pat]);
     pat_rec([field_pat], bool);
     pat_tup([@pat]);
diff --git a/src/comp/syntax/ast_util.rs b/src/comp/syntax/ast_util.rs
index dc1d0e0bbbb..794d4bf5339 100644
--- a/src/comp/syntax/ast_util.rs
+++ b/src/comp/syntax/ast_util.rs
@@ -58,7 +58,7 @@ type pat_id_map = std::map::hashmap<str, node_id>;
 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 } };
+        let name = alt bound.node { pat_bind(n, _) { n } };
         map.insert(name, bound.id);
     };
     ret map;
@@ -67,7 +67,8 @@ fn pat_id_map(pat: @pat) -> pat_id_map {
 // FIXME: could return a constrained type
 fn pat_bindings(pat: @pat, it: block(@pat)) {
     alt pat.node {
-      pat_bind(_) { it(pat); }
+      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); } }
diff --git a/src/comp/syntax/fold.rs b/src/comp/syntax/fold.rs
index 0e76e9a15d1..ce82c4d7c3b 100644
--- a/src/comp/syntax/fold.rs
+++ b/src/comp/syntax/fold.rs
@@ -270,7 +270,9 @@ 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) { pat_bind(fld.fold_ident(ident)) }
+          pat_bind(ident, sub) {
+            pat_bind(fld.fold_ident(ident), option::map(fld.fold_pat, sub))
+          }
           pat_lit(_) { p }
           pat_tag(pth, pats) {
             pat_tag(fld.fold_path(pth), vec::map(fld.fold_pat, pats))
diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs
index 2bf7865bf81..5748637fde4 100644
--- a/src/comp/syntax/parse/parser.rs
+++ b/src/comp/syntax/parse/parser.rs
@@ -1429,10 +1429,9 @@ fn parse_pat(p: parser) -> @ast::pat {
                 if p.get_bad_expr_words().contains_key(fieldname) {
                     p.fatal("found " + fieldname + " in binding position");
                 }
-                subpat =
-                    @{id: p.get_id(),
-                      node: ast::pat_bind(fieldname),
-                      span: ast_util::mk_sp(lo, hi)};
+                subpat = @{id: p.get_id(),
+                           node: ast::pat_bind(fieldname, none),
+                           span: ast_util::mk_sp(lo, hi)};
             }
             fields += [{ident: fieldname, pat: subpat}];
         }
@@ -1479,7 +1478,9 @@ fn parse_pat(p: parser) -> @ast::pat {
                         _ { true }
                       } {
             hi = p.get_hi_pos();
-            pat = ast::pat_bind(parse_value_ident(p));
+            let name = parse_value_ident(p);
+            let sub = eat(p, token::AT) ? some(parse_pat(p)) : none;
+            pat = ast::pat_bind(name, sub);
         } else {
             let tag_path = parse_path_and_ty_param_substs(p);
             hi = tag_path.span.hi;
diff --git a/src/comp/syntax/print/pprust.rs b/src/comp/syntax/print/pprust.rs
index 2bb8463e2b9..ff28edd057a 100644
--- a/src/comp/syntax/print/pprust.rs
+++ b/src/comp/syntax/print/pprust.rs
@@ -1062,7 +1062,13 @@ fn print_pat(s: ps, &&pat: @ast::pat) {
     s.ann.pre(ann_node);
     alt pat.node {
       ast::pat_wild. { word(s.s, "_"); }
-      ast::pat_bind(id) { word(s.s, id); }
+      ast::pat_bind(id, sub) {
+        word(s.s, id);
+        alt sub {
+          some(p) { word(s.s, "@"); print_pat(s, p); }
+          _ {}
+        }
+      }
       ast::pat_tag(path, args) {
         print_path(s, path, true);
         if vec::len(args) > 0u {
diff --git a/src/comp/syntax/visit.rs b/src/comp/syntax/visit.rs
index be4dbef294b..9e61d82287c 100644
--- a/src/comp/syntax/visit.rs
+++ b/src/comp/syntax/visit.rs
@@ -159,8 +159,9 @@ 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) { v.visit_pat(inner, e, v); }
-      pat_uniq(inner) { v.visit_pat(inner, e, v); }
+      pat_box(inner) | pat_uniq(inner) | pat_bind(_, some(inner)) {
+        v.visit_pat(inner, e, v);
+      }
       _ { }
     }
 }