about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>2016-03-06 15:54:44 +0300
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>2016-05-26 11:11:58 +0300
commitd69aeaf662c637b454e8c7a5ddbd69b4978ec211 (patch)
tree45d5b4b43bac7f04ea48136ffb02048278dcfb3a
parentd5759a3417fa395d439f4283825504dd4f78dc87 (diff)
downloadrust-d69aeaf662c637b454e8c7a5ddbd69b4978ec211.tar.gz
rust-d69aeaf662c637b454e8c7a5ddbd69b4978ec211.zip
Implement `..` in tuple (struct) patterns
-rw-r--r--src/doc/reference.md2
-rw-r--r--src/librustc/cfg/construct.rs5
-rw-r--r--src/librustc/hir/fold.rs8
-rw-r--r--src/librustc/hir/intravisit.rs8
-rw-r--r--src/librustc/hir/lowering.rs12
-rw-r--r--src/librustc/hir/mod.rs16
-rw-r--r--src/librustc/hir/pat_util.rs22
-rw-r--r--src/librustc/hir/print.rs41
-rw-r--r--src/librustc/lib.rs2
-rw-r--r--src/librustc/middle/expr_use_visitor.rs2
-rw-r--r--src/librustc/middle/mem_categorization.rs33
-rw-r--r--src/librustc/middle/region.rs4
-rw-r--r--src/librustc/middle/stability.rs9
-rw-r--r--src/librustc_const_eval/check_match.rs33
-rw-r--r--src/librustc_const_eval/eval.rs9
-rw-r--r--src/librustc_const_eval/lib.rs1
-rw-r--r--src/librustc_mir/hair/cx/pattern.rs45
-rw-r--r--src/librustc_privacy/lib.rs13
-rw-r--r--src/librustc_resolve/lib.rs2
-rw-r--r--src/librustc_save_analysis/lib.rs2
-rw-r--r--src/librustc_trans/_match.rs79
-rw-r--r--src/librustc_trans/debuginfo/create_scope_map.rs10
-rw-r--r--src/librustc_typeck/check/_match.rs92
-rw-r--r--src/librustc_typeck/diagnostics.rs25
-rw-r--r--src/librustc_typeck/lib.rs3
-rw-r--r--src/librustdoc/clean/mod.rs4
-rw-r--r--src/libsyntax/ast.rs16
-rw-r--r--src/libsyntax/ext/build.rs4
-rw-r--r--src/libsyntax/feature_gate.rs24
-rw-r--r--src/libsyntax/fold.rs8
-rw-r--r--src/libsyntax/parse/parser.rs65
-rw-r--r--src/libsyntax/print/pprust.rs44
-rw-r--r--src/libsyntax/visit.rs8
-rw-r--r--src/test/compile-fail/issue-32004.rs4
-rw-r--r--src/test/compile-fail/match-pattern-field-mismatch-2.rs2
-rw-r--r--src/test/compile-fail/pat-tuple-bad-type.rs27
-rw-r--r--src/test/compile-fail/pat-tuple-feature-gate.rs17
-rw-r--r--src/test/compile-fail/pat-tuple-overfield.rs28
-rw-r--r--src/test/compile-fail/pattern-error-continue.rs2
-rw-r--r--src/test/parse-fail/pat-lt-bracket-6.rs3
-rw-r--r--src/test/parse-fail/pat-lt-bracket-7.rs3
-rw-r--r--src/test/parse-fail/pat-tuple-1.rs (renamed from src/test/compile-fail/E0024.rs)11
-rw-r--r--src/test/parse-fail/pat-tuple-2.rs17
-rw-r--r--src/test/parse-fail/pat-tuple-3.rs17
-rw-r--r--src/test/parse-fail/pat-tuple-4.rs17
-rw-r--r--src/test/parse-fail/pat-tuple-5.rs17
-rw-r--r--src/test/parse-fail/pat-tuple-6.rs17
-rw-r--r--src/test/run-pass/pat-tuple.rs202
48 files changed, 736 insertions, 299 deletions
diff --git a/src/doc/reference.md b/src/doc/reference.md
index ebb111a2e2e..810138e5a29 100644
--- a/src/doc/reference.md
+++ b/src/doc/reference.md
@@ -2433,6 +2433,8 @@ The currently implemented features of the reference compiler are:
 * - `abi_vectorcall` - Allows the usage of the vectorcall calling convention
                              (e.g. `extern "vectorcall" func fn_();`)
 
+* - `dotdot_in_tuple_patterns` - Allows `..` in tuple (struct) patterns.
+
 If a feature is promoted to a language feature, then all existing programs will
 start to receive compilation warnings about `#![feature]` directives which enabled
 the new feature (because the directive is no longer necessary). However, if a
diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs
index 76699f13959..af47617ea92 100644
--- a/src/librustc/cfg/construct.rs
+++ b/src/librustc/cfg/construct.rs
@@ -100,7 +100,6 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
     fn pat(&mut self, pat: &hir::Pat, pred: CFGIndex) -> CFGIndex {
         match pat.node {
             PatKind::Ident(_, _, None) |
-            PatKind::TupleStruct(_, None) |
             PatKind::Path(..) |
             PatKind::QPath(..) |
             PatKind::Lit(..) |
@@ -116,8 +115,8 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
                 self.add_ast_node(pat.id, &[subpat_exit])
             }
 
-            PatKind::TupleStruct(_, Some(ref subpats)) |
-            PatKind::Tup(ref subpats) => {
+            PatKind::TupleStruct(_, ref subpats, _) |
+            PatKind::Tuple(ref subpats, _) => {
                 let pats_exit = self.pats_all(subpats.iter(), pred);
                 self.add_ast_node(pat.id, &[pats_exit])
             }
diff --git a/src/librustc/hir/fold.rs b/src/librustc/hir/fold.rs
index a91d16f25a2..9cba790c54b 100644
--- a/src/librustc/hir/fold.rs
+++ b/src/librustc/hir/fold.rs
@@ -923,9 +923,9 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
                              sub.map(|x| folder.fold_pat(x)))
                 }
                 PatKind::Lit(e) => PatKind::Lit(folder.fold_expr(e)),
-                PatKind::TupleStruct(pth, pats) => {
+                PatKind::TupleStruct(pth, pats, ddpos) => {
                     PatKind::TupleStruct(folder.fold_path(pth),
-                            pats.map(|pats| pats.move_map(|x| folder.fold_pat(x))))
+                            pats.move_map(|x| folder.fold_pat(x)), ddpos)
                 }
                 PatKind::Path(pth) => {
                     PatKind::Path(folder.fold_path(pth))
@@ -948,7 +948,9 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
                     });
                     PatKind::Struct(pth, fs, etc)
                 }
