about summary refs log tree commit diff
diff options
context:
space:
mode:
authorErick Tryzelaar <erick.tryzelaar@gmail.com>2013-08-10 13:04:16 -0700
committerErick Tryzelaar <erick.tryzelaar@gmail.com>2013-08-10 13:04:16 -0700
commit20953bb1fbfafc3839e739f38ddf7d495eb1fe8b (patch)
treef7492e25ff06c4eeb3d1e480f641344b306c4247
parentf007a46d37d4f2aafd1008e4c0dcc6e694936d96 (diff)
parent437a4c28a338767bab9d003a80bcea38c658791b (diff)
downloadrust-20953bb1fbfafc3839e739f38ddf7d495eb1fe8b.tar.gz
rust-20953bb1fbfafc3839e739f38ddf7d495eb1fe8b.zip
Merge branch 'match' of https://github.com/msullivan/rust into rollup
-rw-r--r--src/librustc/middle/trans/_match.rs215
-rw-r--r--src/test/run-pass/issue-3121.rs1
-rw-r--r--src/test/run-pass/vec-matching-autoslice.rs10
-rw-r--r--src/test/run-pass/vec-matching.rs31
4 files changed, 196 insertions, 61 deletions
diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs
index 1a9c36313df..c98d859337c 100644
--- a/src/librustc/middle/trans/_match.rs
+++ b/src/librustc/middle/trans/_match.rs
@@ -145,6 +145,51 @@
  * - `store_non_ref_bindings()`
  * - `insert_lllocals()`
  *
+ *
+ * ## Notes on vector pattern matching.
+ *
+ * Vector pattern matching is surprisingly tricky. The problem is that
+ * the structure of the vector isn't fully known, and slice matches
+ * can be done on subparts of it.
+ *
+ * The way that vector pattern matches are dealt with, then, is as
+ * follows. First, we make the actual condition associated with a
+ * vector pattern simply a vector length comparison. So the pattern
+ * [1, .. x] gets the condition "vec len >= 1", and the pattern
+ * [.. x] gets the condition "vec len >= 0". The problem here is that
+ * having the condition "vec len >= 1" hold clearly does not mean that
+ * only a pattern that has exactly that condition will match. This
+ * means that it may well be the case that a condition holds, but none
+ * of the patterns matching that condition match; to deal with this,
+ * when doing vector length matches, we have match failures proceed to
+ * the next condition to check.
+ *
+ * There are a couple more subtleties to deal with. While the "actual"
+ * condition associated with vector length tests is simply a test on
+ * the vector length, the actual vec_len Opt entry contains more
+ * information used to restrict which matches are associated with it.
+ * So that all matches in a submatch are matching against the same
+ * values from inside the vector, they are split up by how many
+ * elements they match at the front and at the back of the vector. In
+ * order to make sure that arms are properly checked in order, even
+ * with the overmatching conditions, each vec_len Opt entry is
+ * associated with a range of matches.
+ * Consider the following:
+ *
+ *   match &[1, 2, 3] {
+ *       [1, 1, .. _] => 0,
+ *       [1, 2, 2, .. _] => 1,
+ *       [1, 2, 3, .. _] => 2,
+ *       [1, 2, .. _] => 3,
+ *       _ => 4
+ *   }
+ * The proper arm to match is arm 2, but arms 0 and 3 both have the
+ * condition "len >= 2". If arm 3 was lumped in with arm 0, then the
+ * wrong branch would be taken. Instead, vec_len Opts are associated
+ * with a contiguous range of matches that have the same "shape".
+ * This is sort of ugly and requires a bunch of special handling of
+ * vec_len options.
+ *
  */
 
 
@@ -189,14 +234,19 @@ enum Lit {
     ConstLit(ast::def_id),              // the def ID of the constant
 }
 
