diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs
index 7f0066a1aa2..7d2fd51cefe 100644
--- a/src/librustc/middle/typeck/check/_match.rs
+++ b/src/librustc/middle/typeck/check/_match.rs
@@ -175,11 +175,18 @@ pub fn check_pat_variant(pcx: &pat_ctxt, pat: @ast::pat, path: @ast::Path,
             kind_name = "structure";
         }
         _ => {
-            tcx.sess.span_fatal(
-                pat.span,
-                fmt!("mismatched types: expected `%s` but found enum or \
-                      structure",
-                     fcx.infcx().ty_to_str(expected)));
+            let resolved_expected =
+                fcx.infcx().ty_to_str(fcx.infcx().resolve_type_vars_if_possible(expected));
+            fcx.infcx().type_error_message_str(pat.span,
+                                               |actual| {
+                    fmt!("mismatched types: expected `%s` but found %s",
+                         resolved_expected, actual)},
+                    ~"an enum or structure pattern",
+                    None);
+            fcx.write_error(pat.id);
+            kind_name = "[error]";
+            arg_types = (copy subpats).get_or_default(~[]).map(|_|
+                                                               ty::mk_err());
         }
     }
 
@@ -486,74 +493,44 @@ pub fn check_pat(pcx: &pat_ctxt, pat: @ast::pat, expected: ty::t) {
       }
       ast::pat_tup(ref elts) => {
         let s = structure_of(fcx, pat.span, expected);
-        let ex_elts = match s {
-          ty::ty_tup(ref elts) => elts,
-          _ => {
-            tcx.sess.span_fatal
-                (pat.span,
-                 fmt!("mismatched types: expected `%s`, found tuple",
-                      fcx.infcx().ty_to_str(expected)));
-          }
-        };
         let e_count = elts.len();
-        if e_count != ex_elts.len() {
-            tcx.sess.span_fatal
-                (pat.span, fmt!("mismatched types: expected a tuple \
-                      with %u fields, found one with %u \
-                      fields", ex_elts.len(), e_count));
+        match s {
+            ty::ty_tup(ref ex_elts) if e_count == ex_elts.len() => {
+                for elts.eachi |i, elt| {
+                    check_pat(pcx, *elt, ex_elts[i]);
+                }
+                fcx.write_ty(pat.id, expected);
+            }
+            _ => {
+                for elts.each |elt| {
+                    check_pat(pcx, *elt, ty::mk_err());
+                }
+                let actual = ty::mk_tup(tcx, elts.map(|pat_var| {
+                    fcx.node_ty(pat_var.id)
+                }));
+                // use terr_tuple_size if both types are tuples
+                let type_error = match s {
+                    ty::ty_tup(ref ex_elts) =>
+                        ty::terr_tuple_size(ty::expected_found{expected: ex_elts.len(),
+                                                           found: e_count}),
+                    _ => ty::terr_mismatch
+                };
+                fcx.infcx().report_mismatched_types(pat.span,
+                                                    expected,
+                                                    actual,
+                                                    &type_error);
+                fcx.write_error(pat.id);
+            }
         }
-        let mut i = 0u;
-        for elts.each |elt| {
-            check_pat(pcx, *elt, ex_elts[i]);
-            i += 1u;
-        }
-
-        fcx.write_ty(pat.id, expected);
       }
       ast::pat_box(inner) => {
-        match structure_of(fcx, pat.span, expected) {
-          ty::ty_box(e_inner) => {
-            check_pat(pcx, inner, e_inner.ty);
-            fcx.write_ty(pat.id, expected);
-          }
-          _ => {
-            tcx.sess.span_fatal(
-                pat.span,
-                ~"mismatched types: expected `" +
-                fcx.infcx().ty_to_str(expected) +
-                ~"` found box");
-          }
-        }
+          check_pointer_pat(pcx, At, inner, pat.id, pat.span, expected);
       }
       ast::pat_uniq(inner) => {
-        match structure_of(fcx, pat.span, expected) {
-          ty::ty_uniq(e_inner) => {
-            check_pat(pcx, inner, e_inner.ty);
-            fcx.write_ty(pat.id, expected);
-          }
-          _ => {
-            tcx.sess.span_fatal(
-                pat.span,
-                ~"mismatched types: expected `" +
-                fcx.infcx().ty_to_str(expected) +
-                ~"` found uniq");
-          }
-        }
+          check_pointer_pat(pcx, Uniq, inner, pat.id, pat.span, expected);
       }
       ast::pat_region(inner) => {
-        match structure_of(fcx, pat.span, expected) {
-          ty::ty_rptr(_, e_inner) => {
-            check_pat(pcx, inner, e_inner.ty);
-            fcx.write_ty(pat.id, expected);
-          }
-          _ => {
-            tcx.sess.span_fatal(
-                pat.span,
-                ~"mismatched types: expected `" +
-                fcx.infcx().ty_to_str(expected) +
-                ~"` found borrowed pointer");
-          }
-        }
+          check_pointer_pat(pcx, Borrowed, inner, pat.id, pat.span, expected);
       }
       ast::pat_vec(ref before, slice, ref after) => {
         let default_region_var =
@@ -577,11 +554,25 @@ pub fn check_pat(pcx: &pat_ctxt, pat: @ast::pat, expected: ty::t) {
             (mt, default_region_var)
           },
           _ => {
-            tcx.sess.span_fatal(
-                pat.span,
-                fmt!("mismatched type: expected `%s` but found vector",
-                     fcx.infcx().ty_to_str(expected))
-            );
+              for before.each |&elt| {
+                  check_pat(pcx, elt, ty::mk_err());
+              }
+              for slice.each |&elt| {
+                  check_pat(pcx, elt, ty::mk_err());
+              }
+              for after.each |&elt| {
+                  check_pat(pcx, elt, ty::mk_err());
+              }
+              let resolved_expected =
+                  fcx.infcx().ty_to_str(fcx.infcx().resolve_type_vars_if_possible(expected));
+              fcx.infcx().type_error_message_str(pat.span,
+                  |actual| {
+                      fmt!("mismatched types: expected `%s` but found %s",
+                           resolved_expected, actual)},
+                                                 ~"a vector pattern",
+                                                 None);
+              fcx.write_error(pat.id);
+              return;
           }
         };
         for before.each |elt| {
@@ -605,3 +596,46 @@ pub fn check_pat(pcx: &pat_ctxt, pat: @ast::pat, expected: ty::t) {
     }
 }
 
+// Helper function to check @, ~ and & patterns
+pub fn check_pointer_pat(pcx: &pat_ctxt,
+                         pointer_kind: PointerKind,
+                         inner: @ast::pat,
+                         pat_id: ast::node_id,
+                         span: span,
+                         expected: ty::t) {
+    let fcx = pcx.fcx;
+    let check_inner: &fn(ty::mt) = |e_inner| {
+        check_pat(pcx, inner, e_inner.ty);
+        fcx.write_ty(pat_id, expected);
+    };
+    match structure_of(fcx, span, expected) {
+        ty::ty_box(e_inner) if pointer_kind == At => {
+            check_inner(e_inner);
+        }
+        ty::ty_uniq(e_inner) if pointer_kind == Uniq => {
+            check_inner(e_inner);
+        }
+        ty::ty_rptr(_, e_inner) if pointer_kind == Borrowed => {
+            check_inner(e_inner);
+        }
+        _ => {
+            check_pat(pcx, inner, ty::mk_err());
+            let resolved_expected =
+                fcx.infcx().ty_to_str(fcx.infcx().resolve_type_vars_if_possible(expected));
+            fcx.infcx().type_error_message_str(span, |actual| {
+                    fmt!("mismatched types: expected `%s` but found %s",
+                         resolved_expected, actual)},
+                                               fmt!("an %s pattern", match pointer_kind {
+                                                   At => "@-box",
+                                                   Uniq => "~-box",
+                                                   Borrowed => "&-pointer"
+                                               }),
+                    None);
+            fcx.write_error(pat_id);
+          }
+    }
+}
+
+#[deriving(Eq)]
+enum PointerKind { At, Uniq, Borrowed }
+
diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs
index 7b5a93d4cad..8f709e7cd5a 100644
--- a/src/librustc/middle/typeck/infer/mod.rs
+++ b/src/librustc/middle/typeck/infer/mod.rs
@@ -749,6 +749,19 @@ pub impl InferCtxt {
         }
     }
 
