about summary refs log tree commit diff
path: root/src/rustc
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2012-10-24 18:47:59 -0700
committerPatrick Walton <pcwalton@mimiga.net>2012-10-25 13:59:10 -0700
commit57cd6b3e3f0fc3d1805a167dfa2b4d5afc2f43a5 (patch)
tree3f707466cbd8e540a73a711faf78e3b2c980516c /src/rustc
parent65ee0e1dedf76a49de03e7ecb5eed03d47d54a5e (diff)
downloadrust-57cd6b3e3f0fc3d1805a167dfa2b4d5afc2f43a5.tar.gz
rust-57cd6b3e3f0fc3d1805a167dfa2b4d5afc2f43a5.zip
rustc: Translate and check exhaustiveness of struct-like enum variant patterns. r=nmatsakis
Diffstat (limited to 'src/rustc')
-rw-r--r--src/rustc/middle/check_alt.rs57
-rw-r--r--src/rustc/middle/pat_util.rs2
-rw-r--r--src/rustc/middle/trans/alt.rs66
3 files changed, 105 insertions, 20 deletions
diff --git a/src/rustc/middle/check_alt.rs b/src/rustc/middle/check_alt.rs
index fc040ecc4cd..7ed7829cf34 100644
--- a/src/rustc/middle/check_alt.rs
+++ b/src/rustc/middle/check_alt.rs
@@ -234,8 +234,14 @@ 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_region(*) |
       pat_struct(*) => {
+        match tcx.def_map.find(pat.id) {
+          Some(def_variant(_, id)) => Some(variant(id)),
+          _ => Some(single)
+        }
+      }
+      pat_box(_) | pat_uniq(_) | pat_rec(_, _) | pat_tup(_) |
+      pat_region(*) => {
         Some(single)
       }
     }
@@ -366,25 +372,44 @@ fn specialize(tcx: ty::ctxt, r: ~[@pat], ctor_id: ctor, arity: uint,
         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).sty {
-            ty::ty_class(cid, _) => {
-                class_id = cid;
-                class_fields = ty::lookup_class_fields(tcx, class_id);
+        // Is this a struct or an enum variant?
+        match tcx.def_map.get(r0.id) {
+            def_variant(_, variant_id) => {
+                if variant(variant_id) == ctor_id {
+                    // XXX: Is this right? --pcw
+                    let args = flds.map(|ty_f| {
+                        match vec::find(flds, |f| f.ident == ty_f.ident) {
+                            Some(f) => f.pat,
+                            _ => wild()
+                        }
+                    });
+                    Some(vec::append(args, vec::tail(r)))
+                } else {
+                    None
+                }
             }
             _ => {
-                tcx.sess.span_bug(r0.span, ~"struct pattern didn't resolve \
-                                             to a struct");
+                // Grab the class data that we care about.
+                let class_fields, class_id;
+                match ty::get(left_ty).sty {
+                    ty::ty_class(cid, _) => {
+                        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)))
             }
         }
-        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)))
       }
       pat_tup(args) => Some(vec::append(args, vec::tail(r))),
       pat_box(a) | pat_uniq(a) | pat_region(a) =>