+#[deriving(Eq)]
+pub enum VecLenOpt {
+    vec_len_eq,
+    vec_len_ge(/* length of prefix */uint)
+}
+
 // An option identifying a branch (either a literal, a enum variant or a
 // range)
 enum Opt {
     lit(Lit),
     var(/* disr val */ uint, @adt::Repr),
     range(@ast::expr, @ast::expr),
-    vec_len_eq(uint),
-    vec_len_ge(uint, /* slice */uint)
+    vec_len(/* length */ uint, VecLenOpt, /*range of matches*/(uint, uint))
 }
 
 fn opt_eq(tcx: ty::ctxt, a: &Opt, b: &Opt) -> bool {
@@ -247,9 +297,9 @@ fn opt_eq(tcx: ty::ctxt, a: &Opt, b: &Opt) -> bool {
             }
         }
         (&var(a, _), &var(b, _)) => a == b,
-            (&vec_len_eq(a), &vec_len_eq(b)) => a == b,
-            (&vec_len_ge(a, _), &vec_len_ge(b, _)) => a == b,
-            _ => false
+        (&vec_len(a1, a2, _), &vec_len(b1, b2, _)) =>
+            a1 == b1 && a2 == b2,
+        _ => false
     }
 }
 
@@ -283,10 +333,10 @@ fn trans_opt(bcx: @mut Block, o: &Opt) -> opt_result {
             return range_result(rslt(bcx, consts::const_expr(ccx, l1)),
                                 rslt(bcx, consts::const_expr(ccx, l2)));
         }
-        vec_len_eq(n) => {
+        vec_len(n, vec_len_eq, _) => {
             return single_result(rslt(bcx, C_int(ccx, n as int)));
         }
-        vec_len_ge(n, _) => {
+        vec_len(n, vec_len_ge(_), _) => {
             return lower_bound(rslt(bcx, C_int(ccx, n as int)));
         }
     }
@@ -471,10 +521,11 @@ fn enter_match<'r>(bcx: @mut Block,
 }
 
 fn enter_default<'r>(bcx: @mut Block,
-                         dm: DefMap,
-                         m: &[Match<'r>],
-                         col: uint,
-                         val: ValueRef)
+                     dm: DefMap,
+                     m: &[Match<'r>],
+                     col: uint,
+                     val: ValueRef,
+                     chk: Option<mk_fail>)
                       -> ~[Match<'r>] {
     debug!("enter_default(bcx=%s, m=%s, col=%u, val=%s)",
            bcx.to_str(),
@@ -483,13 +534,36 @@ fn enter_default<'r>(bcx: @mut Block,
            bcx.val_to_str(val));
     let _indenter = indenter();
 
-    do enter_match(bcx, dm, m, col, val) |p| {
+    // Collect all of the matches that can match against anything.
+    let matches = do enter_match(bcx, dm, m, col, val) |p| {
         match p.node {
           ast::pat_wild | ast::pat_tup(_) => Some(~[]),
           ast::pat_ident(_, _, None) if pat_is_binding(dm, p) => Some(~[]),
           _ => None
         }
-    }
+    };
+
+    // Ok, now, this is pretty subtle. A "default" match is a match
+    // that needs to be considered if none of the actual checks on the
+    // value being considered succeed. The subtlety lies in that sometimes
+    // identifier/wildcard matches are *not* default matches. Consider:
+    // "match x { _ if something => foo, true => bar, false => baz }".
+    // There is a wildcard match, but it is *not* a default case. The boolean
+    // case on the value being considered is exhaustive. If the case is
+    // exhaustive, then there are no defaults.
+    //
+    // We detect whether the case is exhaustive in the following
+    // somewhat kludgy way: if the last wildcard/binding match has a
+    // guard, then by non-redundancy, we know that there aren't any
+    // non guarded matches, and thus by exhaustiveness, we know that
+    // we don't need any default cases. If the check *isn't* nonexhaustive
+    // (because chk is Some), then we need the defaults anyways.
+    let is_exhaustive = match matches.last_opt() {
+        Some(m) if m.data.arm.guard.is_some() && chk.is_none() => true,
+        _ => false
+    };
+
+    if is_exhaustive { ~[] } else { matches }
 }
 
 // <pcwalton> nmatsakis: what does enter_opt do?
