about summary refs log tree commit diff
path: root/src/rustc
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2012-10-25 12:35:29 -0700
committerPatrick Walton <pcwalton@mimiga.net>2012-10-25 12:35:29 -0700
commit65ee0e1dedf76a49de03e7ecb5eed03d47d54a5e (patch)
treebae43c245cf5ef71b82c8928023d26ca2155e713 /src/rustc
parent75947b311ae79c2a2113fb7f797c68ffb3d357b3 (diff)
parent588ea599927d62a04c12ccca87196ca415b86274 (diff)
downloadrust-65ee0e1dedf76a49de03e7ecb5eed03d47d54a5e.tar.gz
rust-65ee0e1dedf76a49de03e7ecb5eed03d47d54a5e.zip
Merge pull request #3858 from pcwalton/struct-like-typeck
rustc: Typecheck, privacy check, and borrow check struct-like enum variants. r=tjc
Diffstat (limited to 'src/rustc')
-rw-r--r--src/rustc/middle/mem_categorization.rs39
-rw-r--r--src/rustc/middle/privacy.rs25
-rw-r--r--src/rustc/middle/typeck/check/alt.rs210
3 files changed, 197 insertions, 77 deletions
diff --git a/src/rustc/middle/mem_categorization.rs b/src/rustc/middle/mem_categorization.rs
index e82892f3d1c..8ee9adc4e2f 100644
--- a/src/rustc/middle/mem_categorization.rs
+++ b/src/rustc/middle/mem_categorization.rs
@@ -464,7 +464,7 @@ impl &mem_categorization_ctxt {
             }
 
             let base_cmt = self.cat_expr(base);
-            self.cat_field(expr, base_cmt, f_name)
+            self.cat_field(expr, base_cmt, f_name, expr.id)
           }
 
           ast::expr_index(base, _) => {
@@ -632,9 +632,14 @@ impl &mem_categorization_ctxt {
         }
     }
 
