about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/middle/check_match.rs22
-rw-r--r--src/librustc/middle/pat_util.rs7
-rw-r--r--src/librustc/middle/trans/_match.rs623
-rw-r--r--src/librustc/middle/trans/adt.rs18
-rw-r--r--src/librustc/middle/trans/base.rs6
-rw-r--r--src/libsyntax/attr.rs2
-rw-r--r--src/test/run-pass/issue-16648.rs20
7 files changed, 334 insertions, 364 deletions
diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs
index 5cb7651e99a..c2346824710 100644
--- a/src/librustc/middle/check_match.rs
+++ b/src/librustc/middle/check_match.rs
@@ -28,8 +28,7 @@ use syntax::codemap::{Span, Spanned, DUMMY_SP};
 use syntax::fold::{Folder, noop_fold_pat};
 use syntax::print::pprust::pat_to_string;
 use syntax::parse::token;
-use syntax::visit;
-use syntax::visit::{Visitor, FnKind};
+use syntax::visit::{mod, Visitor, FnKind};
 use util::ppaux::ty_to_string;
 
 struct Matrix(Vec<Vec<Gc<Pat>>>);
@@ -103,7 +102,9 @@ pub enum Constructor {
     /// Ranges of literal values (2..5).
     ConstantRange(const_val, const_val),
     /// Array patterns of length n.
-    Slice(uint)
+    Slice(uint),
+    /// Array patterns with a subslice.
+    SliceWithSubslice(uint, uint)
 }
 
 #[deriving(Clone, PartialEq)]
@@ -270,13 +271,6 @@ fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) {
     }
 }
 