diff --git a/src/rustc/middle/pat_util.rs b/src/rustc/middle/pat_util.rs
index 006065988b9..48ebda9a67e 100644
--- a/src/rustc/middle/pat_util.rs
+++ b/src/rustc/middle/pat_util.rs
@@ -24,7 +24,7 @@ fn pat_id_map(dm: resolve::DefMap, pat: @pat) -> PatIdMap {
 fn pat_is_variant(dm: resolve::DefMap, pat: @pat) -> bool {
     match pat.node {
       pat_enum(_, _) => true,
-      pat_ident(_, _, None) => match dm.find(pat.id) {
+      pat_ident(_, _, None) | pat_struct(*) => match dm.find(pat.id) {
         Some(def_variant(_, _)) => true,
         _ => false
       },
diff --git a/src/rustc/middle/trans/alt.rs b/src/rustc/middle/trans/alt.rs
index c1b499567ed..b3776e49994 100644
--- a/src/rustc/middle/trans/alt.rs
+++ b/src/rustc/middle/trans/alt.rs
@@ -369,6 +369,30 @@ fn enter_default(bcx: block, dm: DefMap, m: &[@Match/&r],
     }
 }
 
+// <pcwalton> nmatsakis: what does enter_opt do?
+// <pcwalton> in trans/alt
+// <pcwalton> trans/alt.rs is like stumbling around in a dark cave
+// <nmatsakis> pcwalton: the enter family of functions adjust the set of
+//             patterns as needed
+// <nmatsakis> yeah, at some point I kind of achieved some level of
+//             understanding
+// <nmatsakis> anyhow, they adjust the patterns given that something of that
+//             kind has been found
+// <nmatsakis> pcwalton: ok, right, so enter_XXX() adjusts the patterns, as I
+//             said
+// <nmatsakis> enter_match() kind of embodies the generic code
+// <nmatsakis> it is provided with a function that tests each pattern to see
+//             if it might possibly apply and so forth
+// <nmatsakis> so, if you have a pattern like {a: _, b: _, _} and one like _
+// <nmatsakis> then _ would be expanded to (_, _)
+// <nmatsakis> one spot for each of the sub-patterns
+// <nmatsakis> enter_opt() is one of the more complex; it covers the fallible
+//             cases
+// <nmatsakis> enter_rec_or_struct() or enter_tuple() are simpler, since they
+//             are infallible patterns
+// <nmatsakis> so all patterns must either be records (resp. tuples) or
+//             wildcards
+
 fn enter_opt(bcx: block, m: &[@Match/&r], opt: &Opt, col: uint,
              variant_size: uint, val: ValueRef)
     -> ~[@Match/&r]
@@ -406,6 +430,35 @@ fn enter_opt(bcx: block, m: &[@Match/&r], opt: &Opt, col: uint,
             ast::pat_range(l1, l2) => {
                 if opt_eq(tcx, &range(l1, l2), opt) {Some(~[])} else {None}
             }
+            ast::pat_struct(_, field_pats, _) => {
+                if opt_eq(tcx, &variant_opt(tcx, p.id), opt) {
+                    // Look up the struct variant ID.
+                    let struct_id;
+                    match tcx.def_map.get(p.id) {
+                        ast::def_variant(_, found_struct_id) => {
+                            struct_id = found_struct_id;
+                        }
+                        _ => {
+                            tcx.sess.span_bug(p.span, ~"expected enum \
+                                                        variant def");
+                        }
+                    }
+
+                    // Reorder the patterns into the same order they were
+                    // specified in the struct definition. Also fill in
+                    // unspecified fields with dummy.
+                    let reordered_patterns = dvec::DVec();
+                    for ty::lookup_class_fields(tcx, struct_id).each |field| {
+                        match field_pats.find(|p| p.ident == field.ident) {
+                            None => reordered_patterns.push(dummy),
+                            Some(fp) => reordered_patterns.push(fp.pat)
+                        }
+                    }
+                    Some(dvec::unwrap(move reordered_patterns))
+                } else {
+                    None
+                }
+            }
             _ => {
                 assert_is_binding_or_wild(bcx, p);
                 Some(vec::from_elem(variant_size, dummy))
@@ -599,12 +652,19 @@ fn extract_variant_args(bcx: block, pat_id: ast::node_id,
     return {vals: args, bcx: bcx};
 }
 
-fn collect_record_or_struct_fields(m: &[@Match], col: uint) -> ~[ast::ident] {
+// NB: This function does not collect fields from struct-like enum variants.
+fn collect_record_or_struct_fields(bcx: block, m: &[@Match], col: uint) ->
+                                   ~[ast::ident] {
     let mut fields: ~[ast::ident] = ~[];
     for vec::each(m) |br| {
         match br.pats[col].node {
           ast::pat_rec(fs, _) => extend(&mut fields, fs),
-          ast::pat_struct(_, fs, _) => extend(&mut fields, fs),
+          ast::pat_struct(_, fs, _) => {
+            match ty::get(node_id_type(bcx, br.pats[col].id)).sty {
+              ty::ty_class(*) => extend(&mut fields, fs),
+              _ => ()
+            }
+          }
           _ => ()
         }
     }
@@ -939,7 +999,7 @@ fn compile_submatch(bcx: block,
 
     root_pats_as_necessary(bcx, m, col, val);
 
-    let rec_fields = collect_record_or_struct_fields(m, col);
+    let rec_fields = collect_record_or_struct_fields(bcx, m, col);
     if rec_fields.len() > 0 {
         let pat_ty = node_id_type(bcx, pat_id);
         do expr::with_field_tys(tcx, pat_ty, None) |_has_dtor, field_tys| {