]> git.lizzy.rs Git - rust.git/commitdiff
auto merge of #8350 : dim-an/rust/fix-struct-match, r=pcwalton
authorbors <bors@rust-lang.org>
Fri, 9 Aug 2013 04:41:05 +0000 (21:41 -0700)
committerbors <bors@rust-lang.org>
Fri, 9 Aug 2013 04:41:05 +0000 (21:41 -0700)
Code that collects fields in struct-like patterns used to ignore
wildcard patterns like `Foo{_}`. But `enter_defaults` considered
struct-like patterns as default in order to overcome this
(accoring to my understanding of situation).

However such behaviour caused code like this:
```
enum E {
    Foo{f: int},
    Bar
}
let e = Bar;
match e {
    Foo{f: _f} => { /* do something (1) */ }
    _ => { /* do something (2) */ }
}
```
consider pattern `Foo{f: _f}` as default. That caused inproper behaviour
and even segfaults while trying to destruct `Bar` as `Foo{f: _f}`.
Issues: #5625 , #5530.

This patch fixes `collect_record_or_struct_fields` to split cases of
single wildcard struct-like pattern and no struct-like pattern at all.
Former case resolved with `enter_rec_or_struct` (and not with
`enter_defaults`).

Closes #5625.
Closes #5530.

src/librustc/driver/driver.rs
src/librustc/middle/trans/_match.rs
src/test/run-pass/issue-5530.rs
src/test/run-pass/match-enum-struct-0.rs [new file with mode: 0644]
src/test/run-pass/match-enum-struct-1.rs [new file with mode: 0644]
src/test/run-pass/match-struct-0.rs [new file with mode: 0644]

