about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2014-07-02 18:48:23 -0700
committerAlex Crichton <alex@alexcrichton.com>2014-07-02 18:48:23 -0700
commitc4ac124edda29eabffc2acc3255aca1a0c2680f0 (patch)
tree488edf8d1734ab70c6556e322260ad98e0910f7a
parentff1dd44b40a7243f43a8d32ba8bd6026197c320b (diff)
parentc8ae065182d19a098aa397fddcb4f4f4eccc0b32 (diff)
downloadrust-c4ac124edda29eabffc2acc3255aca1a0c2680f0.tar.gz
rust-c4ac124edda29eabffc2acc3255aca1a0c2680f0.zip
Merge remote-tracking branch 'origin/master' into 0.11.0-release
Conflicts:
	RELEASES.txt
-rw-r--r--.travis.yml2
-rw-r--r--AUTHORS.txt1
-rw-r--r--RELEASES.txt39
-rw-r--r--src/librustc/middle/check_match.rs488
-rw-r--r--src/librustc/middle/expr_use_visitor.rs107
-rw-r--r--src/librustc/middle/pat_util.rs7
-rw-r--r--src/test/compile-fail/borrowck-overloaded-call.rs74
-rw-r--r--src/test/compile-fail/non-exhaustive-match-nested.rs19
-rw-r--r--src/test/run-pass/issue-15104.rs21
-rw-r--r--src/test/run-pass/match-vec-alternatives.rs82
10 files changed, 604 insertions, 236 deletions
diff --git a/.travis.yml b/.travis.yml
index e3cec33ad07..dd62defa020 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -41,7 +41,7 @@ script: |
     if [[ $LLVM_VERSION != '3.4' ]]; then exit 0; fi
   fi &&
   make tidy &&
-  travis_wait make -j4 rustc-stage1 &&
+  make -j4 rustc-stage1 RUSTFLAGS='-Z time-passes' &&
   make check-stage1-std check-stage1-rpass check-stage1-cfail check-stage1-rfail check-stage1-doc
 
 env:
diff --git a/AUTHORS.txt b/AUTHORS.txt
index e0c488e584a..ab4f234cc99 100644
--- a/AUTHORS.txt
+++ b/AUTHORS.txt
@@ -487,7 +487,6 @@ TyOverby <ty@pre-alpha.com>
 Tycho Sci <tychosci@gmail.com>
 Tyler Bindon <martica@martica.org>
 U-NOV2010\eugals
-User Jyyou <jyyou@plaslab.cs.nctu.edu.tw>
 Utkarsh Kukreti <utkarshkukreti@gmail.com>
 Uwe Dauernheim <uwe@dauernheim.net>
 Vadim Chugunov <vadimcn@gmail.com>
diff --git a/RELEASES.txt b/RELEASES.txt
index 1fc3ebd414f..457328b2c90 100644
--- a/RELEASES.txt
+++ b/RELEASES.txt
@@ -1,7 +1,7 @@
-Version 0.11 (July 2014)
+Version 0.11.0 (July 2014)
 -------------------------
 
-  * ~1700 cahnges, numerous bugfixes
+  * ~1700 changes, numerous bugfixes
 
   * Language
     * ~[T] has been removed from the language. This type is superseded by
@@ -13,15 +13,15 @@ Version 0.11 (July 2014)
     * @T has been removed from the language. This type is superseded by the
       standard library's std::gc::Gc<T> type.
     * Struct fields are now all private by default.
-    * Vector indices and shift amounts are both required to be a `uint` now
+    * Vector indices and shift amounts are both required to be a `uint`
       instead of any integral type.
     * Byte character, byte string, and raw byte string literals are now all
       supported by prefixing the normal literal with a `b`.
     * Multiple ABIs are no longer allowed in an ABI string
-    * The syntax for lifetimes on clousres/procedures has been tweaked
-      slightly.
-    * Floating point modulus has been removed from the language, it is still
-      provided by a library implementation, however.
+    * The syntax for lifetimes on closures/procedures has been tweaked
+      slightly: `<'a>|A, B|: 'b + K -> T`
+    * Floating point modulus has been removed from the language; however it
+      is still provided by a library implementation.
     * Private enum variants are now disallowed.
     * The `priv` keyword has been removed from the language.
     * A closure can no longer be invoked through a &-pointer.
@@ -29,10 +29,9 @@ Version 0.11 (July 2014)
     * The transmute intrinsic no longer works on type parameters.
     * Statics now allow blocks/items in their definition.
     * Trait bounds are separated from objects with + instead of : now.
-    * Mutably borrowed objects can no longer be read while they are
-      borrowed.
+    * Objects can no longer be read while they are mutably borrowed.
     * The address of a static is now marked as insignificant unless the
-      #[inline(never)] attribute is placed on a static.
+      #[inline(never)] attribute is placed it.
     * The #[unsafe_destructor] attribute is now behind a feature gate.
     * Struct literals are no longer allowed in ambiguous positions such as
       if, while, match, and for..in.
@@ -48,13 +47,13 @@ Version 0.11 (July 2014)
   * Libraries
     * The standard library is now a "facade" over a number of underlying
       libraries. This means that development on the standard library should
-      be speeder due to less to compile, as well as a clearer line between
+      be speeder due to smaller crates, as well as a clearer line between
       all dependencies.
     * A new library, libcore, lives under the standard library's facade
       which is Rust's "0-assumption" library, suitable for embedded and
       kernel development for example.
     * A regex crate has been added to the standard distribution. This crate
-      includes statically compiled regulard expressions.
+      includes statically compiled regular expressions.
     * The unwrap/unwrap_err methods on Result require a Show bound for
       better error messages.
     * The return types of the std::comm primitives have been centralized
@@ -72,11 +71,11 @@ Version 0.11 (July 2014)
     * A graphviz crate has been added for creating .dot files.
     * The std::cast module has been migrated into std::mem.
     * The std::local_data api has been migrated from freestanding functions
-      to based on methods.
+      to being based on methods.
     * The Pod trait has been renamed to Copy.
     * jemalloc has been added as the default allocator for types.
-    * The api for allocating memory in rust has been modified for sized
-      deallocation as well as using proper alignment.
+    * The API for allocating memory has been changed to use proper alignment
+      and sized deallocation
     * Connecting a TcpStream or binding a TcpListener is now based on a
       string address and a u16 port. This allows connecting to a hostname as
       opposed to an IP.
@@ -100,11 +99,11 @@ Version 0.11 (July 2014)
       discovery of breaking changes.
     * The compiler will now try to suggest how to annotate lifetimes if a
       lifetime-related error occurs.
-    * Debug info continues to be improved greatly with better support for
-      situations such as LTO and general bug fixes.
+    * Debug info continues to be improved greatly with general bug fixes and
+      better support for situations like link time optimization (LTO).
     * Usage of syntax extensions when cross-compiling has been fixed.
-    * The equivalent of ffunction-sections and fdata-sections have been
-      enabled by default with the equivalent of --gc-sections.
+    * Functionality equivalent to GCC & Clang's -ffunction-sections,
+      -fdata-sections and --gc-sections has been enabled by default
     * The compiler is now stricter about where it will load module files
       from when a module is declared via `mod foo;`.
     * The #[phase(syntax)] attribute has been renamed to #[phase(plugin)].