+
+    fn type_error_message_str(@mut self, sp: span, mk_msg: &fn(~str) -> ~str,
+                          actual_ty: ~str, err: Option<&ty::type_err>) {
+        let error_str = err.map_default(~"", |t_err|
+                         fmt!(" (%s)",
+                              ty::type_err_to_str(self.tcx, *t_err)));
+        self.tcx.sess.span_err(sp,
+           fmt!("%s%s", mk_msg(actual_ty), error_str));
+        for err.each |err| {
+            ty::note_and_explain_type_err(self.tcx, *err)
+        }
+    }
+
     fn type_error_message(@mut self, sp: span, mk_msg: &fn(~str) -> ~str,
                           actual_ty: ty::t, err: Option<&ty::type_err>) {
         let actual_ty = self.resolve_type_vars_if_possible(actual_ty);
@@ -757,15 +770,9 @@ pub impl InferCtxt {
         if ty::type_is_error(actual_ty) {
             return;
         }
-        let error_str = err.map_default(~"", |t_err|
-                         fmt!(" (%s)",
-                              ty::type_err_to_str(self.tcx, *t_err)));
-        self.tcx.sess.span_err(sp,
-           fmt!("%s%s", mk_msg(self.ty_to_str(actual_ty)),
-                error_str));
-        for err.each |err| {
-            ty::note_and_explain_type_err(self.tcx, *err)
-        }
+
+        self.type_error_message_str(sp, mk_msg, self.ty_to_str(actual_ty),
+                                    err);
     }
 
     fn report_mismatched_types(@mut self, sp: span, e: ty::t, a: ty::t,
diff --git a/src/test/compile-fail/issue-5100.rs b/src/test/compile-fail/issue-5100.rs
new file mode 100644
index 00000000000..dbfdb38f721
--- /dev/null
+++ b/src/test/compile-fail/issue-5100.rs
@@ -0,0 +1,44 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+enum A { B, C }
+
+fn main() {
+    match (true, false) {
+        B => (), //~ ERROR expected `(bool,bool)` but found an enum or structure pattern
+        _ => ()
+    }
+
+    match (true, false) {
+        (true, false, false) => () //~ ERROR mismatched types: expected `(bool,bool)` but found `(bool,bool,bool)` (expected a tuple with 2 elements but found one with 3 elements)
+    }
+
+    match (true, false) {
+        @(true, false) => () //~ ERROR mismatched types: expected `(bool,bool)` but found an @-box pattern
+    }
+
+    match (true, false) {
+        ~(true, false) => () //~ ERROR mismatched types: expected `(bool,bool)` but found a ~-box pattern
+    }
+
+    match (true, false) {
+        &(true, false) => () //~ ERROR mismatched types: expected `(bool,bool)` but found an &-pointer pattern
+    }
+
+
+    let v = [('a', 'b')   //~ ERROR expected function but found `(char,char)`
+             ('c', 'd'),
+             ('e', 'f')];
+
+    for v.each |&(x,y)| {} // should be OK
+
+    // Make sure none of the errors above were fatal
+    let x: char = true; //~ ERROR expected `char` but found `bool`
+}