about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2012-08-06 17:01:14 -0700
committerPatrick Walton <pcwalton@mimiga.net>2012-08-06 17:36:24 -0700
commit253dfc338775570a76d6c68bf349b2026e700797 (patch)
tree016895b995fc0bce525eae43c83edecae9e9e750
parent5cb3a94bfbafc6548b2dde42beddc903a97c9eb2 (diff)
downloadrust-253dfc338775570a76d6c68bf349b2026e700797.tar.gz
rust-253dfc338775570a76d6c68bf349b2026e700797.zip
rustc: Implement pattern matching for structs
-rw-r--r--src/libsyntax/ast.rs1
-rw-r--r--src/libsyntax/ast_util.rs3
-rw-r--r--src/libsyntax/fold.rs10
-rw-r--r--src/libsyntax/parse/parser.rs190
-rw-r--r--src/libsyntax/print/pprust.rs18
-rw-r--r--src/libsyntax/visit.rs6
-rw-r--r--src/rustc/middle/borrowck/gather_loans.rs8
-rw-r--r--src/rustc/middle/check_alt.rs41
-rw-r--r--src/rustc/middle/resolve3.rs38
-rw-r--r--src/rustc/middle/trans/alt.rs101
-rw-r--r--src/rustc/middle/trans/base.rs35
-rw-r--r--src/rustc/middle/typeck/check/alt.rs80
-rw-r--r--src/test/run-pass/struct-pattern-matching.rs14
13 files changed, 445 insertions, 100 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 8badafa0e80..a325b8c634a 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -166,6 +166,7 @@ enum pat_ {
     pat_enum(@path, option<~[@pat]>), // "none" means a * pattern where
                                   // we don't bind the fields to names
     pat_rec(~[field_pat], bool),
+    pat_struct(@path, ~[field_pat], bool),
     pat_tup(~[@pat]),
     pat_box(@pat),
     pat_uniq(@pat),
diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs
index 4f261afa3de..45556e941d4 100644
--- a/src/libsyntax/ast_util.rs
+++ b/src/libsyntax/ast_util.rs
@@ -602,7 +602,8 @@ fn walk_pat(pat: @pat, it: fn(@pat)) {
     it(pat);
     match pat.node {
       pat_ident(_, pth, some(p)) => walk_pat(p, it),
-      pat_rec(fields, _) => for fields.each |f| { walk_pat(f.pat, it) }
+      pat_rec(fields, _) | pat_struct(_, fields, _) =>
+        for fields.each |f| { walk_pat(f.pat, it) }
       pat_enum(_, some(s)) | pat_tup(s) => for s.each |p| {
         walk_pat(p, it)
       }
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 40c676f8b80..57cebbf044a 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -354,6 +354,16 @@ fn noop_fold_pat(p: pat_, fld: ast_fold) -> pat_ {
             }
             pat_rec(fs, etc)
           }
+          pat_struct(pth, fields, etc) => {
+            let pth_ = fld.fold_path(pth);
+            let mut fs = ~[];
+            for fields.each |f| {
+                vec::push(fs,
+                          {ident: /* FIXME (#2543) */ copy f.ident,
+                           pat: fld.fold_pat(f.pat)});
+            }
+            pat_struct(pth_, fs, etc)
+          }
           pat_tup(elts) => pat_tup(vec::map(elts, |x| fld.fold_pat(x))),
           pat_box(inner) => pat_box(fld.fold_pat(inner)),
           pat_uniq(inner) => pat_uniq(fld.fold_pat(inner)),
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 932b3c35861..42bdab193be 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -42,14 +42,14 @@ import ast::{_mod, add, alt_check, alt_exhaustive, arg, arm, attribute,
              mac_ellipsis, mac_invoc, mac_invoc_tt, mac_var, matcher,
              match_nonterminal, match_seq, match_tok, method, mode, mt, mul,
              mutability, neg, noreturn, not, pat, pat_box, pat_enum,
-             pat_ident, pat_lit, pat_range, pat_rec, pat_tup, pat_uniq,
-             pat_wild, path, private, proto, proto_bare, proto_block,
-             proto_box, proto_uniq, provided, public, pure_fn, purity,
-             re_anon, re_named, region, rem, required, ret_style, return_val,
-             self_ty, shl, shr, stmt, stmt_decl, stmt_expr, stmt_semi,
-             subtract, sty_box, sty_by_ref, sty_region, sty_uniq, sty_value,
-             token_tree, trait_method, trait_ref, tt_delim, tt_seq, tt_tok,
-             tt_nonterminal, ty, ty_, ty_bot, ty_box, ty_field, ty_fn,
+             pat_ident, pat_lit, pat_range, pat_rec, pat_struct, pat_tup,
+             pat_uniq, pat_wild, path, private, proto, proto_bare,
+             proto_block, proto_box, proto_uniq, provided, public, pure_fn,
+             purity, re_anon, re_named, region, rem, required, ret_style,
+             return_val, self_ty, shl, shr, stmt, stmt_decl, stmt_expr,
+             stmt_semi, subtract, sty_box, sty_by_ref, sty_region, sty_uniq,
+             sty_value, token_tree, trait_method, trait_ref, tt_delim, tt_seq,
+             tt_tok, tt_nonterminal, ty, ty_, ty_bot, ty_box, ty_field, ty_fn,
              ty_infer, ty_mac, ty_method, ty_nil, ty_param, ty_path, ty_ptr,
              ty_rec, ty_rptr, ty_tup, ty_u32, ty_uniq, ty_vec,
              ty_fixed_length, unchecked_blk, uniq, unsafe_blk, unsafe_fn,
@@ -1640,6 +1640,52 @@ class parser {
         };
     }
 
+    fn parse_pat_fields(refutable: bool) -> (~[ast::field_pat], bool) {
+        let mut fields = ~[];
+        let mut etc = false;
+        let mut first = true;
+        while self.token != token::RBRACE {
+            if first { first = false; }
+            else { self.expect(token::COMMA); }
+
+            if self.token == token::UNDERSCORE {
+                self.bump();
+                if self.token != token::RBRACE {
+                    self.fatal(~"expected `}`, found `" +
+                               token_to_str(self.reader, self.token) +
+                               ~"`");
+                }
+                etc = true;
+                break;
+            }
+
+            let lo1 = self.last_span.lo;
+            let fieldname = if self.look_ahead(1u) == token::COLON {
+                self.parse_ident()
+            } else {
+                self.parse_value_ident()
+            };
+            let hi1 = self.last_span.lo;
+            let fieldpath = ast_util::ident_to_path(mk_sp(lo1, hi1),
+                                                    fieldname);
+            let mut subpat;
+            if self.token == token::COLON {
+                self.bump();
+                subpat = self.parse_pat(refutable);
+            } else {
+                subpat = @{
+                    id: self.get_id(),
+                    node: pat_ident(bind_by_implicit_ref,
+                                    fieldpath,
+                                    none),
+                    span: self.last_span
+                };
+            }
+            vec::push(fields, {ident: fieldname, pat: subpat});
+        }
+        return (fields, etc);
+    }
+
     fn parse_pat(refutable: bool) -> @pat {
         maybe_whole!{self, nt_pat};
 
@@ -1685,48 +1731,7 @@ class parser {
           }
           token::LBRACE => {
             self.bump();
-            let mut fields = ~[];
-            let mut etc = false;
-            let mut first = true;
-            while self.token != token::RBRACE {
-                if first { first = false; }
-                else { self.expect(token::COMMA); }
-
-                if self.token == token::UNDERSCORE {
-                    self.bump();
-                    if self.token != token::RBRACE {
-                        self.fatal(~"expected `}`, found `" +
-                                   token_to_str(self.reader, self.token) +
-                                   ~"`");
-                    }
-                    etc = true;
-                    break;
-                }
-
-                let lo1 = self.last_span.lo;
-                let fieldname = if self.look_ahead(1u) == token::COLON {
-                    self.parse_ident()
-                } else {
-                    self.parse_value_ident()
-                };
-                let hi1 = self.last_span.lo;
-                let fieldpath = ast_util::ident_to_path(mk_sp(lo1, hi1),
-                                                        fieldname);
-                let mut subpat;
-                if self.token == token::COLON {
-                    self.bump();
-                    subpat = self.parse_pat(refutable);
-                } else {
-                    subpat = @{
-                        id: self.get_id(),
-                        node: pat_ident(bind_by_implicit_ref,
-                                        fieldpath,
-                                        none),
-                        span: mk_sp(lo, hi)
-                    };
-                }
-                vec::push(fields, {ident: fieldname, pat: subpat});
-            }
+            let (fields, etc) = self.parse_pat_fields(refutable);
             hi = self.span.hi;
             self.bump();
             pat = pat_rec(fields, etc);
@@ -1771,21 +1776,82 @@ class parser {
             } else if !is_plain_ident(self.token) {
                 pat = self.parse_enum_variant(refutable);
             } else {
-                // this is a plain identifier, like `x` or `x(...)`
+                let binding_mode;
+                if self.eat_keyword(~"copy") {
+                    binding_mode = bind_by_value;
+                } else if refutable {
+                    // XXX: Should be bind_by_value, but that's not
+                    // backward compatible.
+                    binding_mode = bind_by_implicit_ref;
+                } else {
+                    binding_mode = bind_by_value;
+                }
+
+                let cannot_be_enum_or_struct;
                 match self.look_ahead(1) {
-                  token::LPAREN | token::LBRACKET | token::LT => {
-                    pat = self.parse_enum_variant(refutable);
-                  }
-                  _ => {
-                    let binding_mode = if refutable {
-                        // XXX: Should be bind_by_value, but that's not
-                        // backward compatible.
-                        bind_by_implicit_ref
+                    token::LPAREN | token::LBRACKET | token::LT |
+                    token::LBRACE =>
+                        cannot_be_enum_or_struct = false,
+                    _ =>
+                        cannot_be_enum_or_struct = true
+                }
+
+                if is_plain_ident(self.token) && cannot_be_enum_or_struct {
+                    let name = self.parse_value_path();
+                    let sub;
+                    if self.eat(token::AT) {
+                        sub = some(self.parse_pat(refutable));
                     } else {
-                        bind_by_value
+                        sub = none;
                     };
-                    pat = self.parse_pat_ident(refutable, binding_mode);
-                  }
+                    pat = pat_ident(binding_mode, name, sub);
+                } else {
+                    let enum_path = self.parse_path_with_tps(true);
+                    match self.token {
+                        token::LBRACE => {
+                            self.bump();
+                            let (fields, etc) =
+                                self.parse_pat_fields(refutable);
+                            self.bump();
+                            pat = pat_struct(enum_path, fields, etc);
+                        }
+                        _ => {
+                            let mut args: ~[@pat] = ~[];
+                            let mut star_pat = false;
+                            match self.token {
+                              token::LPAREN => match self.look_ahead(1u) {
+                                token::BINOP(token::STAR) => {
+                                    // This is a "top constructor only" pat
+                                      self.bump(); self.bump();
+                                      star_pat = true;
+                                      self.expect(token::RPAREN);
+                                  }
+                                _ => {
+                                    args = self.parse_unspanned_seq(
+                                        token::LPAREN, token::RPAREN,
+                                        seq_sep_trailing_disallowed
+                                            (token::COMMA),
+                                        |p| p.parse_pat(refutable));
+                                  }
+                              }
+                              _ => ()
+                            }
+                            // at this point, we're not sure whether it's a
+                            // enum or a bind
+                            if star_pat {
+                                pat = pat_enum(enum_path, none);
+                            }
+                            else if vec::is_empty(args) &&
+                                vec::len(enum_path.idents) == 1u {
+                                pat = pat_ident(binding_mode,
+                                                enum_path,
+                                                none);
+                            }
+                            else {
+                                pat = pat_enum(enum_path, some(args));
+                            }
+                        }
+                    }
                 }
             }
             hi = self.span.hi;
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index cb68ca2dd27..3fd30eb30a9 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1375,6 +1375,24 @@ fn print_pat(s: ps, &&pat: @ast::pat) {
         }
         word(s.s, ~"}");
       }
+      ast::pat_struct(path, fields, etc) => {
+        print_path(s, path, true);
+        word(s.s, ~"{");
+        fn print_field(s: ps, f: ast::field_pat) {
+            cbox(s, indent_unit);
+            word(s.s, *f.ident);
+            word_space(s, ~":");
+            print_pat(s, f.pat);
+            end(s);
+        }
+        fn get_span(f: ast::field_pat) -> codemap::span { return f.pat.span; }
+        commasep_cmnt(s, consistent, fields, print_field, get_span);
+        if etc {
+            if vec::len(fields) != 0u { word_space(s, ~","); }
+            word(s.s, ~"_");
+        }
+        word(s.s, ~"}");
+      }
       ast::pat_tup(elts) => {
         popen(s);
         commasep(s, inconsistent, elts, print_pat);
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 7df0fc739ad..c988c89f8c2 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -222,6 +222,12 @@ fn visit_pat<E>(p: @pat, e: E, v: vt<E>) {
       pat_rec(fields, _) => for fields.each |f| {
         v.visit_pat(f.pat, e, v)
       }
+      pat_struct(path, fields, _) => {
+        visit_path(path, e, v);
+        for fields.each |f| {
+            v.visit_pat(f.pat, e, v);
+        }
+      }
       pat_tup(elts) => for elts.each |elt| {
         v.visit_pat(elt, e, v)
       }
diff --git a/src/rustc/middle/borrowck/gather_loans.rs b/src/rustc/middle/borrowck/gather_loans.rs
index 86455652faa..79696becfeb 100644
--- a/src/rustc/middle/borrowck/gather_loans.rs
+++ b/src/rustc/middle/borrowck/gather_loans.rs
@@ -513,6 +513,14 @@ impl methods for gather_loan_ctxt {
             }
           }
 
+          ast::pat_struct(_, field_pats, _) => {
+            // {f1: p1, ..., fN: pN}
+            for field_pats.each |fp| {
+                let cmt_field = self.bccx.cat_field(fp.pat, cmt, fp.ident);
+                self.gather_pat(cmt_field, fp.pat, arm_id, alt_id);
+            }
+          }
+
           ast::pat_tup(subpats) => {
             // (p1, ..., pN)
             for subpats.each |subpat| {
diff --git a/src/rustc/middle/check_alt.rs b/src/rustc/middle/check_alt.rs
index 7ccccbbcc07..6b135e2e256 100644
--- a/src/rustc/middle/check_alt.rs
+++ b/src/rustc/middle/check_alt.rs
@@ -212,7 +212,8 @@ fn pat_ctor_id(tcx: ty::ctxt, p: @pat) -> option<ctor> {
       pat_range(lo, hi) => {
         some(range(eval_const_expr(tcx, lo), eval_const_expr(tcx, hi)))
       }
-      pat_box(_) | pat_uniq(_) | pat_rec(_, _) | pat_tup(_) => {
+      pat_box(_) | pat_uniq(_) | pat_rec(_, _) | pat_tup(_) |
+      pat_struct(*) => {
         some(single)
       }
     }
@@ -234,7 +235,8 @@ fn is_wild(tcx: ty::ctxt, p: @pat) -> bool {
 
 fn missing_ctor(tcx: ty::ctxt, m: matrix, left_ty: ty::t) -> option<ctor> {
     match ty::get(left_ty).struct {
-      ty::ty_box(_) | ty::ty_uniq(_) | ty::ty_tup(_) | ty::ty_rec(_) => {
+      ty::ty_box(_) | ty::ty_uniq(_) | ty::ty_tup(_) | ty::ty_rec(_) |
+      ty::ty_class(*) => {
         for m.each |r| {
             if !is_wild(tcx, r[0]) { return none; }
         }
@@ -286,6 +288,7 @@ fn ctor_arity(tcx: ty::ctxt, ctor: ctor, ty: ty::t) -> uint {
           some(v) => v.args.len()
         }
       }
+      ty::ty_class(cid, _) => ty::lookup_class_fields(tcx, cid).len(),
       _ => 0u
     }
 }
@@ -327,7 +330,29 @@ fn specialize(tcx: ty::ctxt, r: ~[@pat], ctor_id: ctor, arity: uint,
         };
         let args = vec::map(ty_flds, |ty_f| {
             match vec::find(flds, |f| f.ident == ty_f.ident ) {
-              some(f) => f.pat, _ => wild()
+              some(f) => f.pat,
+              _ => wild()
+            }
+        });
+        some(vec::append(args, vec::tail(r)))
+      }
+      pat_struct(_, flds, _) => {
+        // Grab the class data that we care about.
+        let class_fields, class_id;
+        match ty::get(left_ty).struct {
+            ty::ty_class(cid, substs) => {
+                class_id = cid;
+                class_fields = ty::lookup_class_fields(tcx, class_id);
+            }
+            _ => {
+                tcx.sess.span_bug(r0.span, ~"struct pattern didn't resolve \
+                                             to a struct");
+            }
+        }
+        let args = vec::map(class_fields, |class_field| {
+            match vec::find(flds, |f| f.ident == class_field.ident ) {
+              some(f) => f.pat,
+              _ => wild()
             }
         });
         some(vec::append(args, vec::tail(r)))
@@ -377,7 +402,9 @@ fn check_local(tcx: ty::ctxt, loc: @local, &&s: (), v: visit::vt<()>) {
 fn is_refutable(tcx: ty::ctxt, pat: @pat) -> bool {
     match tcx.def_map.find(pat.id) {
       some(def_variant(enum_id, var_id)) => {
-        if vec::len(*ty::enum_variants(tcx, enum_id)) != 1u { return true; }
+        if vec::len(*ty::enum_variants(tcx, enum_id)) != 1u {
+            return true;
+        }
       }
       _ => ()
     }
@@ -394,6 +421,12 @@ fn is_refutable(tcx: ty::ctxt, pat: @pat) -> bool {
         }
         false
       }
+      pat_struct(_, fields, _) => {
+        for fields.each |it| {
+            if is_refutable(tcx, it.pat) { return true; }
+        }
+        false
+      }
       pat_tup(elts) => {
         for elts.each |elt| { if is_refutable(tcx, elt) { return true; } }
         false
diff --git a/src/rustc/middle/resolve3.rs b/src/rustc/middle/resolve3.rs
index f87385664cc..cf8d1f348c9 100644
--- a/src/rustc/middle/resolve3.rs
+++ b/src/rustc/middle/resolve3.rs
@@ -15,8 +15,8 @@ import syntax::ast::{bound_trait, binding_mode,
 import syntax::ast::{class_member, class_method, crate, crate_num, decl_item};
 import syntax::ast::{def, def_arg, def_binding, def_class, def_const, def_fn};
 import syntax::ast::{def_foreign_mod, def_id, def_local, def_mod};
-import syntax::ast::{def_prim_ty, def_region, def_self, def_ty, def_ty_param,
-                     def_typaram_binder};
+import syntax::ast::{def_prim_ty, def_region, def_self, def_ty, def_ty_param};
+import syntax::ast::{def_typaram_binder};
 import syntax::ast::{def_upvar, def_use, def_variant, expr, expr_assign_op};
 import syntax::ast::{expr_binary, expr_cast, expr_field, expr_fn};
 import syntax::ast::{expr_fn_block, expr_index, expr_path};
@@ -30,13 +30,13 @@ import syntax::ast::{instance_var, item, item_class, item_const, item_enum};
 import syntax::ast::{item_fn, item_mac, item_foreign_mod, item_impl};
 import syntax::ast::{item_mod, item_trait, item_ty, le, local, local_crate};
 import syntax::ast::{lt, method, mul, ne, neg, node_id, pat, pat_enum};
-import syntax::ast::{pat_ident, path, prim_ty, pat_box, pat_uniq, pat_lit};
-import syntax::ast::{pat_range, pat_rec, pat_tup, pat_wild, provided};
-import syntax::ast::{required, rem, self_ty_, shl, stmt_decl, subtract, ty};
-import syntax::ast::{ty_bool, ty_char, ty_f, ty_f32, ty_f64, ty_float, ty_i};
-import syntax::ast::{ty_i16, ty_i32, ty_i64, ty_i8, ty_int, ty_param};
-import syntax::ast::{ty_path, ty_str, ty_u, ty_u16, ty_u32, ty_u64, ty_u8};
-import syntax::ast::{ty_uint, variant, view_item, view_item_export};
+import syntax::ast::{pat_ident, pat_struct, path, prim_ty, pat_box, pat_uniq};
+import syntax::ast::{pat_lit, pat_range, pat_rec, pat_tup, pat_wild};
+import syntax::ast::{provided, required, rem, self_ty_, shl, stmt_decl};
+import syntax::ast::{subtract, ty, ty_bool, ty_char, ty_f, ty_f32, ty_f64};
+import syntax::ast::{ty_float, ty_i, ty_i16, ty_i32, ty_i64, ty_i8, ty_int};
+import syntax::ast::{ty_param, ty_path, ty_str, ty_u, ty_u16, ty_u32, ty_u64};
+import syntax::ast::{ty_u8, ty_uint, variant, view_item, view_item_export};
 import syntax::ast::{view_item_import, view_item_use, view_path_glob};
 import syntax::ast::{view_path_list, view_path_simple};
 import syntax::ast_util::{def_id_of_def, dummy_sp, local_def, new_def_hash};
@@ -4017,6 +4017,26 @@ class Resolver {
                     self.resolve_expr(last_expr, visitor);
                 }
 
+                pat_struct(path, _, _) => {
+                    match self.resolve_path(path, TypeNS, false, visitor) {
+                        some(definition @ def_ty(class_id))
+                                if self.structs.contains_key(class_id) => {
+                            let has_constructor = self.structs.get(class_id);
+                            let class_def = def_class(class_id,
+                                                      has_constructor);
+                            self.record_def(pattern.id, class_def);
+                        }
+                        _ => {
+                            self.session.span_err(path.span,
+                                                  fmt!("`%s` does not name a \
+                                                        structure",
+                                                       connect(path.idents.map
+                                                               (|x| *x),
+                                                               ~"::")));
+                        }
+                    }
+                }
+
                 _ => {
                     // Nothing to do.
                 }
diff --git a/src/rustc/middle/trans/alt.rs b/src/rustc/middle/trans/alt.rs
index 27efab7de14..6e3034f26ef 100644
--- a/src/rustc/middle/trans/alt.rs
+++ b/src/rustc/middle/trans/alt.rs
@@ -187,7 +187,8 @@ fn enter_default(bcx: block, dm: DefMap, m: match_, col: uint, val: ValueRef)
 
     do enter_match(bcx, dm, m, col, val) |p| {
         match p.node {
-          ast::pat_wild | ast::pat_rec(_, _) | ast::pat_tup(_) => some(~[]),
+          ast::pat_wild | ast::pat_rec(_, _) | ast::pat_tup(_) |
+          ast::pat_struct(*) => some(~[]),
           ast::pat_ident(_, _, none) if !pat_is_variant(dm, p) => some(~[]),
           _ => none
         }
@@ -221,12 +222,12 @@ fn enter_opt(bcx: block, m: match_, opt: opt, col: uint,
     }
 }
 
-fn enter_rec(bcx: block, dm: DefMap, m: match_, col: uint,
-             fields: ~[ast::ident], val: ValueRef) -> match_ {
+fn enter_rec_or_struct(bcx: block, dm: DefMap, m: match_, col: uint,
+                       fields: ~[ast::ident], val: ValueRef) -> match_ {
     let dummy = @{id: 0, node: ast::pat_wild, span: dummy_sp()};
     do enter_match(bcx, dm, m, col, val) |p| {
         match p.node {
-          ast::pat_rec(fpats, _) => {
+          ast::pat_rec(fpats, _) | ast::pat_struct(_, fpats, _) => {
             let mut pats = ~[];
             for vec::each(fields) |fname| {
                 let mut pat = dummy;
@@ -344,6 +345,23 @@ fn collect_record_fields(m: match_, col: uint) -> ~[ast::ident] {
     return fields;
 }
 
+fn collect_struct_fields(m: match_, col: uint) -> ~[ast::ident] {
+    let mut fields: ~[ast::ident] = ~[];
+    for vec::each(m) |br| {
+        match br.pats[col].node {
+          ast::pat_struct(_, fs, _) => {
+            for vec::each(fs) |f| {
+                if !vec::any(fields, |x| str::eq(f.ident, x)) {
+                    vec::push(fields, f.ident);
+                }
+            }
+          }
+          _ => ()
+        }
+    }
+    return fields;
+}
+
 fn root_pats_as_necessary(bcx: block, m: match_, col: uint, val: ValueRef) {
     for vec::each(m) |br| {
         let pat_id = br.pats[col].id;
@@ -500,15 +518,56 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef],
 
     let rec_fields = collect_record_fields(m, col);
     // Separate path for extracting and binding record fields
-    if rec_fields.len() > 0u {
+    if rec_fields.len() > 0 {
         let fields = ty::get_fields(node_id_type(bcx, pat_id));
         let mut rec_vals = ~[];
         for vec::each(rec_fields) |field_name| {
             let ix = option::get(ty::field_idx(field_name, fields));
             vec::push(rec_vals, GEPi(bcx, val, ~[0u, ix]));
         }
-        compile_submatch(bcx, enter_rec(bcx, dm, m, col, rec_fields, val),
-                         vec::append(rec_vals, vals_left), chk, exits);
+        compile_submatch(bcx,
+                         enter_rec_or_struct(bcx, dm, m, col, rec_fields,
+                                             val),
+                         vec::append(rec_vals, vals_left),
+                         chk,
+                         exits);
+        return;
+    }
+
+    // Separate path for extracting and binding struct fields.
+    let struct_fields = collect_struct_fields(m, col);
+    if struct_fields.len() > 0 {
+        let class_id, class_fields;
+        match ty::get(node_id_type(bcx, pat_id)).struct {
+            ty::ty_class(cid, _) => {
+                class_id = cid;
+                class_fields = ty::lookup_class_fields(ccx.tcx, class_id);
+            }
+            _ => {
+                ccx.tcx.sess.bug(~"struct pattern didn't resolve to a \
+                                   struct");
+            }
+        }
+
+        // Index the class fields.
+        let field_map = std::map::box_str_hash();
+        for class_fields.eachi |i, class_field| {
+            field_map.insert(class_field.ident, i);
+        }
+
+        // Fetch each field.
+        let mut struct_vals = ~[];
+        for struct_fields.each |field_name| {
+            let index = field_map.get(field_name);
+            let fldptr = base::get_struct_field(bcx, val, class_id, index);
+            vec::push(struct_vals, fldptr);
+        }
+        compile_submatch(bcx,
+                         enter_rec_or_struct(bcx, dm, m, col, struct_fields,
+                                             val),
+                         vec::append(struct_vals, vals_left),
+                         chk,
+                         exits);
         return;
     }
 
@@ -877,6 +936,34 @@ fn bind_irrefutable_pat(bcx: block, pat: @ast::pat, val: ValueRef,
             bcx = bind_irrefutable_pat(bcx, f.pat, fldptr, make_copy);
         }
       }
+      ast::pat_struct(_, fields, _) => {
+        // Grab the class data that we care about.
+        let class_fields, class_id;
+        match ty::get(node_id_type(bcx, pat.id)).struct {
+            ty::ty_class(cid, substs) => {
+                class_id = cid;
+                class_fields = ty::lookup_class_fields(ccx.tcx, class_id);
+            }
+            _ => {
+                ccx.tcx.sess.span_bug(pat.span, ~"struct pattern didn't \
+                                                  resolve to a struct");
+            }
+        }
+
+        // Index the class fields.
+        let field_map = std::map::box_str_hash();
+        for class_fields.eachi |i, class_field| {
+            field_map.insert(class_field.ident, i);
+        }
+
+        // Fetch each field.
+        for fields.each |supplied_field| {
+            let index = field_map.get(supplied_field.ident);
+            let fldptr = base::get_struct_field(bcx, val, class_id, index);
+            bcx = bind_irrefutable_pat(bcx, supplied_field.pat, fldptr,
+                                       make_copy);
+        }
+      }
       ast::pat_tup(elems) => {
         let mut i = 0u;
         for vec::each(elems) |elem| {
diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs
index 6e882d71c00..2f090cdab7c 100644
--- a/src/rustc/middle/trans/base.rs
+++ b/src/rustc/middle/trans/base.rs
@@ -3391,6 +3391,18 @@ fn trans_rec(bcx: block, fields: ~[ast::field],
     return bcx;
 }
 
+// If the class has a destructor, our GEP is a little more
+// complicated.
+fn get_struct_field(block_context: block, dest_address: ValueRef,
+                    class_id: ast::def_id, index: uint) -> ValueRef {
+    if ty::ty_dtor(block_context.tcx(), class_id).is_some() {
+        return GEPi(block_context,
+                    GEPi(block_context, dest_address, ~[0, 1]),
+                    ~[0, index]);
+    }
+    return GEPi(block_context, dest_address, ~[0, index]);
+}
+
 fn trans_struct(block_context: block, span: span, fields: ~[ast::field],
                 base: option<@ast::expr>, id: ast::node_id, dest: dest)
              -> block {
@@ -3434,18 +3446,6 @@ fn trans_struct(block_context: block, span: span, fields: ~[ast::field],
         }
     }
 
-    // If the class has a destructor, our GEP is a little more
-    // complicated.
-    fn get_field(block_context: block, dest_address: ValueRef,
-                 class_id: ast::def_id, index: uint) -> ValueRef {
-        if ty::ty_dtor(block_context.tcx(), class_id).is_some() {
-            return GEPi(block_context,
-                        GEPi(block_context, dest_address, ~[0, 1]),
-                        ~[0, index]);
-        }
-        return GEPi(block_context, dest_address, ~[0, index]);
-    }
-
     // Now translate each field.
     let mut temp_cleanups = ~[];
     for fields.each |field| {
@@ -3468,7 +3468,8 @@ fn trans_struct(block_context: block, span: span, fields: ~[ast::field],
             }
         }
 
-        let dest = get_field(block_context, dest_address, class_id, index);
+        let dest = get_struct_field(block_context, dest_address, class_id,
+                                    index);
 
         block_context = trans_expr_save_in(block_context,
                                            field.node.expr,
@@ -3494,10 +3495,10 @@ fn trans_struct(block_context: block, span: span, fields: ~[ast::field],
                 if exists {
                     again;
                 }
-                let lldestfieldvalue = get_field(block_context,
-                                                 dest_address,
-                                                 class_id,
-                                                 i);
+                let lldestfieldvalue = get_struct_field(block_context,
+                                                        dest_address,
+                                                        class_id,
+                                                        i);
                 let llbasefieldvalue = GEPi(block_context,
                                             llbasevalue,
                                             ~[0, i]);
diff --git a/src/rustc/middle/typeck/check/alt.rs b/src/rustc/middle/typeck/check/alt.rs
index 238d5389688..4fb348e598c 100644
--- a/src/rustc/middle/typeck/check/alt.rs
+++ b/src/rustc/middle/typeck/check/alt.rs
@@ -232,6 +232,86 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
         }
         fcx.write_ty(pat.id, expected);
       }
+      ast::pat_struct(path, fields, etc) => {
+        // Grab the class data that we care about.
+        let class_fields, class_id, substitutions;
+        match structure_of(fcx, pat.span, expected) {
+            ty::ty_class(cid, substs) => {
+                class_id = cid;
+                substitutions = substs;
+                class_fields = ty::lookup_class_fields(tcx, class_id);
+            }
+            _ => {
+                // XXX: This should not be fatal.
+                tcx.sess.span_fatal(pat.span,
+                                    fmt!("mismatched types: expected `%s` \
+                                          but found struct",
+                                         fcx.infcx.ty_to_str(expected)));
+            }
+        }
+
+        // Check to ensure that the struct is the one specified.
+        match tcx.def_map.get(pat.id) {
+            ast::def_class(supplied_def_id, _)
+                    if supplied_def_id == class_id => {
+                // OK.
+            }
+            ast::def_class(*) => {
+                let name = syntax::print::pprust::path_to_str(path);
+                tcx.sess.span_err(pat.span,
+                                  fmt!("mismatched types: expected `%s` but \
+                                        found `%s`",
+                                       fcx.infcx.ty_to_str(expected),
+                                       name));
+            }
+            _ => {
+                tcx.sess.span_bug(pat.span, ~"resolve didn't write in class");
+            }
+        }
+
+        // Index the class fields.
+        let field_map = std::map::box_str_hash();
+        for class_fields.eachi |i, class_field| {
+            field_map.insert(class_field.ident, i);
+        }
+
+        // Typecheck each field.
+        let found_fields = std::map::uint_hash();
+        for fields.each |field| {
+            match field_map.find(field.ident) {
+                some(index) => {
+                    let class_field = class_fields[index];
+                    let field_type = ty::lookup_field_type(tcx,
+                                                           class_id,
+                                                           class_field.id,
+                                                           substitutions);
+                    check_pat(pcx, field.pat, field_type);
+                    found_fields.insert(index, ());
+                }
+                none => {
+                    let name = syntax::print::pprust::path_to_str(path);
+                    tcx.sess.span_err(pat.span,
+                                      fmt!("struct `%s` does not have a field
+                                            named `%s`", name, *field.ident));
+                }
+            }
+        }
+
+        // Report an error if not all the fields were specified.
+        if !etc {
+            for class_fields.eachi |i, field| {
+                if found_fields.contains_key(i) {
+                    again;
+                }
+                tcx.sess.span_err(pat.span,
+                                  fmt!("pattern does not mention field `%s`",
+                                       *field.ident));
+            }
+        }
+
+        // Finally, write in the type.
+        fcx.write_ty(pat.id, expected);
+      }
       ast::pat_tup(elts) => {
         let ex_elts = match structure_of(fcx, pat.span, expected) {
           ty::ty_tup(elts) => elts,
diff --git a/src/test/run-pass/struct-pattern-matching.rs b/src/test/run-pass/struct-pattern-matching.rs
new file mode 100644
index 00000000000..0a4f3b933c0
--- /dev/null
+++ b/src/test/run-pass/struct-pattern-matching.rs
@@ -0,0 +1,14 @@
+struct Foo {
+    x: int;
+    y: int;
+}
+
+fn main() {
+    let a = Foo { x: 1, y: 2 };
+    match a {
+        Foo { x: x, y: y } => io::println(fmt!("yes, %d, %d", x, y))
+    }
+}
+
+
+