-fn raw_pat(p: Gc<Pat>) -> Gc<Pat> {
-    match p.node {
-        PatIdent(_, _, Some(s)) => { raw_pat(s) }
-        _ => { p }
-    }
-}
-
 fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, matrix: &Matrix) {
     match is_useful(cx, matrix, [wild()], ConstructWitness) {
         UsefulWithWitness(pats) => {
@@ -821,6 +815,14 @@ pub fn specialize(cx: &MatchCheckCtxt, r: &[Gc<Pat>],
                     pats.push_all(after.as_slice());
                     Some(pats)
                 },
+                SliceWithSubslice(prefix, suffix)
+                    if before.len() == prefix
+                        && after.len() == suffix
+                        && slice.is_some() => {
+                    let mut pats = before.clone();
+                    pats.push_all(after.as_slice());
+                    Some(pats)
+                }
                 _ => None
             }
         }
diff --git a/src/librustc/middle/pat_util.rs b/src/librustc/middle/pat_util.rs
index 2d9e4ee45be..727f5ad9385 100644
--- a/src/librustc/middle/pat_util.rs
+++ b/src/librustc/middle/pat_util.rs
@@ -119,6 +119,13 @@ pub fn wild() -> Gc<Pat> {
     box (GC) Pat { id: 0, node: PatWild(PatWildSingle), span: DUMMY_SP }
 }
 
+pub fn raw_pat(p: Gc<Pat>) -> Gc<Pat> {
+    match p.node {
+        PatIdent(_, _, Some(s)) => { raw_pat(s) }
+        _ => { p }
+    }
+}
+
 pub fn def_to_path(tcx: &ty::ctxt, id: DefId) -> Path {
     ty::with_path(tcx, id, |mut path| Path {
         global: false,
diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs
index 9531e824298..040577e7048 100644
--- a/src/librustc/middle/trans/_match.rs
+++ b/src/librustc/middle/trans/_match.rs
@@ -186,12 +186,9 @@
  *
  */
 
-#![allow(non_camel_case_types)]
-
 use back::abi;
 use driver::config::FullDebugInfo;
 use llvm::{ValueRef, BasicBlockRef};
-use llvm;
 use middle::check_match::StaticInliner;
 use middle::check_match;
 use middle::const_eval;
@@ -203,17 +200,15 @@ use middle::pat_util::*;
 use middle::resolve::DefMap;
 use middle::trans::adt;
 use middle::trans::base::*;
-use middle::trans::build::{And, BitCast, Br, CondBr, GEPi, InBoundsGEP, Load};
-use middle::trans::build::{Mul, Not, Store, Sub, Switch, add_comment};
+use middle::trans::build::{AddCase, And, BitCast, Br, CondBr, GEPi, InBoundsGEP, Load};
+use middle::trans::build::{Mul, Not, Store, Sub, add_comment};
 use middle::trans::build;
 use middle::trans::callee;
-use middle::trans::cleanup;
-use middle::trans::cleanup::CleanupMethods;
+use middle::trans::cleanup::{mod, CleanupMethods};
 use middle::trans::common::*;
 use middle::trans::consts;
 use middle::trans::datum::*;
-use middle::trans::expr::Dest;
-use middle::trans::expr;
+use middle::trans::expr::{mod, Dest};
 use middle::trans::tvec;
 use middle::trans::type_of;
 use middle::trans::debuginfo;
@@ -223,83 +218,85 @@ use util::ppaux::{Repr, vec_map_to_string};
 
 use std;
 use std::collections::HashMap;
-use std::rc::Rc;
 use std::gc::{Gc};
+use std::rc::Rc;
 use syntax::ast;
 use syntax::ast::Ident;
 use syntax::codemap::Span;
 use syntax::fold::Folder;
 
-#[deriving(PartialEq)]
-pub enum VecLenOpt {
-    vec_len_eq,
-    vec_len_ge(/* length of prefix */uint)
+struct ConstantExpr<'a>(&'a ty::ctxt, Gc<ast::Expr>);
+
+impl<'a> Eq for ConstantExpr<'a> {
+    fn assert_receiver_is_total_eq(&self) {}
+}
+
+impl<'a> PartialEq for ConstantExpr<'a> {
+    fn eq(&self, other: &ConstantExpr<'a>) -> bool {
+        let &ConstantExpr(tcx, expr) = self;
+        let &ConstantExpr(_, other_expr) = other;
+        match const_eval::compare_lit_exprs(tcx, &*expr, &*other_expr) {
+            Some(val1) => val1 == 0,
+            None => fail!("compare_list_exprs: type mismatch"),
+        }
+    }
 }
 
-// An option identifying a branch (either a literal, an enum variant or a
-// range)
-enum Opt {
-    lit(Gc<ast::Expr>),
-    var(ty::Disr, Rc<adt::Repr>, ast::DefId),
-    range(Gc<ast::Expr>, Gc<ast::Expr>),
-    vec_len(/* length */ uint, VecLenOpt, /*range of matches*/(uint, uint))
+// An option identifying a branch (either a literal, an enum variant or a range)
+#[deriving(Eq, PartialEq)]
+enum Opt<'a> {
+    ConstantValue(ConstantExpr<'a>),
+    ConstantRange(ConstantExpr<'a>, ConstantExpr<'a>),
+    Variant(ty::Disr, Rc<adt::Repr>, ast::DefId),
+    SliceLengthEqual(uint),
+    SliceLengthGreaterOrEqual(/* prefix length */ uint, /* suffix length */ uint),
 }
 
-fn opt_eq(tcx: &ty::ctxt, a: &Opt, b: &Opt) -> bool {
-    match (a, b) {
-        (&lit(a_expr), &lit(b_expr)) => {
-            match const_eval::compare_lit_exprs(tcx, &*a_expr, &*b_expr) {
-                Some(val1) => val1 == 0,
-                None => fail!("compare_list_exprs: type mismatch"),
+impl<'a> Opt<'a> {
+    fn trans(&self, mut bcx: &'a Block<'a>) -> OptResult<'a> {
+        let _icx = push_ctxt("match::trans_opt");
+        let ccx = bcx.ccx();
+        match *self {
+            ConstantValue(ConstantExpr(_, lit_expr)) => {
+                let lit_ty = ty::node_id_to_type(bcx.tcx(), lit_expr.id);
+                let (llval, _, _) = consts::const_expr(ccx, &*lit_expr, true);
+                let lit_datum = immediate_rvalue(llval, lit_ty);
+                let lit_datum = unpack_datum!(bcx, lit_datum.to_appropriate_datum(bcx));
+                SingleResult(Result::new(bcx, lit_datum.val))
             }
-        }
-        (&range(ref a1, ref a2), &range(ref b1, ref b2)) => {
-            let m1 = const_eval::compare_lit_exprs(tcx, &**a1, &**b1);
-            let m2 = const_eval::compare_lit_exprs(tcx, &**a2, &**b2);
-            match (m1, m2) {
-                (Some(val1), Some(val2)) => (val1 == 0 && val2 == 0),
-                _ => fail!("compare_list_exprs: type mismatch"),
+            ConstantRange(
+                ConstantExpr(_, ref l1),
+                ConstantExpr(_, ref l2)) => {
+                let (l1, _, _) = consts::const_expr(ccx, &**l1, true);
+                let (l2, _, _) = consts::const_expr(ccx, &**l2, true);
+                RangeResult(Result::new(bcx, l1), Result::new(bcx, l2))
+            }
+            Variant(disr_val, ref repr, _) => {
+                adt::trans_case(bcx, &**repr, disr_val)
+            }
+            SliceLengthEqual(length) => {
+                SingleResult(Result::new(bcx, C_uint(ccx, length)))
+            }
+            SliceLengthGreaterOrEqual(prefix, suffix) => {
+                LowerBound(Result::new(bcx, C_uint(ccx, prefix + suffix)))
             }
         }
-        (&var(a, _, _), &var(b, _, _)) => a == b,
-        (&vec_len(a1, a2, _), &vec_len(b1, b2, _)) =>
-            a1 == b1 && a2 == b2,
-        _ => false
     }
 }
 
-pub enum opt_result<'a> {
-    single_result(Result<'a>),
-    lower_bound(Result<'a>),
-    range_result(Result<'a>, Result<'a>),
+#[deriving(PartialEq)]
+pub enum BranchKind {
+    NoBranch,
+    Single,
+    Switch,
+    Compare,
+    CompareSliceLength
 }
 
-fn trans_opt<'a>(mut bcx: &'a Block<'a>, o: &Opt) -> opt_result<'a> {
-    let _icx = push_ctxt("match::trans_opt");
-    let ccx = bcx.ccx();
-    match *o {
-        lit(lit_expr) => {
-            let lit_ty = ty::node_id_to_type(bcx.tcx(), lit_expr.id);
-            let (llval, _, _) = consts::const_expr(ccx, &*lit_expr, true);
-            let lit_datum = immediate_rvalue(llval, lit_ty);
-            let lit_datum = unpack_datum!(bcx, lit_datum.to_appropriate_datum(bcx));
-            return single_result(Result::new(bcx, lit_datum.val));
-        }
-        var(disr_val, ref repr, _) => {
-            return adt::trans_case(bcx, &**repr, disr_val);
-        }
-        range(ref l1, ref l2) => {
-            let (l1, _, _) = consts::const_expr(ccx, &**l1, true);
-            let (l2, _, _) = consts::const_expr(ccx, &**l2, true);
-            return range_result(Result::new(bcx, l1), Result::new(bcx, l2));
-        }
-        vec_len(n, vec_len_eq, _) => {
-            return single_result(Result::new(bcx, C_int(ccx, n as int)));
-        }
-        vec_len(n, vec_len_ge(_), _) => {
-            return lower_bound(Result::new(bcx, C_int(ccx, n as int)));
-        }
-    }
+pub enum OptResult<'a> {
+    SingleResult(Result<'a>),
+    RangeResult(Result<'a>, Result<'a>),
+    LowerBound(Result<'a>)
 }
 
 #[deriving(Clone)]
@@ -403,7 +400,7 @@ fn expand_nested_bindings<'a, 'b>(
     }).collect()
 }
 
-type enter_pats<'a> = |&[Gc<ast::Pat>]|: 'a -> Option<Vec<Gc<ast::Pat>>>;
+type EnterPatterns<'a> = |&[Gc<ast::Pat>]|: 'a -> Option<Vec<Gc<ast::Pat>>>;
 
 fn enter_match<'a, 'b>(
                bcx: &'b Block<'b>,
@@ -411,7 +408,7 @@ fn enter_match<'a, 'b>(
                m: &'a [Match<'a, 'b>],
                col: uint,
                val: ValueRef,
-               e: enter_pats)
+               e: EnterPatterns)
                -> Vec<Match<'a, 'b>> {
     debug!("enter_match(bcx={}, m={}, col={}, val={})",
            bcx.to_str(),
@@ -425,9 +422,20 @@ fn enter_match<'a, 'b>(
             let this = *br.pats.get(col);
             let mut bound_ptrs = br.bound_ptrs.clone();
             match this.node {
-                ast::PatIdent(_, ref path1, None) => {
+                ast::PatIdent(_, ref path, None) => {
                     if pat_is_binding(dm, &*this) {
-                        bound_ptrs.push((path1.node, val));
+                        bound_ptrs.push((path.node, val));
+                    }
+                }
+                ast::PatVec(ref before, Some(slice), ref after) => {
+                    match slice.node {
+                        ast::PatIdent(_, ref path, None) => {
+                            let subslice_val = bind_subslice_pat(
+                                bcx, this.id, val,
+                                before.len(), after.len());
+                            bound_ptrs.push((path.node, subslice_val));
+                        }
+                        _ => {}
                     }
                 }
                 _ => {}
@@ -513,100 +521,36 @@ fn enter_opt<'a, 'b>(
     let _indenter = indenter();
 
     let ctor = match opt {
-        &lit(expr) => check_match::ConstantValue(
+        &ConstantValue(ConstantExpr(_, expr)) => check_match::ConstantValue(
             const_eval::eval_const_expr(bcx.tcx(), &*expr)
         ),
-        &range(lo, hi) => check_match::ConstantRange(
+        &ConstantRange(ConstantExpr(_, lo), ConstantExpr(_, hi)) => check_match::ConstantRange(
             const_eval::eval_const_expr(bcx.tcx(), &*lo),
             const_eval::eval_const_expr(bcx.tcx(), &*hi)
         ),
-        &vec_len(len, _, _) => check_match::Slice(len),
-        &var(_, _, def_id) => check_match::Variant(def_id)
+        &SliceLengthEqual(n) =>
+            check_match::Slice(n),
+        &SliceLengthGreaterOrEqual(before, after) =>
+            check_match::SliceWithSubslice(before, after),
+        &Variant(_, _, def_id) =>
+            check_match::Variant(def_id)
     };
 
-    let mut i = 0;
-    let tcx = bcx.tcx();
     let mcx = check_match::MatchCheckCtxt { tcx: bcx.tcx() };
-    enter_match(bcx, dm, m, col, val, |pats| {
-        let span = pats[col].span;
-        let specialized = match pats[col].node {
-            ast::PatVec(ref before, slice, ref after) => {
-                let (lo, hi) = match *opt {
-                    vec_len(_, _, (lo, hi)) => (lo, hi),
-                    _ => tcx.sess.span_bug(span,
-                                           "vec pattern but not vec opt")
-                };
-
-                let elems = match slice {
-                    Some(slice) if i >= lo && i <= hi => {
-                        let n = before.len() + after.len();
-                        let this_opt = vec_len(n, vec_len_ge(before.len()),
-                                               (lo, hi));
-                        if opt_eq(tcx, &this_opt, opt) {
-                            let mut new_before = Vec::new();
-                            for pat in before.iter() {
-                                new_before.push(*pat);
-                            }
-                            new_before.push(slice);
-                            for pat in after.iter() {
-                                new_before.push(*pat);
-                            }
-                            Some(new_before)
-                        } else {
-                            None
-                        }
-                    }
-                    None if i >= lo && i <= hi => {
-                        let n = before.len();
-                        if opt_eq(tcx, &vec_len(n, vec_len_eq, (lo,hi)), opt) {
-                            let mut new_before = Vec::new();
-                            for pat in before.iter() {
-                                new_before.push(*pat);
-                            }
-                            Some(new_before)
-                        } else {
-                            None
-                        }
-                    }
-                    _ => None
-                };
-                elems.map(|head| head.append(pats.slice_to(col)).append(pats.slice_from(col + 1)))
-            }
-            _ => {
-                check_match::specialize(&mcx, pats.as_slice(), &ctor, col, variant_size)
-            }
-        };
-        i += 1;
-        specialized
-    })
+    enter_match(bcx, dm, m, col, val, |pats|
+        check_match::specialize(&mcx, pats.as_slice(), &ctor, col, variant_size)
+    )
 }
 
 // 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(bcx: &Block, m: &[Match], col: uint) -> Vec<Opt> {
+fn get_branches<'a>(bcx: &'a Block, m: &[Match], col: uint) -> Vec<Opt<'a>> {
     let ccx = bcx.ccx();
-    fn add_to_set(tcx: &ty::ctxt, set: &mut Vec<Opt>, val: Opt) {
-        if set.iter().any(|l| opt_eq(tcx, l, &val)) {return;}
-        set.push(val);
-    }
-    // Vector comparisons 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 Vec<Opt> , i: uint,
-                         len: uint, vlo: VecLenOpt) {
-        match set.last() {
-            // 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 => {
-                let length = set.len();
-                 *set.get_mut(length - 1) =
-                     vec_len(len, vlo, (start, end+1))
-            }
-            _ => set.push(vec_len(len, vlo, (i, i)))
+
+    fn add_to_set<'a>(set: &mut Vec<Opt<'a>>, opt: Opt<'a>) {
+        if !set.contains(&opt) {
+            set.push(opt);
         }
     }
 
@@ -615,7 +559,7 @@ fn get_options(bcx: &Block, m: &[Match], col: uint) -> Vec<Opt> {
         let cur = *br.pats.get(col);
         match cur.node {
             ast::PatLit(l) => {
-                add_to_set(ccx.tcx(), &mut found, lit(l));
+                add_to_set(&mut found, ConstantValue(ConstantExpr(ccx.tcx(), l)));
             }
             ast::PatIdent(..) | ast::PatEnum(..) | ast::PatStruct(..) => {
                 // This is either an enum variant or a variable binding.
@@ -623,28 +567,30 @@ fn get_options(bcx: &Block, m: &[Match], col: uint) -> Vec<Opt> {
                 match opt_def {
                     Some(def::DefVariant(enum_id, var_id, _)) => {
                         let variant = ty::enum_variant_with_id(ccx.tcx(), enum_id, var_id);
-                        add_to_set(ccx.tcx(), &mut found,
-                                   var(variant.disr_val,
-                                       adt::represent_node(bcx, cur.id), var_id));
+                        add_to_set(&mut found, Variant(
+                            variant.disr_val,
+                            adt::represent_node(bcx, cur.id), var_id
+                        ));
                     }
                     _ => {}
                 }
             }
             ast::PatRange(l1, l2) => {
-                add_to_set(ccx.tcx(), &mut found, range(l1, l2));
+                add_to_set(&mut found, ConstantRange(
+                    ConstantExpr(ccx.tcx(), l1),
+                    ConstantExpr(ccx.tcx(), l2)
+                ));
             }
-            ast::PatVec(ref before, slice, ref after) => {
-                let (len, vec_opt) = match slice {
-                    None => (before.len(), vec_len_eq),
-                    Some(_) => (before.len() + after.len(),
-                                vec_len_ge(before.len()))
-                };
-                add_veclen_to_set(&mut found, i, len, vec_opt);
+            ast::PatVec(ref before, None, ref after) => {
+                add_to_set(&mut found, SliceLengthEqual(before.len() + after.len()));
+            }
+            ast::PatVec(ref before, Some(_), ref after) => {
+                add_to_set(&mut found, SliceLengthGreaterOrEqual(before.len(), after.len()));
             }
             _ => {}
         }
     }
-    return found;
+    found
 }
 
 struct ExtractedBlock<'a> {
@@ -666,62 +612,58 @@ fn extract_variant_args<'a>(
     ExtractedBlock { vals: args, bcx: bcx }
 }
 
-fn match_datum(bcx: &Block,
-               val: ValueRef,
-               pat_id: ast::NodeId)
-               -> Datum<Lvalue> {
+fn match_datum(val: ValueRef, left_ty: ty::t) -> Datum<Lvalue> {
     /*!
      * Helper for converting from the ValueRef that we pass around in
      * the match code, which is always an lvalue, into a Datum. Eventually
      * we should just pass around a Datum and be done with it.
      */
-
-    let ty = node_id_type(bcx, pat_id);
-    Datum::new(val, ty, Lvalue)
+    Datum::new(val, left_ty, Lvalue)
 }
 
+fn bind_subslice_pat<'a>(
+                    bcx: &'a Block<'a>,
+                    pat_id: ast::NodeId,
+                    val: ValueRef,
+                    offset_left: uint,
+                    offset_right: uint) -> ValueRef {
+    let _icx = push_ctxt("match::bind_subslice_pat");
+    let vec_ty = node_id_type(bcx, pat_id);
+    let vt = tvec::vec_types(bcx, ty::sequence_element_type(bcx.tcx(), ty::type_content(vec_ty)));
+    let vec_datum = match_datum(val, vec_ty);
+    let (base, len) = vec_datum.get_vec_base_and_len(bcx);
+
+    let slice_byte_offset = Mul(bcx, vt.llunit_size, C_uint(bcx.ccx(), offset_left));
+    let slice_begin = tvec::pointer_add_byte(bcx, base, slice_byte_offset);
+    let slice_len_offset = C_uint(bcx.ccx(), offset_left + offset_right);
+    let slice_len = Sub(bcx, len, slice_len_offset);
+    let slice_ty = ty::mk_slice(bcx.tcx(),
+                                ty::ReStatic,
+                                ty::mt {ty: vt.unit_ty, mutbl: ast::MutImmutable});
+    let scratch = rvalue_scratch_datum(bcx, slice_ty, "");
+    Store(bcx, slice_begin,
+          GEPi(bcx, scratch.val, [0u, abi::slice_elt_base]));
+    Store(bcx, slice_len, GEPi(bcx, scratch.val, [0u, abi::slice_elt_len]));
+    scratch.val
+}
 
 fn extract_vec_elems<'a>(
                      bcx: &'a Block<'a>,
-                     pat_id: ast::NodeId,
-                     elem_count: uint,
-                     slice: Option<uint>,
+                     left_ty: ty::t,
+                     before: uint,
+                     after: uint,
                      val: ValueRef)
                      -> ExtractedBlock<'a> {
     let _icx = push_ctxt("match::extract_vec_elems");
-    let vec_datum = match_datum(bcx, val, pat_id);
+    let vec_datum = match_datum(val, left_ty);
     let (base, len) = vec_datum.get_vec_base_and_len(bcx);
-    let vec_ty = node_id_type(bcx, pat_id);
-    let vt = tvec::vec_types(bcx, ty::sequence_element_type(bcx.tcx(), ty::type_content(vec_ty)));
-
-    let mut elems = Vec::from_fn(elem_count, |i| {
-        match slice {
-            None => GEPi(bcx, base, [i]),
-            Some(n) if i < n => GEPi(bcx, base, [i]),
-            Some(n) if i > n => {
-                InBoundsGEP(bcx, base, [
-                    Sub(bcx, len,
-                        C_int(bcx.ccx(), (elem_count - i) as int))])
-            }
-            _ => unsafe { llvm::LLVMGetUndef(vt.llunit_ty.to_ref()) }
-        }
-    });
-    if slice.is_some() {
-        let n = slice.unwrap();
-        let slice_byte_offset = Mul(bcx, vt.llunit_size, C_uint(bcx.ccx(), n));
-        let slice_begin = tvec::pointer_add_byte(bcx, base, slice_byte_offset);
-        let slice_len_offset = C_uint(bcx.ccx(), elem_count - 1u);
-        let slice_len = Sub(bcx, len, slice_len_offset);
-        let slice_ty = ty::mk_slice(bcx.tcx(),
-                                    ty::ReStatic,
-                                    ty::mt {ty: vt.unit_ty, mutbl: ast::MutImmutable});
-        let scratch = rvalue_scratch_datum(bcx, slice_ty, "");
-        Store(bcx, slice_begin,
-              GEPi(bcx, scratch.val, [0u, abi::slice_elt_base]));
-        Store(bcx, slice_len, GEPi(bcx, scratch.val, [0u, abi::slice_elt_len]));
-        *elems.get_mut(n) = scratch.val;
-    }
-
+    let mut elems = vec![];
+    elems.extend(range(0, before).map(|i| GEPi(bcx, base, [i])));
+    elems.extend(range(0, after).rev().map(|i| {
+        InBoundsGEP(bcx, base, [
+            Sub(bcx, len, C_uint(bcx.ccx(), i + 1))
+        ])
+    }));
     ExtractedBlock { vals: elems, bcx: bcx }
 }
 
@@ -748,19 +690,19 @@ fn any_region_pat(m: &[Match], col: uint) -> bool {
     any_pat!(m, col, ast::PatRegion(_))
 }
 
-fn any_irrefutable_adt_pat(bcx: &Block, m: &[Match], col: uint) -> bool {
+fn any_irrefutable_adt_pat(tcx: &ty::ctxt, m: &[Match], col: uint) -> bool {
     m.iter().any(|br| {
         let pat = *br.pats.get(col);
         match pat.node {
             ast::PatTup(_) => true,
             ast::PatStruct(..) => {
-                match bcx.tcx().def_map.borrow().find(&pat.id) {
+                match tcx.def_map.borrow().find(&pat.id) {
                     Some(&def::DefVariant(..)) => false,
                     _ => true,
                 }
             }
             ast::PatEnum(..) | ast::PatIdent(_, _, None) => {
-                match bcx.tcx().def_map.borrow().find(&pat.id) {
+                match tcx.def_map.borrow().find(&pat.id) {
                     Some(&def::DefFn(..)) |
                     Some(&def::DefStruct(..)) => true,
                     _ => false
@@ -779,21 +721,21 @@ enum FailureHandler<'a> {
 }
 
 impl<'a> FailureHandler<'a> {
-    fn is_infallible(&self) -> bool {
+    fn is_fallible(&self) -> bool {
         match *self {
-            Infallible => true,
-            _ => false
+            Infallible => false,
+            _ => true
         }
     }
 
-    fn is_fallible(&self) -> bool {
-        !self.is_infallible()
+    fn is_infallible(&self) -> bool {
+        !self.is_fallible()
     }
 
     fn handle_fail(&self, bcx: &Block) {
         match *self {
             Infallible =>
-                fail!("attempted to fail in infallible failure handler!"),
+                fail!("attempted to fail in an infallible failure handler!"),
             JumpToBasicBlock(basic_block) =>
                 Br(bcx, basic_block),
             Unreachable =>
@@ -831,9 +773,6 @@ fn pick_col(m: &[Match]) -> uint {
     return best_col;
 }
 
-#[deriving(PartialEq)]
-pub enum branch_kind { no_branch, single, switch, compare, compare_vec_len }
-
 // Compiles a comparison between two things.
 fn compare_values<'a>(
                   cx: &'a Block<'a>,
@@ -963,7 +902,7 @@ fn compile_guard<'a, 'b>(
         }
     }
 
-    return with_cond(bcx, Not(bcx, val), |bcx| {
+    with_cond(bcx, Not(bcx, val), |bcx| {
         // Guard does not match: remove all bindings from the lllocals table
         for (_, &binding_info) in data.bindings_map.iter() {
             call_lifetime_end(bcx, binding_info.llmatch);
@@ -981,7 +920,7 @@ fn compile_guard<'a, 'b>(
             }
         };
         bcx
-    });
+    })
 }
 
 fn compile_submatch<'a, 'b>(
@@ -1003,7 +942,9 @@ fn compile_submatch<'a, 'b>(
         }
         return;
     }
-    if m[0].pats.len() == 0u {
+
+    let col_count = m[0].pats.len();
+    if col_count == 0u {
         let data = &m[0].data;
         for &(ref ident, ref value_ptr) in m[0].bound_ptrs.iter() {
             let llmatch = data.bindings_map.get(ident).llmatch;
@@ -1069,7 +1010,7 @@ fn compile_submatch_continue<'a, 'b>(
     };
 
     let mcx = check_match::MatchCheckCtxt { tcx: bcx.tcx() };
-    let adt_vals = if any_irrefutable_adt_pat(bcx, m, col) {
+    let adt_vals = if any_irrefutable_adt_pat(bcx.tcx(), m, col) {
         let repr = adt::represent_type(bcx.ccx(), left_ty);
         let arg_count = adt::num_args(&*repr, 0);
         let field_vals: Vec<ValueRef> = std::iter::range(0, arg_count).map(|ix|
@@ -1079,7 +1020,13 @@ fn compile_submatch_continue<'a, 'b>(
     } else if any_uniq_pat(m, col) || any_region_pat(m, col) {
         Some(vec!(Load(bcx, val)))
     } else {
-        None
+        match ty::get(left_ty).sty {
+            ty::ty_vec(_, Some(n)) => {
+                let args = extract_vec_elems(bcx, left_ty, n, 0, val);
+                Some(args.vals)
+            }
+            _ => None
+        }
     };
 
     match adt_vals {
@@ -1095,46 +1042,46 @@ fn compile_submatch_continue<'a, 'b>(
     }
 
     // Decide what kind of branch we need
-    let opts = get_options(bcx, m, col);
+    let opts = get_branches(bcx, m, col);
     debug!("options={:?}", opts);
-    let mut kind = no_branch;
+    let mut kind = NoBranch;
     let mut test_val = val;
     debug!("test_val={}", bcx.val_to_string(test_val));
     if opts.len() > 0u {
         match *opts.get(0) {
-            var(_, ref repr, _) => {
+            ConstantValue(_) | ConstantRange(_, _) => {
+                test_val = load_if_immediate(bcx, val, left_ty);
+                kind = if ty::type_is_integral(left_ty) {
+                    Switch
+                } else {
+                    Compare
+                };
+            }
+            Variant(_, ref repr, _) => {
                 let (the_kind, val_opt) = adt::trans_switch(bcx, &**repr, val);
                 kind = the_kind;
                 for &tval in val_opt.iter() { test_val = tval; }
             }
-            lit(_) => {
-                test_val = load_if_immediate(bcx, val, left_ty);
-                kind = if ty::type_is_integral(left_ty) { switch }
-                else { compare };
-            }
-            range(_, _) => {
-                test_val = Load(bcx, val);
-                kind = compare;
-            },
-            vec_len(..) => {
+            SliceLengthEqual(_) | SliceLengthGreaterOrEqual(_, _) => {
                 let (_, len) = tvec::get_base_and_len(bcx, val, left_ty);
                 test_val = len;
-                kind = compare_vec_len;
+                kind = Switch;
             }
         }
     }
     for o in opts.iter() {
         match *o {
-            range(_, _) => { kind = compare; break }
+            ConstantRange(_, _) => { kind = Compare; break },
+            SliceLengthGreaterOrEqual(_, _) => { kind = CompareSliceLength; break },
             _ => ()
         }
     }
     let else_cx = match kind {
-        no_branch | single => bcx,
+        NoBranch | Single => bcx,
         _ => bcx.fcx.new_temp_block("match_else")
     };
-    let sw = if kind == switch {
-        Switch(bcx, test_val, else_cx.llbb, opts.len())
+    let sw = if kind == Switch {
+        build::Switch(bcx, test_val, else_cx.llbb, opts.len())
     } else {
         C_int(ccx, 0) // Placeholder for when not using a switch
     };
@@ -1151,119 +1098,106 @@ fn compile_submatch_continue<'a, 'b>(
         // for the current conditional branch.
         let mut branch_chk = None;
         let mut opt_cx = else_cx;
-        if !exhaustive || i+1 < len {
+        if !exhaustive || i + 1 < len {
             opt_cx = bcx.fcx.new_temp_block("match_case");
             match kind {
-              single => Br(bcx, opt_cx.llbb),
-              switch => {
-                  match trans_opt(bcx, opt) {
-                      single_result(r) => {
-                        unsafe {
-                          llvm::LLVMAddCase(sw, r.val, opt_cx.llbb);
-                          bcx = r.bcx;
+                Single => Br(bcx, opt_cx.llbb),
+                Switch => {
+                    match opt.trans(bcx) {
+                        SingleResult(r) => {
+                            AddCase(sw, r.val, opt_cx.llbb);
+                            bcx = r.bcx;
+                        }
+                        _ => {
+                            bcx.sess().bug(
+                                "in compile_submatch, expected \
+                                 opt.trans() to return a SingleResult")
                         }
-                      }
-                      _ => {
-                          bcx.sess().bug(
-                              "in compile_submatch, expected \
-                               trans_opt to return a single_result")
-                      }
-                  }
-              }
-              compare | compare_vec_len => {
-                  let t = if kind == compare {
-                      left_ty
-                  } else {
-                      ty::mk_uint() // vector length
-                  };
-                  let Result {bcx: after_cx, val: matches} = {
-                      match trans_opt(bcx, opt) {
-                          single_result(Result {bcx, val}) => {
-                              compare_values(bcx, test_val, val, t)
-                          }
-                          lower_bound(Result {bcx, val}) => {
-                              compare_scalar_types(bcx, test_val, val, t, ast::BiGe)
-                          }
-                          range_result(Result {val: vbegin, ..},
-                                       Result {bcx, val: vend}) => {
-                              let Result {bcx, val: llge} =
-                                  compare_scalar_types(
-                                  bcx, test_val,
-                                  vbegin, t, ast::BiGe);
-                              let Result {bcx, val: llle} =
-                                  compare_scalar_types(
-                                  bcx, test_val, vend,
-                                  t, ast::BiLe);
-                              Result::new(bcx, And(bcx, llge, llle))
-                          }
-                      }
-                  };
-                  bcx = fcx.new_temp_block("compare_next");
-
-                  // If none of the sub-cases match, and the current condition
-                  // is guarded or has multiple patterns, move on to the next
-                  // condition, if there is any, rather than falling back to
-                  // the default.
-                  let guarded = m[i].data.arm.guard.is_some();
-                  let multi_pats = m[i].pats.len() > 1;
-                  if i + 1 < len && (guarded || multi_pats || kind == compare_vec_len) {
-                      branch_chk = Some(JumpToBasicBlock(bcx.llbb));
-                  }
-                  CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb);
-              }
-              _ => ()
+                    }
+                }
+                Compare | CompareSliceLength => {
+                    let t = if kind == Compare {
+                        left_ty
+                    } else {
+                        ty::mk_uint() // vector length
+                    };
+                    let Result { bcx: after_cx, val: matches } = {
+                        match opt.trans(bcx) {
+                            SingleResult(Result { bcx, val }) => {
+                                compare_values(bcx, test_val, val, t)
+                            }
+                            RangeResult(Result { val: vbegin, .. },
+                                        Result { bcx, val: vend }) => {
+                                let Result { bcx, val: llge } =
+                                    compare_scalar_types(
+                                    bcx, test_val,
+                                    vbegin, t, ast::BiGe);
+                                let Result { bcx, val: llle } =
+                                    compare_scalar_types(
+                                    bcx, test_val, vend,
+                                    t, ast::BiLe);
+                                Result::new(bcx, And(bcx, llge, llle))
+                            }
+                            LowerBound(Result { bcx, val }) => {
+                                compare_scalar_types(bcx, test_val, val, t, ast::BiGe)
+                            }
+                        }
+                    };
+                    bcx = fcx.new_temp_block("compare_next");
+
+                    // If none of the sub-cases match, and the current condition
+                    // is guarded or has multiple patterns, move on to the next
+                    // condition, if there is any, rather than falling back to
+                    // the default.
+                    let guarded = m[i].data.arm.guard.is_some();
+                    let multi_pats = m[i].pats.len() > 1;
+                    if i + 1 < len && (guarded || multi_pats || kind == CompareSliceLength) {
+                        branch_chk = Some(JumpToBasicBlock(bcx.llbb));
+                    }
+                    CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb);
+                }
+                _ => ()
             }
-        } else if kind == compare || kind == compare_vec_len {
+        } else if kind == Compare || kind == CompareSliceLength {
             Br(bcx, else_cx.llbb);
         }
 
         let mut size = 0u;
         let mut unpacked = Vec::new();
         match *opt {
-            var(disr_val, ref repr, _) => {
+            Variant(disr_val, ref repr, _) => {
                 let ExtractedBlock {vals: argvals, bcx: new_bcx} =
                     extract_variant_args(opt_cx, &**repr, disr_val, val);
                 size = argvals.len();
                 unpacked = argvals;
                 opt_cx = new_bcx;
             }
-            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_id, n,
-                                             slice, val);
+            SliceLengthEqual(len) => {
+                let args = extract_vec_elems(opt_cx, left_ty, len, 0, val);
                 size = args.vals.len();
                 unpacked = args.vals.clone();
                 opt_cx = args.bcx;
             }
-            lit(_) | range(_, _) => ()
+            SliceLengthGreaterOrEqual(before, after) => {
+                let args = extract_vec_elems(opt_cx, left_ty, before, after, val);
+                size = args.vals.len();
+                unpacked = args.vals.clone();
+                opt_cx = args.bcx;
+            }
+            ConstantValue(_) | ConstantRange(_, _) => ()
         }
         let opt_ms = enter_opt(opt_cx, pat_id, dm, m, opt, col, size, val);
         let opt_vals = unpacked.append(vals_left.as_slice());
-
-        match branch_chk {
-            None => {
-                compile_submatch(opt_cx,
-                                 opt_ms.as_slice(),
-                                 opt_vals.as_slice(),
-                                 chk,
-                                 has_genuine_default)
-            }
-            Some(branch_chk) => {
-                compile_submatch(opt_cx,
-                                 opt_ms.as_slice(),
-                                 opt_vals.as_slice(),
-                                 &branch_chk,
-                                 has_genuine_default)
-            }
-        }
+        compile_submatch(opt_cx,
+                         opt_ms.as_slice(),
+                         opt_vals.as_slice(),
+                         branch_chk.as_ref().unwrap_or(chk),
+                         has_genuine_default);
     }
 
     // Compile the fall-through case, if any
-    if !exhaustive && kind != single {
-        if kind == compare || kind == compare_vec_len {
+    if !exhaustive && kind != Single {
+        if kind == Compare || kind == CompareSliceLength {
             Br(bcx, else_cx.llbb);
         }
         match chk {
@@ -1792,20 +1726,25 @@ fn bind_irrefutable_pat<'a>(
             bcx = bind_irrefutable_pat(bcx, inner, loaded_val, binding_mode, cleanup_scope);
         }
         ast::PatVec(ref before, ref slice, ref after) => {
-            let extracted = extract_vec_elems(
-                bcx, pat.id, before.len() + 1u + after.len(),
-                slice.map(|_| before.len()), val
-            );
+            let pat_ty = node_id_type(bcx, pat.id);
+            let mut extracted = extract_vec_elems(bcx, pat_ty, before.len(), after.len(), val);
+            match slice {
+                &Some(_) => {
+                    extracted.vals.insert(
+                        before.len(),
+                        bind_subslice_pat(bcx, pat.id, val, before.len(), after.len())
+                    );
+                }
+                &None => ()
+            }
             bcx = before
-                .iter().map(|v| Some(*v))
-                .chain(Some(*slice).move_iter())
-                .chain(after.iter().map(|v| Some(*v)))
-                .zip(extracted.vals.iter())
-                .fold(bcx, |bcx, (inner, elem)| {
-                    inner.map_or(bcx, |inner| {
-                        bind_irrefutable_pat(bcx, inner, *elem, binding_mode, cleanup_scope)
-                    })
-                });
+                .iter()
+                .chain(slice.iter())
+                .chain(after.iter())
+                .zip(extracted.vals.move_iter())
+                .fold(bcx, |bcx, (&inner, elem)|
+                    bind_irrefutable_pat(bcx, inner, elem, binding_mode, cleanup_scope)
+                );
         }
         ast::PatMac(..) => {
             bcx.sess().span_bug(pat.span, "unexpanded macro");
diff --git a/src/librustc/middle/trans/adt.rs b/src/librustc/middle/trans/adt.rs
index f9b4c2879dc..8a5581e592a 100644
--- a/src/librustc/middle/trans/adt.rs
+++ b/src/librustc/middle/trans/adt.rs
@@ -74,6 +74,7 @@ type Hint = attr::ReprAttr;
 
 
 /// Representations.
+#[deriving(Eq, PartialEq)]
 pub enum Repr {
     /// C-like enums; basically an int.
     CEnum(IntType, Disr, Disr), // discriminant range (signedness based on the IntType)
@@ -126,6 +127,7 @@ pub enum Repr {
 }
 
 /// For structs, and struct-like parts of anything fancier.
+#[deriving(Eq, PartialEq)]
 pub struct Struct {
     // If the struct is DST, then the size and alignment do not take into
     // account the unsized fields of the struct.
@@ -280,7 +282,7 @@ struct Case {
 }
 
 
-#[deriving(Show)]
+#[deriving(Eq, PartialEq, Show)]
 pub enum PointerField {
     ThinPointer(uint),
     FatPointer(uint, uint)
@@ -573,14 +575,14 @@ fn struct_llfields(cx: &CrateContext, st: &Struct, sizing: bool, dst: bool) -> V
  * This should ideally be less tightly tied to `_match`.
  */
 pub fn trans_switch(bcx: &Block, r: &Repr, scrutinee: ValueRef)
-    -> (_match::branch_kind, Option<ValueRef>) {
+    -> (_match::BranchKind, Option<ValueRef>) {
     match *r {
         CEnum(..) | General(..) |
         RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => {
-            (_match::switch, Some(trans_get_discr(bcx, r, scrutinee, None)))
+            (_match::Switch, Some(trans_get_discr(bcx, r, scrutinee, None)))
         }
         Univariant(..) => {
-            (_match::single, None)
+            (_match::Single, None)
         }
     }
 }
@@ -665,14 +667,14 @@ fn load_discr(bcx: &Block, ity: IntType, ptr: ValueRef, min: Disr, max: Disr)
  * This should ideally be less tightly tied to `_match`.
  */
 pub fn trans_case<'a>(bcx: &'a Block<'a>, r: &Repr, discr: Disr)
-                  -> _match::opt_result<'a> {
+                  -> _match::OptResult<'a> {
     match *r {
         CEnum(ity, _, _) => {
-            _match::single_result(Result::new(bcx, C_integral(ll_inttype(bcx.ccx(), ity),
+            _match::SingleResult(Result::new(bcx, C_integral(ll_inttype(bcx.ccx(), ity),
                                                               discr as u64, true)))
         }
         General(ity, _, _) => {
-            _match::single_result(Result::new(bcx, C_integral(ll_inttype(bcx.ccx(), ity),
+            _match::SingleResult(Result::new(bcx, C_integral(ll_inttype(bcx.ccx(), ity),
                                                               discr as u64, true)))
         }
         Univariant(..) => {
@@ -681,7 +683,7 @@ pub fn trans_case<'a>(bcx: &'a Block<'a>, r: &Repr, discr: Disr)
         RawNullablePointer { .. } |
         StructWrappedNullablePointer { .. } => {
             assert!(discr == 0 || discr == 1);
-            _match::single_result(Result::new(bcx, C_bool(bcx.ccx(), discr != 0)))
+            _match::SingleResult(Result::new(bcx, C_bool(bcx.ccx(), discr != 0)))
         }
     }
 }
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index 7c0a332ce43..a91c3792f85 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -772,11 +772,11 @@ pub fn iter_structural_ty<'r,
           // comparison know not to proceed when the discriminants differ.
 
           match adt::trans_switch(cx, &*repr, av) {
-              (_match::single, None) => {
+              (_match::Single, None) => {
                   cx = iter_variant(cx, &*repr, av, &**variants.get(0),
                                     substs, f);
               }
-              (_match::switch, Some(lldiscrim_a)) => {
+              (_match::Switch, Some(lldiscrim_a)) => {
                   cx = f(cx, lldiscrim_a, ty::mk_int());
                   let unr_cx = fcx.new_temp_block("enum-iter-unr");
                   Unreachable(unr_cx);
@@ -791,7 +791,7 @@ pub fn iter_structural_ty<'r,
                                       variant.disr_val.to_string().as_slice())
                                      .as_slice());
                       match adt::trans_case(cx, &*repr, variant.disr_val) {
-                          _match::single_result(r) => {
+                          _match::SingleResult(r) => {
                               AddCase(llswitch, r.val, variant_cx.llbb)
                           }
                           _ => ccx.sess().unimpl("value from adt::trans_case \
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index 4b2a3073755..c234bea0a33 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -498,7 +498,7 @@ impl ReprAttr {
     }
 }
 
-#[deriving(PartialEq, Show)]
+#[deriving(Eq, Hash, PartialEq, Show)]
 pub enum IntType {
     SignedInt(ast::IntTy),
     UnsignedInt(ast::UintTy)
diff --git a/src/test/run-pass/issue-16648.rs b/src/test/run-pass/issue-16648.rs
new file mode 100644
index 00000000000..7ddb20811a3
--- /dev/null
+++ b/src/test/run-pass/issue-16648.rs
@@ -0,0 +1,20 @@
+// Copyright 2014 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.
+
+fn main() {
+    let x: (int, &[int]) = (2i, &[1i, 2i]);
+    assert_eq!(match x {
+        (0, [_, _]) => 0,
+        (1, _) => 1,
+        (2, [_, _]) => 2,
+        (2, _) => 3,
+        _ => 4
+    }, 2i);
+}