]> git.lizzy.rs Git - rust.git/commitdiff
rustc: Implement translation of pattern matching for tuple structs and unit-like...
authorPatrick Walton <pcwalton@mimiga.net>
Tue, 30 Oct 2012 22:53:06 +0000 (15:53 -0700)
committerPatrick Walton <pcwalton@mimiga.net>
Fri, 2 Nov 2012 16:56:28 +0000 (09:56 -0700)
13 files changed:
src/rustc/middle/borrowck/gather_loans.rs
src/rustc/middle/pat_util.rs
src/rustc/middle/resolve.rs
src/rustc/middle/trans/alt.rs
src/rustc/middle/trans/base.rs
src/rustc/middle/trans/expr.rs
src/rustc/middle/ty.rs
src/rustc/middle/typeck.rs
src/rustc/middle/typeck/check.rs
src/rustc/middle/typeck/check/alt.rs
src/test/run-pass/tuple-struct-destructuring.rs [new file with mode: 0644]
src/test/run-pass/tuple-struct-matching.rs [new file with mode: 0644]
src/test/run-pass/unit-like-struct.rs [new file with mode: 0644]

index e0eb5519d4da34a4818008ffac231c1b39ea8ea4..23f0d9edbfae2b8d28a12966e856c9858959a69d 100644 (file)
@@ -441,7 +441,8 @@ fn gather_pat(&self,
                   alt_id: ast::node_id) {
         do self.bccx.cat_pattern(discr_cmt, root_pat) |cmt, pat| {
             match pat.node {
-              ast::pat_ident(bm, _, _) if !self.pat_is_variant(pat) => {
+              ast::pat_ident(bm, _, _)
+                    if !self.pat_is_variant_or_struct(pat) => {
                 match bm {
                   ast::bind_by_value | ast::bind_by_move => {
                     // copying does not borrow anything, so no check
@@ -492,8 +493,8 @@ fn gather_pat(&self,
         }
     }
 
-    fn pat_is_variant(&self, pat: @ast::pat) -> bool {
-        pat_util::pat_is_variant(self.bccx.tcx.def_map, pat)
+    fn pat_is_variant_or_struct(&self, pat: @ast::pat) -> bool {
+        pat_util::pat_is_variant_or_struct(self.bccx.tcx.def_map, pat)
     }
 }
 
index 48ebda9a67e032c86b506a1fab6af95015e3a9c4..9196a0b22caf8f6ce8068245f4757155dc93f811 100644 (file)
@@ -7,7 +7,7 @@
 use std::map::HashMap;
 
 export pat_binding_ids, pat_bindings, pat_id_map, PatIdMap;
-export pat_is_variant, pat_is_binding_or_wild;
+export pat_is_variant_or_struct, pat_is_binding_or_wild;
 
 type PatIdMap = std::map::HashMap<ident, node_id>;
 
@@ -21,20 +21,21 @@ fn pat_id_map(dm: resolve::DefMap, pat: @pat) -> PatIdMap {
     return map;
 }
 
-fn pat_is_variant(dm: resolve::DefMap, pat: @pat) -> bool {
+fn pat_is_variant_or_struct(dm: resolve::DefMap, pat: @pat) -> bool {
     match pat.node {
-      pat_enum(_, _) => true,
-      pat_ident(_, _, None) | pat_struct(*) => match dm.find(pat.id) {
-        Some(def_variant(_, _)) => true,
+        pat_enum(_, _) | pat_ident(_, _, None) | pat_struct(*) => {
+            match dm.find(pat.id) {
+                Some(def_variant(*)) | Some(def_class(*)) => true,
+                _ => false
+            }
+        }
         _ => false
-      },
-      _ => false
     }
 }
 
 fn pat_is_binding_or_wild(dm: resolve::DefMap, pat: @pat) -> bool {
     match pat.node {
-        pat_ident(*) => !pat_is_variant(dm, pat),
+        pat_ident(*) => !pat_is_variant_or_struct(dm, pat),
         pat_wild => true,
         _ => false
     }
@@ -44,7 +45,8 @@ fn pat_bindings(dm: resolve::DefMap, pat: @pat,
                 it: fn(binding_mode, node_id, span, @path)) {
     do walk_pat(pat) |p| {
         match p.node {
-          pat_ident(binding_mode, pth, _) if !pat_is_variant(dm, p) => {
+          pat_ident(binding_mode, pth, _)
+                if !pat_is_variant_or_struct(dm, p) => {
             it(binding_mode, p.id, p.span, pth);
           }
           _ => {}
index a979c0d7b577b40d431b4b61c622ba1f7c67ccf7..8ebebfa1121668ceeb72619d8ca4f1dce252128b 100644 (file)
@@ -288,10 +288,10 @@ impl AllowCapturingSelfFlag : cmp::Eq {
     pure fn ne(other: &AllowCapturingSelfFlag) -> bool { !self.eq(other) }
 }
 
-enum EnumVariantOrConstResolution {
-    FoundEnumVariant(def),
+enum BareIdentifierPatternResolution {
+    FoundStructOrEnumVariant(def),
     FoundConst,
-    EnumVariantOrConstNotFound
+    BareIdentifierPatternUnresolved
 }
 
 // Specifies how duplicates should be handled when adding a child item if
@@ -4187,28 +4187,31 @@ fn resolve_pattern(pattern: @pat,
                         if !path.global && path.idents.len() == 1u => {
 
                     // The meaning of pat_ident with no type parameters
-                    // depends on whether an enum variant with that name is in
-                    // scope. The probing lookup has to be careful not to emit
-                    // spurious errors. Only matching patterns (match) can
-                    // match nullary variants. For binding patterns (let),
-                    // matching such a variant is simply disallowed (since
-                    // it's rarely what you want).
+                    // depends on whether an enum variant or unit-like struct
+                    // with that name is in scope. The probing lookup has to
+                    // be careful not to emit spurious errors. Only matching
+                    // patterns (match) can match nullary variants or
+                    // unit-like structs. For binding patterns (let), matching
+                    // such a value is simply disallowed (since it's rarely
+                    // what you want).
 
                     let ident = path.idents[0];
 
-                    match self.resolve_enum_variant_or_const(ident) {
-                        FoundEnumVariant(def) if mode == RefutableMode => {
+                    match self.resolve_bare_identifier_pattern(ident) {
+                        FoundStructOrEnumVariant(def)
+                                if mode == RefutableMode => {
                             debug!("(resolving pattern) resolving `%s` to \
-                                    enum variant",
+                                    struct or enum variant",
                                     self.session.str_of(ident));
 
                             self.record_def(pattern.id, def);
                         }
-                        FoundEnumVariant(_) => {
+                        FoundStructOrEnumVariant(_) => {
                             self.session.span_err(pattern.span,
                                                   fmt!("declaration of `%s` \
                                                         shadows an enum \
-                                                        that's in scope",
+                                                        variant or unit-like \
+                                                        struct in scope",
                                                         self.session
                                                         .str_of(ident)));
                         }
@@ -4218,7 +4221,7 @@ enum variant",
                                                    conflicts with a constant \
                                                    in scope");
                         }
-                        EnumVariantOrConstNotFound => {
+                        BareIdentifierPatternUnresolved => {
                             debug!("(resolving pattern) binding `%s`",
                                    self.session.str_of(ident));
 
@@ -4349,13 +4352,11 @@ enum variant",
         }
     }
 
-    fn resolve_enum_variant_or_const(name: ident)
-                                  -> EnumVariantOrConstResolution {
-
+    fn resolve_bare_identifier_pattern(name: ident)
+                                    -> BareIdentifierPatternResolution {
         match self.resolve_item_in_lexical_scope(self.current_module,
-                                               name,
-                                               ValueNS) {
-
+                                                 name,
+                                                 ValueNS) {
             Success(target) => {
                 match target.bindings.value_def {
                     None => {
@@ -4364,14 +4365,14 @@ fn resolve_enum_variant_or_const(name: ident)
                     }
                     Some(def) => {
                         match def.def {
-                            def @ def_variant(*) => {
-                                return FoundEnumVariant(def);
+                            def @ def_variant(*) | def @ def_class(*) => {
+                                return FoundStructOrEnumVariant(def);
                             }
                             def_const(*) => {
                                 return FoundConst;
                             }
                             _ => {
-                                return EnumVariantOrConstNotFound;
+                                return BareIdentifierPatternUnresolved;
                             }
                         }
                     }
@@ -4383,7 +4384,7 @@ fn resolve_enum_variant_or_const(name: ident)
             }
 
             Failed => {
-                return EnumVariantOrConstNotFound;
+                return BareIdentifierPatternUnresolved;
             }
         }
     }
index b3776e499946afd1800731fa63fabe1abcaf64d8..0908eae34dcd77a9112ab1bc0b75188c17bc5460 100644 (file)
 
 fn macros() { include!("macros.rs"); } // FIXME(#3114): Macro import/export.
 
+// An option identifying a literal: either a unit-like struct or an
+// expression.
+enum Lit {
+    UnitLikeStructLit(ast::node_id),    // the node ID of the pattern
+    ExprLit(@ast::expr)
+}
+
 // An option identifying a branch (either a literal, a enum variant or a
 // range)
 enum Opt {
-    lit(@ast::expr),
+    lit(Lit),
     var(/* disr val */int, /* variant dids */{enm: def_id, var: def_id}),
     range(@ast::expr, @ast::expr)
 }
 fn opt_eq(tcx: ty::ctxt, a: &Opt, b: &Opt) -> bool {
     match (*a, *b) {
-      (lit(a), lit(b)) => const_eval::compare_lit_exprs(tcx, a, b) == 0,
+      (lit(ExprLit(a)), lit(ExprLit(b))) =>
+            const_eval::compare_lit_exprs(tcx, a, b) == 0,
+      (lit(UnitLikeStructLit(a)), lit(UnitLikeStructLit(b))) => a == b,
       (range(a1, a2), range(b1, b2)) => {
         const_eval::compare_lit_exprs(tcx, a1, b1) == 0 &&
         const_eval::compare_lit_exprs(tcx, a2, b2) == 0
@@ -182,10 +191,15 @@ fn trans_opt(bcx: block, o: &Opt) -> opt_result {
     let ccx = bcx.ccx();
     let mut bcx = bcx;
     match *o {
-        lit(lit_expr) => {
+        lit(ExprLit(lit_expr)) => {
             let datumblock = expr::trans_to_datum(bcx, lit_expr);
             return single_result(datumblock.to_result());
         }
+        lit(UnitLikeStructLit(pat_id)) => {
+            let struct_ty = ty::node_id_to_type(bcx.tcx(), pat_id);
+            let datumblock = datum::scratch_datum(bcx, struct_ty, true);
+            return single_result(datumblock.to_result(bcx));
+        }
         var(disr_val, _) => {
             return single_result(rslt(bcx, C_int(ccx, disr_val)));
         }
@@ -197,12 +211,23 @@ fn trans_opt(bcx: block, o: &Opt) -> opt_result {
 }
 
 fn variant_opt(tcx: ty::ctxt, pat_id: ast::node_id) -> Opt {
-    let vdef = ast_util::variant_def_ids(tcx.def_map.get(pat_id));
-    let variants = ty::enum_variants(tcx, vdef.enm);
-    for vec::each(*variants) |v| {
-        if vdef.var == v.id { return var(v.disr_val, vdef); }
+    match tcx.def_map.get(pat_id) {
+        ast::def_variant(enum_id, var_id) => {
+            let variants = ty::enum_variants(tcx, enum_id);
+            for vec::each(*variants) |v| {
+                if var_id == v.id {
+                    return var(v.disr_val, {enm: enum_id, var: var_id});
+                }
+            }
+            core::util::unreachable();
+        }
+        ast::def_class(_) => {
+            return lit(UnitLikeStructLit(pat_id));
+        }
+        _ => {
+            tcx.sess.bug(~"non-variant or struct in variant_opt()");
+        }
     }
-    core::util::unreachable();
 }
 
 enum TransBindingMode {
@@ -328,7 +353,7 @@ fn enter_match(bcx: block, dm: DefMap, m: &[@Match/&r],
                 let self = br.pats[col];
                 match self.node {
                     ast::pat_ident(_, path, None) => {
-                        if !pat_is_variant(dm, self) {
+                        if !pat_is_variant_or_struct(dm, self) {
                             let binding_info =
                                 br.data.bindings_map.get(path_to_ident(path));
                             Store(bcx, val, binding_info.llmatch);
@@ -363,7 +388,8 @@ fn enter_default(bcx: block, dm: DefMap, m: &[@Match/&r],
         match p.node {
           ast::pat_wild | ast::pat_rec(_, _) | ast::pat_tup(_) |
           ast::pat_struct(*) => Some(~[]),
-          ast::pat_ident(_, _, None) if !pat_is_variant(dm, p) => Some(~[]),
+          ast::pat_ident(_, _, None)
+                if !pat_is_variant_or_struct(dm, p) => Some(~[]),
           _ => None
         }
     }
@@ -417,7 +443,8 @@ fn enter_opt(bcx: block, m: &[@Match/&r], opt: &Opt, col: uint,
                     None
                 }
             }
-            ast::pat_ident(_, _, None) if pat_is_variant(tcx.def_map, p) => {
+            ast::pat_ident(_, _, None)
+                    if pat_is_variant_or_struct(tcx.def_map, p) => {
                 if opt_eq(tcx, &variant_opt(tcx, p.id), opt) {
                     Some(~[])
                 } else {
@@ -425,7 +452,7 @@ fn enter_opt(bcx: block, m: &[@Match/&r], opt: &Opt, col: uint,
                 }
             }
             ast::pat_lit(l) => {
-                if opt_eq(tcx, &lit(l), opt) {Some(~[])} else {None}
+                if opt_eq(tcx, &lit(ExprLit(l)), opt) {Some(~[])} else {None}
             }
             ast::pat_range(l1, l2) => {
                 if opt_eq(tcx, &range(l1, l2), opt) {Some(~[])} else {None}
@@ -522,6 +549,29 @@ fn enter_tup(bcx: block, dm: DefMap, m: &[@Match/&r],
     }
 }
 
+fn enter_tuple_struct(bcx: block, dm: DefMap, m: &[@Match/&r], col: uint,
+                      val: ValueRef, n_elts: uint)
+    -> ~[@Match/&r]
+{
+    debug!("enter_tuple_struct(bcx=%s, m=%s, col=%u, val=%?)",
+           bcx.to_str(),
+           matches_to_str(bcx, m),
+           col,
+           bcx.val_str(val));
+    let _indenter = indenter();
+
+    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_enum(_, Some(elts)) => Some(elts),
+            _ => {
+                assert_is_binding_or_wild(bcx, p);
+                Some(vec::from_elem(n_elts, dummy))
+            }
+        }
+    }
+}
+
 fn enter_box(bcx: block, dm: DefMap, m: &[@Match/&r],
              col: uint, val: ValueRef)
     -> ~[@Match/&r]
@@ -597,6 +647,9 @@ fn enter_region(bcx: block, dm: DefMap, m: &[@Match/&r],
     }
 }
 
+// Returns the options in one column of matches. An option is something that
+// needs to be conditionally matched at runtime; for example, the discriminant
+// on a set of enum variants or a literal.
 fn get_options(ccx: @crate_ctxt, m: &[@Match], col: uint) -> ~[Opt] {
     fn add_to_set(tcx: ty::ctxt, set: &DVec<Opt>, val: Opt) {
         if set.any(|l| opt_eq(tcx, l, &val)) {return;}
@@ -606,18 +659,40 @@ fn add_to_set(tcx: ty::ctxt, set: &DVec<Opt>, val: Opt) {
     let found = DVec();
     for vec::each(m) |br| {
         let cur = br.pats[col];
-        if pat_is_variant(ccx.tcx.def_map, cur) {
-            add_to_set(ccx.tcx, &found, variant_opt(ccx.tcx, cur.id));
-        } else {
-            match cur.node {
-                ast::pat_lit(l) => {
-                    add_to_set(ccx.tcx, &found, lit(l));
+        match cur.node {
+            ast::pat_lit(l) => {
+                add_to_set(ccx.tcx, &found, lit(ExprLit(l)));
+            }
+            ast::pat_ident(*) => {
+                // This is one of: an enum variant, a unit-like struct, or a
+                // variable binding.
+                match ccx.tcx.def_map.find(cur.id) {
+                    Some(ast::def_variant(*)) => {
+                        add_to_set(ccx.tcx, &found,
+                                   variant_opt(ccx.tcx, cur.id));
+                    }
+                    Some(ast::def_class(*)) => {
+                        add_to_set(ccx.tcx, &found,
+                                   lit(UnitLikeStructLit(cur.id)));
+                    }
+                    _ => {}
                 }
-                ast::pat_range(l1, l2) => {
-                    add_to_set(ccx.tcx, &found, range(l1, l2));
+            }
+            ast::pat_enum(*) | ast::pat_struct(*) => {
+                // This could be one of: a tuple-like enum variant, a
+                // struct-like enum variant, or a struct.
+                match ccx.tcx.def_map.find(cur.id) {
+                    Some(ast::def_variant(*)) => {
+                        add_to_set(ccx.tcx, &found,
+                                   variant_opt(ccx.tcx, cur.id));
+                    }
+                    _ => {}
                 }
-                _ => ()
             }
+            ast::pat_range(l1, l2) => {
+                add_to_set(ccx.tcx, &found, range(l1, l2));
+            }
+            _ => {}
         }
     }
     return dvec::unwrap(move found);
@@ -733,6 +808,21 @@ fn any_tup_pat(m: &[@Match], col: uint) -> bool {
     any_pat!(m, ast::pat_tup(_))
 }
 
+fn any_tuple_struct_pat(bcx: block, m: &[@Match], col: uint) -> bool {
+    vec::any(m, |br| {
+        let pat = br.pats[col];
+        match pat.node {
+            ast::pat_enum(_, Some(_)) => {
+                match bcx.tcx().def_map.find(pat.id) {
+                    Some(ast::def_class(*)) => true,
+                    _ => false
+                }
+            }
+            _ => false
+        }
+    })
+}
+
 type mk_fail = fn@() -> BasicBlockRef;
 
 fn pick_col(m: &[@Match]) -> uint {
@@ -1028,6 +1118,29 @@ fn compile_submatch(bcx: block,
         return;
     }
 
+    if any_tuple_struct_pat(bcx, m, col) {
+        let struct_ty = node_id_type(bcx, pat_id);
+        let struct_element_count;
+        match ty::get(struct_ty).sty {
+            ty::ty_class(struct_id, _) => {
+                struct_element_count =
+                    ty::lookup_class_fields(tcx, struct_id).len();
+            }
+            _ => {
+                ccx.sess.bug(~"non-struct type in tuple struct pattern");
+            }
+        }
+
+        let llstructvals = vec::from_fn(
+            struct_element_count, |i| GEPi(bcx, val, struct_field(i)));
+        compile_submatch(bcx,
+                         enter_tuple_struct(bcx, dm, m, col, val,
+                                            struct_element_count),
+                         vec::append(llstructvals, vals_left),
+                         chk);
+        return;
+    }
+
     // Unbox in case of a box field
     if any_box_pat(m, col) {
         let llbox = Load(bcx, val);
@@ -1316,7 +1429,7 @@ fn bind_irrefutable_pat(bcx: block, pat: @ast::pat, val: ValueRef,
     // Necessary since bind_irrefutable_pat is called outside trans_alt
     match pat.node {
         ast::pat_ident(_, _,inner) => {
-            if pat_is_variant(bcx.tcx().def_map, pat) {
+            if pat_is_variant_or_struct(bcx.tcx().def_map, pat) {
                 return bcx;
             }
 
@@ -1335,15 +1448,39 @@ fn bind_irrefutable_pat(bcx: block, pat: @ast::pat, val: ValueRef,
             for inner.each |inner_pat| {
                 bcx = bind_irrefutable_pat(bcx, *inner_pat, val, true);
             }
-      }
+        }
         ast::pat_enum(_, sub_pats) => {
-            let pat_def = ccx.tcx.def_map.get(pat.id);
-            let vdefs = ast_util::variant_def_ids(pat_def);
-            let args = extract_variant_args(bcx, pat.id, vdefs, val);
-            for sub_pats.each |sub_pat| {
-                for vec::eachi(args.vals) |i, argval| {
-                    bcx = bind_irrefutable_pat(bcx, sub_pat[i],
-                                               *argval, make_copy);
+            match bcx.tcx().def_map.find(pat.id) {
+                Some(ast::def_variant(*)) => {
+                    let pat_def = ccx.tcx.def_map.get(pat.id);
+                    let vdefs = ast_util::variant_def_ids(pat_def);
+                    let args = extract_variant_args(bcx, pat.id, vdefs, val);
+                    for sub_pats.each |sub_pat| {
+                        for vec::eachi(args.vals) |i, argval| {
+                            bcx = bind_irrefutable_pat(bcx,
+                                                       sub_pat[i],
+                                                       *argval,
+                                                       make_copy);
+                        }
+                    }
+                }
+                Some(ast::def_class(*)) => {
+                    match sub_pats {
+                        None => {
+                            // This is a unit-like struct. Nothing to do here.
+                        }
+                        Some(elems) => {
+                            // This is the tuple variant case.
+                            for vec::eachi(elems) |i, elem| {
+                                let fldptr = GEPi(bcx, val, struct_field(i));
+                                bcx = bind_irrefutable_pat(bcx, *elem, fldptr,
+                                                           make_copy);
+                            }
+                        }
+                    }
+                }
+                _ => {
+                    // Nothing to do here.
                 }
             }
         }
index 40792ce45ed4f39d6ee1c67188d18e4e99bc1731..727958e3e5f8cc55259d7e6c167acb277f3b0e41 100644 (file)
@@ -1893,12 +1893,14 @@ fn trans_struct_def(ccx: @crate_ctxt, struct_def: @ast::struct_def,
 
         // If this is a tuple-like struct, translate the constructor.
         match struct_def.ctor_id {
-            None => {}
-            Some(ctor_id) => {
+            // We only need to translate a constructor if there are fields;
+            // otherwise this is a unit-like struct.
+            Some(ctor_id) if struct_def.fields.len() > 0 => {
                 let llfndecl = get_item_val(ccx, ctor_id);
                 trans_tuple_struct(ccx, struct_def.fields, ctor_id, None,
                                    llfndecl);
             }
+            Some(_) | None => {}
         }
     }
 
index 839a8241963f98b5ef9b6ee2cfa67049ac9508d6..519aa9c44ecd8a451912722898bba51ef83f4387 100644 (file)
@@ -664,6 +664,11 @@ fn trans_def_dps_unadjusted(bcx: block, ref_expr: @ast::expr,
                 return bcx;
             }
         }
+        ast::def_class(*) => {
+            // Nothing to do here.
+            // XXX: May not be true in the case of classes with destructors.
+            return bcx;
+        }
         _ => {
             bcx.tcx().sess.span_bug(ref_expr.span, fmt!(
                 "Non-DPS def %? referened by %s",
index 65f77cefd02bb608b7f699b83dd9c5ca5cdbc8d0..8767c4c6253a6e450820c07ea0415190acd38265 100644 (file)
@@ -2980,7 +2980,7 @@ fn expr_kind(tcx: ctxt,
         ast::expr_path(*) => {
             match resolve_expr(tcx, expr) {
                 ast::def_fn(*) | ast::def_static_method(*) |
-                ast::def_variant(*) => RvalueDpsExpr,
+                ast::def_variant(*) | ast::def_class(*) => RvalueDpsExpr,
 
                 // Note: there is actually a good case to be made that
                 // def_args, particularly those of immediate type, ought to
index 2e55dd702f09bc14703538f602aa15fdbcfc8b0a..d4df07f7eb89734544e236643cf3ac3697aa8dbf 100644 (file)
@@ -48,7 +48,7 @@
 use metadata::csearch;
 use util::common::{block_query, loop_query};
 use syntax::codemap::span;
-use pat_util::{pat_is_variant, pat_id_map, PatIdMap};
+use pat_util::{pat_id_map, PatIdMap};
 use middle::ty;
 use middle::ty::{arg, field, node_type_table, mk_nil, ty_param_bounds_and_ty};
 use middle::ty::{ty_param_substs_and_ty, vstore_uniq};
index 044b95915cd3aa3f71b23ddd80d72a4a678336f6..b29e85643f11cec0709bd13d951f4938eb584535 100644 (file)
@@ -376,7 +376,8 @@ fn gather_locals(fcx: @fn_ctxt,
         let visit_pat = fn@(p: @ast::pat, &&e: (), v: visit::vt<()>) {
             match p.node {
               ast::pat_ident(_, path, _)
-                  if !pat_util::pat_is_variant(fcx.ccx.tcx.def_map, p) => {
+                  if !pat_util::pat_is_variant_or_struct(fcx.ccx.tcx.def_map,
+                                                         p) => {
                 assign(p.span, p.id, None);
                 debug!("Pattern binding %s is assigned to %s",
                        tcx.sess.str_of(path.idents[0]),
@@ -467,10 +468,6 @@ fn check_struct(ccx: @crate_ctxt, struct_def: @ast::struct_def,
     for struct_def.methods.each |m| {
         check_method(ccx, *m, self_ty, local_def(id));
     }
-    // Check that there's at least one field
-    if struct_def.fields.len() < 1u {
-        ccx.tcx.sess.span_err(span, ~"a struct must have at least one field");
-    }
     // Check that the class is instantiable
     check_instantiable(ccx.tcx, span, id);
 }
index 5a62ad0461dc6cf602dafa4fe893422a861f22fb..41be34a713cfec57e1333894f58f680819caf0dc 100644 (file)
@@ -1,6 +1,6 @@
 use syntax::print::pprust;
 use syntax::ast_util::{walk_pat};
-use pat_util::{pat_is_variant};
+use pat_util::{pat_is_variant_or_struct};
 
 fn check_alt(fcx: @fn_ctxt,
              expr: @ast::expr,
@@ -74,7 +74,7 @@ fn check_legality_of_move_bindings(fcx: @fn_ctxt,
     if !any_by_move { return; } // pointless micro-optimization
     for pats.each |pat| {
         do walk_pat(*pat) |p| {
-            if !pat_is_variant(def_map, p) {
+            if !pat_is_variant_or_struct(def_map, p) {
                 match p.node {
                     ast::pat_ident(ast::bind_by_move, _, sub) => {
                         // check legality of moving out of the enum
@@ -391,7 +391,8 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
         }
         fcx.write_ty(pat.id, b_ty);
       }
-      ast::pat_ident(bm, name, sub) if !pat_is_variant(tcx.def_map, pat) => {
+      ast::pat_ident(bm, name, sub)
+            if !pat_is_variant_or_struct(tcx.def_map, pat) => {
         let vid = lookup_local(fcx, pat.span, pat.id);
         let mut typ = ty::mk_var(tcx, vid);
 
diff --git a/src/test/run-pass/tuple-struct-destructuring.rs b/src/test/run-pass/tuple-struct-destructuring.rs
new file mode 100644 (file)
index 0000000..cffd9d8
--- /dev/null
@@ -0,0 +1,10 @@
+struct Foo(int, int);
+
+fn main() {
+    let x = Foo(1, 2);
+    let Foo(y, z) = x;
+    io::println(fmt!("%d %d", y, z));
+    assert y == 1;
+    assert z == 2;
+}
+
diff --git a/src/test/run-pass/tuple-struct-matching.rs b/src/test/run-pass/tuple-struct-matching.rs
new file mode 100644 (file)
index 0000000..923e8cb
--- /dev/null
@@ -0,0 +1,13 @@
+struct Foo(int, int);
+
+fn main() {
+    let x = Foo(1, 2);
+    match x {
+        Foo(a, b) => {
+            assert a == 1;
+            assert b == 2;
+            io::println(fmt!("%d %d", a, b));
+        }
+    }
+}
+
diff --git a/src/test/run-pass/unit-like-struct.rs b/src/test/run-pass/unit-like-struct.rs
new file mode 100644 (file)
index 0000000..e5eb50a
--- /dev/null
@@ -0,0 +1,9 @@
+struct Foo;
+
+fn main() {
+    let x: Foo = Foo;
+    match x {
+        Foo => { io::println("hi"); }
+    }
+}
+