index 61ab826e9ee51f5728c25c15edbdaca8d89c355c..28c7df4b33a869e7f999d59db95d931ac637b18a 100644 (file)
@@ -821,7 +821,8 @@ pub fn optgroups() -> ~[getopts::groups::OptGroup] {
   optmulti("", "cfg", "Configure the compilation
                           environment", "SPEC"),
   optflag("",  "emit-llvm",
-                        "Produce an LLVM bitcode file"),
+                        "Produce an LLVM assembly file if used with -S option;
+                         produce an LLVM bitcode file otherwise"),
   optflag("h", "help","Display this message"),
   optmulti("L", "",   "Add a directory to the library search path",
                               "PATH"),
index 9a0dc5f036c76b701fe192d9724b68559b6539e6..327d2e698c1e181915f6bcc41a555479ca967694 100644 (file)
 
 // An option identifying a literal: either a unit-like struct or an
 // expression.
-pub enum Lit {
+enum Lit {
     UnitLikeStructLit(ast::NodeId),    // the node ID of the pattern
     ExprLit(@ast::expr),
     ConstLit(ast::def_id),              // the def ID of the constant
@@ -191,7 +191,7 @@ pub enum Lit {
 
 // An option identifying a branch (either a literal, a enum variant or a
 // range)
-pub enum Opt {
+enum Opt {
     lit(Lit),
     var(/* disr val */ uint, @adt::Repr),
     range(@ast::expr, @ast::expr),
@@ -199,7 +199,7 @@ pub enum Opt {
     vec_len_ge(uint, /* slice */uint)
 }
 
-pub fn opt_eq(tcx: ty::ctxt, a: &Opt, b: &Opt) -> bool {
+fn opt_eq(tcx: ty::ctxt, a: &Opt, b: &Opt) -> bool {
     match (a, b) {
         (&lit(a), &lit(b)) => {
             match (a, b) {
@@ -258,7 +258,7 @@ pub enum opt_result {
     lower_bound(Result),
     range_result(Result, Result),
 }
-pub fn trans_opt(bcx: @mut Block, o: &Opt) -> opt_result {
+fn trans_opt(bcx: @mut Block, o: &Opt) -> opt_result {
     let _icx = push_ctxt("match::trans_opt");
     let ccx = bcx.ccx();
     let bcx = bcx;
@@ -292,7 +292,7 @@ pub fn trans_opt(bcx: @mut Block, o: &Opt) -> opt_result {
     }
 }
 
-pub fn variant_opt(bcx: @mut Block, pat_id: ast::NodeId)
+fn variant_opt(bcx: @mut Block, pat_id: ast::NodeId)
     -> Opt {
     let ccx = bcx.ccx();
     match ccx.tcx.def_map.get_copy(&pat_id) {
@@ -317,7 +317,7 @@ pub fn variant_opt(bcx: @mut Block, pat_id: ast::NodeId)
 }
 
 #[deriving(Clone)]
-pub enum TransBindingMode {
+enum TransBindingMode {
     TrByValue(/*llbinding:*/ ValueRef),
     TrByRef,
 }
@@ -331,24 +331,24 @@ pub enum TransBindingMode {
  * - `id` is the node id of the binding
  * - `ty` is the Rust type of the binding */
  #[deriving(Clone)]
-pub struct BindingInfo {
+struct BindingInfo {
     llmatch: ValueRef,
     trmode: TransBindingMode,
     id: ast::NodeId,
     ty: ty::t,
 }
 
-pub type BindingsMap = HashMap<ident, BindingInfo>;
+type BindingsMap = HashMap<ident, BindingInfo>;
 
 #[deriving(Clone)]
-pub struct ArmData<'self> {
+struct ArmData<'self> {
     bodycx: @mut Block,
     arm: &'self ast::arm,
     bindings_map: @BindingsMap
 }
 
 #[deriving(Clone)]
-pub struct Match<'self> {
+struct Match<'self> {
     pats: ~[@ast::pat],
     data: ArmData<'self>
 }
@@ -364,7 +364,7 @@ fn repr(&self, tcx: ty::ctxt) -> ~str {
     }
 }
 
-pub fn has_nested_bindings(m: &[Match], col: uint) -> bool {
+fn has_nested_bindings(m: &[Match], col: uint) -> bool {
     for br in m.iter() {
         match br.pats[col].node {
           ast::pat_ident(_, _, Some(_)) => return true,
@@ -374,7 +374,7 @@ pub fn has_nested_bindings(m: &[Match], col: uint) -> bool {
     return false;
 }
 
-pub fn expand_nested_bindings<'r>(bcx: @mut Block,
+fn expand_nested_bindings<'r>(bcx: @mut Block,
                                   m: &[Match<'r>],
                                   col: uint,
                                   val: ValueRef)
@@ -409,7 +409,7 @@ pub fn expand_nested_bindings<'r>(bcx: @mut Block,
     }
 }
 
-pub fn assert_is_binding_or_wild(bcx: @mut Block, p: @ast::pat) {
+fn assert_is_binding_or_wild(bcx: @mut Block, p: @ast::pat) {
     if !pat_is_binding_or_wild(bcx.tcx().def_map, p) {
         bcx.sess().span_bug(
             p.span,
@@ -418,9 +418,9 @@ pub fn assert_is_binding_or_wild(bcx: @mut Block, p: @ast::pat) {
     }
 }
 
-pub type enter_pat<'self> = &'self fn(@ast::pat) -> Option<~[@ast::pat]>;
+type enter_pat<'self> = &'self fn(@ast::pat) -> Option<~[@ast::pat]>;
 
-pub fn enter_match<'r>(bcx: @mut Block,
+fn enter_match<'r>(bcx: @mut Block,
                        dm: DefMap,
                        m: &[Match<'r>],
                        col: uint,
@@ -470,7 +470,7 @@ pub fn enter_match<'r>(bcx: @mut Block,
     return result;
 }
 
-pub fn enter_default<'r>(bcx: @mut Block,
+fn enter_default<'r>(bcx: @mut Block,
                          dm: DefMap,
                          m: &[Match<'r>],
                          col: uint,
@@ -485,7 +485,7 @@ pub fn enter_default<'r>(bcx: @mut Block,
 
     do enter_match(bcx, dm, m, col, val) |p| {
         match p.node {
-          ast::pat_wild | ast::pat_tup(_) | ast::pat_struct(*) => Some(~[]),
+          ast::pat_wild | ast::pat_tup(_) => Some(~[]),
           ast::pat_ident(_, _, None) if pat_is_binding(dm, p) => Some(~[]),
           _ => None
         }
@@ -516,7 +516,7 @@ pub fn enter_default<'r>(bcx: @mut Block,
 // <nmatsakis> so all patterns must either be records (resp. tuples) or
 //             wildcards
 
-pub fn enter_opt<'r>(bcx: @mut Block,
+fn enter_opt<'r>(bcx: @mut Block,
                      m: &[Match<'r>],
                      opt: &Opt,
                      col: uint,
@@ -628,7 +628,7 @@ pub fn enter_opt<'r>(bcx: @mut Block,
     }
 }
 
-pub fn enter_rec_or_struct<'r>(bcx: @mut Block,
+fn enter_rec_or_struct<'r>(bcx: @mut Block,
                                dm: DefMap,
                                m: &[Match<'r>],
                                col: uint,
@@ -663,7 +663,7 @@ pub fn enter_rec_or_struct<'r>(bcx: @mut Block,
     }
 }
 
-pub fn enter_tup<'r>(bcx: @mut Block,
+fn enter_tup<'r>(bcx: @mut Block,
                      dm: DefMap,
                      m: &[Match<'r>],
                      col: uint,
@@ -689,7 +689,7 @@ pub fn enter_tup<'r>(bcx: @mut Block,
     }
 }
 
-pub fn enter_tuple_struct<'r>(bcx: @mut Block,
+fn enter_tuple_struct<'r>(bcx: @mut Block,
                               dm: DefMap,
                               m: &[Match<'r>],
                               col: uint,
@@ -715,7 +715,7 @@ pub fn enter_tuple_struct<'r>(bcx: @mut Block,
     }
 }
 
-pub fn enter_box<'r>(bcx: @mut Block,
+fn enter_box<'r>(bcx: @mut Block,
                      dm: DefMap,
                      m: &[Match<'r>],
                      col: uint,
@@ -742,7 +742,7 @@ pub fn enter_box<'r>(bcx: @mut Block,
     }
 }
 
-pub fn enter_uniq<'r>(bcx: @mut Block,
+fn enter_uniq<'r>(bcx: @mut Block,
                       dm: DefMap,
                       m: &[Match<'r>],
                       col: uint,
@@ -769,7 +769,7 @@ pub fn enter_uniq<'r>(bcx: @mut Block,
     }
 }
 
-pub fn enter_region<'r>(bcx: @mut Block,
+fn enter_region<'r>(bcx: @mut Block,
                         dm: DefMap,
                         m: &[Match<'r>],
                         col: uint,
@@ -799,7 +799,7 @@ pub fn enter_region<'r>(bcx: @mut Block,
 // 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.
-pub fn get_options(bcx: @mut Block, m: &[Match], col: uint) -> ~[Opt] {
+fn get_options(bcx: @mut Block, m: &[Match], col: uint) -> ~[Opt] {
     let ccx = bcx.ccx();
     fn add_to_set(tcx: ty::ctxt, set: &mut ~[Opt], val: Opt) {
         if set.iter().any(|l| opt_eq(tcx, l, &val)) {return;}
@@ -865,12 +865,12 @@ fn add_to_set(tcx: ty::ctxt, set: &mut ~[Opt], val: Opt) {
     return found;
 }
 
-pub struct ExtractedBlock {
+struct ExtractedBlock {
     vals: ~[ValueRef],
     bcx: @mut Block
 }
 
-pub fn extract_variant_args(bcx: @mut Block,
+fn extract_variant_args(bcx: @mut Block,
                             repr: &adt::Repr,
                             disr_val: uint,
                             val: ValueRef)
@@ -893,7 +893,7 @@ fn match_datum(bcx: @mut Block, val: ValueRef, pat_id: ast::NodeId) -> Datum {
 }
 
 
-pub fn extract_vec_elems(bcx: @mut Block,
+fn extract_vec_elems(bcx: @mut Block,
                          pat_span: span,
                          pat_id: ast::NodeId,
                          elem_count: uint,
@@ -947,24 +947,37 @@ pub fn extract_vec_elems(bcx: @mut Block,
     ExtractedBlock { vals: elems, bcx: bcx }
 }
 
-// NB: This function does not collect fields from struct-like enum variants.
-pub fn collect_record_or_struct_fields(bcx: @mut Block,
+/// Checks every pattern in `m` at `col` column.
+/// If there are a struct pattern among them function
+/// returns list of all fields that are matched in these patterns.
+/// Function returns None if there is no struct pattern.
+/// Function doesn't collect fields from struct-like enum variants.
+/// Function can return empty list if there is only wildcard struct pattern.
+fn collect_record_or_struct_fields(bcx: @mut Block,
                                        m: &[Match],
                                        col: uint)
-                                    -> ~[ast::ident] {
+                                    -> Option<~[ast::ident]> {
     let mut fields: ~[ast::ident] = ~[];
+    let mut found = false;
     for br in m.iter() {
         match br.pats[col].node {
           ast::pat_struct(_, ref fs, _) => {
             match ty::get(node_id_type(bcx, br.pats[col].id)).sty {
-              ty::ty_struct(*) => extend(&mut fields, *fs),
+              ty::ty_struct(*) => {
+                   extend(&mut fields, *fs);
+                   found = true;
+              }
               _ => ()
             }
           }
           _ => ()
         }
     }
-    return fields;
+    if found {
+        return Some(fields);
+    } else {
+        return None;
+    }
 
     fn extend(idents: &mut ~[ast::ident], field_pats: &[ast::field_pat]) {
         for field_pat in field_pats.iter() {
@@ -976,7 +989,7 @@ fn extend(idents: &mut ~[ast::ident], field_pats: &[ast::field_pat]) {
     }
 }
 
-pub fn pats_require_rooting(bcx: @mut Block,
+fn pats_require_rooting(bcx: @mut Block,
                             m: &[Match],
                             col: uint)
                          -> bool {
@@ -987,7 +1000,7 @@ pub fn pats_require_rooting(bcx: @mut Block,
     }
 }
 
-pub fn root_pats_as_necessary(mut bcx: @mut Block,
+fn root_pats_as_necessary(mut bcx: @mut Block,
                               m: &[Match],
                               col: uint,
                               val: ValueRef)
@@ -1018,23 +1031,23 @@ macro_rules! any_pat (
     )
 )
 
-pub fn any_box_pat(m: &[Match], col: uint) -> bool {
+fn any_box_pat(m: &[Match], col: uint) -> bool {
     any_pat!(m, ast::pat_box(_))
 }
 
-pub fn any_uniq_pat(m: &[Match], col: uint) -> bool {
+fn any_uniq_pat(m: &[Match], col: uint) -> bool {
     any_pat!(m, ast::pat_uniq(_))
 }
 
-pub fn any_region_pat(m: &[Match], col: uint) -> bool {
+fn any_region_pat(m: &[Match], col: uint) -> bool {
     any_pat!(m, ast::pat_region(_))
 }
 
-pub fn any_tup_pat(m: &[Match], col: uint) -> bool {
+fn any_tup_pat(m: &[Match], col: uint) -> bool {
     any_pat!(m, ast::pat_tup(_))
 }
 
-pub fn any_tuple_struct_pat(bcx: @mut Block, m: &[Match], col: uint) -> bool {
+fn any_tuple_struct_pat(bcx: @mut Block, m: &[Match], col: uint) -> bool {
     do m.iter().any |br| {
         let pat = br.pats[col];
         match pat.node {
@@ -1050,9 +1063,9 @@ pub fn any_tuple_struct_pat(bcx: @mut Block, m: &[Match], col: uint) -> bool {
     }
 }
 
-pub type mk_fail = @fn() -> BasicBlockRef;
+type mk_fail = @fn() -> BasicBlockRef;
 
-pub fn pick_col(m: &[Match]) -> uint {
+fn pick_col(m: &[Match]) -> uint {
     fn score(p: &ast::pat) -> uint {
         match p.node {
           ast::pat_lit(_) | ast::pat_enum(_, _) | ast::pat_range(_, _) => 1u,
@@ -1088,7 +1101,7 @@ pub enum branch_kind { no_branch, single, switch, compare, compare_vec_len, }
 // Compiles a comparison between two things.
 //
 // NB: This must produce an i1, not a Rust bool (i8).
-pub fn compare_values(cx: @mut Block,
+fn compare_values(cx: @mut Block,
                       lhs: ValueRef,
                       rhs: ValueRef,
                       rhs_t: ty::t)
@@ -1204,7 +1217,7 @@ fn insert_lllocals(bcx: @mut Block,
     return bcx;
 }
 
-pub fn compile_guard(bcx: @mut Block,
+fn compile_guard(bcx: @mut Block,
                      guard_expr: @ast::expr,
                      data: &ArmData,
                      m: &[Match],
@@ -1261,7 +1274,7 @@ fn drop_bindings(bcx: @mut Block, data: &ArmData) -> @mut Block {
     }
 }
 
-pub fn compile_submatch(bcx: @mut Block,
+fn compile_submatch(bcx: @mut Block,
                         m: &[Match],
                         vals: &[ValueRef],
                         chk: Option<mk_fail>) {
@@ -1336,22 +1349,24 @@ fn compile_submatch_continue(mut bcx: @mut Block,
     // required to root any values.
     assert!(any_box_pat(m, col) || !pats_require_rooting(bcx, 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);
-        let pat_repr = adt::represent_type(bcx.ccx(), pat_ty);
-        do expr::with_field_tys(tcx, pat_ty, None) |discr, field_tys| {
-            let rec_vals = rec_fields.map(|field_name| {
-                let ix = ty::field_idx_strict(tcx, *field_name, field_tys);
-                adt::trans_field_ptr(bcx, pat_repr, val, discr, ix)
-            });
-            compile_submatch(
-                bcx,
-                enter_rec_or_struct(bcx, dm, m, col, rec_fields, val),
-                vec::append(rec_vals, vals_left),
-                chk);
+    match collect_record_or_struct_fields(bcx, m, col) {
+        Some(ref rec_fields) => {
+            let pat_ty = node_id_type(bcx, pat_id);
+            let pat_repr = adt::represent_type(bcx.ccx(), pat_ty);
+            do expr::with_field_tys(tcx, pat_ty, None) |discr, field_tys| {
+                let rec_vals = rec_fields.map(|field_name| {
+                        let ix = ty::field_idx_strict(tcx, *field_name, field_tys);
+                        adt::trans_field_ptr(bcx, pat_repr, val, discr, ix)
+                        });
+                compile_submatch(
+                        bcx,
+                        enter_rec_or_struct(bcx, dm, m, col, *rec_fields, val),
+                        vec::append(rec_vals, vals_left),
+                        chk);
+            }
+            return;
         }
-        return;
+        None => {}
     }
 
     if any_tup_pat(m, col) {
@@ -1670,7 +1685,7 @@ fn create_bindings_map(bcx: @mut Block, pat: @ast::pat) -> BindingsMap {
     return bindings_map;
 }
 
-pub fn trans_match_inner(scope_cx: @mut Block,
+fn trans_match_inner(scope_cx: @mut Block,
                          discr_expr: @ast::expr,
                          arms: &[ast::arm],
                          dest: Dest) -> @mut Block {
@@ -1752,7 +1767,7 @@ fn mk_fail(bcx: @mut Block, sp: span, msg: @str,
     }
 }
 
-pub enum IrrefutablePatternBindingMode {
+enum IrrefutablePatternBindingMode {
     // Stores the association between node ID and LLVM value in `lllocals`.
     BindLocal,
     // Stores the association between node ID and LLVM value in `llargs`.
index 002435fcb36f0c28bdce527c52e92c934b4770e8..8e55ad90c70441bd6091fc36d2b84d50f0cbfe2d 100644 (file)
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// xfail-test
-
 enum Enum {
     Foo { foo: uint },
     Bar { bar: uint }
diff --git a/src/test/run-pass/match-enum-struct-0.rs b/src/test/run-pass/match-enum-struct-0.rs
new file mode 100644 (file)
index 0000000..365729e
--- /dev/null
@@ -0,0 +1,24 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// regression test for issue #5625
+
+enum E {
+    Foo{f : int},
+    Bar
+}
+
+pub fn main() {
+    let e = Bar;
+    match e {
+        Foo{f: _f} => fail!(),
+        _ => (),
+    }
+}
diff --git a/src/test/run-pass/match-enum-struct-1.rs b/src/test/run-pass/match-enum-struct-1.rs
new file mode 100644 (file)
index 0000000..15d24c4
--- /dev/null
@@ -0,0 +1,26 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+enum E {
+    Foo{f : int},
+    Bar
+}
+
+pub fn main() {
+    let e = Foo{f: 1};
+    match e {
+        Foo{_} => (),
+        _ => fail!(),
+    }
+    match e {
+        Foo{f: _f} => (),
+        _ => fail!(),
+    }
+}
diff --git a/src/test/run-pass/match-struct-0.rs b/src/test/run-pass/match-struct-0.rs
new file mode 100644 (file)
index 0000000..67e844c
--- /dev/null
@@ -0,0 +1,29 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct Foo{
+    f : int,
+}
+
+pub fn main() {
+    let f = Foo{f: 1};
+    match f {
+        Foo{f: 0} => fail!(),
+        Foo{_} => (),
+    }
+    match f {
+        Foo{f: 0} => fail!(),
+        Foo{f: _f} => (),
+    }
+    match f {
+        Foo{f: 0} => fail!(),
+        _ => (),
+    }
+}