@@ -128,7 +127,7 @@ Version 0.11 (July 2014)
     * Cross-compiling to mipsel is now supported.
     * Stability attributes are now inherited by default and no longer apply
       to intra-crate usage, only inter-crate usage.
-    * Error message related to non-exhaustive match statements have been
+    * Error message related to non-exhaustive match expressions have been
       greatly improved.
 
 Version 0.10 (April 2014)
diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs
index 27b826b9d1a..1400e207ab1 100644
--- a/src/librustc/middle/check_match.rs
+++ b/src/librustc/middle/check_match.rs
@@ -8,17 +8,16 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![allow(non_camel_case_types)]
-
 use middle::const_eval::{compare_const_vals, const_bool, const_float, const_nil, const_val};
 use middle::const_eval::{eval_const_expr, lookup_const_by_id};
 use middle::def::*;
 use middle::pat_util::*;
 use middle::ty::*;
 use middle::ty;
-
+use std::fmt;
 use std::gc::{Gc, GC};
-use std::iter;
+use std::iter::AdditiveIterator;
+use std::iter::range_inclusive;
 use syntax::ast::*;
 use syntax::ast_util::{is_unguarded, walk_pat};
 use syntax::codemap::{Span, Spanned, DUMMY_SP};
@@ -28,7 +27,71 @@ use syntax::visit;
 use syntax::visit::{Visitor, FnKind};
 use util::ppaux::ty_to_str;
 
-type Matrix = Vec<Vec<Gc<Pat>>>;
+struct Matrix(Vec<Vec<Gc<Pat>>>);
+
+/// Pretty-printer for matrices of patterns, example:
+/// ++++++++++++++++++++++++++
+/// + _     + []             +
+/// ++++++++++++++++++++++++++
+/// + true  + [First]        +
+/// ++++++++++++++++++++++++++
+/// + true  + [Second(true)] +
+/// ++++++++++++++++++++++++++
+/// + false + [_]            +
+/// ++++++++++++++++++++++++++
+/// + _     + [_, _, ..tail] +
+/// ++++++++++++++++++++++++++
+impl fmt::Show for Matrix {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        try!(write!(f, "\n"));
+
+        let &Matrix(ref m) = self;
+        let pretty_printed_matrix: Vec<Vec<String>> = m.iter().map(|row| {
+            row.iter().map(|&pat| pat_to_str(pat)).collect::<Vec<String>>()
+        }).collect();
+
+        let column_count = m.iter().map(|row| row.len()).max().unwrap_or(0u);
+        assert!(m.iter().all(|row| row.len() == column_count));
+        let column_widths: Vec<uint> = range(0, column_count).map(|col| {
+            pretty_printed_matrix.iter().map(|row| row.get(col).len()).max().unwrap_or(0u)
+        }).collect();
+
+        let total_width = column_widths.iter().map(|n| *n).sum() + column_count * 3 + 1;
+        let br = String::from_char(total_width, '+');
+        try!(write!(f, "{}\n", br));
+        for row in pretty_printed_matrix.move_iter() {
+            try!(write!(f, "+"));
+            for (column, pat_str) in row.move_iter().enumerate() {
+                try!(write!(f, " "));
+                f.width = Some(*column_widths.get(column));
+                try!(f.pad(pat_str.as_slice()));
+                try!(write!(f, " +"));
+            }
+            try!(write!(f, "\n"));
+            try!(write!(f, "{}\n", br));
+        }
+        Ok(())
+    }
+}
+
+struct MatchCheckCtxt<'a> {
+    tcx: &'a ty::ctxt
+}
+
+#[deriving(Clone, PartialEq)]
+enum Constructor {
+    /// The constructor of all patterns that don't vary by constructor,
+    /// e.g. struct patterns and fixed-length arrays.
+    Single,
+    /// Enum variants.
+    Variant(DefId),
+    /// Literal values.
+    ConstantValue(const_val),
+    /// Ranges of literal values (2..5).
+    ConstantRange(const_val, const_val),
+    /// Array patterns of length n.
+    Slice(uint)
+}
 
 #[deriving(Clone)]
 enum Usefulness {
@@ -50,22 +113,6 @@ impl Usefulness {
     }
 }
 
-fn def_to_path(tcx: &ty::ctxt, id: DefId) -> Path {
-    ty::with_path(tcx, id, |mut path| Path {
-        global: false,
-        segments: path.last().map(|elem| PathSegment {
-            identifier: Ident::new(elem.name()),
-            lifetimes: vec!(),
-            types: OwnedSlice::empty()
-        }).move_iter().collect(),
-        span: DUMMY_SP,
-    })
-}
-
-struct MatchCheckCtxt<'a> {
-    tcx: &'a ty::ctxt,
-}
-
 impl<'a> Visitor<()> for MatchCheckCtxt<'a> {
     fn visit_expr(&mut self, ex: &Expr, _: ()) {
         check_expr(self, ex);
@@ -78,11 +125,8 @@ impl<'a> Visitor<()> for MatchCheckCtxt<'a> {
     }
 }
 
-pub fn check_crate(tcx: &ty::ctxt,
-                   krate: &Crate) {
-    let mut cx = MatchCheckCtxt {
-        tcx: tcx,
-    };
+pub fn check_crate(tcx: &ty::ctxt, krate: &Crate) {
+    let mut cx = MatchCheckCtxt { tcx: tcx, };
 
     visit::walk_crate(&mut cx, krate, ());
 
@@ -116,12 +160,12 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
                // If the type *is* empty, it's vacuously exhaustive
                return;
             }
-            let m: Matrix = arms
+            let m: Matrix = Matrix(arms
                 .iter()
                 .filter(|&arm| is_unguarded(arm))
                 .flat_map(|arm| arm.pats.iter())
                 .map(|pat| vec!(pat.clone()))
-                .collect();
+                .collect());
             check_exhaustive(cx, ex.span, &m);
         },
         _ => ()
@@ -130,7 +174,7 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
 
 // Check for unreachable patterns
 fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) {
-    let mut seen = Vec::new();
+    let mut seen = Matrix(vec!());
     for arm in arms.iter() {
         for pat in arm.pats.iter() {
             // Check that we do not match against a static NaN (#6804)
@@ -161,7 +205,11 @@ fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) {
                 NotUseful => cx.tcx.sess.span_err(pat.span, "unreachable pattern"),
                 _ => ()
             }
-            if arm.guard.is_none() { seen.push(v); }
+            if arm.guard.is_none() {
+                let Matrix(mut rows) = seen;
+                rows.push(v);
+                seen = Matrix(rows);
+            }
         }
     }
 }