-                PatKind::Tup(elts) => PatKind::Tup(elts.move_map(|x| folder.fold_pat(x))),
+                PatKind::Tuple(elts, ddpos) => {
+                    PatKind::Tuple(elts.move_map(|x| folder.fold_pat(x)), ddpos)
+                }
                 PatKind::Box(inner) => PatKind::Box(folder.fold_pat(inner)),
                 PatKind::Ref(inner, mutbl) => PatKind::Ref(folder.fold_pat(inner), mutbl),
                 PatKind::Range(e1, e2) => {
diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs
index 2e9e433b830..69e38c9646c 100644
--- a/src/librustc/hir/intravisit.rs
+++ b/src/librustc/hir/intravisit.rs
@@ -454,11 +454,9 @@ pub fn walk_assoc_type_binding<'v, V: Visitor<'v>>(visitor: &mut V,
 
 pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
     match pattern.node {
-        PatKind::TupleStruct(ref path, ref opt_children) => {
+        PatKind::TupleStruct(ref path, ref children, _) => {
             visitor.visit_path(path, pattern.id);
-            if let Some(ref children) = *opt_children {
-                walk_list!(visitor, visit_pat, children);
-            }
+            walk_list!(visitor, visit_pat, children);
         }
         PatKind::Path(ref path) => {
             visitor.visit_path(path, pattern.id);
@@ -474,7 +472,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
                 visitor.visit_pat(&field.node.pat)
             }
         }
-        PatKind::Tup(ref tuple_elements) => {
+        PatKind::Tuple(ref tuple_elements, _) => {
             walk_list!(visitor, visit_pat, tuple_elements);
         }
         PatKind::Box(ref subpattern) |
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 28506fd20fe..71153a459bd 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -872,10 +872,10 @@ impl<'a> LoweringContext<'a> {
                     })
                 }
                 PatKind::Lit(ref e) => hir::PatKind::Lit(self.lower_expr(e)),
-                PatKind::TupleStruct(ref pth, ref pats) => {
+                PatKind::TupleStruct(ref pth, ref pats, ddpos) => {
                     hir::PatKind::TupleStruct(self.lower_path(pth),
-                                 pats.as_ref()
-                                     .map(|pats| pats.iter().map(|x| self.lower_pat(x)).collect()))
+                                              pats.iter().map(|x| self.lower_pat(x)).collect(),
+                                              ddpos)
                 }
                 PatKind::Path(ref pth) => {
                     hir::PatKind::Path(self.lower_path(pth))
@@ -903,8 +903,8 @@ impl<'a> LoweringContext<'a> {
                                    .collect();
                     hir::PatKind::Struct(pth, fs, etc)
                 }
-                PatKind::Tup(ref elts) => {
-                    hir::PatKind::Tup(elts.iter().map(|x| self.lower_pat(x)).collect())
+                PatKind::Tuple(ref elts, ddpos) => {
+                    hir::PatKind::Tuple(elts.iter().map(|x| self.lower_pat(x)).collect(), ddpos)
                 }
                 PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
                 PatKind::Ref(ref inner, mutbl) => {
@@ -1857,7 +1857,7 @@ impl<'a> LoweringContext<'a> {
         let pt = if subpats.is_empty() {
             hir::PatKind::Path(path)
         } else {
-            hir::PatKind::TupleStruct(path, Some(subpats))
+            hir::PatKind::TupleStruct(path, subpats, None)
         };
         let pat = self.pat(span, pt);
         self.resolver.record_resolution(pat.id, def);
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index 39a6ec9f3af..961885d1b86 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -470,7 +470,7 @@ impl Pat {
             PatKind::Struct(_, ref fields, _) => {
                 fields.iter().all(|field| field.node.pat.walk_(it))
             }
-            PatKind::TupleStruct(_, Some(ref s)) | PatKind::Tup(ref s) => {
+            PatKind::TupleStruct(_, ref s, _) | PatKind::Tuple(ref s, _) => {
                 s.iter().all(|p| p.walk_(it))
             }
             PatKind::Box(ref s) | PatKind::Ref(ref s, _) => {
@@ -485,7 +485,6 @@ impl Pat {
             PatKind::Lit(_) |
             PatKind::Range(_, _) |
             PatKind::Ident(_, _, _) |
-            PatKind::TupleStruct(..) |
             PatKind::Path(..) |
             PatKind::QPath(_, _) => {
                 true
@@ -539,9 +538,10 @@ pub enum PatKind {
     /// The `bool` is `true` in the presence of a `..`.
     Struct(Path, HirVec<Spanned<FieldPat>>, bool),
 
-    /// A tuple struct/variant pattern `Variant(x, y, z)`.
-    /// "None" means a `Variant(..)` pattern where we don't bind the fields to names.
-    TupleStruct(Path, Option<HirVec<P<Pat>>>),
+    /// A tuple struct/variant pattern `Variant(x, y, .., z)`.
+    /// If the `..` pattern fragment presents, then `Option<usize>` denotes its position.
+    /// 0 <= position <= subpats.len()
+    TupleStruct(Path, HirVec<P<Pat>>, Option<usize>),
 
     /// A path pattern.
     /// Such pattern can be resolved to a unit struct/variant or a constant.
@@ -553,8 +553,10 @@ pub enum PatKind {
     /// PatKind::Path, and the resolver will have to sort that out.
     QPath(QSelf, Path),
 
-    /// A tuple pattern `(a, b)`
-    Tup(HirVec<P<Pat>>),
+    /// A tuple pattern `(a, b)`.
+    /// If the `..` pattern fragment presents, then `Option<usize>` denotes its position.
+    /// 0 <= position <= subpats.len()
+    Tuple(HirVec<P<Pat>>, Option<usize>),
     /// A `box` pattern
     Box(P<Pat>),
     /// A reference pattern, e.g. `&mut (a, b)`
diff --git a/src/librustc/hir/pat_util.rs b/src/librustc/hir/pat_util.rs
index 15f2310607f..f41c4b0840d 100644
--- a/src/librustc/hir/pat_util.rs
+++ b/src/librustc/hir/pat_util.rs
@@ -21,6 +21,28 @@ use std::cell::RefCell;
 
 pub type PatIdMap = FnvHashMap<ast::Name, ast::NodeId>;
 
+#[derive(Clone, Copy)]
+pub struct AjustPos {
+    gap_pos: usize,
+    gap_len: usize,
+}
+
+impl FnOnce<(usize,)> for AjustPos {
+    type Output = usize;
+    extern "rust-call" fn call_once(self, (i,): (usize,)) -> usize {
+        if i < self.gap_pos { i } else { i + self.gap_len }
+    }
+}
+
+// Returns a functional object used to adjust tuple pattern indexes. Example: for 5-tuple and
+// pattern (a, b, .., c) expected_len is 5, actual_len is 3 and gap_pos is Some(2).
+pub fn pat_adjust_pos(expected_len: usize, actual_len: usize, gap_pos: Option<usize>) -> AjustPos {
+    AjustPos {
+        gap_pos: if let Some(gap_pos) = gap_pos { gap_pos } else { expected_len },
+        gap_len: expected_len - actual_len,
+    }
+}
+
 // This is used because same-named variables in alternative patterns need to
 // use the NodeId of their namesake in the first pattern.
 pub fn pat_id_map(dm: &RefCell<DefMap>, pat: &hir::Pat) -> PatIdMap {
diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs
index 4455c7da3ba..d105a72b986 100644
--- a/src/librustc/hir/print.rs
+++ b/src/librustc/hir/print.rs
@@ -1736,16 +1736,23 @@ impl<'a> State<'a> {
                     None => (),
                 }
             }
-            PatKind::TupleStruct(ref path, ref args_) => {
+            PatKind::TupleStruct(ref path, ref elts, ddpos) => {
                 self.print_path(path, true, 0)?;
-                match *args_ {
-                    None => word(&mut self.s, "(..)")?,
-                    Some(ref args) => {
-                        self.popen()?;
-                        self.commasep(Inconsistent, &args[..], |s, p| s.print_pat(&p))?;
-                        self.pclose()?;
+                self.popen()?;
+                if let Some(ddpos) = ddpos {
+                    self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p))?;
+                    if ddpos != 0 {
+                        self.word_space(",")?;
+                    }
+                    word(&mut self.s, "..")?;
+                    if ddpos != elts.len() {
+                        word(&mut self.s, ",")?;
+                        self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p))?;
                     }
+                } else {
+                    try!(self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p)));
                 }
+                try!(self.pclose());
             }
             PatKind::Path(ref path) => {
                 self.print_path(path, true, 0)?;
@@ -1778,11 +1785,23 @@ impl<'a> State<'a> {
                 space(&mut self.s)?;
                 word(&mut self.s, "}")?;
             }
-            PatKind::Tup(ref elts) => {
+            PatKind::Tuple(ref elts, ddpos) => {
                 self.popen()?;
-                self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p))?;
-                if elts.len() == 1 {
-                    word(&mut self.s, ",")?;
+                if let Some(ddpos) = ddpos {
+                    self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p))?;
+                    if ddpos != 0 {
+                        self.word_space(",")?;
+                    }
+                    word(&mut self.s, "..")?;
+                    if ddpos != elts.len() {
+                        word(&mut self.s, ",")?;
+                        self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p))?;
+                    }
+                } else {
+                    self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p))?;
+                    if elts.len() == 1 {
+                        word(&mut self.s, ",")?;
+                    }
                 }
                 self.pclose()?;
             }
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index e1fb701e641..4ecad7f93a5 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -29,6 +29,7 @@
 #![feature(collections)]
 #![feature(const_fn)]
 #![feature(enumset)]
+#![feature(fn_traits)]
 #![feature(iter_arith)]
 #![feature(libc)]
 #![feature(nonzero)]
@@ -38,6 +39,7 @@
 #![feature(slice_patterns)]
 #![feature(staged_api)]
 #![feature(question_mark)]
+#![feature(unboxed_closures)]
 #![cfg_attr(test, feature(test))]
 
 extern crate arena;
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index 4cee8c5d89a..b0add5a23dc 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -1127,7 +1127,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
                     // will visit the substructure recursively.
                 }
 
-                PatKind::Wild | PatKind::Tup(..) | PatKind::Box(..) |
+                PatKind::Wild | PatKind::Tuple(..) | PatKind::Box(..) |
                 PatKind::Ref(..) | PatKind::Lit(..) | PatKind::Range(..) |
                 PatKind::Vec(..) => {
                     // Similarly, each of these cases does not
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 3999b02425d..da3df7ad3e9 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -80,6 +80,7 @@ use ty::adjustment;
 use ty::{self, Ty, TyCtxt};
 
 use hir::{MutImmutable, MutMutable, PatKind};
+use hir::pat_util::pat_adjust_pos;
 use hir;
 use syntax::ast;
 use syntax::codemap::Span;
@@ -1225,31 +1226,40 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
             // _
           }
 
-          PatKind::TupleStruct(_, None) => {
-            // variant(..)
-          }
-          PatKind::TupleStruct(_, Some(ref subpats)) => {
+          PatKind::TupleStruct(_, ref subpats, ddpos) => {
             match opt_def {
-                Some(Def::Variant(..)) => {
+                Some(Def::Variant(enum_def, def_id)) => {
                     // variant(x, y, z)
+                    let variant = self.tcx().lookup_adt_def(enum_def).variant_with_id(def_id);
+                    let adjust = pat_adjust_pos(variant.fields.len(), subpats.len(), ddpos);
                     for (i, subpat) in subpats.iter().enumerate() {
                         let subpat_ty = self.pat_ty(&subpat)?; // see (*2)
 
                         let subcmt =
                             self.cat_imm_interior(
                                 pat, cmt.clone(), subpat_ty,
-                                InteriorField(PositionalField(i)));
+                                InteriorField(PositionalField(adjust(i))));
 
                         self.cat_pattern_(subcmt, &subpat, op)?;
                     }
                 }
                 Some(Def::Struct(..)) => {
+                    let expected_len = match self.pat_ty(&pat) {
+                        Ok(&ty::TyS{sty: ty::TyStruct(adt_def, _), ..}) => {
+                            adt_def.struct_variant().fields.len()
+                        }
+                        ref ty => {
+                            span_bug!(pat.span, "tuple struct pattern unexpected type {:?}", ty);
+                        }
+                    };
+
+                    let adjust = pat_adjust_pos(expected_len, subpats.len(), ddpos);
                     for (i, subpat) in subpats.iter().enumerate() {
                         let subpat_ty = self.pat_ty(&subpat)?; // see (*2)
                         let cmt_field =
                             self.cat_imm_interior(
                                 pat, cmt.clone(), subpat_ty,
-                                InteriorField(PositionalField(i)));
+                                InteriorField(PositionalField(adjust(i))));
                         self.cat_pattern_(cmt_field, &subpat, op)?;
                     }
                 }
@@ -1284,14 +1294,19 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
             }
           }
 
-          PatKind::Tup(ref subpats) => {
+          PatKind::Tuple(ref subpats, ddpos) => {
             // (p1, ..., pN)
+            let expected_len = match self.pat_ty(&pat) {
+                Ok(&ty::TyS{sty: ty::TyTuple(ref tys), ..}) => tys.len(),
+                ref ty => span_bug!(pat.span, "tuple pattern unexpected type {:?}", ty),
+            };
+            let adjust = pat_adjust_pos(expected_len, subpats.len(), ddpos);
             for (i, subpat) in subpats.iter().enumerate() {
                 let subpat_ty = self.pat_ty(&subpat)?; // see (*2)
                 let subcmt =
                     self.cat_imm_interior(
                         pat, cmt.clone(), subpat_ty,
-                        InteriorField(PositionalField(i)));
+                        InteriorField(PositionalField(adjust(i))));
                 self.cat_pattern_(subcmt, &subpat, op)?;
             }
           }
diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs
index 56b4036b7d7..6b2c2dfcd72 100644
--- a/src/librustc/middle/region.rs
+++ b/src/librustc/middle/region.rs
@@ -970,8 +970,8 @@ fn resolve_local(visitor: &mut RegionResolutionVisitor, local: &hir::Local) {
                 pats3.iter().any(|p| is_binding_pat(&p))
             }
 
-            PatKind::TupleStruct(_, Some(ref subpats)) |
-            PatKind::Tup(ref subpats) => {
+            PatKind::TupleStruct(_, ref subpats, _) |
+            PatKind::Tuple(ref subpats, _) => {
                 subpats.iter().any(|p| is_binding_pat(&p))
             }
 
diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs
index c2db6de0370..50b6d661fa8 100644
--- a/src/librustc/middle/stability.rs
+++ b/src/librustc/middle/stability.rs
@@ -33,6 +33,7 @@ use util::nodemap::{DefIdMap, FnvHashSet, FnvHashMap};
 use hir;
 use hir::{Item, Generics, StructField, Variant, PatKind};
 use hir::intravisit::{self, Visitor};
+use hir::pat_util::pat_adjust_pos;
 
 use std::mem::replace;
 use std::cmp::Ordering;
@@ -614,10 +615,10 @@ pub fn check_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, pat: &hir::Pat,
     };
     match pat.node {
         // Foo(a, b, c)
-        // A Variant(..) pattern `PatKind::TupleStruct(_, None)` doesn't have to be recursed into.
-        PatKind::TupleStruct(_, Some(ref pat_fields)) => {
-            for (field, struct_field) in pat_fields.iter().zip(&v.fields) {
-                maybe_do_stability_check(tcx, struct_field.did, field.span, cb)
+        PatKind::TupleStruct(_, ref pat_fields, ddpos) => {
+            let adjust = pat_adjust_pos(v.fields.len(), pat_fields.len(), ddpos);
+            for (i, field) in pat_fields.iter().enumerate() {
+                maybe_do_stability_check(tcx, v.fields[adjust(i)].did, field.span, cb)
             }
         }
         // Foo { a, b, c }
diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs
index 2fb5d796589..f98734f21ad 100644
--- a/src/librustc_const_eval/check_match.rs
+++ b/src/librustc_const_eval/check_match.rs
@@ -374,7 +374,7 @@ fn pat_is_catchall(dm: &DefMap, p: &Pat) -> bool {
         PatKind::Ident(_, _, None) => pat_is_binding(dm, p),
         PatKind::Ident(_, _, Some(ref s)) => pat_is_catchall(dm, &s),
         PatKind::Ref(ref s, _) => pat_is_catchall(dm, &s),
-        PatKind::Tup(ref v) => v.iter().all(|p| pat_is_catchall(dm, &p)),
+        PatKind::Tuple(ref v, _) => v.iter().all(|p| pat_is_catchall(dm, &p)),
         _ => false
     }
 }
@@ -398,7 +398,7 @@ fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, matrix: &Matrix, source: hir:
                 hir::MatchSource::ForLoopDesugar => {
                     // `witnesses[0]` has the form `Some(<head>)`, peel off the `Some`
                     let witness = match witnesses[0].node {
-                        PatKind::TupleStruct(_, Some(ref pats)) => match &pats[..] {
+                        PatKind::TupleStruct(_, ref pats, _) => match &pats[..] {
                             [ref pat] => &**pat,
                             _ => bug!(),
                         },
@@ -559,7 +559,7 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor,
     let pats_len = pats.len();
     let mut pats = pats.into_iter().map(|p| P((*p).clone()));
     let pat = match left_ty.sty {
-        ty::TyTuple(_) => PatKind::Tup(pats.collect()),
+        ty::TyTuple(..) => PatKind::Tuple(pats.collect(), None),
 
         ty::TyEnum(adt, _) | ty::TyStruct(adt, _)  => {
             let v = ctor.variant_for_adt(adt);
@@ -580,7 +580,7 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor,
                     PatKind::Struct(def_to_path(cx.tcx, v.did), field_pats, has_more_fields)
                 }
                 VariantKind::Tuple => {
-                    PatKind::TupleStruct(def_to_path(cx.tcx, v.did), Some(pats.collect()))
+                    PatKind::TupleStruct(def_to_path(cx.tcx, v.did), pats.collect(), None)
                 }
                 VariantKind::Unit => {
                     PatKind::Path(def_to_path(cx.tcx, v.did))
@@ -832,7 +832,7 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: &Pat,
                     vec!(Slice(before.len() + after.len()))
                 }
             },
-        PatKind::Box(_) | PatKind::Tup(_) | PatKind::Ref(..) =>
+        PatKind::Box(..) | PatKind::Tuple(..) | PatKind::Ref(..) =>
             vec!(Single),
         PatKind::Wild =>
             vec!(),
@@ -914,7 +914,7 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
             }
         }
 
-        PatKind::TupleStruct(_, ref args) => {
+        PatKind::TupleStruct(_, ref args, ddpos) => {
             let def = cx.tcx.def_map.borrow().get(&pat_id).unwrap().full_def();
             match def {
                 Def::Const(..) | Def::AssociatedConst(..) =>
@@ -922,10 +922,15 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
                                          been rewritten"),
                 Def::Variant(_, id) if *constructor != Variant(id) => None,
                 Def::Variant(..) | Def::Struct(..) => {
-                    Some(match args {
-                        &Some(ref args) => args.iter().map(|p| &**p).collect(),
-                        &None => vec![DUMMY_WILD_PAT; arity],
-                    })
+                    match ddpos {
+                        Some(ddpos) => {
+                            let mut pats = args[..ddpos].iter().map(|p| &**p).collect(): Vec<_>;
+                            pats.extend(repeat(DUMMY_WILD_PAT).take(arity - args.len()));
+                            pats.extend(args[ddpos..].iter().map(|p| &**p));
+                            Some(pats)
+                        }
+                        None => Some(args.iter().map(|p| &**p).collect())
+                    }
                 }
                 _ => None
             }
@@ -952,7 +957,13 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
             }
         }
 
-        PatKind::Tup(ref args) =>
+        PatKind::Tuple(ref args, Some(ddpos)) => {
+            let mut pats = args[..ddpos].iter().map(|p| &**p).collect(): Vec<_>;
+            pats.extend(repeat(DUMMY_WILD_PAT).take(arity - args.len()));
+            pats.extend(args[ddpos..].iter().map(|p| &**p));
+            Some(pats)
+        }
+        PatKind::Tuple(ref args, None) =>
             Some(args.iter().map(|p| &**p).collect()),
 
         PatKind::Box(ref inner) | PatKind::Ref(ref inner, _) =>
diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs
index 9db24fa4770..b727b778fcd 100644
--- a/src/librustc_const_eval/eval.rs
+++ b/src/librustc_const_eval/eval.rs
@@ -271,10 +271,9 @@ pub fn const_expr_to_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     }
     let pat = match expr.node {
         hir::ExprTup(ref exprs) =>
-            PatKind::Tup(try!(exprs.iter()
-                                  .map(|expr| const_expr_to_pat(tcx, &expr,
-                                                                pat_id, span))
-                                  .collect())),
+            PatKind::Tuple(try!(exprs.iter()
+                                     .map(|expr| const_expr_to_pat(tcx, &expr, pat_id, span))
+                                     .collect()), None),
 
         hir::ExprCall(ref callee, ref args) => {
             let def = *tcx.def_map.borrow().get(&callee.id).unwrap();
@@ -295,7 +294,7 @@ pub fn const_expr_to_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                 .map(|expr| const_expr_to_pat(tcx, &**expr,
                                                               pat_id, span))
                                 .collect());
-            PatKind::TupleStruct(path, Some(pats))
+            PatKind::TupleStruct(path, pats, None)
         }
 
         hir::ExprStruct(ref path, ref fields, None) => {
diff --git a/src/librustc_const_eval/lib.rs b/src/librustc_const_eval/lib.rs
index 9ab6a437a5a..2c796690df4 100644
--- a/src/librustc_const_eval/lib.rs
+++ b/src/librustc_const_eval/lib.rs
@@ -31,6 +31,7 @@
 #![feature(question_mark)]
 #![feature(box_patterns)]
 #![feature(box_syntax)]
+#![feature(type_ascription)]
 
 #[macro_use] extern crate syntax;
 #[macro_use] extern crate log;
diff --git a/src/librustc_mir/hair/cx/pattern.rs b/src/librustc_mir/hair/cx/pattern.rs
index 0118b97dd7f..494d8a5c035 100644
--- a/src/librustc_mir/hair/cx/pattern.rs
+++ b/src/librustc_mir/hair/cx/pattern.rs
@@ -13,7 +13,7 @@ use hair::cx::Cx;
 use rustc_data_structures::fnv::FnvHashMap;
 use rustc_const_eval as const_eval;
 use rustc::hir::def::Def;
-use rustc::hir::pat_util::{pat_is_resolved_const, pat_is_binding};
+use rustc::hir::pat_util::{pat_adjust_pos, pat_is_resolved_const, pat_is_binding};
 use rustc::ty::{self, Ty};
 use rustc::mir::repr::*;
 use rustc::hir::{self, PatKind};
@@ -148,17 +148,24 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> {
                 }
             }
 
-            PatKind::Tup(ref subpatterns) => {
-                let subpatterns =
-                    subpatterns.iter()
-                               .enumerate()
-                               .map(|(i, subpattern)| FieldPattern {
-                                   field: Field::new(i),
-                                   pattern: self.to_pattern(subpattern),
-                               })
-                               .collect();
+            PatKind::Tuple(ref subpatterns, ddpos) => {
+                match self.cx.tcx.node_id_to_type(pat.id).sty {
+                    ty::TyTuple(ref tys) => {
+                        let adjust = pat_adjust_pos(tys.len(), subpatterns.len(), ddpos);
+                        let subpatterns =
+                            subpatterns.iter()
+                                       .enumerate()
+                                       .map(|(i, subpattern)| FieldPattern {
+                                            field: Field::new(adjust(i)),
+                                            pattern: self.to_pattern(subpattern),
+                                       })
+                                       .collect();
+
+                        PatternKind::Leaf { subpatterns: subpatterns }
+                    }
 
-                PatternKind::Leaf { subpatterns: subpatterns }
+                    ref sty => span_bug!(pat.span, "unexpected type for tuple pattern: {:?}", sty),
+                }
             }
 
             PatKind::Ident(bm, ref ident, ref sub)
@@ -208,13 +215,21 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> {
                 self.variant_or_leaf(pat, vec![])
             }
 
-            PatKind::TupleStruct(_, ref opt_subpatterns) => {
+            PatKind::TupleStruct(_, ref subpatterns, ddpos) => {
+                let pat_ty = self.cx.tcx.node_id_to_type(pat.id);
+                let adt_def = match pat_ty.sty {
+                    ty::TyStruct(adt_def, _) | ty::TyEnum(adt_def, _) => adt_def,
+                    _ => span_bug!(pat.span, "tuple struct pattern not applied to struct or enum"),
+                };
+                let def = self.cx.tcx.def_map.borrow().get(&pat.id).unwrap().full_def();
+                let variant_def = adt_def.variant_of_def(def);
+
+                let adjust = pat_adjust_pos(variant_def.fields.len(), subpatterns.len(), ddpos);
                 let subpatterns =
-                    opt_subpatterns.iter()
-                                   .flat_map(|v| v.iter())
+                        subpatterns.iter()
                                    .enumerate()
                                    .map(|(i, field)| FieldPattern {
-                                       field: Field::new(i),
+                                       field: Field::new(adjust(i)),
                                        pattern: self.to_pattern(field),
                                    })
                                    .collect();
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index f1e744098b9..953bcf457b8 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -31,7 +31,7 @@ use std::mem::replace;
 
 use rustc::hir::{self, PatKind};
 use rustc::hir::intravisit::{self, Visitor};
-
+use rustc::hir::pat_util::pat_adjust_pos;
 use rustc::dep_graph::DepNode;
 use rustc::lint;
 use rustc::hir::def::{self, Def};
@@ -488,17 +488,17 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivacyVisitor<'a, 'tcx> {
                     self.check_field(pattern.span, adt, variant.field_named(field.node.name));
                 }
             }
-
-            // Patterns which bind no fields are allowable (the path is check
-            // elsewhere).
-            PatKind::TupleStruct(_, Some(ref fields)) => {
+            PatKind::TupleStruct(_, ref fields, ddpos) => {
                 match self.tcx.pat_ty(pattern).sty {
                     ty::TyStruct(def, _) => {
+                        let adjust = pat_adjust_pos(def.struct_variant().fields.len(),
+                                                    fields.len(), ddpos);
                         for (i, field) in fields.iter().enumerate() {
                             if let PatKind::Wild = field.node {
                                 continue
                             }
-                            self.check_field(field.span, def, &def.struct_variant().fields[i]);
+                            self.check_field(field.span, def,
+                                             &def.struct_variant().fields[adjust(i)]);
                         }
                     }
                     ty::TyEnum(..) => {
@@ -506,7 +506,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivacyVisitor<'a, 'tcx> {
                     }
                     _ => {}
                 }
-
             }
             _ => {}
         }
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 41cc5462816..e0aa46eda18 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -2360,7 +2360,7 @@ impl<'a> Resolver<'a> {
                     }
                 }
 
-                PatKind::TupleStruct(ref path, _) | PatKind::Path(ref path) => {
+                PatKind::TupleStruct(ref path, _, _) | PatKind::Path(ref path) => {
                     // This must be an enum variant, struct or const.
                     let resolution = match self.resolve_possibly_assoc_item(pat_id,
                                                                             None,
diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs
index 8c00a569993..7b0ae100f05 100644
--- a/src/librustc_save_analysis/lib.rs
+++ b/src/librustc_save_analysis/lib.rs
@@ -696,7 +696,7 @@ impl<'v> Visitor<'v> for PathCollector {
                 self.collected_paths.push((p.id, path.clone(),
                                            ast::Mutability::Mutable, recorder::TypeRef));
             }
-            PatKind::TupleStruct(ref path, _) |
+            PatKind::TupleStruct(ref path, _, _) |
             PatKind::Path(ref path) |
             PatKind::QPath(_, ref path) => {
                 self.collected_paths.push((p.id, path.clone(),
diff --git a/src/librustc_trans/_match.rs b/src/librustc_trans/_match.rs
index dbc277f2432..9b168301f73 100644
--- a/src/librustc_trans/_match.rs
+++ b/src/librustc_trans/_match.rs
@@ -792,7 +792,7 @@ fn any_irrefutable_adt_pat(tcx: TyCtxt, m: &[Match], col: usize) -> bool {
     m.iter().any(|br| {
         let pat = br.pats[col];
         match pat.node {
-            PatKind::Tup(_) => true,
+            PatKind::Tuple(..) => true,
             PatKind::Struct(..) | PatKind::TupleStruct(..) |
             PatKind::Path(..) | PatKind::Ident(_, _, None) => {
                 match tcx.def_map.borrow().get(&pat.id).unwrap().full_def() {
@@ -1833,7 +1833,7 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 bcx = bind_irrefutable_pat(bcx, &inner_pat, val, cleanup_scope);
             }
         }
-        PatKind::TupleStruct(_, ref sub_pats) => {
+        PatKind::TupleStruct(_, ref sub_pats, ddpos) => {
             let opt_def = bcx.tcx().def_map.borrow().get(&pat.id).map(|d| d.full_def());
             match opt_def {
                 Some(Def::Variant(enum_id, var_id)) => {
@@ -1843,35 +1843,36 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                                     &repr,
                                                     Disr::from(vinfo.disr_val),
                                                     val);
-                    if let Some(ref sub_pat) = *sub_pats {
-                        for (i, &argval) in args.vals.iter().enumerate() {
-                            bcx = bind_irrefutable_pat(
-                                bcx,
-                                &sub_pat[i],
-                                MatchInput::from_val(argval),
-                                cleanup_scope);
-                        }
+                    let adjust = pat_adjust_pos(vinfo.fields.len(), sub_pats.len(), ddpos);
+                    for (i, subpat) in sub_pats.iter().enumerate() {
+                        bcx = bind_irrefutable_pat(
+                            bcx,
+                            subpat,
+                            MatchInput::from_val(args.vals[adjust(i)]),
+                            cleanup_scope);
                     }
                 }
                 Some(Def::Struct(..)) => {
-                    match *sub_pats {
-                        None => {
-                            // This is a unit-like struct. Nothing to do here.
+                    let expected_len = match *ccx.tcx().pat_ty(&pat) {
+                        ty::TyS{sty: ty::TyStruct(adt_def, _), ..} => {
+                            adt_def.struct_variant().fields.len()
                         }
-                        Some(ref elems) => {
-                            // This is the tuple struct case.
-                            let repr = adt::represent_node(bcx, pat.id);
-                            let val = adt::MaybeSizedValue::sized(val.val);
-                            for (i, elem) in elems.iter().enumerate() {
-                                let fldptr = adt::trans_field_ptr(bcx, &repr,
-                                                                  val, Disr(0), i);
-                                bcx = bind_irrefutable_pat(
-                                    bcx,
-                                    &elem,
-                                    MatchInput::from_val(fldptr),
-                                    cleanup_scope);
-                            }
+                        ref ty => {
+                            span_bug!(pat.span, "tuple struct pattern unexpected type {:?}", ty);
                         }
+                    };
+
+                    let adjust = pat_adjust_pos(expected_len, sub_pats.len(), ddpos);
+                    let repr = adt::represent_node(bcx, pat.id);
+                    let val = adt::MaybeSizedValue::sized(val.val);
+                    for (i, elem) in sub_pats.iter().enumerate() {
+                        let fldptr = adt::trans_field_ptr(bcx, &repr,
+                                                          val, Disr(0), adjust(i));
+                        bcx = bind_irrefutable_pat(
+                            bcx,
+                            &elem,
+                            MatchInput::from_val(fldptr),
+                            cleanup_scope);
                     }
                 }
                 _ => {
@@ -1919,16 +1920,22 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                            cleanup_scope);
             }
         }
-        PatKind::Tup(ref elems) => {
-            let repr = adt::represent_node(bcx, pat.id);
-            let val = adt::MaybeSizedValue::sized(val.val);
-            for (i, elem) in elems.iter().enumerate() {
-                let fldptr = adt::trans_field_ptr(bcx, &repr, val, Disr(0), i);
-                bcx = bind_irrefutable_pat(
-                    bcx,
-                    &elem,
-                    MatchInput::from_val(fldptr),
-                    cleanup_scope);
+        PatKind::Tuple(ref elems, ddpos) => {
+            match tcx.node_id_to_type(pat.id).sty {
+                ty::TyTuple(ref tys) => {
+                    let adjust = pat_adjust_pos(tys.len(), elems.len(), ddpos);
+                    let repr = adt::represent_node(bcx, pat.id);
+                    let val = adt::MaybeSizedValue::sized(val.val);
+                    for (i, elem) in elems.iter().enumerate() {
+                        let fldptr = adt::trans_field_ptr(bcx, &repr, val, Disr(0), adjust(i));
+                        bcx = bind_irrefutable_pat(
+                            bcx,
+                            &elem,
+                            MatchInput::from_val(fldptr),
+                            cleanup_scope);
+                    }
+                }
+                ref sty => span_bug!(pat.span, "unexpected type for tuple pattern: {:?}", sty),
             }
         }
         PatKind::Box(ref inner) => {
diff --git a/src/librustc_trans/debuginfo/create_scope_map.rs b/src/librustc_trans/debuginfo/create_scope_map.rs
index ba592382d1a..aec43e69e51 100644
--- a/src/librustc_trans/debuginfo/create_scope_map.rs
+++ b/src/librustc_trans/debuginfo/create_scope_map.rs
@@ -318,13 +318,11 @@ fn walk_pattern(cx: &CrateContext,
             scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
         }
 
-        PatKind::TupleStruct(_, ref sub_pats_opt) => {
+        PatKind::TupleStruct(_, ref sub_pats, _) => {
             scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
 
-            if let Some(ref sub_pats) = *sub_pats_opt {
-                for p in sub_pats {
-                    walk_pattern(cx, &p, scope_stack, scope_map);
-                }
+            for p in sub_pats {
+                walk_pattern(cx, &p, scope_stack, scope_map);
             }
         }
 
@@ -343,7 +341,7 @@ fn walk_pattern(cx: &CrateContext,
             }
         }
 
-        PatKind::Tup(ref sub_pats) => {
+        PatKind::Tuple(ref sub_pats, _) => {
             scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
 
             for sub_pat in sub_pats {
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index 10c8ea84bfd..ce4ac4e815c 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -11,7 +11,7 @@
 use hir::def::{self, Def};
 use rustc::infer::{self, InferOk, TypeOrigin};
 use hir::pat_util::{PatIdMap, pat_id_map, pat_is_binding};
-use hir::pat_util::pat_is_resolved_const;
+use hir::pat_util::{pat_adjust_pos, pat_is_resolved_const};
 use rustc::ty::subst::Substs;
 use rustc::ty::{self, Ty, TypeFoldable, LvaluePreference};
 use check::{FnCtxt, Expectation};
@@ -213,13 +213,13 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> {
             }
             PatKind::Ident(_, ref path, _) => {
                 let path = hir::Path::from_name(path.span, path.node);
-                self.check_pat_enum(pat, &path, Some(&[]), expected, false);
+                self.check_pat_enum(pat, &path, &[], None, expected, false);
             }
-            PatKind::TupleStruct(ref path, ref subpats) => {
-                self.check_pat_enum(pat, path, subpats.as_ref().map(|v| &v[..]), expected, true);
+            PatKind::TupleStruct(ref path, ref subpats, ddpos) => {
+                self.check_pat_enum(pat, path, &subpats, ddpos, expected, true);
             }
             PatKind::Path(ref path) => {
-                self.check_pat_enum(pat, path, Some(&[]), expected, false);
+                self.check_pat_enum(pat, path, &[], None, expected, false);
             }
             PatKind::QPath(ref qself, ref path) => {
                 let self_ty = self.to_ty(&qself.ty);
@@ -260,14 +260,24 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> {
             PatKind::Struct(ref path, ref fields, etc) => {
                 self.check_pat_struct(pat, path, fields, etc, expected);
             }
-            PatKind::Tup(ref elements) => {
-                let element_tys: Vec<_> =
-                    (0..elements.len()).map(|_| self.next_ty_var()).collect();
+            PatKind::Tuple(ref elements, ddpos) => {
+                let mut expected_len = elements.len();
+                if ddpos.is_some() {
+                    // Require known type only when `..` is present
+                    if let ty::TyTuple(ref tys) =
+                            self.structurally_resolved_type(pat.span, expected).sty {
+                        expected_len = tys.len();
+                    }
+                }
+                let max_len = cmp::max(expected_len, elements.len());
+
+                let element_tys = (0 .. max_len).map(|_| self.next_ty_var()).collect(): Vec<_>;
                 let pat_ty = tcx.mk_tup(element_tys.clone());
                 self.write_ty(pat.id, pat_ty);
                 self.demand_eqtype(pat.span, expected, pat_ty);
-                for (element_pat, element_ty) in elements.iter().zip(element_tys) {
-                    self.check_pat(&element_pat, element_ty);
+                let adjust = pat_adjust_pos(expected_len, elements.len(), ddpos);
+                for i in 0 .. elements.len() {
+                    self.check_pat(&elements[i], &element_tys[adjust(i)]);
                 }
             }
             PatKind::Box(ref inner) => {
@@ -615,7 +625,8 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> {
     fn check_pat_enum(&self,
                       pat: &hir::Pat,
                       path: &hir::Path,
-                      subpats: Option<&'gcx [P<hir::Pat>]>,
+                      subpats: &'gcx [P<hir::Pat>],
+                      ddpos: Option<usize>,
                       expected: Ty<'tcx>,
                       is_tuple_struct_pat: bool)
     {
@@ -628,12 +639,9 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> {
                 self.set_tainted_by_errors();
                 self.write_error(pat.id);
 
-                if let Some(subpats) = subpats {
-                    for pat in subpats {
-                        self.check_pat(&pat, tcx.types.err);
-                    }
+                for pat in subpats {
+                    self.check_pat(&pat, tcx.types.err);
                 }
-
                 return;
             }
         };
@@ -670,15 +678,12 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> {
         };
         self.instantiate_path(segments, path_scheme, &ctor_predicates,
                               opt_ty, def, pat.span, pat.id);
-
         let report_bad_struct_kind = |is_warning| {
             bad_struct_kind_err(tcx.sess, pat, path, is_warning);
             if is_warning { return; }
             self.write_error(pat.id);
-            if let Some(subpats) = subpats {
-                for pat in subpats {
-                    self.check_pat(&pat, tcx.types.err);
-                }
+            for pat in subpats {
+                self.check_pat(&pat, tcx.types.err);
             }
         };
 
@@ -715,11 +720,13 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> {
         };
 
         match (is_tuple_struct_pat, variant.kind()) {
-            (true, ty::VariantKind::Unit) => {
+            (true, ty::VariantKind::Unit) if subpats.is_empty() && ddpos.is_some() => {
                 // Matching unit structs with tuple variant patterns (`UnitVariant(..)`)
                 // is allowed for backward compatibility.
                 report_bad_struct_kind(true);
             }
+            (true, ty::VariantKind::Unit) |
+            (false, ty::VariantKind::Tuple) |
             (_, ty::VariantKind::Struct) => {
                 report_bad_struct_kind(false);
                 return
@@ -727,30 +734,23 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> {
             _ => {}
         }
 
-        if let Some(subpats) = subpats {
-            if subpats.len() == variant.fields.len() {
-                for (subpat, field) in subpats.iter().zip(&variant.fields) {
-                    let field_ty = self.field_ty(subpat.span, field, expected_substs);
-                    self.check_pat(&subpat, field_ty);
-                }
-            } else if variant.fields.is_empty() {
-                span_err!(tcx.sess, pat.span, E0024,
-                          "this pattern has {} field{}, but the corresponding {} has no fields",
-                          subpats.len(), if subpats.len() == 1 {""} else {"s"}, kind_name);
-
-                for pat in subpats {
-                    self.check_pat(&pat, tcx.types.err);
-                }
-            } else {
-                span_err!(tcx.sess, pat.span, E0023,
-                          "this pattern has {} field{}, but the corresponding {} has {} field{}",
-                          subpats.len(), if subpats.len() == 1 {""} else {"s"},
-                          kind_name,
-                          variant.fields.len(), if variant.fields.len() == 1 {""} else {"s"});
-
-                for pat in subpats {
-                    self.check_pat(&pat, tcx.types.err);
-                }
+        let adjust = pat_adjust_pos(variant.fields.len(), subpats.len(), ddpos);
+        if subpats.len() == variant.fields.len() ||
+                subpats.len() < variant.fields.len() && ddpos.is_some() {
+            for (i, subpat) in subpats.iter().enumerate() {
+                let field_ty = self.field_ty(subpat.span,
+                                    &variant.fields[adjust(i)], expected_substs);
+                self.check_pat(&subpat, field_ty);
+            }
+        } else {
+            span_err!(tcx.sess, pat.span, E0023,
+                      "this pattern has {} field{}, but the corresponding {} has {} field{}",
+                      subpats.len(), if subpats.len() == 1 {""} else {"s"},
+                      kind_name,
+                      variant.fields.len(), if variant.fields.len() == 1 {""} else {"s"});
+
+            for pat in subpats {
+                self.check_pat(&pat, tcx.types.err);
             }
         }
     }
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index 40b9b0e01ab..77ff818aa88 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -62,31 +62,6 @@ Check how many fields the enum was declared with and ensure that your pattern
 uses the same number.
 "##,
 
-E0024: r##"
-This error indicates that a pattern attempted to extract the fields of an enum
-variant with no fields. Here's a tiny example of this error:
-
-```compile_fail
-// This enum has two variants.
-enum Number {
-    // This variant has no fields.
-    Zero,
-    // This variant has one field.
-    One(u32)
-}
-
-// Assuming x is a Number we can pattern match on its contents.
-match x {
-    Number::Zero(inside) => {},
-    Number::One(inside) => {},
-}
-```
-
-The pattern match `Zero(inside)` is incorrect because the `Zero` variant
-contains no fields, yet the `inside` name attempts to bind the first field of
-the enum.
-"##,
-
 E0025: r##"
 Each field of a struct can only be bound once in a pattern. Erroneous code
 example:
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index 0b23951db36..282b7582393 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -77,11 +77,12 @@ This API is completely unstable and subject to change.
 #![feature(box_patterns)]
 #![feature(box_syntax)]
 #![feature(iter_arith)]
+#![feature(question_mark)]
 #![feature(quote)]
 #![feature(rustc_diagnostic_macros)]
 #![feature(rustc_private)]
 #![feature(staged_api)]
-#![feature(question_mark)]
+#![feature(type_ascription)]
 
 #[macro_use] extern crate log;
 #[macro_use] extern crate syntax;
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index ca138168b29..4831ee9a2e0 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -2579,7 +2579,7 @@ fn name_from_pat(p: &hir::Pat) -> String {
     match p.node {
         PatKind::Wild => "_".to_string(),
         PatKind::Ident(_, ref p, _) => p.node.to_string(),
-        PatKind::TupleStruct(ref p, _) | PatKind::Path(ref p) => path_to_string(p),
+        PatKind::TupleStruct(ref p, _, _) | PatKind::Path(ref p) => path_to_string(p),
         PatKind::QPath(..) => panic!("tried to get argument name from PatKind::QPath, \
                                 which is not allowed in function arguments"),
         PatKind::Struct(ref name, ref fields, etc) => {
@@ -2590,7 +2590,7 @@ fn name_from_pat(p: &hir::Pat) -> String {
                 if etc { ", ..." } else { "" }
             )
         },
-        PatKind::Tup(ref elts) => format!("({})", elts.iter().map(|p| name_from_pat(&**p))
+        PatKind::Tuple(ref elts, _) => format!("({})", elts.iter().map(|p| name_from_pat(&**p))
                                             .collect::<Vec<String>>().join(", ")),
         PatKind::Box(ref p) => name_from_pat(&**p),
         PatKind::Ref(ref p, _) => name_from_pat(&**p),
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index d9409d3bbd9..6eb588767c4 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -567,7 +567,7 @@ impl Pat {
             PatKind::Struct(_, ref fields, _) => {
                 fields.iter().all(|field| field.node.pat.walk(it))
             }
-            PatKind::TupleStruct(_, Some(ref s)) | PatKind::Tup(ref s) => {
+            PatKind::TupleStruct(_, ref s, _) | PatKind::Tuple(ref s, _) => {
                 s.iter().all(|p| p.walk(it))
             }
             PatKind::Box(ref s) | PatKind::Ref(ref s, _) => {
@@ -582,7 +582,6 @@ impl Pat {
             PatKind::Lit(_) |
             PatKind::Range(_, _) |
             PatKind::Ident(_, _, _) |
-            PatKind::TupleStruct(..) |
             PatKind::Path(..) |
             PatKind::QPath(_, _) |
             PatKind::Mac(_) => {
@@ -631,9 +630,10 @@ pub enum PatKind {
     /// The `bool` is `true` in the presence of a `..`.
     Struct(Path, Vec<Spanned<FieldPat>>, bool),
 
-    /// A tuple struct/variant pattern `Variant(x, y, z)`.
-    /// "None" means a `Variant(..)` pattern where we don't bind the fields to names.
-    TupleStruct(Path, Option<Vec<P<Pat>>>),
+    /// A tuple struct/variant pattern `Variant(x, y, .., z)`.
+    /// If the `..` pattern fragment presents, then `Option<usize>` denotes its position.
+    /// 0 <= position <= subpats.len()
+    TupleStruct(Path, Vec<P<Pat>>, Option<usize>),
 
     /// A path pattern.
     /// Such pattern can be resolved to a unit struct/variant or a constant.
@@ -645,8 +645,10 @@ pub enum PatKind {
     /// PatKind::Path, and the resolver will have to sort that out.
     QPath(QSelf, Path),
 
-    /// A tuple pattern `(a, b)`
-    Tup(Vec<P<Pat>>),
+    /// A tuple pattern `(a, b)`.
+    /// If the `..` pattern fragment presents, then `Option<usize>` denotes its position.
+    /// 0 <= position <= subpats.len()
+    Tuple(Vec<P<Pat>>, Option<usize>),
     /// A `box` pattern
     Box(P<Pat>),
     /// A reference pattern, e.g. `&mut (a, b)`
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index 7958162986c..3a1cdae9bfb 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -832,7 +832,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
         let pat = if subpats.is_empty() {
             PatKind::Path(path)
         } else {
-            PatKind::TupleStruct(path, Some(subpats))
+            PatKind::TupleStruct(path, subpats, None)
         };
         self.pat(span, pat)
     }
@@ -842,7 +842,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
         self.pat(span, pat)
     }
     fn pat_tuple(&self, span: Span, pats: Vec<P<ast::Pat>>) -> P<ast::Pat> {
-        self.pat(span, PatKind::Tup(pats))
+        self.pat(span, PatKind::Tuple(pats, None))
     }
 
     fn pat_some(&self, span: Span, pat: P<ast::Pat>) -> P<ast::Pat> {
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index acef98f2afc..b5bab09c70e 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -274,7 +274,10 @@ declare_features! (
     (active, drop_types_in_const, "1.9.0", Some(33156)),
 
     // Allows cfg(target_has_atomic = "...").
-    (active, cfg_target_has_atomic, "1.9.0", Some(32976))
+    (active, cfg_target_has_atomic, "1.9.0", Some(32976)),
+
+    // Allows `..` in tuple (struct) patterns
+    (active, dotdot_in_tuple_patterns, "1.10.0", Some(33627))
 );
 
 declare_features! (
@@ -315,7 +318,6 @@ declare_features! (
     // Allows `#[deprecated]` attribute
     (accepted, deprecated, "1.9.0", Some(29935))
 );
-
 // (changing above list without updating src/doc/reference.md makes @cmr sad)
 
 #[derive(PartialEq, Copy, Clone, Debug)]
@@ -1021,6 +1023,24 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
                                   pattern.span,
                                   "box pattern syntax is experimental");
             }
+            PatKind::Tuple(_, ddpos)
+                    if ddpos.is_some() => {
+                gate_feature_post!(&self, dotdot_in_tuple_patterns,
+                                  pattern.span,
+                                  "`..` in tuple patterns is experimental");
+            }
+            PatKind::TupleStruct(_, ref fields, ddpos)
+                    if ddpos.is_some() && !fields.is_empty() => {
+                gate_feature_post!(&self, dotdot_in_tuple_patterns,
+                                  pattern.span,
+                                  "`..` in tuple struct patterns is experimental");
+            }
+            PatKind::TupleStruct(_, ref fields, ddpos)
+                    if ddpos.is_none() && fields.is_empty() => {
+                self.context.span_handler.struct_span_err(pattern.span,
+                                                          "nullary enum variants are written with \
+                                                           no trailing `( )`").emit();
+            }
             _ => {}
         }
         visit::walk_pat(self, pattern)
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 2c325080c0c..c9d23427657 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -1115,9 +1115,9 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
                         sub.map(|x| folder.fold_pat(x)))
             }
             PatKind::Lit(e) => PatKind::Lit(folder.fold_expr(e)),
-            PatKind::TupleStruct(pth, pats) => {
+            PatKind::TupleStruct(pth, pats, ddpos) => {
                 PatKind::TupleStruct(folder.fold_path(pth),
-                        pats.map(|pats| pats.move_map(|x| folder.fold_pat(x))))
+                        pats.move_map(|x| folder.fold_pat(x)), ddpos)
             }
             PatKind::Path(pth) => {
                 PatKind::Path(folder.fold_path(pth))
@@ -1138,7 +1138,9 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
                 });
                 PatKind::Struct(pth, fs, etc)
             }
-            PatKind::Tup(elts) => PatKind::Tup(elts.move_map(|x| folder.fold_pat(x))),
+            PatKind::Tuple(elts, ddpos) => {
+                PatKind::Tuple(elts.move_map(|x| folder.fold_pat(x)), ddpos)
+            }
             PatKind::Box(inner) => PatKind::Box(folder.fold_pat(inner)),
             PatKind::Ref(inner, mutbl) => PatKind::Ref(folder.fold_pat(inner), mutbl),
             PatKind::Range(e1, e2) => {
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index fc62cee92fd..a4f12769b5c 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -954,25 +954,6 @@ impl<'a> Parser<'a> {
         Ok(result)
     }
 
-    /// Parse a sequence parameter of enum variant. For consistency purposes,
-    /// these should not be empty.
-    pub fn parse_enum_variant_seq<T, F>(&mut self,
-                                        bra: &token::Token,
-                                        ket: &token::Token,
-                                        sep: SeqSep,
-                                        f: F)
-                                        -> PResult<'a, Vec<T>> where
-        F: FnMut(&mut Parser<'a>) -> PResult<'a,  T>,
-    {
-        let result = self.parse_unspanned_seq(bra, ket, sep, f)?;
-        if result.is_empty() {
-            let last_span = self.last_span;
-            self.span_err(last_span,
-            "nullary enum variants are written with no trailing `( )`");
-        }
-        Ok(result)
-    }
-
     // NB: Do not use this function unless you actually plan to place the
     // spanned list in the AST.
     pub fn parse_seq<T, F>(&mut self,
@@ -3433,21 +3414,29 @@ impl<'a> Parser<'a> {
         };
     }
 
-    fn parse_pat_tuple_elements(&mut self) -> PResult<'a, Vec<P<Pat>>> {
+    fn parse_pat_tuple_elements(&mut self, unary_needs_comma: bool)
+                                -> PResult<'a, (Vec<P<Pat>>, Option<usize>)> {
         let mut fields = vec![];
-        if !self.check(&token::CloseDelim(token::Paren)) {
-            fields.push(self.parse_pat()?);
-            if self.look_ahead(1, |t| *t != token::CloseDelim(token::Paren)) {
-                while self.eat(&token::Comma) &&
-                      !self.check(&token::CloseDelim(token::Paren)) {
+        let mut ddpos = None;
+
+        while !self.check(&token::CloseDelim(token::Paren)) {
+            if ddpos.is_none() && self.eat(&token::DotDot) {
+                ddpos = Some(fields.len());
+                if self.eat(&token::Comma) {
+                    // `..` needs to be followed by `)` or `, pat`, `..,)` is disallowed.
                     fields.push(self.parse_pat()?);
                 }
+            } else {
+                fields.push(self.parse_pat()?);
             }
-            if fields.len() == 1 {
+
+            if !self.check(&token::CloseDelim(token::Paren)) ||
+                    (unary_needs_comma && fields.len() == 1 && ddpos.is_none()) {
                 self.expect(&token::Comma)?;
             }
         }
-        Ok(fields)
+
+        Ok((fields, ddpos))
     }
 
     fn parse_pat_vec_elements(
@@ -3626,9 +3615,9 @@ impl<'a> Parser<'a> {
           token::OpenDelim(token::Paren) => {
             // Parse (pat,pat,pat,...) as tuple pattern
             self.bump();
-            let fields = self.parse_pat_tuple_elements()?;
+            let (fields, ddpos) = self.parse_pat_tuple_elements(true)?;
             self.expect(&token::CloseDelim(token::Paren))?;
-            pat = PatKind::Tup(fields);
+            pat = PatKind::Tuple(fields, ddpos);
           }
           token::OpenDelim(token::Bracket) => {
             // Parse [pat,pat,...] as slice pattern
@@ -3713,20 +3702,10 @@ impl<'a> Parser<'a> {
                             return Err(self.fatal("unexpected `(` after qualified path"));
                         }
                         // Parse tuple struct or enum pattern
-                        if self.look_ahead(1, |t| *t == token::DotDot) {
-                            // This is a "top constructor only" pat
-                            self.bump();
-                            self.bump();
-                            self.expect(&token::CloseDelim(token::Paren))?;
-                            pat = PatKind::TupleStruct(path, None);
-                        } else {
-                            let args = self.parse_enum_variant_seq(
-                                &token::OpenDelim(token::Paren),
-                                &token::CloseDelim(token::Paren),
-                                SeqSep::trailing_allowed(token::Comma),
-                                |p| p.parse_pat())?;
-                            pat = PatKind::TupleStruct(path, Some(args));
-                        }
+                        self.bump();
+                        let (fields, ddpos) = self.parse_pat_tuple_elements(false)?;
+                        self.expect(&token::CloseDelim(token::Paren))?;
+                        pat = PatKind::TupleStruct(path, fields, ddpos)
                       }
                       _ => {
                         pat = match qself {
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index ebb4927d69c..6c3f3e28727 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -2472,17 +2472,23 @@ impl<'a> State<'a> {
                     None => ()
                 }
             }
-            PatKind::TupleStruct(ref path, ref args_) => {
+            PatKind::TupleStruct(ref path, ref elts, ddpos) => {
                 self.print_path(path, true, 0)?;
-                match *args_ {
-                    None => word(&mut self.s, "(..)")?,
-                    Some(ref args) => {
-                        self.popen()?;
-                        self.commasep(Inconsistent, &args[..],
-                                          |s, p| s.print_pat(&p))?;
-                        self.pclose()?;
+                self.popen()?;
+                if let Some(ddpos) = ddpos {
+                    self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p))?;
+                    if ddpos != 0 {
+                        self.word_space(",")?;
+                    }
+                    word(&mut self.s, "..")?;
+                    if ddpos != elts.len() {
+                        word(&mut self.s, ",")?;
+                        self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p))?;
                     }
+                } else {
+                    self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p))?;
                 }
+                self.pclose()?;
             }
             PatKind::Path(ref path) => {
                 self.print_path(path, true, 0)?;
@@ -2513,13 +2519,23 @@ impl<'a> State<'a> {
                 space(&mut self.s)?;
                 word(&mut self.s, "}")?;
             }
-            PatKind::Tup(ref elts) => {
+            PatKind::Tuple(ref elts, ddpos) => {
                 self.popen()?;
-                self.commasep(Inconsistent,
-                                   &elts[..],
-                                   |s, p| s.print_pat(&p))?;
-                if elts.len() == 1 {
-                    word(&mut self.s, ",")?;
+                if let Some(ddpos) = ddpos {
+                    self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p))?;
+                    if ddpos != 0 {
+                        self.word_space(",")?;
+                    }
+                    word(&mut self.s, "..")?;
+                    if ddpos != elts.len() {
+                        word(&mut self.s, ",")?;
+                        self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p))?;
+                    }
+                } else {
+                    self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p))?;
+                    if elts.len() == 1 {
+                        word(&mut self.s, ",")?;
+                    }
                 }
                 self.pclose()?;
             }
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index f50a480e5e5..d41d170c084 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -423,11 +423,9 @@ pub fn walk_assoc_type_binding<'v, V: Visitor<'v>>(visitor: &mut V,
 
 pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
     match pattern.node {
-        PatKind::TupleStruct(ref path, ref opt_children) => {
+        PatKind::TupleStruct(ref path, ref children, _) => {
             visitor.visit_path(path, pattern.id);
-            if let Some(ref children) = *opt_children {
-                walk_list!(visitor, visit_pat, children);
-            }
+            walk_list!(visitor, visit_pat, children);
         }
         PatKind::Path(ref path) => {
             visitor.visit_path(path, pattern.id);
@@ -443,7 +441,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
                 visitor.visit_pat(&field.node.pat)
             }
         }
-        PatKind::Tup(ref tuple_elements) => {
+        PatKind::Tuple(ref tuple_elements, _) => {
             walk_list!(visitor, visit_pat, tuple_elements);
         }
         PatKind::Box(ref subpattern) |
diff --git a/src/test/compile-fail/issue-32004.rs b/src/test/compile-fail/issue-32004.rs
index 0227a80fd75..8d74154655f 100644
--- a/src/test/compile-fail/issue-32004.rs
+++ b/src/test/compile-fail/issue-32004.rs
@@ -18,12 +18,12 @@ struct S;
 fn main() {
     match Foo::Baz {
         Foo::Bar => {}
-        //~^ ERROR this pattern has 0 fields, but the corresponding variant
+        //~^ ERROR `Foo::Bar` does not name a tuple variant or a tuple struct
         _ => {}
     }
 
     match S {
         S(()) => {}
-        //~^ ERROR this pattern has 1 field, but the corresponding struct
+        //~^ ERROR `S` does not name a tuple variant or a tuple struct
     }
 }
diff --git a/src/test/compile-fail/match-pattern-field-mismatch-2.rs b/src/test/compile-fail/match-pattern-field-mismatch-2.rs
index e63ddf6c7fd..a4ba93ea173 100644
--- a/src/test/compile-fail/match-pattern-field-mismatch-2.rs
+++ b/src/test/compile-fail/match-pattern-field-mismatch-2.rs
@@ -20,7 +20,7 @@ fn main() {
           color::rgb(_, _, _) => { }
           color::cmyk(_, _, _, _) => { }
           color::no_color(_) => { }
-          //~^ ERROR this pattern has 1 field, but the corresponding variant has no fields
+          //~^ ERROR `color::no_color` does not name a tuple variant or a tuple struct
         }
     }
 }
diff --git a/src/test/compile-fail/pat-tuple-bad-type.rs b/src/test/compile-fail/pat-tuple-bad-type.rs
new file mode 100644
index 00000000000..0d50a30dd05
--- /dev/null
+++ b/src/test/compile-fail/pat-tuple-bad-type.rs
@@ -0,0 +1,27 @@
+// Copyright 2016 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(dotdot_in_tuple_patterns)]
+
+fn main() {
+    let x;
+
+    match x {
+        (..) => {} //~ ERROR the type of this value must be known in this context
+        _ => {}
+    }
+
+    match 0u8 {
+        (..) => {} //~ ERROR mismatched types
+        _ => {}
+    }
+
+    x = 10;
+}
diff --git a/src/test/compile-fail/pat-tuple-feature-gate.rs b/src/test/compile-fail/pat-tuple-feature-gate.rs
new file mode 100644
index 00000000000..55ca05bdef3
--- /dev/null
+++ b/src/test/compile-fail/pat-tuple-feature-gate.rs
@@ -0,0 +1,17 @@
+// Copyright 2016 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() {
+    match 0 {
+        (..) => {} //~ ERROR `..` in tuple patterns is experimental
+        (pat, ..) => {} //~ ERROR `..` in tuple patterns is experimental
+        S(pat, ..) => {} //~ ERROR `..` in tuple struct patterns is experimental
+    }
+}
diff --git a/src/test/compile-fail/pat-tuple-overfield.rs b/src/test/compile-fail/pat-tuple-overfield.rs
new file mode 100644
index 00000000000..034ef4a72e2
--- /dev/null
+++ b/src/test/compile-fail/pat-tuple-overfield.rs
@@ -0,0 +1,28 @@
+// Copyright 2016 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(dotdot_in_tuple_patterns)]
+
+struct S(u8, u8, u8);
+
+fn main() {
+    match (1, 2, 3) {
+        (1, 2, 3, 4) => {} //~ ERROR mismatched types
+        (1, 2, .., 3, 4) => {} //~ ERROR mismatched types
+        _ => {}
+    }
+    match S(1, 2, 3) {
+        S(1, 2, 3, 4) => {}
+        //~^ ERROR this pattern has 4 fields, but the corresponding struct has 3 fields
+        S(1, 2, .., 3, 4) => {}
+        //~^ ERROR this pattern has 4 fields, but the corresponding struct has 3 fields
+        _ => {}
+    }
+}
diff --git a/src/test/compile-fail/pattern-error-continue.rs b/src/test/compile-fail/pattern-error-continue.rs
index d9f3bb3c40f..507012e8c5c 100644
--- a/src/test/compile-fail/pattern-error-continue.rs
+++ b/src/test/compile-fail/pattern-error-continue.rs
@@ -25,7 +25,7 @@ fn f(_c: char) {}
 fn main() {
     match A::B(1, 2) {
         A::B(_, _, _) => (), //~ ERROR this pattern has 3 fields, but
-        A::D(_) => (),       //~ ERROR this pattern has 1 field, but
+        A::D(_) => (),       //~ ERROR `A::D` does not name a tuple variant or a tuple struct
         _ => ()
     }
     match 'c' {
diff --git a/src/test/parse-fail/pat-lt-bracket-6.rs b/src/test/parse-fail/pat-lt-bracket-6.rs
index 5ed8f6dee8c..fb78e558a95 100644
--- a/src/test/parse-fail/pat-lt-bracket-6.rs
+++ b/src/test/parse-fail/pat-lt-bracket-6.rs
@@ -9,6 +9,5 @@
 // except according to those terms.
 
 fn main() {
-    let Test(&desc[..]) = x; //~ error: expected one of `,` or `@`, found `[`
-    //~^ ERROR expected one of `:`, `;`, `=`, or `@`, found `[`
+    let Test(&desc[..]) = x; //~ ERROR: expected one of `)`, `,`, or `@`, found `[`
 }
diff --git a/src/test/parse-fail/pat-lt-bracket-7.rs b/src/test/parse-fail/pat-lt-bracket-7.rs
index 00681e61497..d75589d8889 100644
--- a/src/test/parse-fail/pat-lt-bracket-7.rs
+++ b/src/test/parse-fail/pat-lt-bracket-7.rs
@@ -9,6 +9,5 @@
 // except according to those terms.
 
 fn main() {
-    for thing(x[]) in foo {} //~ error: expected one of `,` or `@`, found `[`
-    //~^ ERROR expected one of `@` or `in`, found `[`
+    for thing(x[]) in foo {} //~ ERROR: expected one of `)`, `,`, or `@`, found `[`
 }
diff --git a/src/test/compile-fail/E0024.rs b/src/test/parse-fail/pat-tuple-1.rs
index 18f4dcf19d7..945d0740654 100644
--- a/src/test/compile-fail/E0024.rs
+++ b/src/test/parse-fail/pat-tuple-1.rs
@@ -8,15 +8,10 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-enum Number {
-    Zero,
-    One(u32)
-}
+// compile-flags: -Z parse-only
 
 fn main() {
-    let x = Number::Zero;
-    match x {
-        Number::Zero(inside) => {}, //~ ERROR E0024
-        Number::One(inside) => {},
+    match 0 {
+        (, ..) => {} //~ ERROR expected pattern, found `,`
     }
 }
diff --git a/src/test/parse-fail/pat-tuple-2.rs b/src/test/parse-fail/pat-tuple-2.rs
new file mode 100644
index 00000000000..ad52fa57870
--- /dev/null
+++ b/src/test/parse-fail/pat-tuple-2.rs
@@ -0,0 +1,17 @@
+// Copyright 2016 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.
+
+// compile-flags: -Z parse-only
+
+fn main() {
+    match 0 {
+        (pat, ..,) => {} //~ ERROR expected pattern, found `)`
+    }
+}
diff --git a/src/test/parse-fail/pat-tuple-3.rs b/src/test/parse-fail/pat-tuple-3.rs
new file mode 100644
index 00000000000..95e44ae134c
--- /dev/null
+++ b/src/test/parse-fail/pat-tuple-3.rs
@@ -0,0 +1,17 @@
+// Copyright 2016 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.
+
+// compile-flags: -Z parse-only
+
+fn main() {
+    match 0 {
+        (.., pat, ..) => {} //~ ERROR expected pattern, found `..`
+    }
+}
diff --git a/src/test/parse-fail/pat-tuple-4.rs b/src/test/parse-fail/pat-tuple-4.rs
new file mode 100644
index 00000000000..f4c3afa07f1
--- /dev/null
+++ b/src/test/parse-fail/pat-tuple-4.rs
@@ -0,0 +1,17 @@
+// Copyright 2016 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.
+
+// compile-flags: -Z parse-only
+
+fn main() {
+    match 0 {
+        (.. pat) => {} //~ ERROR expected one of `)` or `,`, found `pat`
+    }
+}
diff --git a/src/test/parse-fail/pat-tuple-5.rs b/src/test/parse-fail/pat-tuple-5.rs
new file mode 100644
index 00000000000..145d1f9d8ec
--- /dev/null
+++ b/src/test/parse-fail/pat-tuple-5.rs
@@ -0,0 +1,17 @@
+// Copyright 2016 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.
+
+// compile-flags: -Z parse-only
+
+fn main() {
+    match 0 {
+        (pat ..) => {} //~ ERROR expected one of `)`, `,`, or `@`, found `..`
+    }
+}
diff --git a/src/test/parse-fail/pat-tuple-6.rs b/src/test/parse-fail/pat-tuple-6.rs
new file mode 100644
index 00000000000..3252d92fe1b
--- /dev/null
+++ b/src/test/parse-fail/pat-tuple-6.rs
@@ -0,0 +1,17 @@
+// Copyright 2016 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.
+
+// compile-flags: -Z parse-only
+
+fn main() {
+    match 0 {
+        (pat) => {} //~ ERROR expected one of `,` or `@`, found `)`
+    }
+}
diff --git a/src/test/run-pass/pat-tuple.rs b/src/test/run-pass/pat-tuple.rs
new file mode 100644
index 00000000000..ccea068f715
--- /dev/null
+++ b/src/test/run-pass/pat-tuple.rs
@@ -0,0 +1,202 @@
+// Copyright 2016 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(dotdot_in_tuple_patterns)]
+
+fn b() {
+    let x = (1, 2, 3);
+    match x {
+        (a, b, ..) => {
+            assert_eq!(a, 1);
+            assert_eq!(b, 2);
+        }
+    }
+    match x {
+        (.., b, c) => {
+            assert_eq!(b, 2);
+            assert_eq!(c, 3);
+        }
+    }
+    match x {
+        (a, .., c) => {
+            assert_eq!(a, 1);
+            assert_eq!(c, 3);
+        }
+    }
+    match x {
+        (a, b, c) => {
+            assert_eq!(a, 1);
+            assert_eq!(b, 2);
+            assert_eq!(c, 3);
+        }
+    }
+}
+
+fn bs() {
+    struct S(u8, u8, u8);
+
+    let x = S(1, 2, 3);
+    match x {
+        S(a, b, ..) => {
+            assert_eq!(a, 1);
+            assert_eq!(b, 2);
+        }
+    }
+    match x {
+        S(.., b, c) => {
+            assert_eq!(b, 2);
+            assert_eq!(c, 3);
+        }
+    }
+    match x {
+        S(a, .., c) => {
+            assert_eq!(a, 1);
+            assert_eq!(c, 3);
+        }
+    }
+    match x {
+        S(a, b, c) => {
+            assert_eq!(a, 1);
+            assert_eq!(b, 2);
+            assert_eq!(c, 3);
+        }
+    }
+}
+
+fn c() {
+    let x = (1,);
+    match x {
+        (2, ..) => panic!(),
+        (..) => ()
+    }
+}
+
+fn cs() {
+    struct S(u8);
+
+    let x = S(1);
+    match x {
+        S(2, ..) => panic!(),
+        S(..) => ()
+    }
+}
+
+fn d() {
+    let x = (1, 2, 3);
+    let branch = match x {
+        (1, 1, ..) => 0,
+        (1, 2, 3, ..) => 1,
+        (1, 2, ..) => 2,
+        _ => 3
+    };
+    assert_eq!(branch, 1);
+}
+
+fn ds() {
+    struct S(u8, u8, u8);
+
+    let x = S(1, 2, 3);
+    let branch = match x {
+        S(1, 1, ..) => 0,
+        S(1, 2, 3, ..) => 1,
+        S(1, 2, ..) => 2,
+        _ => 3
+    };
+    assert_eq!(branch, 1);
+}
+
+fn f() {
+    let x = (1, 2, 3);
+    match x {
+        (1, 2, 4) => unreachable!(),
+        (0, 2, 3, ..) => unreachable!(),
+        (0, .., 3) => unreachable!(),
+        (0, ..) => unreachable!(),
+        (1, 2, 3) => (),
+        (_, _, _) => unreachable!(),
+    }
+    match x {
+        (..) => (),
+    }
+    match x {
+        (_, _, _, ..) => (),
+    }
+    match x {
+        (a, b, c) => {
+            assert_eq!(1, a);
+            assert_eq!(2, b);
+            assert_eq!(3, c);
+        }
+    }
+}
+
+fn fs() {
+    struct S(u8, u8, u8);
+
+    let x = S(1, 2, 3);
+    match x {
+        S(1, 2, 4) => unreachable!(),
+        S(0, 2, 3, ..) => unreachable!(),
+        S(0, .., 3) => unreachable!(),
+        S(0, ..) => unreachable!(),
+        S(1, 2, 3) => (),
+        S(_, _, _) => unreachable!(),
+    }
+    match x {
+        S(..) => (),
+    }
+    match x {
+        S(_, _, _, ..) => (),
+    }
+    match x {
+        S(a, b, c) => {
+            assert_eq!(1, a);
+            assert_eq!(2, b);
+            assert_eq!(3, c);
+        }
+    }
+}
+
+fn g() {
+    struct S;
+    struct Z;
+    struct W;
+    let x = (S, Z, W);
+    match x { (S, ..) => {} }
+    match x { (.., W) => {} }
+    match x { (S, .., W) => {} }
+    match x { (.., Z, _) => {} }
+}
+
+fn gs() {
+    struct SS(S, Z, W);
+
+    struct S;
+    struct Z;
+    struct W;
+    let x = SS(S, Z, W);
+    match x { SS(S, ..) => {} }
+    match x { SS(.., W) => {} }
+    match x { SS(S, .., W) => {} }
+    match x { SS(.., Z, _) => {} }
+}
+
+fn main() {
+    b();
+    bs();
+    c();
+    cs();
+    d();
+    ds();
+    f();
+    fs();
+    g();
+    gs();
+}