about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2012-11-02 09:56:09 -0700
committerPatrick Walton <pcwalton@mimiga.net>2012-11-02 09:56:09 -0700
commit106f9976ab1baec06af41f7009f539cd8371a840 (patch)
tree8c9f1afbbba04dc89e519f785d8175b1b66964f7
parentb62844e755d36fddff303c0639303bb0f56c5d41 (diff)
downloadrust-106f9976ab1baec06af41f7009f539cd8371a840.tar.gz
rust-106f9976ab1baec06af41f7009f539cd8371a840.zip
rustc: Implement typechecking, exhaustiveness checking, and borrow checking for pattern matching of tuple structs. r=nmatsakis
Conflicts:

	src/rustc/middle/typeck/check/alt.rs
-rw-r--r--src/rustc/middle/borrowck.rs4
-rw-r--r--src/rustc/middle/borrowck/loan.rs3
-rw-r--r--src/rustc/middle/borrowck/preserve.rs3
-rw-r--r--src/rustc/middle/check_alt.rs9
-rw-r--r--src/rustc/middle/mem_categorization.rs48
-rw-r--r--src/rustc/middle/resolve.rs5
-rw-r--r--src/rustc/middle/ty.rs23
-rw-r--r--src/rustc/middle/typeck/check/alt.rs142
-rw-r--r--src/test/compile-fail/tuple-struct-nonexhaustive.rs11
9 files changed, 177 insertions, 71 deletions
diff --git a/src/rustc/middle/borrowck.rs b/src/rustc/middle/borrowck.rs
index 1b1ec022d3f..ccc2ca9888a 100644
--- a/src/rustc/middle/borrowck.rs
+++ b/src/rustc/middle/borrowck.rs
@@ -594,7 +594,7 @@ impl borrowck_ctxt {
 // mutable structure.
 fn inherent_mutability(ck: comp_kind) -> mutability {
     match ck {
-      comp_tuple | comp_variant(_)        => m_imm,
-      comp_field(_, m) | comp_index(_, m) => m
+      comp_tuple | comp_anon_field | comp_variant(_) => m_imm,
+      comp_field(_, m) | comp_index(_, m)            => m
     }
 }
diff --git a/src/rustc/middle/borrowck/loan.rs b/src/rustc/middle/borrowck/loan.rs
index 71414e6e724..b3023497220 100644
--- a/src/rustc/middle/borrowck/loan.rs
+++ b/src/rustc/middle/borrowck/loan.rs
@@ -116,7 +116,8 @@ impl LoanContext {
             // overwritten and the component along with it.
             self.loan_stable_comp(cmt, cmt_base, req_mutbl, m)
           }
-          cat_comp(cmt_base, comp_tuple) => {
+          cat_comp(cmt_base, comp_tuple) |
+          cat_comp(cmt_base, comp_anon_field) => {
             // As above.
             self.loan_stable_comp(cmt, cmt_base, req_mutbl, m_imm)
           }
diff --git a/src/rustc/middle/borrowck/preserve.rs b/src/rustc/middle/borrowck/preserve.rs
index 556ea7867cf..c27ceca055f 100644
--- a/src/rustc/middle/borrowck/preserve.rs
+++ b/src/rustc/middle/borrowck/preserve.rs
@@ -119,7 +119,8 @@ priv impl &preserve_ctxt {
           }
           cat_comp(cmt_base, comp_field(*)) |
           cat_comp(cmt_base, comp_index(*)) |
-          cat_comp(cmt_base, comp_tuple) => {
+          cat_comp(cmt_base, comp_tuple) |
+          cat_comp(cmt_base, comp_anon_field) => {
             // Most embedded components: if the base is stable, the
             // type never changes.
             self.preserve(cmt_base)
diff --git a/src/rustc/middle/check_alt.rs b/src/rustc/middle/check_alt.rs
index 7ed7829cf34..5b35ed98a83 100644
--- a/src/rustc/middle/check_alt.rs
+++ b/src/rustc/middle/check_alt.rs
@@ -355,6 +355,15 @@ fn specialize(tcx: ty::ctxt, r: ~[@pat], ctor_id: ctor, arity: uint,
             Some(vec::append(args, vec::tail(r)))
           }
           def_variant(_, _) => None,
+          def_class(*) => {
+            // XXX: Is this right? --pcw
+            let new_args;
+            match args {
+              Some(args) => new_args = args,
+              None => new_args = vec::from_elem(arity, wild())
+            }
+            Some(vec::append(new_args, vec::tail(r)))
+          }
           _ => None
         }
       }
diff --git a/src/rustc/middle/mem_categorization.rs b/src/rustc/middle/mem_categorization.rs
index 83eddc1c21b..55bf41b573e 100644
--- a/src/rustc/middle/mem_categorization.rs
+++ b/src/rustc/middle/mem_categorization.rs
@@ -162,6 +162,8 @@ impl ptr_kind : cmp::Eq {
 // structure accessible without a dereference":
 enum comp_kind {
     comp_tuple,                  // elt in a tuple
+    comp_anon_field,             // anonymous field (in e.g.
+                                 // struct Foo(int, int);
     comp_variant(ast::def_id),   // internals to a variant of given enum
     comp_field(ast::ident,       // name of field
                ast::mutability), // declared mutability of field
@@ -178,6 +180,12 @@ impl comp_kind : cmp::Eq {
                     _ => false
                 }
             }
+            comp_anon_field => {
+                match (*other) {
+                    comp_anon_field => true,
+                    _ => false
+                }
+            }
             comp_variant(e0a) => {
                 match (*other) {
                     comp_variant(e0b) => e0a == e0b,
@@ -775,6 +783,14 @@ impl &mem_categorization_ctxt {
           ty: self.tcx.ty(elt)}
     }
 
+    fn cat_anon_struct_field<N: ast_node>(elt: N, cmt: cmt) -> cmt {
+        @{id: elt.id(), span: elt.span(),
+          cat: cat_comp(cmt, comp_anon_field),
+          lp: cmt.lp.map(|l| @lp_comp(*l, comp_anon_field)),
+          mutbl: cmt.mutbl, // imm iff in an immutable context
+          ty: self.tcx.ty(elt)}
+    }
+
     fn cat_method_ref(expr: @ast::expr, expr_ty: ty::t) -> cmt {
         @{id:expr.id, span:expr.span,
           cat:cat_special(sk_method), lp:None,
@@ -834,16 +850,26 @@ impl &mem_categorization_ctxt {
             // variant(*)
           }
           ast::pat_enum(_, Some(subpats)) => {
-            // variant(x, y, z)
-            let enum_did = match self.tcx.def_map.find(pat.id) {
-              Some(ast::def_variant(enum_did, _)) => enum_did,
-              e => tcx.sess.span_bug(pat.span,
-                                     fmt!("resolved to %?, not variant", e))
-            };
-
-            for subpats.each |subpat| {
-                let subcmt = self.cat_variant(*subpat, enum_did, cmt);
-                self.cat_pattern(subcmt, *subpat, op);
+            match self.tcx.def_map.find(pat.id) {
+                Some(ast::def_variant(enum_did, _)) => {
+                    // variant(x, y, z)
+                    for subpats.each |subpat| {
+                        let subcmt = self.cat_variant(*subpat, enum_did, cmt);
+                        self.cat_pattern(subcmt, *subpat, op);
+                    }
+                }
+                Some(ast::def_class(*)) => {
+                    for subpats.each |subpat| {
+                        let cmt_field = self.cat_anon_struct_field(*subpat,
+                                                                   cmt);
+                        self.cat_pattern(cmt_field, *subpat, op);
+                    }
+                }
+                _ => {
+                    self.tcx.sess.span_bug(
+                        pat.span,
+                        ~"enum pattern didn't resolve to enum or struct");
+                }
             }
           }
 
@@ -934,6 +960,7 @@ impl &mem_categorization_ctxt {
           comp_field(fld, _) => self.tcx.sess.str_of(fld),
           comp_index(*) => ~"[]",
           comp_tuple => ~"()",
+          comp_anon_field => ~"<anonymous field>",
           comp_variant(_) => ~"<enum>"
         }
     }
@@ -986,6 +1013,7 @@ impl &mem_categorization_ctxt {
           }
           cat_comp(_, comp_field(*)) => mut_str + ~" field",
           cat_comp(_, comp_tuple) => ~"tuple content",
+          cat_comp(_, comp_anon_field) => ~"anonymous field",
           cat_comp(_, comp_variant(_)) => ~"enum content",
           cat_comp(_, comp_index(t, _)) => {
             match ty::get(t).sty {
diff --git a/src/rustc/middle/resolve.rs b/src/rustc/middle/resolve.rs
index 97551162de5..a979c0d7b57 100644
--- a/src/rustc/middle/resolve.rs
+++ b/src/rustc/middle/resolve.rs
@@ -4286,9 +4286,10 @@ impl Resolver {
                 }
 
                 pat_ident(_, path, _) | pat_enum(path, _) => {
-                    // These two must be enum variants.
+                    // These two must be enum variants or structs.
                     match self.resolve_path(path, ValueNS, false, visitor) {
-                        Some(def @ def_variant(*)) => {
+                        Some(def @ def_variant(*)) |
+                                Some(def @ def_class(*)) => {
                             self.record_def(pattern.id, def);
                         }
                         Some(_) => {
diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs
index 1402d1c9324..65f77cefd02 100644
--- a/src/rustc/middle/ty.rs
+++ b/src/rustc/middle/ty.rs
@@ -3554,6 +3554,29 @@ fn ty_to_def_id(ty: t) -> Option<ast::def_id> {
     }
 }
 
+/// Returns the def ID of the constructor for the given tuple-like struct, or
+/// None if the struct is not tuple-like. Fails if the given def ID does not
+/// refer to a struct at all.
+fn struct_ctor_id(cx: ctxt, struct_did: ast::def_id) -> Option<ast::def_id> {
+    if struct_did.crate != ast::local_crate {
+        // XXX: Cross-crate functionality.
+        cx.sess.unimpl(~"constructor ID of cross-crate tuple structs");
+    }
+
+    match cx.items.find(struct_did.node) {
+        Some(ast_map::node_item(item, _)) => {
+            match item.node {
+                ast::item_class(struct_def, _) => {
+                    struct_def.ctor_id.map(|ctor_id|
+                        ast_util::local_def(*ctor_id))
+                }
+                _ => cx.sess.bug(~"called struct_ctor_id on non-struct")
+            }
+        }
+        _ => cx.sess.bug(~"called struct_ctor_id on non-struct")
+    }
+}
+
 // Enum information
 type variant_info = @{args: ~[t], ctor_ty: t, name: ast::ident,
                       id: ast::def_id, disr_val: int};
diff --git a/src/rustc/middle/typeck/check/alt.rs b/src/rustc/middle/typeck/check/alt.rs
index dc1b14e4beb..5a62ad0461d 100644
--- a/src/rustc/middle/typeck/check/alt.rs
+++ b/src/rustc/middle/typeck/check/alt.rs
@@ -123,70 +123,102 @@ fn check_pat_variant(pcx: pat_ctxt, pat: @ast::pat, path: @ast::path,
     let fcx = pcx.fcx;
     let tcx = pcx.fcx.ccx.tcx;
 
-    // Lookup the enum and variant def ids:
-    let v_def = lookup_def(pcx.fcx, path.span, pat.id);
-    let v_def_ids = ast_util::variant_def_ids(v_def);
-
-    // Assign the pattern the type of the *enum*, not the variant.
-    let enum_tpt = ty::lookup_item_type(tcx, v_def_ids.enm);
-    instantiate_path(pcx.fcx, path, enum_tpt, pat.span, pat.id,
-                     pcx.block_region);
+    let arg_types, kind_name;
 
     // structure_of requires type variables to be resolved.
     // So when we pass in <expected>, it's an error if it
     // contains type variables.
 
-    // Take the enum type params out of `expected`.
+    // Check to see whether this is an enum or a struct.
     match structure_of(pcx.fcx, pat.span, expected) {
-      ty::ty_enum(_, ref expected_substs) => {
-        // check that the type of the value being matched is a subtype
-        // of the type of the pattern:
-        let pat_ty = fcx.node_ty(pat.id);
-        demand::suptype(fcx, pat.span, pat_ty, expected);
-
-        // Get the expected types of the arguments.
-        let arg_types = {
-            let vinfo =
-                ty::enum_variant_with_id(
-                    tcx, v_def_ids.enm, v_def_ids.var);
-            vinfo.args.map(|t| { ty::subst(tcx, expected_substs, *t) })
-        };
-        let arg_len = arg_types.len(), subpats_len = match subpats {
-            None => arg_len,
-            Some(ps) => ps.len()
-        };
-        if arg_len > 0 {
-            // 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_fatal(pat.span, s);
-            }
-
-            do subpats.iter() |pats| {
-                for vec::each2(*pats, arg_types) |subpat, arg_ty| {
-                  check_pat(pcx, *subpat, *arg_ty);
-                }
+        ty::ty_enum(_, ref expected_substs) => {
+            // Lookup the enum and variant def ids:
+            let v_def = lookup_def(pcx.fcx, path.span, pat.id);
+            let v_def_ids = ast_util::variant_def_ids(v_def);
+
+            // Assign the pattern the type of the *enum*, not the variant.
+            let enum_tpt = ty::lookup_item_type(tcx, v_def_ids.enm);
+            instantiate_path(pcx.fcx, path, enum_tpt, pat.span, pat.id,
+                             pcx.block_region);
+
+            // check that the type of the value being matched is a subtype
+            // of the type of the pattern:
+            let pat_ty = fcx.node_ty(pat.id);
+            demand::suptype(fcx, pat.span, pat_ty, expected);
+
+            // Get the expected types of the arguments.
+            arg_types = {
+                let vinfo =
+                    ty::enum_variant_with_id(
+                        tcx, v_def_ids.enm, v_def_ids.var);
+                vinfo.args.map(|t| { ty::subst(tcx, expected_substs, *t) })
             };
-        } else if subpats_len > 0 {
-            tcx.sess.span_fatal
-                (pat.span, fmt!("this pattern has %u field%s, \
-                                 but the corresponding variant has no fields",
-                                subpats_len,
-                                if subpats_len == 1u { ~"" }
-                                else { ~"s" }));
+
+            kind_name = "variant";
         }
-      }
-      _ => {
+        ty::ty_class(struct_def_id, ref expected_substs) => {
+            // Assign the pattern the type of the struct.
+            let struct_tpt = ty::lookup_item_type(tcx, struct_def_id);
+            instantiate_path(pcx.fcx, path, struct_tpt, pat.span, pat.id,
+                             pcx.block_region);
+
+            // Check that the type of the value being matched is a subtype of
+            // the type of the pattern.
+            let pat_ty = fcx.node_ty(pat.id);
+            demand::suptype(fcx, pat.span, pat_ty, expected);
+
+            // Get the expected types of the arguments.
+            let class_fields = ty::class_items_as_fields(
+                tcx, struct_def_id, expected_substs);
+            arg_types = class_fields.map(|field| field.mt.ty);
+
+            kind_name = "structure";
+        }
+        _ => {
+            tcx.sess.span_fatal(
+                pat.span,
+                fmt!("mismatched types: expected enum or structure but \
+                      found `%s`",
+                     fcx.infcx().ty_to_str(expected)));
+        }
+    }
+
+    let arg_len = arg_types.len();
+
+    // Count the number of subpatterns.
+    let subpats_len;
+    match subpats {
+        None => subpats_len = arg_len,
+        Some(subpats) => subpats_len = subpats.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 \
+                          %s has %u field%s",
+                         subpats_len,
+                         if subpats_len == 1u { ~"" } else { ~"s" },
+                         kind_name,
+                         arg_len,
+                         if arg_len == 1u { ~"" } else { ~"s" });
+            // XXX: This should not be fatal.
+            tcx.sess.span_fatal(pat.span, s);
+        }
+
+        do subpats.iter() |pats| {
+            for vec::each2(*pats, arg_types) |subpat, arg_ty| {
+              check_pat(pcx, *subpat, *arg_ty);
+            }
+        };
+    } else if subpats_len > 0u {
         tcx.sess.span_fatal
-            (pat.span,
-             fmt!("mismatched types: expected enum but found `%s`",
-                  fcx.infcx().ty_to_str(expected)));
-      }
+            (pat.span, fmt!("this pattern has %u field%s, but the \
+                             corresponding %s has no fields",
+                            subpats_len,
+                            if subpats_len == 1u { ~"" }
+                            else { ~"s" },
+                            kind_name));
     }
 }
 
diff --git a/src/test/compile-fail/tuple-struct-nonexhaustive.rs b/src/test/compile-fail/tuple-struct-nonexhaustive.rs
new file mode 100644
index 00000000000..a32d598686c
--- /dev/null
+++ b/src/test/compile-fail/tuple-struct-nonexhaustive.rs
@@ -0,0 +1,11 @@
+struct Foo(int, int);
+
+fn main() {
+    let x = Foo(1, 2);
+    match x {   //~ ERROR non-exhaustive
+        Foo(1, b) => io::println(fmt!("%d", b)),
+        Foo(2, b) => io::println(fmt!("%d", b))
+    }
+}
+
+