@@ -175,10 +223,6 @@ fn raw_pat(p: Gc<Pat>) -> Gc<Pat> {
 
 fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, m: &Matrix) {
     match is_useful(cx, m, [wild()], ConstructWitness) {
-        NotUseful => {
-            // This is good, wildcard pattern isn't reachable
-            return;
-        }
         Useful(pats) => {
             let witness = match pats.as_slice() {
                 [witness] => witness,
@@ -188,38 +232,58 @@ fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, m: &Matrix) {
             let msg = format!("non-exhaustive patterns: `{0}` not covered", pat_to_str(&*witness));
             cx.tcx.sess.span_err(sp, msg.as_slice());
         }
+        NotUseful => {
+            // This is good, wildcard pattern isn't reachable
+        }
     }
 }
 
-#[deriving(Clone, PartialEq)]
-enum ctor {
-    single,
-    variant(DefId),
-    val(const_val),
-    range(const_val, const_val),
-    vec(uint)
-}
-
 fn const_val_to_expr(value: &const_val) -> Gc<Expr> {
     let node = match value {
         &const_bool(b) => LitBool(b),
         &const_nil => LitNil,
         _ => unreachable!()
     };
-    box(GC) Expr {
+    box (GC) Expr {
         id: 0,
         node: ExprLit(box(GC) Spanned { node: node, span: DUMMY_SP }),
         span: DUMMY_SP
     }
 }
 
-fn construct_witness(cx: &MatchCheckCtxt, ctor: &ctor, pats: Vec<Gc<Pat>>, lty: ty::t) -> Gc<Pat> {
-    let pat = match ty::get(lty).sty {
+fn def_to_path(tcx: &ty::ctxt, id: DefId) -> Path {
+    ty::with_path(tcx, id, |mut path| Path {
+        global: false,
+        segments: path.last().map(|elem| PathSegment {
+            identifier: Ident::new(elem.name()),
+            lifetimes: vec!(),
+            types: OwnedSlice::empty()
+        }).move_iter().collect(),
+        span: DUMMY_SP,
+    })
+}
+
+/// Constructs a partial witness for a pattern given a list of
+/// patterns expanded by the specialization step.
+///
+/// When a pattern P is discovered to be useful, this function is used bottom-up
+/// to reconstruct a complete witness, e.g. a pattern P' that covers a subset
+/// of values, V, where each value in that set is not covered by any previously
+/// used patterns and is covered by the pattern P'. Examples:
+///
+/// left_ty: tuple of 3 elements
+/// pats: [10, 20, _]           => (10, 20, _)
+///
+/// left_ty: struct X { a: (bool, &'static str), b: uint}
+/// pats: [(false, "foo"), 42]  => X { a: (false, "foo"), b: 42 }
+fn construct_witness(cx: &MatchCheckCtxt, ctor: &Constructor,
+                     pats: Vec<Gc<Pat>>, left_ty: ty::t) -> Gc<Pat> {
+    let pat = match ty::get(left_ty).sty {
         ty::ty_tup(_) => PatTup(pats),
 
         ty::ty_enum(cid, _) | ty::ty_struct(cid, _)  => {
             let (vid, is_structure) = match ctor {
-                &variant(vid) => (vid,
+                &Variant(vid) => (vid,
                     ty::enum_variant_with_id(cx.tcx, cid, vid).arg_names.is_some()),
                 _ => (cid, true)
             };
@@ -235,103 +299,95 @@ fn construct_witness(cx: &MatchCheckCtxt, ctor: &ctor, pats: Vec<Gc<Pat>>, lty:
             } else {
                 PatEnum(def_to_path(cx.tcx, vid), Some(pats))
             }
-        },
+        }
 
         ty::ty_rptr(_, ty::mt { ty: ty, .. }) => {
             match ty::get(ty).sty {
+               ty::ty_vec(_, Some(n)) => match ctor {
+                    &Single => {
+                        assert_eq!(pats.len(), n);
+                        PatVec(pats, None, vec!())
+                    },
+                    _ => unreachable!()
+                },
                 ty::ty_vec(_, None) => match ctor {
-                    &vec(_) => PatVec(pats, None, vec!()),
+                    &Slice(n) => {
+                        assert_eq!(pats.len(), n);
+                        PatVec(pats, None, vec!())
+                    },
                     _ => unreachable!()
                 },
                 ty::ty_str => PatWild,
+
                 _ => {
                     assert_eq!(pats.len(), 1);
                     PatRegion(pats.get(0).clone())
                 }
             }
-        },
+        }
 
         ty::ty_box(_) => {
             assert_eq!(pats.len(), 1);
             PatBox(pats.get(0).clone())
-        },
+        }
+
+        ty::ty_vec(_, Some(len)) => {
+            assert_eq!(pats.len(), len);
+            PatVec(pats, None, vec!())
+        }
 
         _ => {
-            match ctor {
-                &vec(_) => PatVec(pats, None, vec!()),
-                &val(ref v) => PatLit(const_val_to_expr(v)),
+            match *ctor {
+                ConstantValue(ref v) => PatLit(const_val_to_expr(v)),
                 _ => PatWild
             }
         }
     };
 
-    box(GC) Pat {
+    box (GC) Pat {
         id: 0,
         node: pat,
         span: DUMMY_SP
     }
 }
 
-fn missing_constructor(cx: &MatchCheckCtxt, m: &Matrix, left_ty: ty::t) -> Option<ctor> {
-    let used_constructors: Vec<ctor> = m.iter()
-        .filter_map(|r| pat_ctor_id(cx, left_ty, *r.get(0)))
+fn missing_constructor(cx: &MatchCheckCtxt, &Matrix(ref rows): &Matrix,
+                       left_ty: ty::t, max_slice_length: uint) -> Option<Constructor> {
+    let used_constructors: Vec<Constructor> = rows.iter()
+        .flat_map(|row| pat_constructors(cx, *row.get(0), left_ty, max_slice_length).move_iter())
         .collect();
-
-    all_constructors(cx, m, left_ty)
+    all_constructors(cx, left_ty, max_slice_length)
         .move_iter()
         .find(|c| !used_constructors.contains(c))
 }
 
-fn all_constructors(cx: &MatchCheckCtxt, m: &Matrix, left_ty: ty::t) -> Vec<ctor> {
-    // This produces a list of all vector constructors that we would expect to appear
-    // in an exhaustive set of patterns. Because such a list would normally be infinite,
-    // we narrow it down to only those constructors that actually appear in the inspected
-    // column, plus, any that are missing and not covered by a pattern with a destructured slice.
-    fn vec_constructors(m: &Matrix) -> Vec<ctor> {
-        let max_vec_len = m.iter().map(|r| match r.get(0).node {
-            PatVec(ref before, _, ref after) => before.len() + after.len(),
-            _ => 0u
-        }).max().unwrap_or(0u);
-        let min_vec_len_with_slice = m.iter().map(|r| match r.get(0).node {
-            PatVec(ref before, Some(_), ref after) => before.len() + after.len(),
-            _ => max_vec_len + 1
-        }).min().unwrap_or(max_vec_len + 1);
-        let other_lengths = m.iter().map(|r| match r.get(0).node {
-            PatVec(ref before, _, ref after) => before.len() + after.len(),
-            _ => 0u
-        }).filter(|&len| len > min_vec_len_with_slice);
-        iter::range_inclusive(0u, min_vec_len_with_slice)
-            .chain(other_lengths)
-            .map(|len| vec(len))
-            .collect()
-    }
-
+/// This determines the set of all possible constructors of a pattern matching
+/// values of type `left_ty`. For vectors, this would normally be an infinite set
+/// but is instead bounded by the maximum fixed length of slice patterns in
+/// the column of patterns being analyzed.
+fn all_constructors(cx: &MatchCheckCtxt, left_ty: ty::t,
+                    max_slice_length: uint) -> Vec<Constructor> {
     match ty::get(left_ty).sty {
         ty::ty_bool =>
-            [true, false].iter().map(|b| val(const_bool(*b))).collect(),
+            [true, false].iter().map(|b| ConstantValue(const_bool(*b))).collect(),
 
         ty::ty_nil =>
-            vec!(val(const_nil)),
+            vec!(ConstantValue(const_nil)),
 
         ty::ty_rptr(_, ty::mt { ty: ty, .. }) => match ty::get(ty).sty {
-            ty::ty_vec(_, None) => vec_constructors(m),
-            _ => vec!(single)
+            ty::ty_vec(_, None) =>
+                range_inclusive(0, max_slice_length).map(|length| Slice(length)).collect(),
+            _ => vec!(Single)
         },
 
         ty::ty_enum(eid, _) =>
             ty::enum_variants(cx.tcx, eid)
                 .iter()
-                .map(|va| variant(va.id))
+                .map(|va| Variant(va.id))
                 .collect(),
 
-        ty::ty_vec(_, None) =>
-            vec_constructors(m),
-
-        ty::ty_vec(_, Some(n)) =>
-            vec!(vec(n)),
-
         _ =>
-            vec!(single)
+            vec!(Single)
     }
 }
 
@@ -348,15 +404,16 @@ fn all_constructors(cx: &MatchCheckCtxt, m: &Matrix, left_ty: ty::t) -> Vec<ctor
 
 // Note: is_useful doesn't work on empty types, as the paper notes.
 // So it assumes that v is non-empty.
-fn is_useful(cx: &MatchCheckCtxt, m: &Matrix, v: &[Gc<Pat>],
-             witness: WitnessPreference) -> Usefulness {
-    if m.len() == 0u {
+fn is_useful(cx: &MatchCheckCtxt, m @ &Matrix(ref rows): &Matrix,
+             v: &[Gc<Pat>], witness: WitnessPreference) -> Usefulness {
+    debug!("{:}", m);
+    if rows.len() == 0u {
         return Useful(vec!());
     }
-    if m.get(0).len() == 0u {
+    if rows.get(0).len() == 0u {
         return NotUseful;
     }
-    let real_pat = match m.iter().find(|r| r.get(0).id != 0) {
+    let real_pat = match rows.iter().find(|r| r.get(0).id != 0) {
         Some(r) => {
             match r.get(0).node {
                 // An arm of the form `ref x @ sub_pat` has type
@@ -374,10 +431,16 @@ fn is_useful(cx: &MatchCheckCtxt, m: &Matrix, v: &[Gc<Pat>],
         ty::pat_ty(cx.tcx, &*real_pat)
     };
 
-    match pat_ctor_id(cx, left_ty, v[0]) {
-        None => match missing_constructor(cx, m, left_ty) {
+    let max_slice_length = rows.iter().filter_map(|row| match row.get(0).node {
+        PatVec(ref before, _, ref after) => Some(before.len() + after.len()),
+        _ => None
+    }).max().map_or(0, |v| v + 1);
+
+    let constructors = pat_constructors(cx, v[0], left_ty, max_slice_length);
+    if constructors.is_empty() {
+        match missing_constructor(cx, m, left_ty, max_slice_length) {
             None => {
-                all_constructors(cx, m, left_ty).move_iter().filter_map(|c| {
+                all_constructors(cx, left_ty, max_slice_length).move_iter().filter_map(|c| {
                     is_useful_specialized(cx, m, v, c.clone(),
                                           left_ty, witness).useful().map(|pats| {
                         Useful(match witness {
@@ -400,14 +463,15 @@ fn is_useful(cx: &MatchCheckCtxt, m: &Matrix, v: &[Gc<Pat>],
                 }).nth(0).unwrap_or(NotUseful)
             },
 
-            Some(ctor) => {
-                let matrix = m.iter().filter_map(|r| default(cx, r.as_slice())).collect();
+            Some(constructor) => {
+                let matrix = Matrix(rows.iter().filter_map(|r|
+                    default(cx, r.as_slice())).collect());
                 match is_useful(cx, &matrix, v.tail(), witness) {
                     Useful(pats) => Useful(match witness {
                         ConstructWitness => {
-                            let arity = constructor_arity(cx, &ctor, left_ty);
+                            let arity = constructor_arity(cx, &constructor, left_ty);
                             let wild_pats = Vec::from_elem(arity, wild());
-                            let enum_pat = construct_witness(cx, &ctor, wild_pats, left_ty);
+                            let enum_pat = construct_witness(cx, &constructor, wild_pats, left_ty);
                             (vec!(enum_pat)).append(pats.as_slice())
                         }
                         LeaveOutWitness => vec!()
@@ -415,64 +479,82 @@ fn is_useful(cx: &MatchCheckCtxt, m: &Matrix, v: &[Gc<Pat>],
                     result => result
                 }
             }
-        },
-
-        Some(v0_ctor) => is_useful_specialized(cx, m, v, v0_ctor, left_ty, witness)
+        }
+    } else {
+        constructors.move_iter().filter_map(|c| {
+            is_useful_specialized(cx, m, v, c.clone(), left_ty, witness)
+                .useful().map(|pats| Useful(pats))
+        }).nth(0).unwrap_or(NotUseful)
     }
 }
 
-fn is_useful_specialized(cx: &MatchCheckCtxt, m: &Matrix, v: &[Gc<Pat>],
-                         ctor: ctor, lty: ty::t, witness: WitnessPreference) -> Usefulness {
+fn is_useful_specialized(cx: &MatchCheckCtxt, &Matrix(ref m): &Matrix, v: &[Gc<Pat>],
+                         ctor: Constructor, lty: ty::t, witness: WitnessPreference) -> Usefulness {
     let arity = constructor_arity(cx, &ctor, lty);
-    let matrix = m.iter().filter_map(|r| {
+    let matrix = Matrix(m.iter().filter_map(|r| {
         specialize(cx, r.as_slice(), &ctor, arity)
-    }).collect();
+    }).collect());
     match specialize(cx, v, &ctor, arity) {
         Some(v) => is_useful(cx, &matrix, v.as_slice(), witness),
         None => NotUseful
     }
 }
 
-fn pat_ctor_id(cx: &MatchCheckCtxt, left_ty: ty::t, p: Gc<Pat>) -> Option<ctor> {
+/// Determines the constructors that the given pattern can be specialized to.
+///
+/// In most cases, there's only one constructor that a specific pattern
+/// represents, such as a specific enum variant or a specific literal value.
+/// Slice patterns, however, can match slices of different lengths. For instance,
+/// `[a, b, ..tail]` can match a slice of length 2, 3, 4 and so on.
+///
+/// On the other hand, a wild pattern and an identifier pattern cannot be
+/// specialized in any way.
+fn pat_constructors(cx: &MatchCheckCtxt, p: Gc<Pat>,
+                    left_ty: ty::t, max_slice_length: uint) -> Vec<Constructor> {
     let pat = raw_pat(p);
     match pat.node {
         PatIdent(..) =>
             match cx.tcx.def_map.borrow().find(&pat.id) {
                 Some(&DefStatic(did, false)) => {
                     let const_expr = lookup_const_by_id(cx.tcx, did).unwrap();
-                    Some(val(eval_const_expr(cx.tcx, &*const_expr)))
+                    vec!(ConstantValue(eval_const_expr(cx.tcx, &*const_expr)))
                 },
-                Some(&DefVariant(_, id, _)) => Some(variant(id)),
-                _ => None
+                Some(&DefVariant(_, id, _)) => vec!(Variant(id)),
+                _ => vec!()
             },
         PatEnum(..) =>
             match cx.tcx.def_map.borrow().find(&pat.id) {
                 Some(&DefStatic(did, false)) => {
                     let const_expr = lookup_const_by_id(cx.tcx, did).unwrap();
-                    Some(val(eval_const_expr(cx.tcx, &*const_expr)))
+                    vec!(ConstantValue(eval_const_expr(cx.tcx, &*const_expr)))
                 },
-                Some(&DefVariant(_, id, _)) => Some(variant(id)),
-                _ => Some(single)
+                Some(&DefVariant(_, id, _)) => vec!(Variant(id)),
+                _ => vec!(Single)
             },
         PatStruct(..) =>
             match cx.tcx.def_map.borrow().find(&pat.id) {
-                Some(&DefVariant(_, id, _)) => Some(variant(id)),
-                _ => Some(single)
+                Some(&DefVariant(_, id, _)) => vec!(Variant(id)),
+                _ => vec!(Single)
             },
         PatLit(expr) =>
-            Some(val(eval_const_expr(cx.tcx, &*expr))),
+            vec!(ConstantValue(eval_const_expr(cx.tcx, &*expr))),
         PatRange(lo, hi) =>
-            Some(range(eval_const_expr(cx.tcx, &*lo), eval_const_expr(cx.tcx, &*hi))),
-        PatVec(ref before, _, ref after) => match ty::get(left_ty).sty {
-            ty::ty_vec(_, Some(n)) =>
-                Some(vec(n)),
-            _ =>
-                Some(vec(before.len() + after.len()))
-        },
+            vec!(ConstantRange(eval_const_expr(cx.tcx, &*lo), eval_const_expr(cx.tcx, &*hi))),
+        PatVec(ref before, ref slice, ref after) =>
+            match ty::get(left_ty).sty {
+                ty::ty_vec(_, Some(_)) => vec!(Single),
+                _                      => if slice.is_some() {
+                    range_inclusive(before.len() + after.len(), max_slice_length)
+                        .map(|length| Slice(length))
+                        .collect()
+                } else {
+                    vec!(Slice(before.len() + after.len()))
+                }
+            },
         PatBox(_) | PatTup(_) | PatRegion(..) =>
-            Some(single),
+            vec!(Single),
         PatWild | PatWildMulti =>
-            None,
+            vec!(),
         PatMac(_) =>
             cx.tcx.sess.bug("unexpanded macro")
     }
@@ -482,53 +564,53 @@ fn is_wild(cx: &MatchCheckCtxt, p: Gc<Pat>) -> bool {
     let pat = raw_pat(p);
     match pat.node {
         PatWild | PatWildMulti => true,
-        PatIdent(_, _, _) => {
+        PatIdent(_, _, _) =>
             match cx.tcx.def_map.borrow().find(&pat.id) {
                 Some(&DefVariant(_, _, _)) | Some(&DefStatic(..)) => false,
                 _ => true
-            }
-        }
+            },
+        PatVec(ref before, Some(_), ref after) =>
+            before.is_empty() && after.is_empty(),
         _ => false
     }
 }
 
-fn constructor_arity(cx: &MatchCheckCtxt, ctor: &ctor, ty: ty::t) -> uint {
+/// This computes the arity of a constructor. The arity of a constructor
+/// is how many subpattern patterns of that constructor should be expanded to.
+///
+/// For instance, a tuple pattern (_, 42u, Some([])) has the arity of 3.
+/// A struct pattern's arity is the number of fields it contains, etc.
+fn constructor_arity(cx: &MatchCheckCtxt, ctor: &Constructor, ty: ty::t) -> uint {
     match ty::get(ty).sty {
         ty::ty_tup(ref fs) => fs.len(),
         ty::ty_box(_) | ty::ty_uniq(_) => 1u,
         ty::ty_rptr(_, ty::mt { ty: ty, .. }) => match ty::get(ty).sty {
             ty::ty_vec(_, None) => match *ctor {
-                vec(n) => n,
-                _ => 0u
+                Slice(length) => length,
+                _ => unreachable!()
             },
             ty::ty_str => 0u,
             _ => 1u
         },
         ty::ty_enum(eid, _) => {
             match *ctor {
-                variant(id) => enum_variant_with_id(cx.tcx, eid, id).args.len(),
+                Variant(id) => enum_variant_with_id(cx.tcx, eid, id).args.len(),
                 _ => unreachable!()
             }
         }
         ty::ty_struct(cid, _) => ty::lookup_struct_fields(cx.tcx, cid).len(),
-        ty::ty_vec(_, _) => match *ctor {
-            vec(n) => n,
-            _ => 0u
-        },
+        ty::ty_vec(_, Some(n)) => n,
         _ => 0u
     }
 }
 
-fn wild() -> Gc<Pat> {
-    box(GC) Pat {id: 0, node: PatWild, span: DUMMY_SP}
-}
-
-fn range_covered_by_constructor(ctor_id: &ctor, from: &const_val, to: &const_val) -> Option<bool> {
-    let (c_from, c_to) = match *ctor_id {
-        val(ref value)          => (value, value),
-        range(ref from, ref to) => (from, to),
-        single                  => return Some(true),
-        _                       => unreachable!()
+fn range_covered_by_constructor(ctor: &Constructor,
+                                from: &const_val,to: &const_val) -> Option<bool> {
+    let (c_from, c_to) = match *ctor {
+        ConstantValue(ref value)        => (value, value),
+        ConstantRange(ref from, ref to) => (from, to),
+        Single                          => return Some(true),
+        _                               => unreachable!()
     };
     let cmp_from = compare_const_vals(c_from, from);
     let cmp_to = compare_const_vals(c_to, to);
@@ -538,22 +620,30 @@ fn range_covered_by_constructor(ctor_id: &ctor, from: &const_val, to: &const_val
     }
 }
 
+/// This is the main specialization step. It expands the first pattern in the given row
+/// into `arity` patterns based on the constructor. For most patterns, the step is trivial,
+/// for instance tuple patterns are flattened and box patterns expand into their inner pattern.
+///
+/// OTOH, slice patterns with a subslice pattern (..tail) can be expanded into multiple
+/// different patterns.
+/// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing
+/// fields filled with wild patterns.
 fn specialize(cx: &MatchCheckCtxt, r: &[Gc<Pat>],
-              ctor_id: &ctor, arity: uint) -> Option<Vec<Gc<Pat>>> {
+              constructor: &Constructor, arity: uint) -> Option<Vec<Gc<Pat>>> {
     let &Pat {
-        id: ref pat_id, node: ref n, span: ref pat_span
+        id: pat_id, node: ref node, span: pat_span
     } = &(*raw_pat(r[0]));
-    let head: Option<Vec<Gc<Pat>>> = match n {
-        &PatWild => {
-            Some(Vec::from_elem(arity, wild()))
-        }
-        &PatWildMulti => {
-            Some(Vec::from_elem(arity, wild()))
-        }
+    let head: Option<Vec<Gc<Pat>>> = match node {
+        &PatWild =>
+            Some(Vec::from_elem(arity, wild())),
+
+        &PatWildMulti =>
+            Some(Vec::from_elem(arity, wild())),
+
         &PatIdent(_, _, _) => {
-            let opt_def = cx.tcx.def_map.borrow().find_copy(pat_id);
+            let opt_def = cx.tcx.def_map.borrow().find_copy(&pat_id);
             match opt_def {
-                Some(DefVariant(_, id, _)) => if *ctor_id == variant(id) {
+                Some(DefVariant(_, id, _)) => if *constructor == Variant(id) {
                     Some(vec!())
                 } else {
                     None
@@ -561,11 +651,11 @@ fn specialize(cx: &MatchCheckCtxt, r: &[Gc<Pat>],
                 Some(DefStatic(did, _)) => {
                     let const_expr = lookup_const_by_id(cx.tcx, did).unwrap();
                     let e_v = eval_const_expr(cx.tcx, &*const_expr);
-                    match range_covered_by_constructor(ctor_id, &e_v, &e_v) {
+                    match range_covered_by_constructor(constructor, &e_v, &e_v) {
                         Some(true) => Some(vec!()),
                         Some(false) => None,
                         None => {
-                            cx.tcx.sess.span_err(*pat_span, "mismatched types between arms");
+                            cx.tcx.sess.span_err(pat_span, "mismatched types between arms");
                             None
                         }
                     }
@@ -575,22 +665,23 @@ fn specialize(cx: &MatchCheckCtxt, r: &[Gc<Pat>],
                 }
             }
         }
+
         &PatEnum(_, ref args) => {
-            let def = cx.tcx.def_map.borrow().get_copy(pat_id);
+            let def = cx.tcx.def_map.borrow().get_copy(&pat_id);
             match def {
                 DefStatic(did, _) => {
                     let const_expr = lookup_const_by_id(cx.tcx, did).unwrap();
                     let e_v = eval_const_expr(cx.tcx, &*const_expr);
-                    match range_covered_by_constructor(ctor_id, &e_v, &e_v) {
+                    match range_covered_by_constructor(constructor, &e_v, &e_v) {
                         Some(true) => Some(vec!()),
                         Some(false) => None,
                         None => {
-                            cx.tcx.sess.span_err(*pat_span, "mismatched types between arms");
+                            cx.tcx.sess.span_err(pat_span, "mismatched types between arms");
                             None
                         }
                     }
                 }
-                DefVariant(_, id, _) if *ctor_id != variant(id) => None,
+                DefVariant(_, id, _) if *constructor != Variant(id) => None,
                 DefVariant(..) | DefFn(..) | DefStruct(..) => {
                     Some(match args {
                         &Some(ref args) => args.clone(),
@@ -603,9 +694,9 @@ fn specialize(cx: &MatchCheckCtxt, r: &[Gc<Pat>],
 
         &PatStruct(_, ref pattern_fields, _) => {
             // Is this a struct or an enum variant?
-            let def = cx.tcx.def_map.borrow().get_copy(pat_id);
+            let def = cx.tcx.def_map.borrow().get_copy(&pat_id);
             let class_id = match def {
-                DefVariant(_, variant_id, _) => if *ctor_id == variant(variant_id) {
+                DefVariant(_, variant_id, _) => if *constructor == Variant(variant_id) {
                     Some(variant_id)
                 } else {
                     None
@@ -633,11 +724,11 @@ fn specialize(cx: &MatchCheckCtxt, r: &[Gc<Pat>],
 
         &PatLit(ref expr) => {
             let expr_value = eval_const_expr(cx.tcx, &**expr);
-            match range_covered_by_constructor(ctor_id, &expr_value, &expr_value) {
+            match range_covered_by_constructor(constructor, &expr_value, &expr_value) {
                 Some(true) => Some(vec!()),
                 Some(false) => None,
                 None => {
-                    cx.tcx.sess.span_err(*pat_span, "mismatched types between arms");
+                    cx.tcx.sess.span_err(pat_span, "mismatched types between arms");
                     None
                 }
             }
@@ -646,41 +737,42 @@ fn specialize(cx: &MatchCheckCtxt, r: &[Gc<Pat>],
         &PatRange(ref from, ref to) => {
             let from_value = eval_const_expr(cx.tcx, &**from);
             let to_value = eval_const_expr(cx.tcx, &**to);
-            match range_covered_by_constructor(ctor_id, &from_value, &to_value) {
+            match range_covered_by_constructor(constructor, &from_value, &to_value) {
                 Some(true) => Some(vec!()),
                 Some(false) => None,
                 None => {
-                    cx.tcx.sess.span_err(*pat_span, "mismatched types between arms");
+                    cx.tcx.sess.span_err(pat_span, "mismatched types between arms");
                     None
                 }
             }
         }
 
         &PatVec(ref before, ref slice, ref after) => {
-            match *ctor_id {
-                vec(_) => {
-                    let num_elements = before.len() + after.len();
-                    if num_elements < arity && slice.is_some() {
-                        let mut result = Vec::new();
-                        result.push_all(before.as_slice());
-                        result.grow_fn(arity - num_elements, |_| wild());
-                        result.push_all(after.as_slice());
-                        Some(result)
-                    } else if num_elements == arity {
-                        let mut result = Vec::new();
-                        result.push_all(before.as_slice());
-                        result.push_all(after.as_slice());
-                        Some(result)
-                    } else {
-                        None
-                    }
-                }
+            match *constructor {
+                // Fixed-length vectors.
+                Single => {
+                    let mut pats = before.clone();
+                    pats.grow_fn(arity - before.len() - after.len(), |_| wild());
+                    pats.push_all(after.as_slice());
+                    Some(pats)
+                },
+                Slice(length) if before.len() + after.len() <= length && slice.is_some() => {
+                    let mut pats = before.clone();
+                    pats.grow_fn(arity - before.len() - after.len(), |_| wild());
+                    pats.push_all(after.as_slice());
+                    Some(pats)
+                },
+                Slice(length) if before.len() + after.len() == length => {
+                    let mut pats = before.clone();
+                    pats.push_all(after.as_slice());
+                    Some(pats)
+                },
                 _ => None
             }
         }
 
         &PatMac(_) => {
-            cx.tcx.sess.span_err(*pat_span, "unexpanded macro");
+            cx.tcx.sess.span_err(pat_span, "unexpanded macro");
             None
         }
     };
@@ -740,7 +832,7 @@ fn check_fn(cx: &mut MatchCheckCtxt,
 }
 
 fn is_refutable(cx: &MatchCheckCtxt, pat: Gc<Pat>) -> Option<Gc<Pat>> {
-    let pats = vec!(vec!(pat));
+    let pats = Matrix(vec!(vec!(pat)));
     is_useful(cx, &pats, [wild()], ConstructWitness)
         .useful()
         .map(|pats| {
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index 3e72341ebb0..1e06b3b1fd4 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -19,13 +19,14 @@ use middle::def;
 use middle::freevars;
 use middle::pat_util;
 use middle::ty;
-use middle::typeck::MethodCall;
+use middle::typeck::{MethodCall, MethodObject, MethodOrigin, MethodParam};
+use middle::typeck::{MethodStatic};
 use middle::typeck;
-use syntax::ast;
-use syntax::codemap::{Span};
 use util::ppaux::Repr;
 
 use std::gc::Gc;
+use syntax::ast;
+use syntax::codemap::Span;
 
 ///////////////////////////////////////////////////////////////////////////
 // The Delegate trait
@@ -101,6 +102,74 @@ pub enum MutateMode {
     WriteAndRead, // x += y
 }
 
+enum OverloadedCallType {
+    FnOverloadedCall,
+    FnMutOverloadedCall,
+    FnOnceOverloadedCall,
+}
+
+impl OverloadedCallType {
+    fn from_trait_id(tcx: &ty::ctxt, trait_id: ast::DefId)
+                     -> OverloadedCallType {
+        for &(maybe_function_trait, overloaded_call_type) in [
+            (tcx.lang_items.fn_once_trait(), FnOnceOverloadedCall),
+            (tcx.lang_items.fn_mut_trait(), FnMutOverloadedCall),
+            (tcx.lang_items.fn_trait(), FnOverloadedCall)
+        ].iter() {
+            match maybe_function_trait {
+                Some(function_trait) if function_trait == trait_id => {
+                    return overloaded_call_type
+                }
+                _ => continue,
+            }
+        }
+
+        tcx.sess.bug("overloaded call didn't map to known function trait")
+    }
+
+    fn from_method_id(tcx: &ty::ctxt, method_id: ast::DefId)
+                      -> OverloadedCallType {
+        let method_descriptor =
+            match tcx.methods.borrow_mut().find(&method_id) {
+                None => {
+                    tcx.sess.bug("overloaded call method wasn't in method \
+                                  map")
+                }
+                Some(ref method_descriptor) => (*method_descriptor).clone(),
+            };
+        let impl_id = match method_descriptor.container {
+            ty::TraitContainer(_) => {
+                tcx.sess.bug("statically resolved overloaded call method \
+                              belonged to a trait?!")
+            }
+            ty::ImplContainer(impl_id) => impl_id,
+        };
+        let trait_ref = match ty::impl_trait_ref(tcx, impl_id) {
+            None => {
+                tcx.sess.bug("statically resolved overloaded call impl \
+                              didn't implement a trait?!")
+            }
+            Some(ref trait_ref) => (*trait_ref).clone(),
+        };
+        OverloadedCallType::from_trait_id(tcx, trait_ref.def_id)
+    }
+
+    fn from_method_origin(tcx: &ty::ctxt, origin: &MethodOrigin)
+                          -> OverloadedCallType {
+        match *origin {
+            MethodStatic(def_id) => {
+                OverloadedCallType::from_method_id(tcx, def_id)
+            }
+            MethodParam(ref method_param) => {
+                OverloadedCallType::from_trait_id(tcx, method_param.trait_id)
+            }
+            MethodObject(ref method_object) => {
+                OverloadedCallType::from_trait_id(tcx, method_object.trait_id)
+            }
+        }
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // The ExprUseVisitor type
 //
@@ -413,19 +482,37 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> {
                 }
             }
             _ => {
-                match self.tcx()
-                          .method_map
-                          .borrow()
-                          .find(&MethodCall::expr(call.id)) {
-                    Some(_) => {
-                        // FIXME(#14774, pcwalton): Implement this.
+                let overloaded_call_type =
+                    match self.tcx()
+                              .method_map
+                              .borrow()
+                              .find(&MethodCall::expr(call.id)) {
+                    Some(ref method_callee) => {
+                        OverloadedCallType::from_method_origin(
+                            self.tcx(),
+                            &method_callee.origin)
                     }
                     None => {
                         self.tcx().sess.span_bug(
                             callee.span,
                             format!("unexpected callee type {}",
-                                    callee_ty.repr(self.tcx())).as_slice());
+                                    callee_ty.repr(self.tcx())).as_slice())
+                    }
+                };
+                match overloaded_call_type {
+                    FnMutOverloadedCall => {
+                        self.borrow_expr(callee,
+                                         ty::ReScope(call.id),
+                                         ty::MutBorrow,
+                                         ClosureInvocation);
+                    }
+                    FnOverloadedCall => {
+                        self.borrow_expr(callee,
+                                         ty::ReScope(call.id),
+                                         ty::ImmBorrow,
+                                         ClosureInvocation);
                     }
+                    FnOnceOverloadedCall => self.consume_expr(callee),
                 }
             }
         }
diff --git a/src/librustc/middle/pat_util.rs b/src/librustc/middle/pat_util.rs
index 44ed0192d1d..24d97f5aac3 100644
--- a/src/librustc/middle/pat_util.rs
+++ b/src/librustc/middle/pat_util.rs
@@ -12,9 +12,10 @@ use middle::def::*;
 use middle::resolve;
 
 use std::collections::HashMap;
+use std::gc::{Gc, GC};
 use syntax::ast::*;
 use syntax::ast_util::{path_to_ident, walk_pat};
-use syntax::codemap::Span;
+use syntax::codemap::{Span, DUMMY_SP};
 
 pub type PatIdMap = HashMap<Ident, NodeId>;
 
@@ -111,3 +112,7 @@ pub fn simple_identifier<'a>(pat: &'a Pat) -> Option<&'a Path> {
         }
     }
 }
+
+pub fn wild() -> Gc<Pat> {
+    box (GC) Pat { id: 0, node: PatWild, span: DUMMY_SP }
+}
diff --git a/src/test/compile-fail/borrowck-overloaded-call.rs b/src/test/compile-fail/borrowck-overloaded-call.rs
new file mode 100644
index 00000000000..349a20313fa
--- /dev/null
+++ b/src/test/compile-fail/borrowck-overloaded-call.rs
@@ -0,0 +1,74 @@
+// Copyright 2012 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.
+
+#![feature(overloaded_calls)]
+
+use std::ops::{Fn, FnMut, FnOnce};
+
+struct SFn {
+    x: int,
+    y: int,
+}
+
+impl Fn<(int,),int> for SFn {
+    fn call(&self, (z,): (int,)) -> int {
+        self.x * self.y * z
+    }
+}
+
+struct SFnMut {
+    x: int,
+    y: int,
+}
+
+impl FnMut<(int,),int> for SFnMut {
+    fn call_mut(&mut self, (z,): (int,)) -> int {
+        self.x * self.y * z
+    }
+}
+
+struct SFnOnce {
+    x: String,
+}
+
+impl FnOnce<(String,),uint> for SFnOnce {
+    fn call_once(self, (z,): (String,)) -> uint {
+        self.x.len() + z.len()
+    }
+}
+
+fn f() {
+    let mut s = SFn {
+        x: 1,
+        y: 2,
+    };
+    let sp = &mut s;
+    s(3);   //~ ERROR cannot borrow `s` as immutable because it is also borrowed as mutable
+    //~^ ERROR cannot borrow `s` as immutable because it is also borrowed as mutable
+}
+
+fn g() {
+    let s = SFnMut {
+        x: 1,
+        y: 2,
+    };
+    s(3);   //~ ERROR cannot borrow immutable local variable `s` as mutable
+}
+
+fn h() {
+    let s = SFnOnce {
+        x: "hello".to_string(),
+    };
+    s(" world".to_string());
+    s(" world".to_string());    //~ ERROR use of moved value: `s`
+}
+
+fn main() {}
+
diff --git a/src/test/compile-fail/non-exhaustive-match-nested.rs b/src/test/compile-fail/non-exhaustive-match-nested.rs
index 483168bb8bc..439c82a6df0 100644
--- a/src/test/compile-fail/non-exhaustive-match-nested.rs
+++ b/src/test/compile-fail/non-exhaustive-match-nested.rs
@@ -1,4 +1,4 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -11,10 +11,19 @@
 enum t { a(u), b }
 enum u { c, d }
 
+fn match_nested_vecs<'a, T>(l1: Option<&'a [T]>, l2: Result<&'a [T], ()>) -> &'static str {
+    match (l1, l2) { //~ ERROR non-exhaustive patterns: `(Some([]), Err(_))` not covered
+        (Some([]), Ok([])) => "Some(empty), Ok(empty)",
+        (Some([_, ..]), Ok(_)) | (Some([_, ..]), Err(())) => "Some(non-empty), any",
+        (None, Ok([])) | (None, Err(())) | (None, Ok([_])) => "None, Ok(less than one element)",
+        (None, Ok([_, _, ..])) => "None, Ok(at least two elements)"
+    }
+}
+
 fn main() {
-  let x = a(c);
-  match x { //~ ERROR non-exhaustive patterns: `a(c)` not covered
-      a(d) => { fail!("hello"); }
-      b => { fail!("goodbye"); }
+    let x = a(c);
+    match x { //~ ERROR non-exhaustive patterns: `a(c)` not covered
+        a(d) => { fail!("hello"); }
+        b => { fail!("goodbye"); }
     }
 }
diff --git a/src/test/run-pass/issue-15104.rs b/src/test/run-pass/issue-15104.rs
new file mode 100644
index 00000000000..d2711339ccb
--- /dev/null
+++ b/src/test/run-pass/issue-15104.rs
@@ -0,0 +1,21 @@
+// 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() {
+    assert_eq!(count_members(&[1, 2, 3, 4]), 4);
+}
+
+fn count_members(v: &[uint]) -> uint {
+    match v {
+        []         => 0,
+        [_]        => 1,
+        [_x, ..xs] => 1 + count_members(xs)
+    }
+}
diff --git a/src/test/run-pass/match-vec-alternatives.rs b/src/test/run-pass/match-vec-alternatives.rs
new file mode 100644
index 00000000000..ffbc4e85cb6
--- /dev/null
+++ b/src/test/run-pass/match-vec-alternatives.rs
@@ -0,0 +1,82 @@
+// 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 match_vecs<'a, T>(l1: &'a [T], l2: &'a [T]) -> &'static str {
+    match (l1, l2) {
+        ([], []) => "both empty",
+        ([], [..]) | ([..], []) => "one empty",
+        ([..], [..]) => "both non-empty"
+    }
+}
+
+fn match_vecs_cons<'a, T>(l1: &'a [T], l2: &'a [T]) -> &'static str {
+    match (l1, l2) {
+        ([], []) => "both empty",
+        ([], [_, ..]) | ([_, ..], []) => "one empty",
+        ([_, ..], [_, ..]) => "both non-empty"
+    }
+}
+
+fn match_vecs_snoc<'a, T>(l1: &'a [T], l2: &'a [T]) -> &'static str {
+    match (l1, l2) {
+        ([], []) => "both empty",
+        ([], [.., _]) | ([.., _], []) => "one empty",
+        ([.., _], [.., _]) => "both non-empty"
+    }
+}
+
+fn match_nested_vecs_cons<'a, T>(l1: Option<&'a [T]>, l2: Result<&'a [T], ()>) -> &'static str {
+    match (l1, l2) {
+        (Some([]), Ok([])) => "Some(empty), Ok(empty)",
+        (Some([_, ..]), Ok(_)) | (Some([_, ..]), Err(())) => "Some(non-empty), any",
+        (None, Ok([])) | (None, Err(())) | (None, Ok([_])) => "None, Ok(less than one element)",
+        (None, Ok([_, _, ..])) => "None, Ok(at least two elements)",
+        _ => "other"
+    }
+}
+
+fn match_nested_vecs_snoc<'a, T>(l1: Option<&'a [T]>, l2: Result<&'a [T], ()>) -> &'static str {
+    match (l1, l2) {
+        (Some([]), Ok([])) => "Some(empty), Ok(empty)",
+        (Some([.., _]), Ok(_)) | (Some([.., _]), Err(())) => "Some(non-empty), any",
+        (None, Ok([])) | (None, Err(())) | (None, Ok([_])) => "None, Ok(less than one element)",
+        (None, Ok([.., _, _])) => "None, Ok(at least two elements)",
+        _ => "other"
+    }
+}
+
+fn main() {
+    assert_eq!(match_vecs(&[1i, 2], &[2i, 3]), "both non-empty");
+    assert_eq!(match_vecs(&[], &[1i, 2, 3, 4]), "one empty");
+    assert_eq!(match_vecs::<uint>(&[], &[]), "both empty");
+    assert_eq!(match_vecs(&[1i, 2, 3], &[]), "one empty");
+
+    assert_eq!(match_vecs_cons(&[1i, 2], &[2i, 3]), "both non-empty");
+    assert_eq!(match_vecs_cons(&[], &[1i, 2, 3, 4]), "one empty");
+    assert_eq!(match_vecs_cons::<uint>(&[], &[]), "both empty");
+    assert_eq!(match_vecs_cons(&[1i, 2, 3], &[]), "one empty");
+
+    assert_eq!(match_vecs_snoc(&[1i, 2], &[2i, 3]), "both non-empty");
+    assert_eq!(match_vecs_snoc(&[], &[1i, 2, 3, 4]), "one empty");
+    assert_eq!(match_vecs_snoc::<uint>(&[], &[]), "both empty");
+    assert_eq!(match_vecs_snoc(&[1i, 2, 3], &[]), "one empty");
+
+    assert_eq!(match_nested_vecs_cons(None, Ok(&[4u, 2u])), "None, Ok(at least two elements)");
+    assert_eq!(match_nested_vecs_cons::<uint>(None, Err(())), "None, Ok(less than one element)");
+    assert_eq!(match_nested_vecs_cons::<bool>(Some(&[]), Ok(&[])), "Some(empty), Ok(empty)");
+    assert_eq!(match_nested_vecs_cons(Some(&[1i]), Err(())), "Some(non-empty), any");
+    assert_eq!(match_nested_vecs_cons(Some(&[(42i, ())]), Ok(&[(1i, ())])), "Some(non-empty), any");
+
+    assert_eq!(match_nested_vecs_snoc(None, Ok(&[4u, 2u])), "None, Ok(at least two elements)");
+    assert_eq!(match_nested_vecs_snoc::<uint>(None, Err(())), "None, Ok(less than one element)");
+    assert_eq!(match_nested_vecs_snoc::<bool>(Some(&[]), Ok(&[])), "Some(empty), Ok(empty)");
+    assert_eq!(match_nested_vecs_snoc(Some(&[1i]), Err(())), "Some(non-empty), any");
+    assert_eq!(match_nested_vecs_snoc(Some(&[(42i, ())]), Ok(&[(1i, ())])), "Some(non-empty), any");
+}