-    fn cat_field<N:ast_node>(node: N, base_cmt: cmt,
-                             f_name: ast::ident) -> cmt {
-        let f_mutbl = match field_mutbl(self.tcx, base_cmt.ty, f_name) {
+    /// The `field_id` parameter is the ID of the enclosing expression or
+    /// pattern. It is used to determine which variant of an enum is in use.
+    fn cat_field<N:ast_node>(node: N,
+                             base_cmt: cmt,
+                             f_name: ast::ident,
+                             field_id: ast::node_id) -> cmt {
+        let f_mutbl = match field_mutbl(self.tcx, base_cmt.ty, f_name,
+                                        field_id) {
           Some(f_mutbl) => f_mutbl,
           None => {
             self.tcx.sess.span_bug(
@@ -851,7 +856,7 @@ impl &mem_categorization_ctxt {
           ast::pat_rec(field_pats, _) => {
             // {f1: p1, ..., fN: pN}
             for field_pats.each |fp| {
-                let cmt_field = self.cat_field(fp.pat, cmt, fp.ident);
+                let cmt_field = self.cat_field(fp.pat, cmt, fp.ident, pat.id);
                 self.cat_pattern(cmt_field, fp.pat, op);
             }
           }
@@ -859,7 +864,7 @@ impl &mem_categorization_ctxt {
           ast::pat_struct(_, field_pats, _) => {
             // {f1: p1, ..., fN: pN}
             for field_pats.each |fp| {
-                let cmt_field = self.cat_field(fp.pat, cmt, fp.ident);
+                let cmt_field = self.cat_field(fp.pat, cmt, fp.ident, pat.id);
                 self.cat_pattern(cmt_field, fp.pat, op);
             }
           }
@@ -998,9 +1003,13 @@ impl &mem_categorization_ctxt {
     }
 }
 
+/// The node_id here is the node of the expression that references the field.
+/// This function looks it up in the def map in case the type happens to be
+/// an enum to determine which variant is in use.
 fn field_mutbl(tcx: ty::ctxt,
                base_ty: ty::t,
-               f_name: ast::ident) -> Option<ast::mutability> {
+               f_name: ast::ident,
+               node_id: ast::node_id) -> Option<ast::mutability> {
     // Need to refactor so that records/class fields can be treated uniformly.
     match ty::get(base_ty).sty {
       ty::ty_rec(fields) => {
@@ -1021,6 +1030,22 @@ fn field_mutbl(tcx: ty::ctxt,
             }
         }
       }
+      ty::ty_enum(*) => {
+        match tcx.def_map.get(node_id) {
+          ast::def_variant(_, variant_id) => {
+            for ty::lookup_class_fields(tcx, variant_id).each |fld| {
+                if fld.ident == f_name {
+                    let m = match fld.mutability {
+                      ast::class_mutable => ast::m_mutbl,
+                      ast::class_immutable => ast::m_imm
+                    };
+                    return Some(m);
+                }
+            }
+          }
+          _ => {}
+        }
+      }
       _ => { }
     }
 
diff --git a/src/rustc/middle/privacy.rs b/src/rustc/middle/privacy.rs
index acf56ad81e9..dfdd3541169 100644
--- a/src/rustc/middle/privacy.rs
+++ b/src/rustc/middle/privacy.rs
@@ -239,6 +239,31 @@ fn check_crate(tcx: ty::ctxt, method_map: &method_map, crate: @ast::crate) {
                                 }
                             }
                         }
+                        ty_enum(enum_id, _) => {
+                            if enum_id.crate != local_crate ||
+                                    !privileged_items.contains(
+                                        &enum_id.node) {
+                                match tcx.def_map.find(pattern.id) {
+                                    Some(def_variant(_, variant_id)) => {
+                                        for fields.each |field| {
+                                            debug!("(privacy checking) \
+                                                    checking field in \
+                                                    struct variant pattern");
+                                            check_field(pattern.span,
+                                                        variant_id,
+                                                        field.ident);
+                                        }
+                                    }
+                                    _ => {
+                                        tcx.sess.span_bug(pattern.span,
+                                                          ~"resolve didn't \
+                                                            map enum struct \
+                                                            pattern to a \
+                                                            variant def");
+                                    }
+                                }
+                            }
+                        }
                         _ => {
                             tcx.sess.span_bug(pattern.span,
                                               ~"struct pattern didn't have \
diff --git a/src/rustc/middle/typeck/check/alt.rs b/src/rustc/middle/typeck/check/alt.rs
index caace605198..a8308bb1b3c 100644
--- a/src/rustc/middle/typeck/check/alt.rs
+++ b/src/rustc/middle/typeck/check/alt.rs
@@ -186,6 +186,139 @@ fn check_pat_variant(pcx: pat_ctxt, pat: @ast::pat, path: @ast::path,
     }
 }
 
+/// `path` is the AST path item naming the type of this struct.
+/// `fields` is the field patterns of the struct pattern.
+/// `class_fields` describes the type of each field of the struct.
+/// `class_id` is the ID of the struct.
+/// `substitutions` are the type substitutions applied to this struct type
+/// (e.g. K,V in HashMap<K,V>).
+/// `etc` is true if the pattern said '...' and false otherwise.
+fn check_struct_pat_fields(pcx: pat_ctxt,
+                           span: span,
+                           path: @ast::path,
+                           fields: ~[ast::field_pat],
+                           class_fields: ~[ty::field_ty],
+                           class_id: ast::def_id,
+                           substitutions: &ty::substs,
+                           etc: bool) {
+    let tcx = pcx.fcx.ccx.tcx;
+
+    // Index the class fields.
+    let field_map = std::map::HashMap();
+    for class_fields.eachi |i, class_field| {
+        field_map.insert(class_field.ident, i);
+    }
+
+    // Typecheck each field.
+    let found_fields = std::map::HashMap();
+    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 = pprust::path_to_str(path, tcx.sess.intr());
+                tcx.sess.span_err(span,
+                                  fmt!("struct `%s` does not have a field
+                                        named `%s`", name,
+                                       tcx.sess.str_of(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) {
+                loop;
+            }
+            tcx.sess.span_err(span,
+                              fmt!("pattern does not mention field `%s`",
+                                   tcx.sess.str_of(field.ident)));
+        }
+    }
+}
+
+fn check_struct_pat(pcx: pat_ctxt, pat_id: ast::node_id, span: span,
+                    expected: ty::t, path: @ast::path,
+                    fields: ~[ast::field_pat], etc: bool,
+                    class_id: ast::def_id, substitutions: &ty::substs) {
+    let fcx = pcx.fcx;
+    let tcx = pcx.fcx.ccx.tcx;
+
+    let class_fields = ty::lookup_class_fields(tcx, class_id);
+
+    // Check to ensure that the struct is the one specified.
+    match tcx.def_map.find(pat_id) {
+        Some(ast::def_class(supplied_def_id))
+                if supplied_def_id == class_id => {
+            // OK.
+        }
+        Some(ast::def_class(*)) | Some(ast::def_variant(*)) => {
+            let name = pprust::path_to_str(path, tcx.sess.intr());
+            tcx.sess.span_err(span,
+                              fmt!("mismatched types: expected `%s` but \
+                                    found `%s`",
+                                   fcx.infcx().ty_to_str(expected),
+                                   name));
+        }
+        _ => {
+            tcx.sess.span_bug(span, ~"resolve didn't write in class");
+        }
+    }
+
+    // Forbid pattern-matching structs with destructors.
+    if ty::has_dtor(tcx, class_id) {
+        tcx.sess.span_err(span, ~"deconstructing struct not allowed in \
+                                  pattern (it has a destructor)");
+    }
+
+    check_struct_pat_fields(pcx, span, path, fields, class_fields, class_id,
+                            substitutions, etc);
+}
+
+fn check_struct_like_enum_variant_pat(pcx: pat_ctxt,
+                                      pat_id: ast::node_id,
+                                      span: span,
+                                      expected: ty::t,
+                                      path: @ast::path,
+                                      fields: ~[ast::field_pat],
+                                      etc: bool,
+                                      enum_id: ast::def_id,
+                                      substitutions: &ty::substs) {
+    let fcx = pcx.fcx;
+    let tcx = pcx.fcx.ccx.tcx;
+
+    // Find the variant that was specified.
+    match tcx.def_map.find(pat_id) {
+        Some(ast::def_variant(found_enum_id, variant_id))
+                if found_enum_id == enum_id => {
+            // Get the struct fields from this struct-like enum variant.
+            let class_fields = ty::lookup_class_fields(tcx, variant_id);
+
+            check_struct_pat_fields(pcx, span, path, fields, class_fields,
+                                    variant_id, substitutions, etc);
+        }
+        Some(ast::def_class(*)) | Some(ast::def_variant(*)) => {
+            let name = pprust::path_to_str(path, tcx.sess.intr());
+            tcx.sess.span_err(span,
+                              fmt!("mismatched types: expected `%s` but \
+                                    found `%s`",
+                                   fcx.infcx().ty_to_str(expected),
+                                   name));
+        }
+        _ => {
+            tcx.sess.span_bug(span, ~"resolve didn't write in variant");
+        }
+    }
+}
+
 // Pattern checking is top-down rather than bottom-up so that bindings get
 // their types immediately.
 fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
@@ -306,13 +439,16 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
       }
       ast::pat_struct(path, fields, etc) => {
         // Grab the class data that we care about.
-        let class_fields, class_id, substitutions;
         let structure = structure_of(fcx, pat.span, expected);
         match structure {
             ty::ty_class(cid, ref substs) => {
-                class_id = cid;
-                substitutions = substs;
-                class_fields = ty::lookup_class_fields(tcx, class_id);
+                check_struct_pat(pcx, pat.id, pat.span, expected, path,
+                                 fields, etc, cid, substs);
+            }
+            ty::ty_enum(eid, ref substs) => {
+                check_struct_like_enum_variant_pat(
+                    pcx, pat.id, pat.span, expected, path, fields, etc, eid,
+                    substs);
             }
             _ => {
                 // XXX: This should not be fatal.
@@ -323,72 +459,6 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
             }
         }
 
-        // 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 = pprust::path_to_str(path, tcx.sess.intr());
-                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");
-            }
-        }
-
-        // Forbid pattern-matching structs with destructors.
-        if ty::has_dtor(tcx, class_id) {
-            tcx.sess.span_err(pat.span, ~"deconstructing struct not allowed \
-                                          in pattern (it has a destructor)");
-        }
-
-        // Index the class fields.
-        let field_map = std::map::HashMap();
-        for class_fields.eachi |i, class_field| {
-            field_map.insert(class_field.ident, i);
-        }
-
-        // Typecheck each field.
-        let found_fields = std::map::HashMap();
-        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 = pprust::path_to_str(path, tcx.sess.intr());
-                    tcx.sess.span_err(pat.span,
-                                      fmt!("struct `%s` does not have a field
-                                            named `%s`", name,
-                                           tcx.sess.str_of(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) {
-                    loop;
-                }
-                tcx.sess.span_err(pat.span,
-                                  fmt!("pattern does not mention field `%s`",
-                                       tcx.sess.str_of(field.ident)));
-            }
-        }
-
         // Finally, write in the type.
         fcx.write_ty(pat.id, expected);
       }