@@ -523,17 +597,19 @@ fn enter_opt<'r>(bcx: @mut Block,
                      variant_size: uint,
                      val: ValueRef)
                   -> ~[Match<'r>] {
-    debug!("enter_opt(bcx=%s, m=%s, col=%u, val=%s)",
+    debug!("enter_opt(bcx=%s, m=%s, opt=%?, col=%u, val=%s)",
            bcx.to_str(),
            m.repr(bcx.tcx()),
+           *opt,
            col,
            bcx.val_to_str(val));
     let _indenter = indenter();
 
     let tcx = bcx.tcx();
     let dummy = @ast::pat {id: 0, node: ast::pat_wild, span: dummy_sp()};
+    let mut i = 0;
     do enter_match(bcx, tcx.def_map, m, col, val) |p| {
-        match p.node {
+        let answer = match p.node {
             ast::pat_enum(*) |
             ast::pat_ident(_, _, None) if pat_is_const(tcx.def_map, p) => {
                 let const_def = tcx.def_map.get_copy(&p.id);
@@ -599,32 +675,53 @@ fn enter_opt<'r>(bcx: @mut Block,
                 }
             }
             ast::pat_vec(ref before, slice, ref after) => {
+                let (lo, hi) = match *opt {
+                    vec_len(_, _, (lo, hi)) => (lo, hi),
+                    _ => tcx.sess.span_bug(p.span,
+                                           "vec pattern but not vec opt")
+                };
+
                 match slice {
-                    Some(slice) => {
+                    Some(slice) if i >= lo && i <= hi => {
                         let n = before.len() + after.len();
-                        let i = before.len();
-                        if opt_eq(tcx, &vec_len_ge(n, i), opt) {
+                        let this_opt = vec_len(n, vec_len_ge(before.len()),
+                                               (lo, hi));
+                        if opt_eq(tcx, &this_opt, opt) {
                             Some(vec::append_one((*before).clone(), slice) +
                                     *after)
                         } else {
                             None
                         }
                     }
-                    None => {
+                    None if i >= lo && i <= hi => {
                         let n = before.len();
-                        if opt_eq(tcx, &vec_len_eq(n), opt) {
+                        if opt_eq(tcx, &vec_len(n, vec_len_eq, (lo,hi)), opt) {
                             Some((*before).clone())
                         } else {
                             None
                         }
                     }
+                    _ => None
                 }
             }
             _ => {
                 assert_is_binding_or_wild(bcx, p);
-                Some(vec::from_elem(variant_size, dummy))
+                // In most cases, a binding/wildcard match be
+                // considered to match against any Opt. However, when
+                // doing vector pattern matching, submatches are
+                // considered even if the eventual match might be from
+                // a different submatch. Thus, when a submatch fails
+                // when doing a vector match, we proceed to the next
+                // submatch. Thus, including a default match would
+                // cause the default match to fire spuriously.
+                match *opt {
+                    vec_len(*) => None,
+                    _ => Some(vec::from_elem(variant_size, dummy))
+                }
             }
-        }
+        };
+        i += 1;
+        answer
     }
 }
 
@@ -805,9 +902,25 @@ fn get_options(bcx: @mut Block, m: &[Match], col: uint) -> ~[Opt] {
         if set.iter().any(|l| opt_eq(tcx, l, &val)) {return;}
         set.push(val);
     }
+    // Vector comparisions are special in that since the actual
+    // conditions over-match, we need to be careful about them. This
+    // means that in order to properly handle things in order, we need
+    // to not always merge conditions.
+    fn add_veclen_to_set(set: &mut ~[Opt], i: uint,
+                         len: uint, vlo: VecLenOpt) {
+        match set.last_opt() {
+            // If the last condition in the list matches the one we want
+            // to add, then extend its range. Otherwise, make a new
+            // vec_len with a range just covering the new entry.
+            Some(&vec_len(len2, vlo2, (start, end)))
+                 if len == len2 && vlo == vlo2 =>
+                 set[set.len() - 1] = vec_len(len, vlo, (start, end+1)),
+            _ => set.push(vec_len(len, vlo, (i, i)))
+        }
+    }
 
     let mut found = ~[];
-    for br in m.iter() {
+    for (i, br) in m.iter().enumerate() {
         let cur = br.pats[col];
         match cur.node {
             ast::pat_lit(l) => {
@@ -852,12 +965,12 @@ fn get_options(bcx: @mut Block, m: &[Match], col: uint) -> ~[Opt] {
                 add_to_set(ccx.tcx, &mut found, range(l1, l2));
             }
             ast::pat_vec(ref before, slice, ref after) => {
-                let opt = match slice {
-                    None => vec_len_eq(before.len()),
-                    Some(_) => vec_len_ge(before.len() + after.len(),
-                                          before.len())
+                let (len, vec_opt) = match slice {
+                    None => (before.len(), vec_len_eq),
+                    Some(_) => (before.len() + after.len(),
+                                vec_len_ge(before.len()))
                 };
-                add_to_set(ccx.tcx, &mut found, opt);
+                add_veclen_to_set(&mut found, i, len, vec_opt);
             }
             _ => {}
         }
@@ -1075,13 +1188,13 @@ fn pick_col(m: &[Match]) -> uint {
     }
     let mut scores = vec::from_elem(m[0].pats.len(), 0u);
     for br in m.iter() {
-        let mut i = 0u;
-        for p in br.pats.iter() { scores[i] += score(*p); i += 1u; }
+        for (i, p) in br.pats.iter().enumerate() {
+            scores[i] += score(*p);
+        }
     }
     let mut max_score = 0u;
     let mut best_col = 0u;
-    let mut i = 0u;
-    for score in scores.iter() {
+    for (i, score) in scores.iter().enumerate() {
         let score = *score;
 
         // Irrefutable columns always go first, they'd only be duplicated in
@@ -1090,7 +1203,6 @@ fn pick_col(m: &[Match]) -> uint {
         // If no irrefutable ones are found, we pick the one with the biggest
         // branching factor.
         if score > max_score { max_score = score; best_col = i; }
-        i += 1u;
     }
     return best_col;
 }
@@ -1460,7 +1572,7 @@ fn compile_submatch_continue(mut bcx: @mut Block,
                 test_val = Load(bcx, val);
                 kind = compare;
             },
-            vec_len_eq(*) | vec_len_ge(*) => {
+            vec_len(*) => {
                 let vt = tvec::vec_types(bcx, node_id_type(bcx, pat_id));
                 let unboxed = load_if_immediate(bcx, val, vt.vec_ty);
                 let (_, len) = tvec::get_base_and_len(
@@ -1487,16 +1599,19 @@ fn compile_submatch_continue(mut bcx: @mut Block,
         C_int(ccx, 0) // Placeholder for when not using a switch
     };
 
-    let defaults = enter_default(else_cx, dm, m, col, val);
+    let defaults = enter_default(else_cx, dm, m, col, val, chk);
     let exhaustive = chk.is_none() && defaults.len() == 0u;
     let len = opts.len();
-    let mut i = 0u;
 
     // Compile subtrees for each option
-    for opt in opts.iter() {
-        i += 1u;
+    for (i, opt) in opts.iter().enumerate() {
+        // In some cases in vector pattern matching, we need to override
+        // the failure case so that instead of failing, it proceeds to
+        // try more matching. branch_chk, then, is the proper failure case
+        // for the current conditional branch.
+        let mut branch_chk = chk;
         let mut opt_cx = else_cx;
-        if !exhaustive || i < len {
+        if !exhaustive || i+1 < len {
             opt_cx = sub_block(bcx, "match_case");
             match kind {
               single => Br(bcx, opt_cx.llbb),
@@ -1586,6 +1701,10 @@ fn compile_submatch_continue(mut bcx: @mut Block,
                       }
                   };
                   bcx = sub_block(after_cx, "compare_vec_len_next");
+
+                  // If none of these subcases match, move on to the
+                  // next condition.
+                  branch_chk = Some::<mk_fail>(|| bcx.llbb);
                   CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb);
               }
               _ => ()
@@ -1604,17 +1723,13 @@ fn compile_submatch_continue(mut bcx: @mut Block,
                 unpacked = argvals;
                 opt_cx = new_bcx;
             }
-            vec_len_eq(n) | vec_len_ge(n, _) => {
-                let n = match *opt {
-                    vec_len_ge(*) => n + 1u,
-                    _ => n
-                };
-                let slice = match *opt {
-                    vec_len_ge(_, i) => Some(i),
-                    _ => None
+            vec_len(n, vt, _) => {
+                let (n, slice) = match vt {
+                    vec_len_ge(i) => (n + 1u, Some(i)),
+                    vec_len_eq => (n, None)
                 };
-                let args = extract_vec_elems(opt_cx, pat_span, pat_id, n, slice,
-                                             val, test_val);
+                let args = extract_vec_elems(opt_cx, pat_span, pat_id, n,
+                                             slice, val, test_val);
                 size = args.vals.len();
                 unpacked = args.vals.clone();
                 opt_cx = args.bcx;
@@ -1623,7 +1738,7 @@ fn compile_submatch_continue(mut bcx: @mut Block,
         }
         let opt_ms = enter_opt(opt_cx, m, opt, col, size, val);
         let opt_vals = vec::append(unpacked, vals_left);
-        compile_submatch(opt_cx, opt_ms, opt_vals, chk);
+        compile_submatch(opt_cx, opt_ms, opt_vals, branch_chk);
     }
 
     // Compile the fall-through case, if any
diff --git a/src/test/run-pass/issue-3121.rs b/src/test/run-pass/issue-3121.rs
index 522a68856b6..206dc383cb3 100644
--- a/src/test/run-pass/issue-3121.rs
+++ b/src/test/run-pass/issue-3121.rs
@@ -8,7 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// xfail-test
 enum side { mayo, catsup, vinegar }
 enum order { hamburger, fries(side), shake }
 enum meal { to_go(order), for_here(order) }
diff --git a/src/test/run-pass/vec-matching-autoslice.rs b/src/test/run-pass/vec-matching-autoslice.rs
index d04deeac52e..13a8e324d43 100644
--- a/src/test/run-pass/vec-matching-autoslice.rs
+++ b/src/test/run-pass/vec-matching-autoslice.rs
@@ -1,22 +1,22 @@
 pub fn main() {
     let x = @[1, 2, 3];
     match x {
-        [2, .._] => ::std::util::unreachable(),
+        [2, .._] => fail!(),
         [1, ..tail] => {
             assert_eq!(tail, [2, 3]);
         }
-        [_] => ::std::util::unreachable(),
-        [] => ::std::util::unreachable()
+        [_] => fail!(),
+        [] => fail!()
     }
 
     let y = (~[(1, true), (2, false)], 0.5);
     match y {
-        ([_, _, _], 0.5) => ::std::util::unreachable(),
+        ([_, _, _], 0.5) => fail!(),
         ([(1, a), (b, false), ..tail], _) => {
             assert_eq!(a, true);
             assert_eq!(b, 2);
             assert!(tail.is_empty());
         }
-        ([..tail], _) => ::std::util::unreachable()
+        ([..tail], _) => fail!()
     }
 }
diff --git a/src/test/run-pass/vec-matching.rs b/src/test/run-pass/vec-matching.rs
index fb73c7e097d..c09fb8d6bc7 100644
--- a/src/test/run-pass/vec-matching.rs
+++ b/src/test/run-pass/vec-matching.rs
@@ -1,14 +1,14 @@
 fn a() {
     let x = ~[1];
     match x {
-        [_, _, _, _, _, .._] => ::std::util::unreachable(),
-        [.._, _, _, _, _] => ::std::util::unreachable(),
-        [_, .._, _, _] => ::std::util::unreachable(),
-        [_, _] => ::std::util::unreachable(),
+        [_, _, _, _, _, .._] => fail!(),
+        [.._, _, _, _, _] => fail!(),
+        [_, .._, _, _] => fail!(),
+        [_, _] => fail!(),
         [a] => {
             assert_eq!(a, 1);
         }
-        [] => ::std::util::unreachable()
+        [] => fail!()
     }
 }
 
@@ -48,7 +48,28 @@ fn b() {
     }
 }
 
+fn c() {
+    let x = [1];
+    match x {
+        [2, .. _] => fail!(),
+        [.. _] => ()
+    }
+}
+
+fn d() {
+    let x = [1, 2, 3];
+    let branch = match x {
+        [1, 1, .. _] => 0,
+        [1, 2, 3, .. _] => 1,
+        [1, 2, .. _] => 2,
+        _ => 3
+    };
+    assert_eq!(branch, 1);
+}
+
 pub fn main() {
     a();
     b();
+    c();
+    d();
 }