diff --git a/src/doc/unstable-book/src/language-features/slice-patterns.md b/src/doc/unstable-book/src/language-features/slice-patterns.md
index 00c81f03ba173..cdb74495884a8 100644
--- a/src/doc/unstable-book/src/language-features/slice-patterns.md
+++ b/src/doc/unstable-book/src/language-features/slice-patterns.md
@@ -17,7 +17,7 @@ matched against that pattern. For example:
 fn is_symmetric(list: &[u32]) -> bool {
     match list {
         &[] | &[_] => true,
-        &[x, ref inside.., y] if x == y => is_symmetric(inside),
+        &[x, ref inside @ .., y] if x == y => is_symmetric(inside),
         &[..] => false,
     }
 }
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 639994ed14d86..bc2c835e21050 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -58,6 +58,7 @@ use std::mem;
 use smallvec::SmallVec;
 use syntax::attr;
 use syntax::ast;
+use syntax::ptr::P as AstP;
 use syntax::ast::*;
 use syntax::errors;
 use syntax::ext::hygiene::ExpnId;
@@ -468,7 +469,7 @@ impl<'a> LoweringContext<'a> {
             fn visit_pat(&mut self, p: &'tcx Pat) {
                 match p.node {
                     // Doesn't generate a HIR node
-                    PatKind::Paren(..) => {},
+                    PatKind::Paren(..) | PatKind::Rest => {},
                     _ => {
                         if let Some(owner) = self.hir_id_owner {
                             self.lctx.lower_node_id_with_owner(p.id, owner);
@@ -1157,7 +1158,7 @@ impl<'a> LoweringContext<'a> {
         &mut self,
         capture_clause: CaptureBy,
         closure_node_id: NodeId,
-        ret_ty: Option<syntax::ptr::P<Ty>>,
+        ret_ty: Option<AstP<Ty>>,
         span: Span,
         body: impl FnOnce(&mut LoweringContext<'_>) -> hir::Expr,
     ) -> hir::ExprKind {
@@ -4172,33 +4173,11 @@ impl<'a> LoweringContext<'a> {
         let node = match p.node {
             PatKind::Wild => hir::PatKind::Wild,
             PatKind::Ident(ref binding_mode, ident, ref sub) => {
-                match self.resolver.get_partial_res(p.id).map(|d| d.base_res()) {
-                    // `None` can occur in body-less function signatures
-                    res @ None | res @ Some(Res::Local(_)) => {
-                        let canonical_id = match res {
-                            Some(Res::Local(id)) => id,
-                            _ => p.id,
-                        };
-
-                        hir::PatKind::Binding(
-                            self.lower_binding_mode(binding_mode),
-                            self.lower_node_id(canonical_id),
-                            ident,
-                            sub.as_ref().map(|x| self.lower_pat(x)),
-                        )
-                    }
-                    Some(res) => hir::PatKind::Path(hir::QPath::Resolved(
-                        None,
-                        P(hir::Path {
-                            span: ident.span,
-                            res: self.lower_res(res),
-                            segments: hir_vec![hir::PathSegment::from_ident(ident)],
-                        }),
-                    )),
-                }
+                let lower_sub = |this: &mut Self| sub.as_ref().map(|x| this.lower_pat(x));
+                self.lower_pat_ident(p, binding_mode, ident, lower_sub)
             }
             PatKind::Lit(ref e) => hir::PatKind::Lit(P(self.lower_expr(e))),
-            PatKind::TupleStruct(ref path, ref pats, ddpos) => {
+            PatKind::TupleStruct(ref path, ref pats) => {
                 let qpath = self.lower_qpath(
                     p.id,
                     &None,
@@ -4206,11 +4185,8 @@ impl<'a> LoweringContext<'a> {
                     ParamMode::Optional,
                     ImplTraitContext::disallowed(),
                 );
-                hir::PatKind::TupleStruct(
-                    qpath,
-                    pats.iter().map(|x| self.lower_pat(x)).collect(),
-                    ddpos,
-                )
+                let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct");
+                hir::PatKind::TupleStruct(qpath, pats, ddpos)
             }
             PatKind::Path(ref qself, ref path) => {
                 let qpath = self.lower_qpath(
@@ -4247,8 +4223,9 @@ impl<'a> LoweringContext<'a> {
                     .collect();
                 hir::PatKind::Struct(qpath, fs, etc)
             }
-            PatKind::Tuple(ref elts, ddpos) => {
-                hir::PatKind::Tuple(elts.iter().map(|x| self.lower_pat(x)).collect(), ddpos)
+            PatKind::Tuple(ref pats) => {
+                let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple");
+                hir::PatKind::Tuple(pats, ddpos)
             }
             PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
             PatKind::Ref(ref inner, mutbl) => {
@@ -4259,15 +4236,138 @@ impl<'a> LoweringContext<'a> {
                 P(self.lower_expr(e2)),
                 self.lower_range_end(end),
             ),
-            PatKind::Slice(ref before, ref slice, ref after) => hir::PatKind::Slice(
-                before.iter().map(|x| self.lower_pat(x)).collect(),
-                slice.as_ref().map(|x| self.lower_pat(x)),
-                after.iter().map(|x| self.lower_pat(x)).collect(),
-            ),
+            PatKind::Slice(ref pats) => self.lower_pat_slice(pats),
+            PatKind::Rest => {
+                // If we reach here the `..` pattern is not semantically allowed.
+                self.ban_illegal_rest_pat(p.span)
+            }
             PatKind::Paren(ref inner) => return self.lower_pat(inner),
             PatKind::Mac(_) => panic!("Shouldn't exist here"),
         };
 
+        self.pat_with_node_id_of(p, node)
+    }
+
+    fn lower_pat_tuple(
+        &mut self,
+        pats: &[AstP<Pat>],
+        ctx: &str,
+    ) -> (HirVec<P<hir::Pat>>, Option<usize>) {
+        let mut elems = Vec::with_capacity(pats.len());
+        let mut rest = None;
+
+        let mut iter = pats.iter().enumerate();
+        while let Some((idx, pat)) = iter.next() {
+            // Interpret the first `..` pattern as a subtuple pattern.
+            if pat.is_rest() {
+                rest = Some((idx, pat.span));
+                break;
+            }
+            // It was not a subslice pattern so lower it normally.
+            elems.push(self.lower_pat(pat));
+        }
+
+        while let Some((_, pat)) = iter.next() {
+            // There was a previous subtuple pattern; make sure we don't allow more.
+            if pat.is_rest() {
+                self.ban_extra_rest_pat(pat.span, rest.unwrap().1, ctx);
+            } else {
+                elems.push(self.lower_pat(pat));
+            }
+        }
+
+        (elems.into(), rest.map(|(ddpos, _)| ddpos))
+    }
+
+    fn lower_pat_slice(&mut self, pats: &[AstP<Pat>]) -> hir::PatKind {
+        let mut before = Vec::new();
+        let mut after = Vec::new();
+        let mut slice = None;
+        let mut prev_rest_span = None;
+
+        let mut iter = pats.iter();
+        while let Some(pat) = iter.next() {
+            // Interpret the first `((ref mut?)? x @)? ..` pattern as a subslice pattern.
+            match pat.node {
+                PatKind::Rest => {
+                    prev_rest_span = Some(pat.span);
+                    slice = Some(self.pat_wild_with_node_id_of(pat));
+                    break;
+                },
+                PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => {
+                    prev_rest_span = Some(sub.span);
+                    let lower_sub = |this: &mut Self| Some(this.pat_wild_with_node_id_of(sub));
+                    let node = self.lower_pat_ident(pat, bm, ident, lower_sub);
+                    slice = Some(self.pat_with_node_id_of(pat, node));
+                    break;
+                },
+                _ => {}
+            }
+
+            // It was not a subslice pattern so lower it normally.
+            before.push(self.lower_pat(pat));
+        }
+
+        while let Some(pat) = iter.next() {
+            // There was a previous subslice pattern; make sure we don't allow more.
+            let rest_span = match pat.node {
+                PatKind::Rest => Some(pat.span),
+                PatKind::Ident(.., Some(ref sub)) if sub.is_rest() => {
+                    // The `HirValidator` is merciless; add a `_` pattern to avoid ICEs.
+                    after.push(self.pat_wild_with_node_id_of(pat));
+                    Some(sub.span)
+                },
+                _ => None,
+            };
+            if let Some(rest_span) = rest_span {
+                self.ban_extra_rest_pat(rest_span, prev_rest_span.unwrap(), "slice");
+            } else {
+                after.push(self.lower_pat(pat));
+            }
+        }
+
+        hir::PatKind::Slice(before.into(), slice, after.into())
+    }
+
+    fn lower_pat_ident(
+        &mut self,
+        p: &Pat,
+        binding_mode: &BindingMode,
+        ident: Ident,
+        lower_sub: impl FnOnce(&mut Self) -> Option<P<hir::Pat>>,
+    ) -> hir::PatKind {
+        match self.resolver.get_partial_res(p.id).map(|d| d.base_res()) {
+            // `None` can occur in body-less function signatures
+            res @ None | res @ Some(Res::Local(_)) => {
+                let canonical_id = match res {
+                    Some(Res::Local(id)) => id,
+                    _ => p.id,
+                };
+
+                hir::PatKind::Binding(
+                    self.lower_binding_mode(binding_mode),
+                    self.lower_node_id(canonical_id),
+                    ident,
+                    lower_sub(self),
+                )
+            }
+            Some(res) => hir::PatKind::Path(hir::QPath::Resolved(
+                None,
+                P(hir::Path {
+                    span: ident.span,
+                    res: self.lower_res(res),
+                    segments: hir_vec![hir::PathSegment::from_ident(ident)],
+                }),
+            )),
+        }
+    }
+
+    fn pat_wild_with_node_id_of(&mut self, p: &Pat) -> P<hir::Pat> {
+        self.pat_with_node_id_of(p, hir::PatKind::Wild)
+    }
+
+    /// Construct a `Pat` with the `HirId` of `p.id` lowered.
+    fn pat_with_node_id_of(&mut self, p: &Pat, node: hir::PatKind) -> P<hir::Pat> {
         P(hir::Pat {
             hir_id: self.lower_node_id(p.id),
             node,
@@ -4275,6 +4375,28 @@ impl<'a> LoweringContext<'a> {
         })
     }
 
+    /// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.
+    fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
+        self.diagnostic()
+            .struct_span_err(sp, &format!("`..` can only be used once per {} pattern", ctx))
+            .span_label(sp, &format!("can only be used once per {} pattern", ctx))
+            .span_label(prev_sp, "previously used here")
+            .emit();
+    }
+
+    /// Used to ban the `..` pattern in places it shouldn't be semantically.
+    fn ban_illegal_rest_pat(&self, sp: Span) -> hir::PatKind {
+        self.diagnostic()
+            .struct_span_err(sp, "`..` patterns are not allowed here")
+            .note("only allowed in tuple, tuple struct, and slice patterns")
+            .emit();
+
+        // We're not in a list context so `..` can be reasonably treated
+        // as `_` because it should always be valid and roughly matches the
+        // intent of `..` (notice that the rest of a single slot is that slot).
+        hir::PatKind::Wild
+    }
+
     fn lower_range_end(&mut self, e: &RangeEnd) -> hir::RangeEnd {
         match *e {
             RangeEnd::Included(_) => hir::RangeEnd::Included,
diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs
index 1baeda41498ed..f759ec7f219d1 100644
--- a/src/librustc_mir/hair/pattern/mod.rs
+++ b/src/librustc_mir/hair/pattern/mod.rs
@@ -162,7 +162,7 @@ pub enum PatternKind<'tcx> {
 
     /// Matches against a slice, checking the length and extracting elements.
     /// irrefutable when there is a slice pattern and both `prefix` and `suffix` are empty.
-    /// e.g., `&[ref xs..]`.
+    /// e.g., `&[ref xs @ ..]`.
     Slice {
         prefix: Vec<Pattern<'tcx>>,
         slice: Option<Pattern<'tcx>>,
diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs
index b550029d9786d..473ba21c77b6d 100644
--- a/src/librustc_passes/ast_validation.rs
+++ b/src/librustc_passes/ast_validation.rs
@@ -287,7 +287,7 @@ impl<'a> AstValidator<'a> {
     // ```
     fn check_expr_within_pat(&self, expr: &Expr, allow_paths: bool) {
         match expr.node {
-            ExprKind::Lit(..) => {}
+            ExprKind::Lit(..) | ExprKind::Err => {}
             ExprKind::Path(..) if allow_paths => {}
             ExprKind::Unary(UnOp::Neg, ref inner)
                 if match inner.node { ExprKind::Lit(_) => true, _ => false } => {}
diff --git a/src/librustc_target/abi/mod.rs b/src/librustc_target/abi/mod.rs
index 4a4c6799c005e..80fcb45d0b9bc 100644
--- a/src/librustc_target/abi/mod.rs
+++ b/src/librustc_target/abi/mod.rs
@@ -105,20 +105,34 @@ impl TargetDataLayout {
         let mut dl = TargetDataLayout::default();
         let mut i128_align_src = 64;
         for spec in target.data_layout.split('-') {
-            match spec.split(':').collect::<Vec<_>>()[..] {
+            let spec_parts = spec.split(':').collect::<Vec<_>>();
+
+            match &*spec_parts {
                 ["e"] => dl.endian = Endian::Little,
                 ["E"] => dl.endian = Endian::Big,
                 [p] if p.starts_with("P") => {
                     dl.instruction_address_space = parse_address_space(&p[1..], "P")?
                 }
-                ["a", ref a..] => dl.aggregate_align = align(a, "a")?,
-                ["f32", ref a..] => dl.f32_align = align(a, "f32")?,
-                ["f64", ref a..] => dl.f64_align = align(a, "f64")?,
-                [p @ "p", s, ref a..] | [p @ "p0", s, ref a..] => {
+                // FIXME: Ping cfg(bootstrap) -- Use `ref a @ ..` with new bootstrap compiler.
+                ["a", ..] => {
+                    let a = &spec_parts[1..]; // FIXME inline into pattern.
+                    dl.aggregate_align = align(a, "a")?
+                }
+                ["f32", ..] => {
+                    let a = &spec_parts[1..]; // FIXME inline into pattern.
+                    dl.f32_align = align(a, "f32")?
+                }
+                ["f64", ..] => {
+                    let a = &spec_parts[1..]; // FIXME inline into pattern.
+                    dl.f64_align = align(a, "f64")?
+                }
+                [p @ "p", s, ..] | [p @ "p0", s, ..] => {
+                    let a = &spec_parts[2..]; // FIXME inline into pattern.
                     dl.pointer_size = size(s, p)?;
                     dl.pointer_align = align(a, p)?;
                 }
-                [s, ref a..] if s.starts_with("i") => {
+                [s, ..] if s.starts_with("i") => {
+                    let a = &spec_parts[1..]; // FIXME inline into pattern.
                     let bits = match s[1..].parse::<u64>() {
                         Ok(bits) => bits,
                         Err(_) => {
@@ -142,7 +156,8 @@ impl TargetDataLayout {
                         dl.i128_align = a;
                     }
                 }
-                [s, ref a..] if s.starts_with("v") => {
+                [s, ..] if s.starts_with("v") => {
+                    let a = &spec_parts[1..]; // FIXME inline into pattern.
                     let v_size = size(&s[1..], "v")?;
                     let a = align(a, s)?;
                     if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) {
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index 3f0604b84b7d7..defa6fe4ce15e 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -196,7 +196,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let rhs_ty = self.check_expr(end);
 
                 // Check that both end-points are of numeric or char type.
-                let numeric_or_char = |ty: Ty<'_>| ty.is_numeric() || ty.is_char();
+                let numeric_or_char = |ty: Ty<'_>| {
+                    ty.is_numeric()
+                    || ty.is_char()
+                    || ty.references_error()
+                };
                 let lhs_compat = numeric_or_char(lhs_ty);
                 let rhs_compat = numeric_or_char(rhs_ty);
 
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 36977b878dd7e..66c726b2485d1 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1818,7 +1818,9 @@ fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: &'tcx ty::AdtDef, sp: Span, d
     );
     let mut err = struct_span_err!(tcx.sess, sp, E0731, "transparent enum {}", msg);
     err.span_label(sp, &msg);
-    if let &[ref start.., ref end] = &variant_spans[..] {
+    if let &[.., ref end] = &variant_spans[..] {
+        // FIXME: Ping cfg(bootstrap) -- Use `ref start @ ..` with new bootstrap compiler.
+        let start = &variant_spans[..variant_spans.len() - 1];
         for variant_span in start {
             err.span_label(*variant_span, "");
         }
diff --git a/src/librustc_typeck/error_codes.rs b/src/librustc_typeck/error_codes.rs
index 19d5e8b3e8447..b4c856921198e 100644
--- a/src/librustc_typeck/error_codes.rs
+++ b/src/librustc_typeck/error_codes.rs
@@ -3497,8 +3497,8 @@ Example of erroneous code:
 
 let r = &[1, 2];
 match r {
-    &[a, b, c, rest..] => { // error: pattern requires at least 3
-                            //        elements but array has 2
+    &[a, b, c, rest @ ..] => { // error: pattern requires at least 3
+                               //        elements but array has 2
         println!("a={}, b={}, c={} rest={:?}", a, b, c, rest);
     }
 }
@@ -3512,7 +3512,7 @@ requires. You can match an arbitrary number of remaining elements with `..`:
 
 let r = &[1, 2, 3, 4, 5];
 match r {
-    &[a, b, c, rest..] => { // ok!
+    &[a, b, c, rest @ ..] => { // ok!
         // prints `a=1, b=2, c=3 rest=[4, 5]`
         println!("a={}, b={}, c={} rest={:?}", a, b, c, rest);
     }
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index dbfad3ef7f4de..a7453f4f9dac2 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -519,21 +519,28 @@ impl fmt::Debug for Pat {
 }
 
 impl Pat {
+    /// Attempt reparsing the pattern as a type.
+    /// This is intended for use by diagnostics.
     pub(super) fn to_ty(&self) -> Option<P<Ty>> {
         let node = match &self.node {
+            // In a type expression `_` is an inference variable.
             PatKind::Wild => TyKind::Infer,
+            // An IDENT pattern with no binding mode would be valid as path to a type. E.g. `u32`.
             PatKind::Ident(BindingMode::ByValue(Mutability::Immutable), ident, None) => {
                 TyKind::Path(None, Path::from_ident(*ident))
             }
             PatKind::Path(qself, path) => TyKind::Path(qself.clone(), path.clone()),
             PatKind::Mac(mac) => TyKind::Mac(mac.clone()),
+            // `&mut? P` can be reinterpreted as `&mut? T` where `T` is `P` reparsed as a type.
             PatKind::Ref(pat, mutbl) => pat
                 .to_ty()
                 .map(|ty| TyKind::Rptr(None, MutTy { ty, mutbl: *mutbl }))?,
-            PatKind::Slice(pats, None, _) if pats.len() == 1 => {
-                pats[0].to_ty().map(TyKind::Slice)?
-            }
-            PatKind::Tuple(pats, None) => {
+            // A slice/array pattern `[P]` can be reparsed as `[T]`, an unsized array,
+            // when `P` can be reparsed as a type `T`.
+            PatKind::Slice(pats) if pats.len() == 1 => pats[0].to_ty().map(TyKind::Slice)?,
+            // A tuple pattern `(P0, .., Pn)` can be reparsed as `(T0, .., Tn)`
+            // assuming `T0` to `Tn` are all syntactically valid as types.
+            PatKind::Tuple(pats) => {
                 let mut tys = Vec::with_capacity(pats.len());
                 // FIXME(#48994) - could just be collected into an Option<Vec>
                 for pat in pats {
@@ -559,19 +566,15 @@ impl Pat {
             return false;
         }
 
-        match self.node {
-            PatKind::Ident(_, _, Some(ref p)) => p.walk(it),
-            PatKind::Struct(_, ref fields, _) => fields.iter().all(|field| field.node.pat.walk(it)),
-            PatKind::TupleStruct(_, ref s, _) | PatKind::Tuple(ref s, _) => {
+        match &self.node {
+            PatKind::Ident(_, _, Some(p)) => p.walk(it),
+            PatKind::Struct(_, fields, _) => fields.iter().all(|field| field.node.pat.walk(it)),
+            PatKind::TupleStruct(_, s) | PatKind::Tuple(s) | PatKind::Slice(s) => {
                 s.iter().all(|p| p.walk(it))
             }
-            PatKind::Box(ref s) | PatKind::Ref(ref s, _) | PatKind::Paren(ref s) => s.walk(it),
-            PatKind::Slice(ref before, ref slice, ref after) => {
-                before.iter().all(|p| p.walk(it))
-                    && slice.iter().all(|p| p.walk(it))
-                    && after.iter().all(|p| p.walk(it))
-            }
+            PatKind::Box(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => s.walk(it),
             PatKind::Wild
+            | PatKind::Rest
             | PatKind::Lit(_)
             | PatKind::Range(..)
             | PatKind::Ident(..)
@@ -579,6 +582,14 @@ impl Pat {
             | PatKind::Mac(_) => true,
         }
     }
+
+    /// Is this a `..` pattern?
+    pub fn is_rest(&self) -> bool {
+        match self.node {
+            PatKind::Rest => true,
+            _ => false,
+        }
+    }
 }
 
 /// A single field in a struct pattern
@@ -630,9 +641,7 @@ pub enum PatKind {
     Struct(Path, Vec<Spanned<FieldPat>>, /* recovered */ bool),
 
     /// A tuple struct/variant pattern (`Variant(x, y, .., z)`).
-    /// If the `..` pattern fragment is present, then `Option<usize>` denotes its position.
-    /// `0 <= position <= subpats.len()`.
-    TupleStruct(Path, Vec<P<Pat>>, Option<usize>),
+    TupleStruct(Path, Vec<P<Pat>>),
 
     /// A possibly qualified path pattern.
     /// Unqualified path patterns `A::B::C` can legally refer to variants, structs, constants
@@ -641,9 +650,7 @@ pub enum PatKind {
     Path(Option<QSelf>, Path),
 
     /// A tuple pattern (`(a, b)`).
-    /// If the `..` pattern fragment is present, then `Option<usize>` denotes its position.
-    /// `0 <= position <= subpats.len()`.
-    Tuple(Vec<P<Pat>>, Option<usize>),
+    Tuple(Vec<P<Pat>>),
 
     /// A `box` pattern.
     Box(P<Pat>),
@@ -657,9 +664,22 @@ pub enum PatKind {
     /// A range pattern (e.g., `1...2`, `1..=2` or `1..2`).
     Range(P<Expr>, P<Expr>, Spanned<RangeEnd>),
 
-    /// `[a, b, ..i, y, z]` is represented as:
-    ///     `PatKind::Slice(box [a, b], Some(i), box [y, z])`
-    Slice(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),
+    /// A slice pattern `[a, b, c]`.
+    Slice(Vec<P<Pat>>),
+
+    /// A rest pattern `..`.
+    ///
+    /// Syntactically it is valid anywhere.
+    ///
+    /// Semantically however, it only has meaning immediately inside:
+    /// - a slice pattern: `[a, .., b]`,
+    /// - a binding pattern immediately inside a slice pattern: `[a, r @ ..]`,
+    /// - a tuple pattern: `(a, .., b)`,
+    /// - a tuple struct/variant pattern: `$path(a, .., b)`.
+    ///
+    /// In all of these cases, an additional restriction applies,
+    /// only one rest pattern may occur in the pattern sequences.
+    Rest,
 
     /// Parentheses in patterns used for grouping (i.e., `(PAT)`).
     Paren(P<Pat>),
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index baf1031de1e7c..528b27d6153df 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -840,14 +840,14 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
     }
     fn pat_tuple_struct(&self, span: Span, path: ast::Path,
                         subpats: Vec<P<ast::Pat>>) -> P<ast::Pat> {
-        self.pat(span, PatKind::TupleStruct(path, subpats, None))
+        self.pat(span, PatKind::TupleStruct(path, subpats))
     }
     fn pat_struct(&self, span: Span, path: ast::Path,
                   field_pats: Vec<Spanned<ast::FieldPat>>) -> P<ast::Pat> {
         self.pat(span, PatKind::Struct(path, field_pats, false))
     }
     fn pat_tuple(&self, span: Span, pats: Vec<P<ast::Pat>>) -> P<ast::Pat> {
-        self.pat(span, PatKind::Tuple(pats, None))
+        self.pat(span, PatKind::Tuple(pats))
     }
 
     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 72184b0bd6400..384474b08f6ee 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -2151,11 +2151,23 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
     }
 
     fn visit_pat(&mut self, pattern: &'a ast::Pat) {
-        match pattern.node {
-            PatKind::Slice(_, Some(ref subslice), _) => {
-                gate_feature_post!(&self, slice_patterns,
-                                   subslice.span,
-                                   "syntax for subslices in slice patterns is not yet stabilized");
+        match &pattern.node {
+            PatKind::Slice(pats) => {
+                for pat in &*pats {
+                    let span = pat.span;
+                    let inner_pat = match &pat.node {
+                        PatKind::Ident(.., Some(pat)) => pat,
+                        _ => pat,
+                    };
+                    if inner_pat.is_rest() {
+                        gate_feature_post!(
+                            &self,
+                            slice_patterns,
+                            span,
+                            "subslice patterns are unstable"
+                        );
+                    }
+                }
             }
             PatKind::Box(..) => {
                 gate_feature_post!(&self, box_patterns,
diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs
index dc656222fbc10..86525406718b6 100644
--- a/src/libsyntax/mut_visit.rs
+++ b/src/libsyntax/mut_visit.rs
@@ -1020,15 +1020,15 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
     let Pat { id, node, span } = pat.deref_mut();
     vis.visit_id(id);
     match node {
-        PatKind::Wild => {}
+        PatKind::Wild | PatKind::Rest => {}
         PatKind::Ident(_binding_mode, ident, sub) => {
             vis.visit_ident(ident);
             visit_opt(sub, |sub| vis.visit_pat(sub));
         }
         PatKind::Lit(e) => vis.visit_expr(e),
-        PatKind::TupleStruct(path, pats, _ddpos) => {
+        PatKind::TupleStruct(path, elems) => {
             vis.visit_path(path);
-            visit_vec(pats, |pat| vis.visit_pat(pat));
+            visit_vec(elems, |elem| vis.visit_pat(elem));
         }
         PatKind::Path(qself, path) => {
             vis.visit_qself(qself);
@@ -1043,7 +1043,7 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
                 vis.visit_span(span);
             };
         }
-        PatKind::Tuple(elts, _ddpos) => visit_vec(elts, |elt| vis.visit_pat(elt)),
+        PatKind::Tuple(elems) => visit_vec(elems, |elem| vis.visit_pat(elem)),
         PatKind::Box(inner) => vis.visit_pat(inner),
         PatKind::Ref(inner, _mutbl) => vis.visit_pat(inner),
         PatKind::Range(e1, e2, Spanned { span: _, node: _ }) => {
@@ -1051,11 +1051,7 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
             vis.visit_expr(e2);
             vis.visit_span(span);
         }
-        PatKind::Slice(before, slice, after) => {
-            visit_vec(before, |pat| vis.visit_pat(pat));
-            visit_opt(slice, |slice| vis.visit_pat(slice));
-            visit_vec(after, |pat| vis.visit_pat(pat));
-        }
+        PatKind::Slice(elems) => visit_vec(elems, |elem| vis.visit_pat(elem)),
         PatKind::Paren(inner) => vis.visit_pat(inner),
         PatKind::Mac(mac) => vis.visit_mac(mac),
     }
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index a5b6f0d683629..8f8ed4111808d 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -890,14 +890,13 @@ impl<'a> Parser<'a> {
     /// Parses a sequence, including the closing delimiter. The function
     /// `f` must consume tokens until reaching the next separator or
     /// closing bracket.
-    pub fn parse_seq_to_end<T, F>(&mut self,
-                                  ket: &TokenKind,
-                                  sep: SeqSep,
-                                  f: F)
-                                  -> PResult<'a, Vec<T>> where
-        F: FnMut(&mut Parser<'a>) -> PResult<'a,  T>,
-    {
-        let (val, recovered) = self.parse_seq_to_before_end(ket, sep, f)?;
+    pub fn parse_seq_to_end<T>(
+        &mut self,
+        ket: &TokenKind,
+        sep: SeqSep,
+        f: impl FnMut(&mut Parser<'a>) -> PResult<'a,  T>,
+    ) -> PResult<'a, Vec<T>> {
+        let (val, _, recovered) = self.parse_seq_to_before_end(ket, sep, f)?;
         if !recovered {
             self.bump();
         }
@@ -907,39 +906,39 @@ impl<'a> Parser<'a> {
     /// Parses a sequence, not including the closing delimiter. The function
     /// `f` must consume tokens until reaching the next separator or
     /// closing bracket.
-    pub fn parse_seq_to_before_end<T, F>(
+    pub fn parse_seq_to_before_end<T>(
         &mut self,
         ket: &TokenKind,
         sep: SeqSep,
-        f: F,
-    ) -> PResult<'a, (Vec<T>, bool)>
-        where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>
-    {
+        f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
+    ) -> PResult<'a, (Vec<T>, bool, bool)> {
         self.parse_seq_to_before_tokens(&[ket], sep, TokenExpectType::Expect, f)
     }
 
-    crate fn parse_seq_to_before_tokens<T, F>(
+    fn expect_any_with_type(&mut self, kets: &[&TokenKind], expect: TokenExpectType) -> bool {
+        kets.iter().any(|k| {
+            match expect {
+                TokenExpectType::Expect => self.check(k),
+                TokenExpectType::NoExpect => self.token == **k,
+            }
+        })
+    }
+
+    crate fn parse_seq_to_before_tokens<T>(
         &mut self,
         kets: &[&TokenKind],
         sep: SeqSep,
         expect: TokenExpectType,
-        mut f: F,
-    ) -> PResult<'a, (Vec<T>, bool /* recovered */)>
-        where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>
-    {
+        mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
+    ) -> PResult<'a, (Vec<T>, bool /* trailing */, bool /* recovered */)> {
         let mut first = true;
         let mut recovered = false;
+        let mut trailing = false;
         let mut v = vec![];
-        while !kets.iter().any(|k| {
-                match expect {
-                    TokenExpectType::Expect => self.check(k),
-                    TokenExpectType::NoExpect => self.token == **k,
-                }
-            }) {
-            match self.token.kind {
-                token::CloseDelim(..) | token::Eof => break,
-                _ => {}
-            };
+        while !self.expect_any_with_type(kets, expect) {
+            if let token::CloseDelim(..) | token::Eof = self.token.kind {
+                break
+            }
             if let Some(ref t) = sep.sep {
                 if first {
                     first = false;
@@ -973,12 +972,8 @@ impl<'a> Parser<'a> {
                     }
                 }
             }
-            if sep.trailing_sep_allowed && kets.iter().any(|k| {
-                match expect {
-                    TokenExpectType::Expect => self.check(k),
-                    TokenExpectType::NoExpect => self.token == **k,
-                }
-            }) {
+            if sep.trailing_sep_allowed && self.expect_any_with_type(kets, expect) {
+                trailing = true;
                 break;
             }
 
@@ -986,27 +981,45 @@ impl<'a> Parser<'a> {
             v.push(t);
         }
 
-        Ok((v, recovered))
+        Ok((v, trailing, recovered))
     }
 
     /// Parses a sequence, including the closing delimiter. The function
     /// `f` must consume tokens until reaching the next separator or
     /// closing bracket.
-    fn parse_unspanned_seq<T, F>(
+    fn parse_unspanned_seq<T>(
         &mut self,
         bra: &TokenKind,
         ket: &TokenKind,
         sep: SeqSep,
-        f: F,
-    ) -> PResult<'a, Vec<T>> where
-        F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
-    {
+        f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
+    ) -> PResult<'a, (Vec<T>, bool)> {
         self.expect(bra)?;
-        let (result, recovered) = self.parse_seq_to_before_end(ket, sep, f)?;
+        let (result, trailing, recovered) = self.parse_seq_to_before_end(ket, sep, f)?;
         if !recovered {
             self.eat(ket);
         }
-        Ok(result)
+        Ok((result, trailing))
+    }
+
+    fn parse_delim_comma_seq<T>(
+        &mut self,
+        delim: DelimToken,
+        f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
+    ) -> PResult<'a, (Vec<T>, bool)> {
+        self.parse_unspanned_seq(
+            &token::OpenDelim(delim),
+            &token::CloseDelim(delim),
+            SeqSep::trailing_allowed(token::Comma),
+            f,
+        )
+    }
+
+    fn parse_paren_comma_seq<T>(
+        &mut self,
+        f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
+    ) -> PResult<'a, (Vec<T>, bool)> {
+        self.parse_delim_comma_seq(token::Paren, f)
     }
 
     /// Advance the parser by one token
@@ -1804,15 +1817,7 @@ impl<'a> Parser<'a> {
                 AngleBracketedArgs { args, constraints, span }.into()
             } else {
                 // `(T, U) -> R`
-                self.bump(); // `(`
-                let (inputs, recovered) = self.parse_seq_to_before_tokens(
-                    &[&token::CloseDelim(token::Paren)],
-                    SeqSep::trailing_allowed(token::Comma),
-                    TokenExpectType::Expect,
-                    |p| p.parse_ty())?;
-                if !recovered {
-                    self.bump(); // `)`
-                }
+                let (inputs, _) = self.parse_paren_comma_seq(|p| p.parse_ty())?;
                 let span = lo.to(self.prev_span);
                 let output = if self.eat(&token::RArrow) {
                     Some(self.parse_ty_common(false, false, false)?)
@@ -2516,12 +2521,7 @@ impl<'a> Parser<'a> {
         Ok(match self.token.kind {
             token::OpenDelim(token::Paren) => {
                 // Method call `expr.f()`
-                let mut args = self.parse_unspanned_seq(
-                    &token::OpenDelim(token::Paren),
-                    &token::CloseDelim(token::Paren),
-                    SeqSep::trailing_allowed(token::Comma),
-                    |p| Ok(p.parse_expr()?)
-                )?;
+                let mut args = self.parse_paren_expr_seq()?;
                 args.insert(0, self_arg);
 
                 let span = lo.to(self.prev_span);
@@ -2606,12 +2606,7 @@ impl<'a> Parser<'a> {
             match self.token.kind {
                 // expr(...)
                 token::OpenDelim(token::Paren) => {
-                    let seq = self.parse_unspanned_seq(
-                        &token::OpenDelim(token::Paren),
-                        &token::CloseDelim(token::Paren),
-                        SeqSep::trailing_allowed(token::Comma),
-                        |p| Ok(p.parse_expr()?)
-                    ).map(|es| {
+                    let seq = self.parse_paren_expr_seq().map(|es| {
                         let nd = self.mk_call(e, es);
                         let hi = self.prev_span;
                         self.mk_expr(lo.to(hi), nd, ThinVec::new())
@@ -2635,6 +2630,10 @@ impl<'a> Parser<'a> {
         return Ok(e);
     }
 
+    fn parse_paren_expr_seq(&mut self) -> PResult<'a, Vec<P<Expr>>> {
+        self.parse_paren_comma_seq(|p| p.parse_expr()).map(|(r, _)| r)
+    }
+
     crate fn process_potential_macro_variable(&mut self) {
         self.token = match self.token.kind {
             token::Dollar if self.token.span.ctxt() != SyntaxContext::empty() &&
@@ -3536,122 +3535,6 @@ impl<'a> Parser<'a> {
         };
     }
 
-    // Parses a parenthesized list of patterns like
-    // `()`, `(p)`, `(p,)`, `(p, q)`, or `(p, .., q)`. Returns:
-    // - a vector of the patterns that were parsed
-    // - an option indicating the index of the `..` element
-    // - a boolean indicating whether a trailing comma was present.
-    // Trailing commas are significant because (p) and (p,) are different patterns.
-    fn parse_parenthesized_pat_list(&mut self) -> PResult<'a, (Vec<P<Pat>>, Option<usize>, bool)> {
-        self.expect(&token::OpenDelim(token::Paren))?;
-        let result = match self.parse_pat_list() {
-            Ok(result) => result,
-            Err(mut err) => { // recover from parse error in tuple pattern list
-                err.emit();
-                self.consume_block(token::Paren);
-                return Ok((vec![], Some(0), false));
-            }
-        };
-        self.expect(&token::CloseDelim(token::Paren))?;
-        Ok(result)
-    }
-
-    fn parse_pat_list(&mut self) -> PResult<'a, (Vec<P<Pat>>, Option<usize>, bool)> {
-        let mut fields = Vec::new();
-        let mut ddpos = None;
-        let mut prev_dd_sp = None;
-        let mut trailing_comma = false;
-        loop {
-            if self.eat(&token::DotDot) {
-                if ddpos.is_none() {
-                    ddpos = Some(fields.len());
-                    prev_dd_sp = Some(self.prev_span);
-                } else {
-                    // Emit a friendly error, ignore `..` and continue parsing
-                    let mut err = self.struct_span_err(
-                        self.prev_span,
-                        "`..` can only be used once per tuple or tuple struct pattern",
-                    );
-                    err.span_label(self.prev_span, "can only be used once per pattern");
-                    if let Some(sp) = prev_dd_sp {
-                        err.span_label(sp, "previously present here");
-                    }
-                    err.emit();
-                }
-            } else if !self.check(&token::CloseDelim(token::Paren)) {
-                fields.push(self.parse_pat(None)?);
-            } else {
-                break
-            }
-
-            trailing_comma = self.eat(&token::Comma);
-            if !trailing_comma {
-                break
-            }
-        }
-
-        if ddpos == Some(fields.len()) && trailing_comma {
-            // `..` needs to be followed by `)` or `, pat`, `..,)` is disallowed.
-            let msg = "trailing comma is not permitted after `..`";
-            self.struct_span_err(self.prev_span, msg)
-                .span_label(self.prev_span, msg)
-                .emit();
-        }
-
-        Ok((fields, ddpos, trailing_comma))
-    }
-
-    fn parse_pat_vec_elements(
-        &mut self,
-    ) -> PResult<'a, (Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>)> {
-        let mut before = Vec::new();
-        let mut slice = None;
-        let mut after = Vec::new();
-        let mut first = true;
-        let mut before_slice = true;
-
-        while self.token != token::CloseDelim(token::Bracket) {
-            if first {
-                first = false;
-            } else {
-                self.expect(&token::Comma)?;
-
-                if self.token == token::CloseDelim(token::Bracket)
-                        && (before_slice || !after.is_empty()) {
-                    break
-                }
-            }
-
-            if before_slice {
-                if self.eat(&token::DotDot) {
-
-                    if self.check(&token::Comma) ||
-                            self.check(&token::CloseDelim(token::Bracket)) {
-                        slice = Some(P(Pat {
-                            id: ast::DUMMY_NODE_ID,
-                            node: PatKind::Wild,
-                            span: self.prev_span,
-                        }));
-                        before_slice = false;
-                    }
-                    continue
-                }
-            }
-
-            let subpat = self.parse_pat(None)?;
-            if before_slice && self.eat(&token::DotDot) {
-                slice = Some(subpat);
-                before_slice = false;
-            } else if before_slice {
-                before.push(subpat);
-            } else {
-                after.push(subpat);
-            }
-        }
-
-        Ok((before, slice, after))
-    }
-
     fn parse_pat_field(
         &mut self,
         lo: Span,
@@ -3847,20 +3730,34 @@ impl<'a> Parser<'a> {
         }
     }
 
-    // helper function to decide whether to parse as ident binding or to try to do
-    // something more complex like range patterns
+    /// Is the current token suitable as the start of a range patterns end?
+    fn is_pat_range_end_start(&self) -> bool {
+        self.token.is_path_start() // e.g. `MY_CONST`;
+            || self.token == token::Dot // e.g. `.5` for recovery;
+            || self.token.can_begin_literal_or_bool() // e.g. `42`.
+    }
+
+    // Helper function to decide whether to parse as ident binding
+    // or to try to do something more complex like range patterns.
     fn parse_as_ident(&mut self) -> bool {
         self.look_ahead(1, |t| match t.kind {
             token::OpenDelim(token::Paren) | token::OpenDelim(token::Brace) |
-            token::DotDotDot | token::DotDotEq | token::ModSep | token::Not => Some(false),
-            // ensure slice patterns [a, b.., c] and [a, b, c..] don't go into the
-            // range pattern branch
-            token::DotDot => None,
-            _ => Some(true),
-        }).unwrap_or_else(|| self.look_ahead(2, |t| match t.kind {
-            token::Comma | token::CloseDelim(token::Bracket) => true,
-            _ => false,
-        }))
+            token::DotDotDot | token::DotDotEq | token::DotDot |
+            token::ModSep | token::Not => false,
+            _ => true,
+        })
+    }
+
+    /// Parse and throw away a parentesized comma separated
+    /// sequence of patterns until `)` is reached.
+    fn skip_pat_list(&mut self) -> PResult<'a, ()> {
+        while !self.check(&token::CloseDelim(token::Paren)) {
+            self.parse_pat(None)?;
+            if !self.eat(&token::Comma) {
+                return Ok(())
+            }
+        }
+        Ok(())
     }
 
     /// A wrapper around `parse_pat` with some special error handling for the
@@ -3876,7 +3773,7 @@ impl<'a> Parser<'a> {
             // later.
             let comma_span = self.token.span;
             self.bump();
-            if let Err(mut err) = self.parse_pat_list() {
+            if let Err(mut err) = self.skip_pat_list() {
                 // We didn't expect this to work anyway; we just wanted
                 // to advance to the end of the comma-sequence so we know
                 // the span to suggest parenthesizing
@@ -3908,6 +3805,53 @@ impl<'a> Parser<'a> {
         self.parse_pat_with_range_pat(true, expected)
     }
 
+    /// Parse a range-to pattern, e.g. `..X` and `..=X` for recovery.
+    fn parse_pat_range_to(&mut self, re: RangeEnd, form: &str) -> PResult<'a, PatKind> {
+        let lo = self.prev_span;
+        let end = self.parse_pat_range_end()?;
+        let range_span = lo.to(end.span);
+        let begin = self.mk_expr(range_span, ExprKind::Err, ThinVec::new());
+
+        self.diagnostic()
+            .struct_span_err(range_span, &format!("`{}X` range patterns are not supported", form))
+            .span_suggestion(
+                range_span,
+                "try using the minimum value for the type",
+                format!("MIN{}{}", form, pprust::expr_to_string(&end)),
+                Applicability::HasPlaceholders,
+            )
+            .emit();
+
+        Ok(PatKind::Range(begin, end, respan(lo, re)))
+    }
+
+    /// Parse the end of a `X..Y`, `X..=Y`, or `X...Y` range pattern  or recover
+    /// if that end is missing treating it as `X..`, `X..=`, or `X...` respectively.
+    fn parse_pat_range_end_opt(&mut self, begin: &Expr, form: &str) -> PResult<'a, P<Expr>> {
+        if self.is_pat_range_end_start() {
+            // Parsing e.g. `X..=Y`.
+            self.parse_pat_range_end()
+        } else {
+            // Parsing e.g. `X..`.
+            let range_span = begin.span.to(self.prev_span);
+
+            self.diagnostic()
+                .struct_span_err(
+                    range_span,
+                    &format!("`X{}` range patterns are not supported", form),
+                )
+                .span_suggestion(
+                    range_span,
+                    "try using the maximum value for the type",
+                    format!("{}{}MAX", pprust::expr_to_string(&begin), form),
+                    Applicability::HasPlaceholders,
+                )
+                .emit();
+
+            Ok(self.mk_expr(range_span, ExprKind::Err, ThinVec::new()))
+        }
+    }
+
     /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
     /// allowed).
     fn parse_pat_with_range_pat(
@@ -3934,20 +3878,41 @@ impl<'a> Parser<'a> {
                 pat = PatKind::Ref(subpat, mutbl);
             }
             token::OpenDelim(token::Paren) => {
-                // Parse (pat,pat,pat,...) as tuple pattern
-                let (fields, ddpos, trailing_comma) = self.parse_parenthesized_pat_list()?;
-                pat = if fields.len() == 1 && ddpos.is_none() && !trailing_comma {
+                // Parse a tuple or parenthesis pattern.
+                let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| p.parse_pat(None))?;
+
+                // Here, `(pat,)` is a tuple pattern.
+                // For backward compatibility, `(..)` is a tuple pattern as well.
+                pat = if fields.len() == 1 && !(trailing_comma || fields[0].is_rest()) {
                     PatKind::Paren(fields.into_iter().nth(0).unwrap())
                 } else {
-                    PatKind::Tuple(fields, ddpos)
+                    PatKind::Tuple(fields)
                 };
             }
             token::OpenDelim(token::Bracket) => {
-                // Parse [pat,pat,...] as slice pattern
+                // Parse `[pat, pat,...]` as a slice pattern.
+                let (slice, _) = self.parse_delim_comma_seq(token::Bracket, |p| p.parse_pat(None))?;
+                pat = PatKind::Slice(slice);
+            }
+            token::DotDot => {
+                self.bump();
+                pat = if self.is_pat_range_end_start() {
+                    // Parse `..42` for recovery.
+                    self.parse_pat_range_to(RangeEnd::Excluded, "..")?
+                } else {
+                    // A rest pattern `..`.
+                    PatKind::Rest
+                };
+            }
+            token::DotDotEq => {
+                // Parse `..=42` for recovery.
                 self.bump();
-                let (before, slice, after) = self.parse_pat_vec_elements()?;
-                self.expect(&token::CloseDelim(token::Bracket))?;
-                pat = PatKind::Slice(before, slice, after);
+                pat = self.parse_pat_range_to(RangeEnd::Included(RangeSyntax::DotDotEq), "..=")?;
+            }
+            token::DotDotDot => {
+                // Parse `...42` for recovery.
+                self.bump();
+                pat = self.parse_pat_range_to(RangeEnd::Included(RangeSyntax::DotDotDot), "...")?;
             }
             // At this point, token != &, &&, (, [
             _ => if self.eat_keyword(kw::Underscore) {
@@ -4004,10 +3969,10 @@ impl<'a> Parser<'a> {
                         pat = PatKind::Mac(mac);
                     }
                     token::DotDotDot | token::DotDotEq | token::DotDot => {
-                        let end_kind = match self.token.kind {
-                            token::DotDot => RangeEnd::Excluded,
-                            token::DotDotDot => RangeEnd::Included(RangeSyntax::DotDotDot),
-                            token::DotDotEq => RangeEnd::Included(RangeSyntax::DotDotEq),
+                        let (end_kind, form) = match self.token.kind {
+                            token::DotDot => (RangeEnd::Excluded, ".."),
+                            token::DotDotDot => (RangeEnd::Included(RangeSyntax::DotDotDot), "..."),
+                            token::DotDotEq => (RangeEnd::Included(RangeSyntax::DotDotEq), "..="),
                             _ => panic!("can only parse `..`/`...`/`..=` for ranges \
                                          (checked above)"),
                         };
@@ -4016,9 +3981,8 @@ impl<'a> Parser<'a> {
                         let span = lo.to(self.prev_span);
                         let begin = self.mk_expr(span, ExprKind::Path(qself, path), ThinVec::new());
                         self.bump();
-                        let end = self.parse_pat_range_end()?;
-                        let op = Spanned { span: op_span, node: end_kind };
-                        pat = PatKind::Range(begin, end, op);
+                        let end = self.parse_pat_range_end_opt(&begin, form)?;
+                        pat = PatKind::Range(begin, end, respan(op_span, end_kind));
                     }
                     token::OpenDelim(token::Brace) => {
                         if qself.is_some() {
@@ -4045,8 +4009,8 @@ impl<'a> Parser<'a> {
                             return Err(err);
                         }
                         // Parse tuple struct or enum pattern
-                        let (fields, ddpos, _) = self.parse_parenthesized_pat_list()?;
-                        pat = PatKind::TupleStruct(path, fields, ddpos)
+                        let (fields, _) = self.parse_paren_comma_seq(|p| p.parse_pat(None))?;
+                        pat = PatKind::TupleStruct(path, fields)
                     }
                     _ => pat = PatKind::Path(qself, path),
                 }
@@ -4057,19 +4021,18 @@ impl<'a> Parser<'a> {
                         let op_span = self.token.span;
                         if self.check(&token::DotDot) || self.check(&token::DotDotEq) ||
                                 self.check(&token::DotDotDot) {
-                            let end_kind = if self.eat(&token::DotDotDot) {
-                                RangeEnd::Included(RangeSyntax::DotDotDot)
+                            let (end_kind, form) = if self.eat(&token::DotDotDot) {
+                                (RangeEnd::Included(RangeSyntax::DotDotDot), "...")
                             } else if self.eat(&token::DotDotEq) {
-                                RangeEnd::Included(RangeSyntax::DotDotEq)
+                                (RangeEnd::Included(RangeSyntax::DotDotEq), "..=")
                             } else if self.eat(&token::DotDot) {
-                                RangeEnd::Excluded
+                                (RangeEnd::Excluded, "..")
                             } else {
                                 panic!("impossible case: we already matched \
                                         on a range-operator token")
                             };
-                            let end = self.parse_pat_range_end()?;
-                            let op = Spanned { span: op_span, node: end_kind };
-                            pat = PatKind::Range(begin, end, op);
+                            let end = self.parse_pat_range_end_opt(&begin, form)?;
+                            pat = PatKind::Range(begin, end, respan(op_span, end_kind))
                         } else {
                             pat = PatKind::Lit(begin);
                         }
@@ -5359,59 +5322,48 @@ impl<'a> Parser<'a> {
 
     fn parse_fn_args(&mut self, named_args: bool, allow_c_variadic: bool)
                      -> PResult<'a, (Vec<Arg> , bool)> {
-        self.expect(&token::OpenDelim(token::Paren))?;
-
         let sp = self.token.span;
         let mut c_variadic = false;
-        let (args, recovered): (Vec<Option<Arg>>, bool) =
-            self.parse_seq_to_before_end(
-                &token::CloseDelim(token::Paren),
-                SeqSep::trailing_allowed(token::Comma),
-                |p| {
-                    let do_not_enforce_named_arguments_for_c_variadic =
-                        |token: &token::Token| -> bool {
-                            if token == &token::DotDotDot {
-                                false
-                            } else {
-                                named_args
-                            }
-                        };
-                    match p.parse_arg_general(
-                        false,
-                        allow_c_variadic,
-                        do_not_enforce_named_arguments_for_c_variadic
-                    ) {
-                        Ok(arg) => {
-                            if let TyKind::CVarArgs = arg.ty.node {
-                                c_variadic = true;
-                                if p.token != token::CloseDelim(token::Paren) {
-                                    let span = p.token.span;
-                                    p.span_err(span,
-                                        "`...` must be the last argument of a C-variadic function");
-                                    Ok(None)
-                                } else {
-                                    Ok(Some(arg))
-                                }
-                            } else {
-                                Ok(Some(arg))
-                            }
-                        },
-                        Err(mut e) => {
-                            e.emit();
-                            let lo = p.prev_span;
-                            // Skip every token until next possible arg or end.
-                            p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(token::Paren)]);
-                            // Create a placeholder argument for proper arg count (issue #34264).
-                            let span = lo.to(p.prev_span);
-                            Ok(Some(dummy_arg(Ident::new(kw::Invalid, span))))
+        let (args, _): (Vec<Option<Arg>>, _) = self.parse_paren_comma_seq(|p| {
+            let do_not_enforce_named_arguments_for_c_variadic =
+                |token: &token::Token| -> bool {
+                    if token == &token::DotDotDot {
+                        false
+                    } else {
+                        named_args
+                    }
+                };
+            match p.parse_arg_general(
+                false,
+                allow_c_variadic,
+                do_not_enforce_named_arguments_for_c_variadic
+            ) {
+                Ok(arg) => {
+                    if let TyKind::CVarArgs = arg.ty.node {
+                        c_variadic = true;
+                        if p.token != token::CloseDelim(token::Paren) {
+                            let span = p.token.span;
+                            p.span_err(span,
+                                "`...` must be the last argument of a C-variadic function");
+                            Ok(None)
+                        } else {
+                            Ok(Some(arg))
                         }
+                    } else {
+                        Ok(Some(arg))
                     }
+                },
+                Err(mut e) => {
+                    e.emit();
+                    let lo = p.prev_span;
+                    // Skip every token until next possible arg or end.
+                    p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(token::Paren)]);
+                    // Create a placeholder argument for proper arg count (issue #34264).
+                    let span = lo.to(p.prev_span);
+                    Ok(Some(dummy_arg(Ident::new(kw::Invalid, span))))
                 }
-            )?;
-
-        if !recovered {
-            self.eat(&token::CloseDelim(token::Paren));
-        }
+            }
+        })?;
 
         let args: Vec<_> = args.into_iter().filter_map(|x| x).collect();
 
@@ -5573,7 +5525,7 @@ impl<'a> Parser<'a> {
                 (vec![self_arg], false)
             } else if self.eat(&token::Comma) {
                 let mut fn_inputs = vec![self_arg];
-                let (mut input, recovered) = self.parse_seq_to_before_end(
+                let (mut input, _, recovered) = self.parse_seq_to_before_end(
                     &token::CloseDelim(token::Paren), sep, parse_arg_fn)?;
                 fn_inputs.append(&mut input);
                 (fn_inputs, recovered)
@@ -5584,7 +5536,9 @@ impl<'a> Parser<'a> {
                 }
             }
         } else {
-            self.parse_seq_to_before_end(&token::CloseDelim(token::Paren), sep, parse_arg_fn)?
+            let (input, _, recovered) =
+                self.parse_seq_to_before_end(&token::CloseDelim(token::Paren), sep, parse_arg_fn)?;
+            (input, recovered)
         };
 
         if !recovered {
@@ -6185,26 +6139,20 @@ impl<'a> Parser<'a> {
     fn parse_tuple_struct_body(&mut self) -> PResult<'a, Vec<StructField>> {
         // This is the case where we find `struct Foo<T>(T) where T: Copy;`
         // Unit like structs are handled in parse_item_struct function
-        let fields = self.parse_unspanned_seq(
-            &token::OpenDelim(token::Paren),
-            &token::CloseDelim(token::Paren),
-            SeqSep::trailing_allowed(token::Comma),
-            |p| {
-                let attrs = p.parse_outer_attributes()?;
-                let lo = p.token.span;
-                let vis = p.parse_visibility(true)?;
-                let ty = p.parse_ty()?;
-                Ok(StructField {
-                    span: lo.to(ty.span),
-                    vis,
-                    ident: None,
-                    id: ast::DUMMY_NODE_ID,
-                    ty,
-                    attrs,
-                })
-            })?;
-
-        Ok(fields)
+        self.parse_paren_comma_seq(|p| {
+            let attrs = p.parse_outer_attributes()?;
+            let lo = p.token.span;
+            let vis = p.parse_visibility(true)?;
+            let ty = p.parse_ty()?;
+            Ok(StructField {
+                span: lo.to(ty.span),
+                vis,
+                ident: None,
+                id: ast::DUMMY_NODE_ID,
+                ty,
+                attrs,
+            })
+        }).map(|(r, _)| r)
     }
 
     /// Parses a structure field declaration.
@@ -7786,11 +7734,8 @@ impl<'a> Parser<'a> {
     /// USE_TREE_LIST = Ø | (USE_TREE `,`)* USE_TREE [`,`]
     /// ```
     fn parse_use_tree_list(&mut self) -> PResult<'a, Vec<(UseTree, ast::NodeId)>> {
-        self.parse_unspanned_seq(&token::OpenDelim(token::Brace),
-                                 &token::CloseDelim(token::Brace),
-                                 SeqSep::trailing_allowed(token::Comma), |this| {
-            Ok((this.parse_use_tree()?, ast::DUMMY_NODE_ID))
-        })
+        self.parse_delim_comma_seq(token::Brace, |p| Ok((p.parse_use_tree()?, ast::DUMMY_NODE_ID)))
+            .map(|(r, _)| r)
     }
 
     fn parse_rename(&mut self) -> PResult<'a, Option<Ident>> {
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 16e0bace92584..36cd656f7277e 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -2369,22 +2369,10 @@ impl<'a> State<'a> {
                     self.print_pat(p);
                 }
             }
-            PatKind::TupleStruct(ref path, ref elts, ddpos) => {
+            PatKind::TupleStruct(ref path, ref elts) => {
                 self.print_path(path, true, 0);
                 self.popen();
-                if let Some(ddpos) = ddpos {
-                    self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(p));
-                    if ddpos != 0 {
-                        self.word_space(",");
-                    }
-                    self.s.word("..");
-                    if ddpos != elts.len() {
-                        self.s.word(",");
-                        self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(p));
-                    }
-                } else {
-                    self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(p));
-                }
+                self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(p));
                 self.pclose();
             }
             PatKind::Path(None, ref path) => {
@@ -2416,23 +2404,11 @@ impl<'a> State<'a> {
                 self.s.space();
                 self.s.word("}");
             }
-            PatKind::Tuple(ref elts, ddpos) => {
+            PatKind::Tuple(ref elts) => {
                 self.popen();
-                if let Some(ddpos) = ddpos {
-                    self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(p));
-                    if ddpos != 0 {
-                        self.word_space(",");
-                    }
-                    self.s.word("..");
-                    if ddpos != elts.len() {
-                        self.s.word(",");
-                        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 {
-                        self.s.word(",");
-                    }
+                self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(p));
+                if elts.len() == 1 {
+                    self.s.word(",");
                 }
                 self.pclose();
             }
@@ -2458,26 +2434,12 @@ impl<'a> State<'a> {
                 }
                 self.print_expr(end);
             }
-            PatKind::Slice(ref before, ref slice, ref after) => {
+            PatKind::Slice(ref elts) => {
                 self.s.word("[");
-                self.commasep(Inconsistent,
-                                   &before[..],
-                                   |s, p| s.print_pat(p));
-                if let Some(ref p) = *slice {
-                    if !before.is_empty() { self.word_space(","); }
-                    if let PatKind::Wild = p.node {
-                        // Print nothing
-                    } else {
-                        self.print_pat(p);
-                    }
-                    self.s.word("..");
-                    if !after.is_empty() { self.word_space(","); }
-                }
-                self.commasep(Inconsistent,
-                                   &after[..],
-                                   |s, p| s.print_pat(p));
+                self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(p));
                 self.s.word("]");
             }
+            PatKind::Rest => self.s.word(".."),
             PatKind::Paren(ref inner) => {
                 self.popen();
                 self.print_pat(inner);
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 9ec9550f93ab9..ff6440fb9dceb 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -428,9 +428,9 @@ pub fn walk_assoc_ty_constraint<'a, V: Visitor<'a>>(visitor: &mut V,
 
 pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
     match pattern.node {
-        PatKind::TupleStruct(ref path, ref children, _) => {
+        PatKind::TupleStruct(ref path, ref elems) => {
             visitor.visit_path(path, pattern.id);
-            walk_list!(visitor, visit_pat, children);
+            walk_list!(visitor, visit_pat, elems);
         }
         PatKind::Path(ref opt_qself, ref path) => {
             if let Some(ref qself) = *opt_qself {
@@ -446,8 +446,8 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
                 visitor.visit_pat(&field.node.pat)
             }
         }
-        PatKind::Tuple(ref tuple_elements, _) => {
-            walk_list!(visitor, visit_pat, tuple_elements);
+        PatKind::Tuple(ref elems) => {
+            walk_list!(visitor, visit_pat, elems);
         }
         PatKind::Box(ref subpattern) |
         PatKind::Ref(ref subpattern, _) |
@@ -463,11 +463,9 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
             visitor.visit_expr(lower_bound);
             visitor.visit_expr(upper_bound);
         }
-        PatKind::Wild => (),
-        PatKind::Slice(ref prepatterns, ref slice_pattern, ref postpatterns) => {
-            walk_list!(visitor, visit_pat, prepatterns);
-            walk_list!(visitor, visit_pat, slice_pattern);
-            walk_list!(visitor, visit_pat, postpatterns);
+        PatKind::Wild | PatKind::Rest => {},
+        PatKind::Slice(ref elems) => {
+            walk_list!(visitor, visit_pat, elems);
         }
         PatKind::Mac(ref mac) => visitor.visit_mac(mac),
     }
diff --git a/src/test/mir-opt/uniform_array_move_out.rs b/src/test/mir-opt/uniform_array_move_out.rs
index fac5ef3b03ce1..c249154c71e23 100644
--- a/src/test/mir-opt/uniform_array_move_out.rs
+++ b/src/test/mir-opt/uniform_array_move_out.rs
@@ -8,7 +8,7 @@ fn move_out_from_end() {
 
 fn move_out_by_subslice() {
     let a = [box 1, box 2];
-    let [_y..] = a;
+    let [_y @ ..] = a;
 }
 
 fn main() {
diff --git a/src/test/ui/array-slice-vec/vec-matching-fold.rs b/src/test/ui/array-slice-vec/vec-matching-fold.rs
index 2b19c49c42700..f416160db2422 100644
--- a/src/test/ui/array-slice-vec/vec-matching-fold.rs
+++ b/src/test/ui/array-slice-vec/vec-matching-fold.rs
@@ -11,7 +11,7 @@ fn foldl<T, U, F>(values: &[T],
     U: Clone+Debug, T:Debug,
     F: FnMut(U, &T) -> U,
 {    match values {
-        &[ref head, ref tail..] =>
+        &[ref head, ref tail @ ..] =>
             foldl(tail, function(initial, head), function),
         &[] => {
             // FIXME: call guards
@@ -28,7 +28,7 @@ fn foldr<T, U, F>(values: &[T],
     F: FnMut(&T, U) -> U,
 {
     match values {
-        &[ref head.., ref tail] =>
+        &[ref head @ .., ref tail] =>
             foldr(head, function(tail, initial), function),
         &[] => {
             // FIXME: call guards
diff --git a/src/test/ui/array-slice-vec/vec-matching-legal-tail-element-borrow.rs b/src/test/ui/array-slice-vec/vec-matching-legal-tail-element-borrow.rs
index bce03b3375e51..f0602c328b071 100644
--- a/src/test/ui/array-slice-vec/vec-matching-legal-tail-element-borrow.rs
+++ b/src/test/ui/array-slice-vec/vec-matching-legal-tail-element-borrow.rs
@@ -8,7 +8,7 @@ pub fn main() {
     let x: &[isize] = &[1, 2, 3, 4, 5];
     if !x.is_empty() {
         let el = match x {
-            &[1, ref tail..] => &tail[0],
+            &[1, ref tail @ ..] => &tail[0],
             _ => unreachable!()
         };
         println!("{}", *el);
diff --git a/src/test/ui/array-slice-vec/vec-matching.rs b/src/test/ui/array-slice-vec/vec-matching.rs
index a37c25160fa76..49c736bd72847 100644
--- a/src/test/ui/array-slice-vec/vec-matching.rs
+++ b/src/test/ui/array-slice-vec/vec-matching.rs
@@ -14,7 +14,7 @@ fn a() {
 fn b() {
     let x = [1, 2, 3];
     match x {
-        [a, b, c..] => {
+        [a, b, c @ ..] => {
             assert_eq!(a, 1);
             assert_eq!(b, 2);
             let expected: &[_] = &[3];
@@ -22,7 +22,7 @@ fn b() {
         }
     }
     match x {
-        [a.., b, c] => {
+        [a @ .., b, c] => {
             let expected: &[_] = &[1];
             assert_eq!(a, expected);
             assert_eq!(b, 2);
@@ -30,7 +30,7 @@ fn b() {
         }
     }
     match x {
-        [a, b.., c] => {
+        [a, b @ .., c] => {
             assert_eq!(a, 1);
             let expected: &[_] = &[2];
             assert_eq!(b, expected);
@@ -50,7 +50,7 @@ fn b() {
 fn b_slice() {
     let x : &[_] = &[1, 2, 3];
     match x {
-        &[a, b, ref c..] => {
+        &[a, b, ref c @ ..] => {
             assert_eq!(a, 1);
             assert_eq!(b, 2);
             let expected: &[_] = &[3];
@@ -59,7 +59,7 @@ fn b_slice() {
         _ => unreachable!()
     }
     match x {
-        &[ref a.., b, c] => {
+        &[ref a @ .., b, c] => {
             let expected: &[_] = &[1];
             assert_eq!(a, expected);
             assert_eq!(b, 2);
@@ -68,7 +68,7 @@ fn b_slice() {
         _ => unreachable!()
     }
     match x {
-        &[a, ref b.., c] => {
+        &[a, ref b @ .., c] => {
             assert_eq!(a, 1);
             let expected: &[_] = &[2];
             assert_eq!(b, expected);
@@ -134,20 +134,6 @@ fn e() {
     assert_eq!(c, 1);
 }
 
-fn f() {
-    let x = &[1, 2, 3, 4, 5];
-    let [a, [b, [c, ..].., d].., e] = *x;
-    assert_eq!((a, b, c, d, e), (1, 2, 3, 4, 5));
-
-    let x: &[isize] = x;
-    let (a, b, c, d, e) = match *x {
-        [a, [b, [c, ..].., d].., e] => (a, b, c, d, e),
-        _ => unimplemented!()
-    };
-
-    assert_eq!((a, b, c, d, e), (1, 2, 3, 4, 5));
-}
-
 pub fn main() {
     a();
     b();
@@ -155,5 +141,4 @@ pub fn main() {
     c();
     d();
     e();
-    f();
 }
diff --git a/src/test/ui/array-slice-vec/vec-tail-matching.rs b/src/test/ui/array-slice-vec/vec-tail-matching.rs
index 84d246dff825c..3c7b160dcc540 100644
--- a/src/test/ui/array-slice-vec/vec-tail-matching.rs
+++ b/src/test/ui/array-slice-vec/vec-tail-matching.rs
@@ -13,14 +13,14 @@ pub fn main() {
         Foo { string: "baz" }
     ];
     match x {
-        [ref first, ref tail..] => {
+        [ref first, ref tail @ ..] => {
             assert_eq!(first.string, "foo");
             assert_eq!(tail.len(), 2);
             assert_eq!(tail[0].string, "bar");
             assert_eq!(tail[1].string, "baz");
 
             match *(tail as &[_]) {
-                [Foo { .. }, _, Foo { .. }, ref _tail..] => {
+                [Foo { .. }, _, Foo { .. }, ref _tail @ ..] => {
                     unreachable!();
                 }
                 [Foo { string: ref a }, Foo { string: ref b }] => {
diff --git a/src/test/ui/associated-path-shl.rs b/src/test/ui/associated-path-shl.rs
index d159082ab7ad5..20a6fd83faa32 100644
--- a/src/test/ui/associated-path-shl.rs
+++ b/src/test/ui/associated-path-shl.rs
@@ -5,6 +5,5 @@ fn main() {
     let _ = <<A>::B>::C; //~ ERROR cannot find type `A` in this scope
     let <<A>::B>::C; //~ ERROR cannot find type `A` in this scope
     let 0 ..= <<A>::B>::C; //~ ERROR cannot find type `A` in this scope
-                           //~^ ERROR only char and numeric types are allowed in range patterns
     <<A>::B>::C; //~ ERROR cannot find type `A` in this scope
 }
diff --git a/src/test/ui/associated-path-shl.stderr b/src/test/ui/associated-path-shl.stderr
index 23918ed2e3912..71ee93f4835fb 100644
--- a/src/test/ui/associated-path-shl.stderr
+++ b/src/test/ui/associated-path-shl.stderr
@@ -23,21 +23,11 @@ LL |     let 0 ..= <<A>::B>::C;
    |                 ^ not found in this scope
 
 error[E0412]: cannot find type `A` in this scope
-  --> $DIR/associated-path-shl.rs:9:7
+  --> $DIR/associated-path-shl.rs:8:7
    |
 LL |     <<A>::B>::C;
    |       ^ not found in this scope
 
-error[E0029]: only char and numeric types are allowed in range patterns
-  --> $DIR/associated-path-shl.rs:7:15
-   |
-LL |     let 0 ..= <<A>::B>::C;
-   |               ^^^^^^^^^^^ ranges require char or numeric types
-   |
-   = note: start type: {integer}
-   = note: end type: [type error]
-
-error: aborting due to 6 previous errors
+error: aborting due to 5 previous errors
 
-Some errors have detailed explanations: E0029, E0412.
-For more information about an error, try `rustc --explain E0029`.
+For more information about this error, try `rustc --explain E0412`.
diff --git a/src/test/ui/binding/irrefutable-slice-patterns.rs b/src/test/ui/binding/irrefutable-slice-patterns.rs
index 733e6b7b57f72..ac733ef6e9c86 100644
--- a/src/test/ui/binding/irrefutable-slice-patterns.rs
+++ b/src/test/ui/binding/irrefutable-slice-patterns.rs
@@ -4,7 +4,7 @@
 #![feature(slice_patterns)]
 
 fn foo(s: &[i32]) -> &[i32] {
-    let &[ref xs..] = s;
+    let &[ref xs @ ..] = s;
     xs
 }
 
diff --git a/src/test/ui/binding/zero_sized_subslice_match.rs b/src/test/ui/binding/zero_sized_subslice_match.rs
index 51e1c024bffff..5326fa612a87b 100644
--- a/src/test/ui/binding/zero_sized_subslice_match.rs
+++ b/src/test/ui/binding/zero_sized_subslice_match.rs
@@ -7,6 +7,6 @@ fn main() {
     // The subslice used to go out of bounds for zero-sized array items, check that this doesn't
     // happen anymore
     match x {
-        [_, ref y..] => assert_eq!(&x[1] as *const (), &y[0] as *const ())
+        [_, ref y @ ..] => assert_eq!(&x[1] as *const (), &y[0] as *const ())
     }
 }
diff --git a/src/test/ui/borrowck/borrowck-describe-lvalue.nll.stderr b/src/test/ui/borrowck/borrowck-describe-lvalue.nll.stderr
index f1e1ae18839c4..20f05353d4633 100644
--- a/src/test/ui/borrowck/borrowck-describe-lvalue.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-describe-lvalue.nll.stderr
@@ -192,8 +192,8 @@ error[E0503]: cannot use `v[..]` because it was mutably borrowed
 LL |         let x = &mut v;
    |                 ------ borrow of `v` occurs here
 LL |         match v {
-LL |             &[x..] => println!("{:?}", x),
-   |               ^ use of borrowed `v`
+LL |             &[x @ ..] => println!("{:?}", x),
+   |               ^^^^^^ use of borrowed `v`
 ...
 LL |         drop(x);
    |              - borrow later used here
@@ -204,8 +204,8 @@ error[E0503]: cannot use `v[..]` because it was mutably borrowed
 LL |         let x = &mut v;
    |                 ------ borrow of `v` occurs here
 ...
-LL |             &[_, x..] => println!("{:?}", x),
-   |                  ^ use of borrowed `v`
+LL |             &[_, x @ ..] => println!("{:?}", x),
+   |                  ^^^^^^ use of borrowed `v`
 ...
 LL |         drop(x);
    |              - borrow later used here
@@ -216,8 +216,8 @@ error[E0503]: cannot use `v[..]` because it was mutably borrowed
 LL |         let x = &mut v;
    |                 ------ borrow of `v` occurs here
 ...
-LL |             &[x.., _] => println!("{:?}", x),
-   |               ^ use of borrowed `v`
+LL |             &[x @ .., _] => println!("{:?}", x),
+   |               ^^^^^^ use of borrowed `v`
 ...
 LL |         drop(x);
    |              - borrow later used here
@@ -228,8 +228,8 @@ error[E0503]: cannot use `v[..]` because it was mutably borrowed
 LL |         let x = &mut v;
    |                 ------ borrow of `v` occurs here
 ...
-LL |             &[_, x.., _] => println!("{:?}", x),
-   |                  ^ use of borrowed `v`
+LL |             &[_, x @ .., _] => println!("{:?}", x),
+   |                  ^^^^^^ use of borrowed `v`
 ...
 LL |         drop(x);
    |              - borrow later used here
diff --git a/src/test/ui/borrowck/borrowck-describe-lvalue.rs b/src/test/ui/borrowck/borrowck-describe-lvalue.rs
index c8dbf4e691816..c27d9519dc798 100644
--- a/src/test/ui/borrowck/borrowck-describe-lvalue.rs
+++ b/src/test/ui/borrowck/borrowck-describe-lvalue.rs
@@ -140,22 +140,22 @@ fn main() {
         let mut v = &[1, 2, 3, 4, 5];
         let x = &mut v;
         match v {
-            &[x..] => println!("{:?}", x),
+            &[x @ ..] => println!("{:?}", x),
                 //~^ ERROR cannot use `v[..]` because it was mutably borrowed
             _ => panic!("other case"),
         }
         match v {
-            &[_, x..] => println!("{:?}", x),
+            &[_, x @ ..] => println!("{:?}", x),
                 //~^ ERROR cannot use `v[..]` because it was mutably borrowed
             _ => panic!("other case"),
         }
         match v {
-            &[x.., _] => println!("{:?}", x),
+            &[x @ .., _] => println!("{:?}", x),
                 //~^ ERROR cannot use `v[..]` because it was mutably borrowed
             _ => panic!("other case"),
         }
         match v {
-            &[_, x.., _] => println!("{:?}", x),
+            &[_, x @ .., _] => println!("{:?}", x),
                 //~^ ERROR cannot use `v[..]` because it was mutably borrowed
             _ => panic!("other case"),
         }
diff --git a/src/test/ui/borrowck/borrowck-describe-lvalue.stderr b/src/test/ui/borrowck/borrowck-describe-lvalue.stderr
index 14b9b50f0c32a..38d847a90ff95 100644
--- a/src/test/ui/borrowck/borrowck-describe-lvalue.stderr
+++ b/src/test/ui/borrowck/borrowck-describe-lvalue.stderr
@@ -192,8 +192,8 @@ error[E0503]: cannot use `v[..]` because it was mutably borrowed
 LL |         let x = &mut v;
    |                 ------ borrow of `v` occurs here
 LL |         match v {
-LL |             &[x..] => println!("{:?}", x),
-   |               ^ use of borrowed `v`
+LL |             &[x @ ..] => println!("{:?}", x),
+   |               ^^^^^^ use of borrowed `v`
 ...
 LL |         drop(x);
    |              - borrow later used here
@@ -204,8 +204,8 @@ error[E0503]: cannot use `v[..]` because it was mutably borrowed
 LL |         let x = &mut v;
    |                 ------ borrow of `v` occurs here
 ...
-LL |             &[_, x..] => println!("{:?}", x),
-   |                  ^ use of borrowed `v`
+LL |             &[_, x @ ..] => println!("{:?}", x),
+   |                  ^^^^^^ use of borrowed `v`
 ...
 LL |         drop(x);
    |              - borrow later used here
@@ -216,8 +216,8 @@ error[E0503]: cannot use `v[..]` because it was mutably borrowed
 LL |         let x = &mut v;
    |                 ------ borrow of `v` occurs here
 ...
-LL |             &[x.., _] => println!("{:?}", x),
-   |               ^ use of borrowed `v`
+LL |             &[x @ .., _] => println!("{:?}", x),
+   |               ^^^^^^ use of borrowed `v`
 ...
 LL |         drop(x);
    |              - borrow later used here
@@ -228,8 +228,8 @@ error[E0503]: cannot use `v[..]` because it was mutably borrowed
 LL |         let x = &mut v;
    |                 ------ borrow of `v` occurs here
 ...
-LL |             &[_, x.., _] => println!("{:?}", x),
-   |                  ^ use of borrowed `v`
+LL |             &[_, x @ .., _] => println!("{:?}", x),
+   |                  ^^^^^^ use of borrowed `v`
 ...
 LL |         drop(x);
    |              - borrow later used here
diff --git a/src/test/ui/borrowck/borrowck-move-out-from-array.rs b/src/test/ui/borrowck/borrowck-move-out-from-array.rs
index 856b03edd2d72..ee6abf407a304 100644
--- a/src/test/ui/borrowck/borrowck-move-out-from-array.rs
+++ b/src/test/ui/borrowck/borrowck-move-out-from-array.rs
@@ -10,7 +10,7 @@ fn move_out_from_begin_and_end() {
 fn move_out_by_const_index_and_subslice() {
     let a = [box 1, box 2];
     let [_x, _] = a;
-    let [_y..] = a; //~ ERROR [E0382]
+    let [_y @ ..] = a; //~ ERROR [E0382]
 }
 
 fn main() {}
diff --git a/src/test/ui/borrowck/borrowck-move-out-from-array.stderr b/src/test/ui/borrowck/borrowck-move-out-from-array.stderr
index 16722a456defa..b34c03e6deff8 100644
--- a/src/test/ui/borrowck/borrowck-move-out-from-array.stderr
+++ b/src/test/ui/borrowck/borrowck-move-out-from-array.stderr
@@ -13,8 +13,8 @@ error[E0382]: use of moved value: `a[..]`
    |
 LL |     let [_x, _] = a;
    |          -- value moved here
-LL |     let [_y..] = a;
-   |          ^^ value used here after move
+LL |     let [_y @ ..] = a;
+   |          ^^^^^^^ value used here after move
    |
    = note: move occurs because `a[..]` has type `std::boxed::Box<i32>`, which does not implement the `Copy` trait
 
diff --git a/src/test/ui/borrowck/borrowck-move-out-of-vec-tail.rs b/src/test/ui/borrowck/borrowck-move-out-of-vec-tail.rs
index cc524c1ac3e6e..fa9a3c217db77 100644
--- a/src/test/ui/borrowck/borrowck-move-out-of-vec-tail.rs
+++ b/src/test/ui/borrowck/borrowck-move-out-of-vec-tail.rs
@@ -15,7 +15,7 @@ pub fn main() {
     ];
     let x: &[Foo] = &x;
     match *x {
-        [_, ref tail..] => {
+        [_, ref tail @ ..] => {
             match tail {
             //~^ ERROR cannot move out of type `[Foo]`
                 &[Foo { string: a },
diff --git a/src/test/ui/borrowck/borrowck-slice-pattern-element-loan-rpass.rs b/src/test/ui/borrowck/borrowck-slice-pattern-element-loan-rpass.rs
index 7675147c8ec2a..048813b2b93e6 100644
--- a/src/test/ui/borrowck/borrowck-slice-pattern-element-loan-rpass.rs
+++ b/src/test/ui/borrowck/borrowck-slice-pattern-element-loan-rpass.rs
@@ -5,7 +5,7 @@
 
 fn mut_head_tail<'a, A>(v: &'a mut [A]) -> Option<(&'a mut A, &'a mut [A])> {
     match *v {
-        [ref mut head, ref mut tail..] => {
+        [ref mut head, ref mut tail @ ..] => {
             Some((head, tail))
         }
         [] => None
diff --git a/src/test/ui/borrowck/borrowck-slice-pattern-element-loan.rs b/src/test/ui/borrowck/borrowck-slice-pattern-element-loan.rs
index 5de8dd3305e37..a6b54f9537ddc 100644
--- a/src/test/ui/borrowck/borrowck-slice-pattern-element-loan.rs
+++ b/src/test/ui/borrowck/borrowck-slice-pattern-element-loan.rs
@@ -70,7 +70,7 @@ fn const_index_mixed(s: &mut [i32]) {
 
 fn const_index_and_subslice_ok(s: &mut [i32]) {
     if let [ref first, ref second, ..] = *s {
-        if let [_, _, ref mut tail..] = *s {
+        if let [_, _, ref mut tail @ ..] = *s {
             nop(&[first, second]);
             nop_subslice(tail);
         }
@@ -79,7 +79,7 @@ fn const_index_and_subslice_ok(s: &mut [i32]) {
 
 fn const_index_and_subslice_err(s: &mut [i32]) {
     if let [ref first, ref second, ..] = *s {
-        if let [_, ref mut tail..] = *s { //~ERROR
+        if let [_, ref mut tail @ ..] = *s { //~ERROR
             nop(&[first, second]);
             nop_subslice(tail);
         }
@@ -88,7 +88,7 @@ fn const_index_and_subslice_err(s: &mut [i32]) {
 
 fn const_index_and_subslice_from_end_ok(s: &mut [i32]) {
     if let [.., ref second, ref first] = *s {
-        if let [ref mut tail.., _, _] = *s {
+        if let [ref mut tail @ .., _, _] = *s {
             nop(&[first, second]);
             nop_subslice(tail);
         }
@@ -97,7 +97,7 @@ fn const_index_and_subslice_from_end_ok(s: &mut [i32]) {
 
 fn const_index_and_subslice_from_end_err(s: &mut [i32]) {
     if let [.., ref second, ref first] = *s {
-        if let [ref mut tail.., _] = *s { //~ERROR
+        if let [ref mut tail @ .., _] = *s { //~ERROR
             nop(&[first, second]);
             nop_subslice(tail);
         }
@@ -105,8 +105,8 @@ fn const_index_and_subslice_from_end_err(s: &mut [i32]) {
 }
 
 fn subslices(s: &mut [i32]) {
-    if let [_, _, _, ref s1..] = *s {
-        if let [ref mut s2.., _, _, _] = *s { //~ERROR
+    if let [_, _, _, ref s1 @ ..] = *s {
+        if let [ref mut s2 @ .., _, _, _] = *s { //~ERROR
             nop_subslice(s1);
             nop_subslice(s2);
         }
diff --git a/src/test/ui/borrowck/borrowck-slice-pattern-element-loan.stderr b/src/test/ui/borrowck/borrowck-slice-pattern-element-loan.stderr
index f716ee68b0002..2c019f4461182 100644
--- a/src/test/ui/borrowck/borrowck-slice-pattern-element-loan.stderr
+++ b/src/test/ui/borrowck/borrowck-slice-pattern-element-loan.stderr
@@ -89,8 +89,8 @@ error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as im
    |
 LL |     if let [ref first, ref second, ..] = *s {
    |                        ---------- immutable borrow occurs here
-LL |         if let [_, ref mut tail..] = *s {
-   |                    ^^^^^^^^^^^^ mutable borrow occurs here
+LL |         if let [_, ref mut tail @ ..] = *s {
+   |                    ^^^^^^^^^^^^^^^^^ mutable borrow occurs here
 LL |             nop(&[first, second]);
    |                          ------ immutable borrow later used here
 
@@ -99,18 +99,18 @@ error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as im
    |
 LL |     if let [.., ref second, ref first] = *s {
    |                 ---------- immutable borrow occurs here
-LL |         if let [ref mut tail.., _] = *s {
-   |                 ^^^^^^^^^^^^ mutable borrow occurs here
+LL |         if let [ref mut tail @ .., _] = *s {
+   |                 ^^^^^^^^^^^^^^^^^ mutable borrow occurs here
 LL |             nop(&[first, second]);
    |                          ------ immutable borrow later used here
 
 error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as immutable
   --> $DIR/borrowck-slice-pattern-element-loan.rs:109:17
    |
-LL |     if let [_, _, _, ref s1..] = *s {
-   |                      ------ immutable borrow occurs here
-LL |         if let [ref mut s2.., _, _, _] = *s {
-   |                 ^^^^^^^^^^ mutable borrow occurs here
+LL |     if let [_, _, _, ref s1 @ ..] = *s {
+   |                      ----------- immutable borrow occurs here
+LL |         if let [ref mut s2 @ .., _, _, _] = *s {
+   |                 ^^^^^^^^^^^^^^^ mutable borrow occurs here
 LL |             nop_subslice(s1);
    |                          -- immutable borrow later used here
 
diff --git a/src/test/ui/borrowck/borrowck-vec-pattern-element-loan.rs b/src/test/ui/borrowck/borrowck-vec-pattern-element-loan.rs
index 100384d78c8ea..53a9bcef74a22 100644
--- a/src/test/ui/borrowck/borrowck-vec-pattern-element-loan.rs
+++ b/src/test/ui/borrowck/borrowck-vec-pattern-element-loan.rs
@@ -4,7 +4,7 @@ fn a<'a>() -> &'a [isize] {
     let vec = vec![1, 2, 3, 4];
     let vec: &[isize] = &vec;
     let tail = match vec {
-        &[_, ref tail..] => tail,
+        &[_, ref tail @ ..] => tail,
         _ => panic!("a")
     };
     tail //~ ERROR cannot return value referencing local variable `vec`
@@ -14,7 +14,7 @@ fn b<'a>() -> &'a [isize] {
     let vec = vec![1, 2, 3, 4];
     let vec: &[isize] = &vec;
     let init = match vec {
-        &[ref init.., _] => init,
+        &[ref init @ .., _] => init,
         _ => panic!("b")
     };
     init //~ ERROR cannot return value referencing local variable `vec`
@@ -24,7 +24,7 @@ fn c<'a>() -> &'a [isize] {
     let vec = vec![1, 2, 3, 4];
     let vec: &[isize] = &vec;
     let slice = match vec {
-        &[_, ref slice.., _] => slice,
+        &[_, ref slice @ .., _] => slice,
         _ => panic!("c")
     };
     slice //~ ERROR cannot return value referencing local variable `vec`
diff --git a/src/test/ui/borrowck/borrowck-vec-pattern-loan-from-mut.rs b/src/test/ui/borrowck/borrowck-vec-pattern-loan-from-mut.rs
index 4d99a92b18ba8..dd9023f6d9f79 100644
--- a/src/test/ui/borrowck/borrowck-vec-pattern-loan-from-mut.rs
+++ b/src/test/ui/borrowck/borrowck-vec-pattern-loan-from-mut.rs
@@ -4,7 +4,7 @@ fn a() {
     let mut v = vec![1, 2, 3];
     let vb: &mut [isize] = &mut v;
     match vb {
-        &mut [_a, ref tail..] => {
+        &mut [_a, ref tail @ ..] => {
             v.push(tail[0] + tail[1]); //~ ERROR cannot borrow
         }
         _ => {}
diff --git a/src/test/ui/borrowck/borrowck-vec-pattern-move-tail.rs b/src/test/ui/borrowck/borrowck-vec-pattern-move-tail.rs
index efc52530716c8..420223009a45b 100644
--- a/src/test/ui/borrowck/borrowck-vec-pattern-move-tail.rs
+++ b/src/test/ui/borrowck/borrowck-vec-pattern-move-tail.rs
@@ -5,7 +5,7 @@
 fn main() {
     let mut a = [1, 2, 3, 4];
     let t = match a {
-        [1, 2, ref tail..] => tail,
+        [1, 2, ref tail @ ..] => tail,
         _ => unreachable!()
     };
     println!("t[0]: {}", t[0]);
diff --git a/src/test/ui/borrowck/borrowck-vec-pattern-move-tail.stderr b/src/test/ui/borrowck/borrowck-vec-pattern-move-tail.stderr
index b2f553ba49f70..9f8e6fe3b6898 100644
--- a/src/test/ui/borrowck/borrowck-vec-pattern-move-tail.stderr
+++ b/src/test/ui/borrowck/borrowck-vec-pattern-move-tail.stderr
@@ -1,8 +1,8 @@
 error[E0506]: cannot assign to `a[_]` because it is borrowed
   --> $DIR/borrowck-vec-pattern-move-tail.rs:12:5
    |
-LL |         [1, 2, ref tail..] => tail,
-   |                -------- borrow of `a[_]` occurs here
+LL |         [1, 2, ref tail @ ..] => tail,
+   |                ------------- borrow of `a[_]` occurs here
 ...
 LL |     a[2] = 0;
    |     ^^^^^^^^ assignment to borrowed `a[_]` occurs here
diff --git a/src/test/ui/borrowck/borrowck-vec-pattern-nesting.rs b/src/test/ui/borrowck/borrowck-vec-pattern-nesting.rs
index 6448149391def..a215305f684dd 100644
--- a/src/test/ui/borrowck/borrowck-vec-pattern-nesting.rs
+++ b/src/test/ui/borrowck/borrowck-vec-pattern-nesting.rs
@@ -19,7 +19,7 @@ fn b() {
     let mut vec = vec![box 1, box 2, box 3];
     let vec: &mut [Box<isize>] = &mut vec;
     match vec {
-        &mut [ref _b..] => {
+        &mut [ref _b @ ..] => {
         //~^ borrow of `vec[_]` occurs here
             vec[0] = box 4; //~ ERROR cannot assign
             //~^ NOTE assignment to borrowed `vec[_]` occurs here
diff --git a/src/test/ui/borrowck/borrowck-vec-pattern-nesting.stderr b/src/test/ui/borrowck/borrowck-vec-pattern-nesting.stderr
index 072501f23ff84..f54a3a4072cd2 100644
--- a/src/test/ui/borrowck/borrowck-vec-pattern-nesting.stderr
+++ b/src/test/ui/borrowck/borrowck-vec-pattern-nesting.stderr
@@ -13,8 +13,8 @@ LL |             _a.use_ref();
 error[E0506]: cannot assign to `vec[_]` because it is borrowed
   --> $DIR/borrowck-vec-pattern-nesting.rs:24:13
    |
-LL |         &mut [ref _b..] => {
-   |               ------ borrow of `vec[_]` occurs here
+LL |         &mut [ref _b @ ..] => {
+   |               ----------- borrow of `vec[_]` occurs here
 LL |
 LL |             vec[0] = box 4;
    |             ^^^^^^ assignment to borrowed `vec[_]` occurs here
diff --git a/src/test/ui/borrowck/borrowck-vec-pattern-tail-element-loan.rs b/src/test/ui/borrowck/borrowck-vec-pattern-tail-element-loan.rs
index e602e75886d95..c35be2f6be62c 100644
--- a/src/test/ui/borrowck/borrowck-vec-pattern-tail-element-loan.rs
+++ b/src/test/ui/borrowck/borrowck-vec-pattern-tail-element-loan.rs
@@ -4,7 +4,7 @@ fn a<'a>() -> &'a isize {
     let vec = vec![1, 2, 3, 4];
     let vec: &[isize] = &vec;
     let tail = match vec {
-        &[_a, ref tail..] => &tail[0],
+        &[_a, ref tail @ ..] => &tail[0],
         _ => panic!("foo")
     };
     tail //~ ERROR cannot return value referencing local variable `vec`
diff --git a/src/test/ui/drop/dynamic-drop-async.rs b/src/test/ui/drop/dynamic-drop-async.rs
index 9226145d9354d..f3f5c382275fe 100644
--- a/src/test/ui/drop/dynamic-drop-async.rs
+++ b/src/test/ui/drop/dynamic-drop-async.rs
@@ -217,7 +217,7 @@ async fn subslice_pattern_from_end_with_drop(a: Rc<Allocator>, arg: bool, arg2:
     if arg {
         let [.., _x, _] = arr;
     } else {
-        let [_, _y..] = arr;
+        let [_, _y @ ..] = arr;
     }
     a.alloc().await;
 }
@@ -226,7 +226,7 @@ async fn subslice_pattern_reassign(a: Rc<Allocator>) {
     let mut ar = [a.alloc().await, a.alloc().await, a.alloc().await];
     let [_, _, _x] = ar;
     ar = [a.alloc().await, a.alloc().await, a.alloc().await];
-    let [_, _y..] = ar;
+    let [_, _y @ ..] = ar;
     a.alloc().await;
 }
 
diff --git a/src/test/ui/drop/dynamic-drop.rs b/src/test/ui/drop/dynamic-drop.rs
index eb1a3f3a9f9cb..8516bc3d96424 100644
--- a/src/test/ui/drop/dynamic-drop.rs
+++ b/src/test/ui/drop/dynamic-drop.rs
@@ -237,7 +237,7 @@ fn subslice_pattern_from_end(a: &Allocator, arg: bool) {
     if arg {
         let[.., _x, _] = a;
     } else {
-        let[_, _y..] = a;
+        let[_, _y @ ..] = a;
     }
 }
 
@@ -251,7 +251,7 @@ fn subslice_pattern_from_end_with_drop(a: &Allocator, arg: bool, arg2: bool) {
     if arg {
         let[.., _x, _] = a;
     } else {
-        let[_, _y..] = a;
+        let[_, _y @ ..] = a;
     }
 }
 
@@ -266,7 +266,7 @@ fn subslice_pattern_reassign(a: &Allocator) {
     let mut ar = [a.alloc(), a.alloc(), a.alloc()];
     let[_, _, _x] = ar;
     ar = [a.alloc(), a.alloc(), a.alloc()];
-    let[_, _y..] = ar;
+    let[_, _y @ ..] = ar;
 }
 
 fn panic_after_return(a: &Allocator) -> Ptr<'_> {
diff --git a/src/test/ui/error-codes/E0528.rs b/src/test/ui/error-codes/E0528.rs
index f2681fa043dab..17d03b14fc6e1 100644
--- a/src/test/ui/error-codes/E0528.rs
+++ b/src/test/ui/error-codes/E0528.rs
@@ -3,7 +3,7 @@
 fn main() {
     let r = &[1, 2];
     match r {
-        &[a, b, c, rest..] => {
+        &[a, b, c, rest @ ..] => {
         //~^ ERROR E0528
         }
     }
diff --git a/src/test/ui/error-codes/E0528.stderr b/src/test/ui/error-codes/E0528.stderr
index a7205af50542a..0f566091145bf 100644
--- a/src/test/ui/error-codes/E0528.stderr
+++ b/src/test/ui/error-codes/E0528.stderr
@@ -1,8 +1,8 @@
 error[E0528]: pattern requires at least 3 elements but array has 2
   --> $DIR/E0528.rs:6:10
    |
-LL |         &[a, b, c, rest..] => {
-   |          ^^^^^^^^^^^^^^^^^ pattern cannot match array of 2 elements
+LL |         &[a, b, c, rest @ ..] => {
+   |          ^^^^^^^^^^^^^^^^^^^^ pattern cannot match array of 2 elements
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision.rs b/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision.rs
index d95c4b0999405..d97b693f52098 100644
--- a/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision.rs
+++ b/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision.rs
@@ -2,7 +2,9 @@
 
 fn main() {
     match [5..4, 99..105, 43..44] {
-        [_, 99.., _] => {}, //~ ERROR unexpected token: `,`
+        [_, 99.., _] => {},
+        //~^ ERROR `X..` range patterns are not supported
+        //~| ERROR mismatched types
         _ => {},
     }
 }
diff --git a/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision.stderr b/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision.stderr
index 359725a41c105..4ecd8515ee164 100644
--- a/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision.stderr
+++ b/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision.stderr
@@ -1,8 +1,20 @@
-error: unexpected token: `,`
-  --> $DIR/exclusive_range_pattern_syntax_collision.rs:5:17
+error: `X..` range patterns are not supported
+  --> $DIR/exclusive_range_pattern_syntax_collision.rs:5:13
    |
 LL |         [_, 99.., _] => {},
-   |                 ^
+   |             ^^^^ help: try using the maximum value for the type: `99..MAX`
 
-error: aborting due to previous error
+error[E0308]: mismatched types
+  --> $DIR/exclusive_range_pattern_syntax_collision.rs:5:13
+   |
+LL |     match [5..4, 99..105, 43..44] {
+   |           ----------------------- this match expression has type `std::ops::Range<{integer}>`
+LL |         [_, 99.., _] => {},
+   |             ^^^^ expected struct `std::ops::Range`, found integer
+   |
+   = note: expected type `std::ops::Range<{integer}>`
+              found type `{integer}`
+
+error: aborting due to 2 previous errors
 
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision2.rs b/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision2.rs
index 95677e34dd798..09f459c9862ee 100644
--- a/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision2.rs
+++ b/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision2.rs
@@ -2,7 +2,10 @@
 
 fn main() {
     match [5..4, 99..105, 43..44] {
-        [_, 99..] => {}, //~ ERROR unexpected token: `]`
+        [_, 99..] => {},
+        //~^ ERROR `X..` range patterns are not supported
+        //~| ERROR pattern requires 2 elements but array has 3
+        //~| ERROR mismatched types
         _ => {},
     }
 }
diff --git a/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision2.stderr b/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision2.stderr
index 8f849d7b3f87c..922d26923158b 100644
--- a/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision2.stderr
+++ b/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision2.stderr
@@ -1,8 +1,27 @@
-error: unexpected token: `]`
-  --> $DIR/exclusive_range_pattern_syntax_collision2.rs:5:17
+error: `X..` range patterns are not supported
+  --> $DIR/exclusive_range_pattern_syntax_collision2.rs:5:13
    |
 LL |         [_, 99..] => {},
-   |                 ^
+   |             ^^^^ help: try using the maximum value for the type: `99..MAX`
 
-error: aborting due to previous error
+error[E0527]: pattern requires 2 elements but array has 3
+  --> $DIR/exclusive_range_pattern_syntax_collision2.rs:5:9
+   |
+LL |         [_, 99..] => {},
+   |         ^^^^^^^^^ expected 3 elements
+
+error[E0308]: mismatched types
+  --> $DIR/exclusive_range_pattern_syntax_collision2.rs:5:13
+   |
+LL |     match [5..4, 99..105, 43..44] {
+   |           ----------------------- this match expression has type `std::ops::Range<{integer}>`
+LL |         [_, 99..] => {},
+   |             ^^^^ expected struct `std::ops::Range`, found integer
+   |
+   = note: expected type `std::ops::Range<{integer}>`
+              found type `{integer}`
+
+error: aborting due to 3 previous errors
 
+Some errors have detailed explanations: E0308, E0527.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision3.rs b/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision3.rs
index 3bf5da710ef9f..95e58b1d48c88 100644
--- a/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision3.rs
+++ b/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision3.rs
@@ -2,7 +2,10 @@
 
 fn main() {
     match [5..4, 99..105, 43..44] {
-        [..9, 99..100, _] => {}, //~ ERROR expected one of `,` or `]`, found `9`
+        [..9, 99..100, _] => {},
+        //~^ ERROR `..X` range patterns are not supported
+        //~| ERROR mismatched types
+        //~| ERROR mismatched types
         _ => {},
     }
 }
diff --git a/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision3.stderr b/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision3.stderr
index a09ba3562e098..8907b875f8e11 100644
--- a/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision3.stderr
+++ b/src/test/ui/exclusive-range/exclusive_range_pattern_syntax_collision3.stderr
@@ -1,8 +1,31 @@
-error: expected one of `,` or `]`, found `9`
-  --> $DIR/exclusive_range_pattern_syntax_collision3.rs:5:12
+error: `..X` range patterns are not supported
+  --> $DIR/exclusive_range_pattern_syntax_collision3.rs:5:10
    |
 LL |         [..9, 99..100, _] => {},
-   |            ^ expected one of `,` or `]` here
+   |          ^^^ help: try using the minimum value for the type: `MIN..9`
 
-error: aborting due to previous error
+error[E0308]: mismatched types
+  --> $DIR/exclusive_range_pattern_syntax_collision3.rs:5:10
+   |
+LL |     match [5..4, 99..105, 43..44] {
+   |           ----------------------- this match expression has type `std::ops::Range<{integer}>`
+LL |         [..9, 99..100, _] => {},
+   |          ^^^ expected struct `std::ops::Range`, found integer
+   |
+   = note: expected type `std::ops::Range<{integer}>`
+              found type `{integer}`
+
+error[E0308]: mismatched types
+  --> $DIR/exclusive_range_pattern_syntax_collision3.rs:5:15
+   |
+LL |     match [5..4, 99..105, 43..44] {
+   |           ----------------------- this match expression has type `std::ops::Range<{integer}>`
+LL |         [..9, 99..100, _] => {},
+   |               ^^^^^^^ expected struct `std::ops::Range`, found integer
+   |
+   = note: expected type `std::ops::Range<{integer}>`
+              found type `{integer}`
+
+error: aborting due to 3 previous errors
 
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/feature-gates/feature-gate-slice-patterns.rs b/src/test/ui/feature-gates/feature-gate-slice-patterns.rs
index 0165321debe6b..f2a1b135b69cb 100644
--- a/src/test/ui/feature-gates/feature-gate-slice-patterns.rs
+++ b/src/test/ui/feature-gates/feature-gate-slice-patterns.rs
@@ -3,15 +3,15 @@
 fn main() {
     let x = [1, 2, 3, 4, 5];
     match x {
-        [1, 2, ..] => {} //~ ERROR syntax for subslices in slice patterns is not yet stabilized
-        [1, .., 5] => {} //~ ERROR syntax for subslices in slice patterns is not yet stabilized
-        [.., 4, 5] => {} //~ ERROR syntax for subslices in slice patterns is not yet stabilized
+        [1, 2, ..] => {} //~ ERROR subslice patterns are unstable
+        [1, .., 5] => {} //~ ERROR subslice patterns are unstable
+        [.., 4, 5] => {} //~ ERROR subslice patterns are unstable
     }
 
     let x = [ 1, 2, 3, 4, 5 ];
     match x {
-        [ xs.., 4, 5 ] => {} //~ ERROR syntax for subslices in slice patterns is not yet stabilized
-        [ 1, xs.., 5 ] => {} //~ ERROR syntax for subslices in slice patterns is not yet stabilized
-        [ 1, 2, xs.. ] => {} //~ ERROR syntax for subslices in slice patterns is not yet stabilized
+        [ xs @ .., 4, 5 ] => {} //~ ERROR subslice patterns are unstable
+        [ 1, xs @ .., 5 ] => {} //~ ERROR subslice patterns are unstable
+        [ 1, 2, xs @ .. ] => {} //~ ERROR subslice patterns are unstable
     }
 }
diff --git a/src/test/ui/feature-gates/feature-gate-slice-patterns.stderr b/src/test/ui/feature-gates/feature-gate-slice-patterns.stderr
index e88fddaa81fbe..d4946a42b8f3d 100644
--- a/src/test/ui/feature-gates/feature-gate-slice-patterns.stderr
+++ b/src/test/ui/feature-gates/feature-gate-slice-patterns.stderr
@@ -1,4 +1,4 @@
-error[E0658]: syntax for subslices in slice patterns is not yet stabilized
+error[E0658]: subslice patterns are unstable
   --> $DIR/feature-gate-slice-patterns.rs:6:16
    |
 LL |         [1, 2, ..] => {}
@@ -7,7 +7,7 @@ LL |         [1, 2, ..] => {}
    = note: for more information, see https://github.com/rust-lang/rust/issues/62254
    = help: add `#![feature(slice_patterns)]` to the crate attributes to enable
 
-error[E0658]: syntax for subslices in slice patterns is not yet stabilized
+error[E0658]: subslice patterns are unstable
   --> $DIR/feature-gate-slice-patterns.rs:7:13
    |
 LL |         [1, .., 5] => {}
@@ -16,7 +16,7 @@ LL |         [1, .., 5] => {}
    = note: for more information, see https://github.com/rust-lang/rust/issues/62254
    = help: add `#![feature(slice_patterns)]` to the crate attributes to enable
 
-error[E0658]: syntax for subslices in slice patterns is not yet stabilized
+error[E0658]: subslice patterns are unstable
   --> $DIR/feature-gate-slice-patterns.rs:8:10
    |
 LL |         [.., 4, 5] => {}
@@ -25,29 +25,29 @@ LL |         [.., 4, 5] => {}
    = note: for more information, see https://github.com/rust-lang/rust/issues/62254
    = help: add `#![feature(slice_patterns)]` to the crate attributes to enable
 
-error[E0658]: syntax for subslices in slice patterns is not yet stabilized
+error[E0658]: subslice patterns are unstable
   --> $DIR/feature-gate-slice-patterns.rs:13:11
    |
-LL |         [ xs.., 4, 5 ] => {}
-   |           ^^
+LL |         [ xs @ .., 4, 5 ] => {}
+   |           ^^^^^^^
    |
    = note: for more information, see https://github.com/rust-lang/rust/issues/62254
    = help: add `#![feature(slice_patterns)]` to the crate attributes to enable
 
-error[E0658]: syntax for subslices in slice patterns is not yet stabilized
+error[E0658]: subslice patterns are unstable
   --> $DIR/feature-gate-slice-patterns.rs:14:14
    |
-LL |         [ 1, xs.., 5 ] => {}
-   |              ^^
+LL |         [ 1, xs @ .., 5 ] => {}
+   |              ^^^^^^^
    |
    = note: for more information, see https://github.com/rust-lang/rust/issues/62254
    = help: add `#![feature(slice_patterns)]` to the crate attributes to enable
 
-error[E0658]: syntax for subslices in slice patterns is not yet stabilized
+error[E0658]: subslice patterns are unstable
   --> $DIR/feature-gate-slice-patterns.rs:15:17
    |
-LL |         [ 1, 2, xs.. ] => {}
-   |                 ^^
+LL |         [ 1, 2, xs @ .. ] => {}
+   |                 ^^^^^^^
    |
    = note: for more information, see https://github.com/rust-lang/rust/issues/62254
    = help: add `#![feature(slice_patterns)]` to the crate attributes to enable
diff --git a/src/test/ui/issues/issue-12369.rs b/src/test/ui/issues/issue-12369.rs
index 8df8efefd05ed..0866131807463 100644
--- a/src/test/ui/issues/issue-12369.rs
+++ b/src/test/ui/issues/issue-12369.rs
@@ -6,7 +6,7 @@ fn main() {
     let v: isize = match &*sl {
         &[] => 0,
         &[a,b,c] => 3,
-        &[a, ref rest..] => a,
-        &[10,a, ref rest..] => 10 //~ ERROR: unreachable pattern
+        &[a, ref rest @ ..] => a,
+        &[10,a, ref rest @ ..] => 10 //~ ERROR: unreachable pattern
     };
 }
diff --git a/src/test/ui/issues/issue-12369.stderr b/src/test/ui/issues/issue-12369.stderr
index fec9078dc4090..f27425e28c61d 100644
--- a/src/test/ui/issues/issue-12369.stderr
+++ b/src/test/ui/issues/issue-12369.stderr
@@ -1,8 +1,8 @@
 error: unreachable pattern
   --> $DIR/issue-12369.rs:10:9
    |
-LL |         &[10,a, ref rest..] => 10
-   |         ^^^^^^^^^^^^^^^^^^^
+LL |         &[10,a, ref rest @ ..] => 10
+   |         ^^^^^^^^^^^^^^^^^^^^^^
    |
 note: lint level defined here
   --> $DIR/issue-12369.rs:2:9
diff --git a/src/test/ui/issues/issue-15080.rs b/src/test/ui/issues/issue-15080.rs
index 4558118a8091c..b11b1cda38ae1 100644
--- a/src/test/ui/issues/issue-15080.rs
+++ b/src/test/ui/issues/issue-15080.rs
@@ -7,11 +7,11 @@ fn main() {
     let mut result = vec![];
     loop {
         x = match *x {
-            [1, n, 3, ref rest..] => {
+            [1, n, 3, ref rest @ ..] => {
                 result.push(n);
                 rest
             }
-            [n, ref rest..] => {
+            [n, ref rest @ ..] => {
                 result.push(n);
                 rest
             }
diff --git a/src/test/ui/issues/issue-15104.rs b/src/test/ui/issues/issue-15104.rs
index 3a03a52c32485..ee977541137db 100644
--- a/src/test/ui/issues/issue-15104.rs
+++ b/src/test/ui/issues/issue-15104.rs
@@ -9,6 +9,6 @@ fn count_members(v: &[usize]) -> usize {
     match *v {
         []         => 0,
         [_]        => 1,
-        [_, ref xs..] => 1 + count_members(xs)
+        [_, ref xs @ ..] => 1 + count_members(xs)
     }
 }
diff --git a/src/test/ui/issues/issue-17877.rs b/src/test/ui/issues/issue-17877.rs
index af22b1ad8f0bc..fefa3f2f87304 100644
--- a/src/test/ui/issues/issue-17877.rs
+++ b/src/test/ui/issues/issue-17877.rs
@@ -7,8 +7,8 @@ fn main() {
     }, 42_usize);
 
     assert_eq!(match [0u8; 1024] {
-        [1, _..] => 0_usize,
-        [0, _..] => 1_usize,
+        [1, ..] => 0_usize,
+        [0, ..] => 1_usize,
         _ => 2_usize
     }, 1_usize);
 }
diff --git a/src/test/ui/issues/issue-26158.rs b/src/test/ui/issues/issue-26158.rs
deleted file mode 100644
index 11f47b6d02a73..0000000000000
--- a/src/test/ui/issues/issue-26158.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-#![feature(slice_patterns)]
-
-fn main() {
-    let x: &[u32] = &[];
-    let &[[ref _a, ref _b..]..] = x; //~ ERROR refutable pattern
-}
diff --git a/src/test/ui/issues/issue-26158.stderr b/src/test/ui/issues/issue-26158.stderr
deleted file mode 100644
index 3a4dd79e81053..0000000000000
--- a/src/test/ui/issues/issue-26158.stderr
+++ /dev/null
@@ -1,9 +0,0 @@
-error[E0005]: refutable pattern in local binding: `&[]` not covered
-  --> $DIR/issue-26158.rs:5:9
-   |
-LL |     let &[[ref _a, ref _b..]..] = x;
-   |         ^^^^^^^^^^^^^^^^^^^^^^^ pattern `&[]` not covered
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0005`.
diff --git a/src/test/ui/issues/issue-37598.rs b/src/test/ui/issues/issue-37598.rs
index 1bec3d340d665..31b3aba6bc204 100644
--- a/src/test/ui/issues/issue-37598.rs
+++ b/src/test/ui/issues/issue-37598.rs
@@ -1,11 +1,10 @@
-// build-pass (FIXME(62277): could be check-pass?)
-#![allow(dead_code)]
+// check-pass
 #![feature(slice_patterns)]
 
 fn check(list: &[u8]) {
     match list {
         &[] => {},
-        &[_u1, _u2, ref _next..] => {},
+        &[_u1, _u2, ref _next @ ..] => {},
         &[_u1] => {},
     }
 }
diff --git a/src/test/ui/issues/issue-7784.rs b/src/test/ui/issues/issue-7784.rs
index b75e547079ef5..5b70bd6e5ff54 100644
--- a/src/test/ui/issues/issue-7784.rs
+++ b/src/test/ui/issues/issue-7784.rs
@@ -24,7 +24,7 @@ fn main() {
     assert_eq!(d, "baz");
 
     let out = bar("baz", "foo");
-    let [a, xs.., d] = out;
+    let [a, xs @ .., d] = out;
     assert_eq!(a, "baz");
     assert_eq!(xs, ["foo", "foo"]);
     assert_eq!(d, "baz");
diff --git a/src/test/ui/match/match-vec-mismatch.rs b/src/test/ui/match/match-vec-mismatch.rs
index 5e61c1b22a085..a0ef92743ac5a 100644
--- a/src/test/ui/match/match-vec-mismatch.rs
+++ b/src/test/ui/match/match-vec-mismatch.rs
@@ -19,10 +19,10 @@ fn main() {
     match [0, 1, 2] {
         [0] => {}, //~ ERROR pattern requires
 
-        [0, 1, x..] => {
+        [0, 1, x @ ..] => {
             let a: [_; 1] = x;
         }
-        [0, 1, 2, 3, x..] => {} //~ ERROR pattern requires
+        [0, 1, 2, 3, x @ ..] => {} //~ ERROR pattern requires
     };
 
     match does_not_exist { //~ ERROR cannot find value `does_not_exist` in this scope
diff --git a/src/test/ui/match/match-vec-mismatch.stderr b/src/test/ui/match/match-vec-mismatch.stderr
index 47f9d48e26290..2f1bbb7621659 100644
--- a/src/test/ui/match/match-vec-mismatch.stderr
+++ b/src/test/ui/match/match-vec-mismatch.stderr
@@ -19,8 +19,8 @@ LL |         [0] => {},
 error[E0528]: pattern requires at least 4 elements but array has 3
   --> $DIR/match-vec-mismatch.rs:25:9
    |
-LL |         [0, 1, 2, 3, x..] => {}
-   |         ^^^^^^^^^^^^^^^^^ pattern cannot match array of 3 elements
+LL |         [0, 1, 2, 3, x @ ..] => {}
+   |         ^^^^^^^^^^^^^^^^^^^^ pattern cannot match array of 3 elements
 
 error[E0282]: type annotations needed
   --> $DIR/match-vec-mismatch.rs:36:9
diff --git a/src/test/ui/match/match-vec-unreachable.rs b/src/test/ui/match/match-vec-unreachable.rs
index 9e167f37ba9d9..78810525bad0f 100644
--- a/src/test/ui/match/match-vec-unreachable.rs
+++ b/src/test/ui/match/match-vec-unreachable.rs
@@ -23,7 +23,7 @@ fn main() {
     let x: Vec<char> = vec!['a', 'b', 'c'];
     let x: &[char] = &x;
     match *x {
-        ['a', 'b', 'c', ref _tail..] => {}
+        ['a', 'b', 'c', ref _tail @ ..] => {}
         ['a', 'b', 'c'] => {} //~ ERROR unreachable pattern
         _ => {}
     }
diff --git a/src/test/ui/non-exhaustive/non-exhaustive-match.rs b/src/test/ui/non-exhaustive/non-exhaustive-match.rs
index e888bcf516891..8cc5f4042cccb 100644
--- a/src/test/ui/non-exhaustive/non-exhaustive-match.rs
+++ b/src/test/ui/non-exhaustive/non-exhaustive-match.rs
@@ -32,14 +32,14 @@ fn main() {
     let vec = vec![Some(42), None, Some(21)];
     let vec: &[Option<isize>] = &vec;
     match *vec { //~ ERROR non-exhaustive patterns: `[]` not covered
-        [Some(..), None, ref tail..] => {}
-        [Some(..), Some(..), ref tail..] => {}
+        [Some(..), None, ref tail @ ..] => {}
+        [Some(..), Some(..), ref tail @ ..] => {}
         [None] => {}
     }
     let vec = vec![1];
     let vec: &[isize] = &vec;
     match *vec {
-        [_, ref tail..] => (),
+        [_, ref tail @ ..] => (),
         [] => ()
     }
     let vec = vec![0.5f32];
@@ -53,10 +53,10 @@ fn main() {
     let vec = vec![Some(42), None, Some(21)];
     let vec: &[Option<isize>] = &vec;
     match *vec {
-        [Some(..), None, ref tail..] => {}
-        [Some(..), Some(..), ref tail..] => {}
-        [None, None, ref tail..] => {}
-        [None, Some(..), ref tail..] => {}
+        [Some(..), None, ref tail @ ..] => {}
+        [Some(..), Some(..), ref tail @ ..] => {}
+        [None, None, ref tail @ ..] => {}
+        [None, Some(..), ref tail @ ..] => {}
         [Some(_)] => {}
         [None] => {}
         [] => {}
diff --git a/src/test/ui/non-exhaustive/non-exhaustive-pattern-witness.rs b/src/test/ui/non-exhaustive/non-exhaustive-pattern-witness.rs
index 9fcd4bdad7264..4ca1cbcebccf5 100644
--- a/src/test/ui/non-exhaustive/non-exhaustive-pattern-witness.rs
+++ b/src/test/ui/non-exhaustive/non-exhaustive-pattern-witness.rs
@@ -77,7 +77,7 @@ fn vectors_with_nested_enums() {
         [Enum::Second(true), Enum::First] => (),
         [Enum::Second(true), Enum::Second(true)] => (),
         [Enum::Second(false), _] => (),
-        [_, _, ref tail.., _] => ()
+        [_, _, ref tail @ .., _] => ()
     }
 }
 
diff --git a/src/test/ui/parser/match-vec-invalid.rs b/src/test/ui/parser/match-vec-invalid.rs
index e5e85ba8ca602..00f4374b256d2 100644
--- a/src/test/ui/parser/match-vec-invalid.rs
+++ b/src/test/ui/parser/match-vec-invalid.rs
@@ -1,7 +1,13 @@
 fn main() {
-    let a = Vec::new();
+    let a: &[u8] = &[];
     match a {
-        [1, tail.., tail..] => {}, //~ ERROR: expected one of `,` or `@`, found `..`
+        [1, tail @ .., tail @ ..] => {},
+        //~^ ERROR identifier `tail` is bound more than once in the same pattern
+        //~| ERROR subslice patterns are unstable
+        //~| ERROR subslice patterns are unstable
+        //~| ERROR `..` can only be used once per slice pattern
         _ => ()
     }
 }
+
+const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types
diff --git a/src/test/ui/parser/match-vec-invalid.stderr b/src/test/ui/parser/match-vec-invalid.stderr
index fee8d248dcf07..0956ac21b7f1e 100644
--- a/src/test/ui/parser/match-vec-invalid.stderr
+++ b/src/test/ui/parser/match-vec-invalid.stderr
@@ -1,8 +1,45 @@
-error: expected one of `,` or `@`, found `..`
-  --> $DIR/match-vec-invalid.rs:4:25
+error[E0416]: identifier `tail` is bound more than once in the same pattern
+  --> $DIR/match-vec-invalid.rs:4:24
    |
-LL |         [1, tail.., tail..] => {},
-   |                         ^^ expected one of `,` or `@` here
+LL |         [1, tail @ .., tail @ ..] => {},
+   |                        ^^^^ used in a pattern more than once
 
-error: aborting due to previous error
+error[E0658]: subslice patterns are unstable
+  --> $DIR/match-vec-invalid.rs:4:13
+   |
+LL |         [1, tail @ .., tail @ ..] => {},
+   |             ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/62254
+   = help: add `#![feature(slice_patterns)]` to the crate attributes to enable
+
+error[E0658]: subslice patterns are unstable
+  --> $DIR/match-vec-invalid.rs:4:24
+   |
+LL |         [1, tail @ .., tail @ ..] => {},
+   |                        ^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/62254
+   = help: add `#![feature(slice_patterns)]` to the crate attributes to enable
+
+error: `..` can only be used once per slice pattern
+  --> $DIR/match-vec-invalid.rs:4:31
+   |
+LL |         [1, tail @ .., tail @ ..] => {},
+   |                    --         ^^ can only be used once per slice pattern
+   |                    |
+   |                    previously used here
+
+error[E0308]: mismatched types
+  --> $DIR/match-vec-invalid.rs:13:30
+   |
+LL | const RECOVERY_WITNESS: () = 0;
+   |                              ^ expected (), found integer
+   |
+   = note: expected type `()`
+              found type `{integer}`
+
+error: aborting due to 5 previous errors
 
+Some errors have detailed explanations: E0308, E0416, E0658.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/parser/pat-lt-bracket-6.rs b/src/test/ui/parser/pat-lt-bracket-6.rs
index 9bad0cb25c1e4..7b9721830993e 100644
--- a/src/test/ui/parser/pat-lt-bracket-6.rs
+++ b/src/test/ui/parser/pat-lt-bracket-6.rs
@@ -1,3 +1,9 @@
 fn main() {
+    struct Test(&'static u8, [u8; 0]);
+    let x = Test(&0, []);
+
     let Test(&desc[..]) = x; //~ ERROR: expected one of `)`, `,`, or `@`, found `[`
+    //~^ ERROR subslice patterns are unstable
 }
+
+const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types
diff --git a/src/test/ui/parser/pat-lt-bracket-6.stderr b/src/test/ui/parser/pat-lt-bracket-6.stderr
index 2ee4bdb20fe88..201465b2c850c 100644
--- a/src/test/ui/parser/pat-lt-bracket-6.stderr
+++ b/src/test/ui/parser/pat-lt-bracket-6.stderr
@@ -1,8 +1,28 @@
 error: expected one of `)`, `,`, or `@`, found `[`
-  --> $DIR/pat-lt-bracket-6.rs:2:19
+  --> $DIR/pat-lt-bracket-6.rs:5:19
    |
 LL |     let Test(&desc[..]) = x;
    |                   ^ expected one of `)`, `,`, or `@` here
 
-error: aborting due to previous error
+error[E0658]: subslice patterns are unstable
+  --> $DIR/pat-lt-bracket-6.rs:5:20
+   |
+LL |     let Test(&desc[..]) = x;
+   |                    ^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/62254
+   = help: add `#![feature(slice_patterns)]` to the crate attributes to enable
+
+error[E0308]: mismatched types
+  --> $DIR/pat-lt-bracket-6.rs:9:30
+   |
+LL | const RECOVERY_WITNESS: () = 0;
+   |                              ^ expected (), found integer
+   |
+   = note: expected type `()`
+              found type `{integer}`
+
+error: aborting due to 3 previous errors
 
+Some errors have detailed explanations: E0308, E0658.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/parser/pat-lt-bracket-7.rs b/src/test/ui/parser/pat-lt-bracket-7.rs
index 36c0d7733733a..020fdb845e8b5 100644
--- a/src/test/ui/parser/pat-lt-bracket-7.rs
+++ b/src/test/ui/parser/pat-lt-bracket-7.rs
@@ -1,3 +1,8 @@
 fn main() {
-    for thing(x[]) in foo {} //~ ERROR: expected one of `)`, `,`, or `@`, found `[`
+    struct Thing(u8, [u8; 0]);
+    let foo = core::iter::empty();
+
+    for Thing(x[]) in foo {} //~ ERROR: expected one of `)`, `,`, or `@`, found `[`
 }
+
+const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types
diff --git a/src/test/ui/parser/pat-lt-bracket-7.stderr b/src/test/ui/parser/pat-lt-bracket-7.stderr
index 5552ea46d9b9d..17557efa49e80 100644
--- a/src/test/ui/parser/pat-lt-bracket-7.stderr
+++ b/src/test/ui/parser/pat-lt-bracket-7.stderr
@@ -1,8 +1,18 @@
 error: expected one of `)`, `,`, or `@`, found `[`
-  --> $DIR/pat-lt-bracket-7.rs:2:16
+  --> $DIR/pat-lt-bracket-7.rs:5:16
    |
-LL |     for thing(x[]) in foo {}
+LL |     for Thing(x[]) in foo {}
    |                ^ expected one of `)`, `,`, or `@` here
 
-error: aborting due to previous error
+error[E0308]: mismatched types
+  --> $DIR/pat-lt-bracket-7.rs:8:30
+   |
+LL | const RECOVERY_WITNESS: () = 0;
+   |                              ^ expected (), found integer
+   |
+   = note: expected type `()`
+              found type `{integer}`
+
+error: aborting due to 2 previous errors
 
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/parser/pat-tuple-2.rs b/src/test/ui/parser/pat-tuple-2.rs
index fd25499381a28..a8f3debd3d634 100644
--- a/src/test/ui/parser/pat-tuple-2.rs
+++ b/src/test/ui/parser/pat-tuple-2.rs
@@ -1,6 +1,7 @@
+// check-pass
+
 fn main() {
     match (0, 1, 2) {
         (pat, ..,) => {}
-        //~^ ERROR trailing comma is not permitted after `..`
     }
 }
diff --git a/src/test/ui/parser/pat-tuple-2.stderr b/src/test/ui/parser/pat-tuple-2.stderr
deleted file mode 100644
index c3a5c39a8e32a..0000000000000
--- a/src/test/ui/parser/pat-tuple-2.stderr
+++ /dev/null
@@ -1,8 +0,0 @@
-error: trailing comma is not permitted after `..`
-  --> $DIR/pat-tuple-2.rs:3:17
-   |
-LL |         (pat, ..,) => {}
-   |                 ^ trailing comma is not permitted after `..`
-
-error: aborting due to previous error
-
diff --git a/src/test/ui/parser/pat-tuple-3.rs b/src/test/ui/parser/pat-tuple-3.rs
index e1e975d3c3ea0..1486ab231aab4 100644
--- a/src/test/ui/parser/pat-tuple-3.rs
+++ b/src/test/ui/parser/pat-tuple-3.rs
@@ -1,6 +1,6 @@
 fn main() {
     match (0, 1, 2) {
         (.., pat, ..) => {}
-        //~^ ERROR `..` can only be used once per tuple or tuple struct pattern
+        //~^ ERROR `..` can only be used once per tuple pattern
     }
 }
diff --git a/src/test/ui/parser/pat-tuple-3.stderr b/src/test/ui/parser/pat-tuple-3.stderr
index c9f14bb90429b..9ac0611c5c933 100644
--- a/src/test/ui/parser/pat-tuple-3.stderr
+++ b/src/test/ui/parser/pat-tuple-3.stderr
@@ -1,10 +1,10 @@
-error: `..` can only be used once per tuple or tuple struct pattern
+error: `..` can only be used once per tuple pattern
   --> $DIR/pat-tuple-3.rs:3:19
    |
 LL |         (.., pat, ..) => {}
-   |          --       ^^ can only be used once per pattern
+   |          --       ^^ can only be used once per tuple pattern
    |          |
-   |          previously present here
+   |          previously used here
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/pat-tuple-4.rs b/src/test/ui/parser/pat-tuple-4.rs
index 76f60d94bc86e..2f03160430a22 100644
--- a/src/test/ui/parser/pat-tuple-4.rs
+++ b/src/test/ui/parser/pat-tuple-4.rs
@@ -1,5 +1,11 @@
 fn main() {
+    const PAT: u8 = 0;
+
     match 0 {
-        (.. pat) => {} //~ ERROR expected one of `)` or `,`, found `pat`
+        (.. PAT) => {}
+        //~^ ERROR `..X` range patterns are not supported
+        //~| ERROR exclusive range pattern syntax is experimental
     }
 }
+
+const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types
diff --git a/src/test/ui/parser/pat-tuple-4.stderr b/src/test/ui/parser/pat-tuple-4.stderr
index 26b92fae31391..af3ecce184649 100644
--- a/src/test/ui/parser/pat-tuple-4.stderr
+++ b/src/test/ui/parser/pat-tuple-4.stderr
@@ -1,8 +1,28 @@
-error: expected one of `)` or `,`, found `pat`
-  --> $DIR/pat-tuple-4.rs:3:13
+error: `..X` range patterns are not supported
+  --> $DIR/pat-tuple-4.rs:5:10
    |
-LL |         (.. pat) => {}
-   |             ^^^ expected one of `)` or `,` here
+LL |         (.. PAT) => {}
+   |          ^^^^^^ help: try using the minimum value for the type: `MIN..PAT`
 
-error: aborting due to previous error
+error[E0658]: exclusive range pattern syntax is experimental
+  --> $DIR/pat-tuple-4.rs:5:10
+   |
+LL |         (.. PAT) => {}
+   |          ^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/37854
+   = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable
+
+error[E0308]: mismatched types
+  --> $DIR/pat-tuple-4.rs:11:30
+   |
+LL | const RECOVERY_WITNESS: () = 0;
+   |                              ^ expected (), found integer
+   |
+   = note: expected type `()`
+              found type `{integer}`
+
+error: aborting due to 3 previous errors
 
+Some errors have detailed explanations: E0308, E0658.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/parser/pat-tuple-5.rs b/src/test/ui/parser/pat-tuple-5.rs
index d4f05a5eb523e..5334ef93bb3bd 100644
--- a/src/test/ui/parser/pat-tuple-5.rs
+++ b/src/test/ui/parser/pat-tuple-5.rs
@@ -1,5 +1,10 @@
 fn main() {
+    const PAT: u8 = 0;
+
     match (0, 1) {
-        (pat ..) => {} //~ ERROR unexpected token: `)`
+        (PAT ..) => {}
+        //~^ ERROR `X..` range patterns are not supported
+        //~| ERROR exclusive range pattern syntax is experimental
+        //~| ERROR mismatched types
     }
 }
diff --git a/src/test/ui/parser/pat-tuple-5.stderr b/src/test/ui/parser/pat-tuple-5.stderr
index f9832214c6800..09ebdc29a2161 100644
--- a/src/test/ui/parser/pat-tuple-5.stderr
+++ b/src/test/ui/parser/pat-tuple-5.stderr
@@ -1,8 +1,30 @@
-error: unexpected token: `)`
-  --> $DIR/pat-tuple-5.rs:3:16
+error: `X..` range patterns are not supported
+  --> $DIR/pat-tuple-5.rs:5:10
    |
-LL |         (pat ..) => {}
-   |                ^
+LL |         (PAT ..) => {}
+   |          ^^^^^^ help: try using the maximum value for the type: `PAT..MAX`
 
-error: aborting due to previous error
+error[E0658]: exclusive range pattern syntax is experimental
+  --> $DIR/pat-tuple-5.rs:5:10
+   |
+LL |         (PAT ..) => {}
+   |          ^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/37854
+   = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable
+
+error[E0308]: mismatched types
+  --> $DIR/pat-tuple-5.rs:5:10
+   |
+LL |     match (0, 1) {
+   |           ------ this match expression has type `({integer}, {integer})`
+LL |         (PAT ..) => {}
+   |          ^^^^^^ expected tuple, found u8
+   |
+   = note: expected type `({integer}, {integer})`
+              found type `u8`
+
+error: aborting due to 3 previous errors
 
+Some errors have detailed explanations: E0308, E0658.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/parser/recover-range-pats.rs b/src/test/ui/parser/recover-range-pats.rs
new file mode 100644
index 0000000000000..c66652ff4fa01
--- /dev/null
+++ b/src/test/ui/parser/recover-range-pats.rs
@@ -0,0 +1,123 @@
+// Here we test all kinds of range patterns in terms of parsing / recovery.
+// We want to ensure that:
+// 1. Things parse as they should.
+// 2. Or at least we have parser recovery if they don't.
+
+#![feature(exclusive_range_pattern)]
+#![deny(ellipsis_inclusive_range_patterns)]
+
+fn main() {}
+
+const X: u8 = 0;
+const Y: u8 = 3;
+
+fn exclusive_from_to() {
+    if let 0..3 = 0 {} // OK.
+    if let 0..Y = 0 {} // OK.
+    if let X..3 = 0 {} // OK.
+    if let X..Y = 0 {} // OK.
+    if let true..Y = 0 {} //~ ERROR only char and numeric types
+    if let X..true = 0 {} //~ ERROR only char and numeric types
+    if let .0..Y = 0 {} //~ ERROR mismatched types
+    //~^ ERROR float literals must have an integer part
+    if let X.. .0 = 0 {} //~ ERROR mismatched types
+    //~^ ERROR float literals must have an integer part
+}
+
+fn inclusive_from_to() {
+    if let 0..=3 = 0 {} // OK.
+    if let 0..=Y = 0 {} // OK.
+    if let X..=3 = 0 {} // OK.
+    if let X..=Y = 0 {} // OK.
+    if let true..=Y = 0 {} //~ ERROR only char and numeric types
+    if let X..=true = 0 {} //~ ERROR only char and numeric types
+    if let .0..=Y = 0 {} //~ ERROR mismatched types
+    //~^ ERROR float literals must have an integer part
+    if let X..=.0 = 0 {} //~ ERROR mismatched types
+    //~^ ERROR float literals must have an integer part
+}
+
+fn inclusive2_from_to() {
+    if let 0...3 = 0 {} //~ ERROR `...` range patterns are deprecated
+    if let 0...Y = 0 {} //~ ERROR `...` range patterns are deprecated
+    if let X...3 = 0 {} //~ ERROR `...` range patterns are deprecated
+    if let X...Y = 0 {} //~ ERROR `...` range patterns are deprecated
+    if let true...Y = 0 {} //~ ERROR only char and numeric types
+    //~^ ERROR `...` range patterns are deprecated
+    if let X...true = 0 {} //~ ERROR only char and numeric types
+    //~^ ERROR `...` range patterns are deprecated
+    if let .0...Y = 0 {} //~ ERROR mismatched types
+    //~^ ERROR float literals must have an integer part
+    //~| ERROR `...` range patterns are deprecated
+    if let X... .0 = 0 {} //~ ERROR mismatched types
+    //~^ ERROR float literals must have an integer part
+    //~| ERROR `...` range patterns are deprecated
+}
+
+fn exclusive_from() {
+    if let 0.. = 0 {} //~ ERROR `X..` range patterns are not supported
+    if let X.. = 0 {} //~ ERROR `X..` range patterns are not supported
+    if let true.. = 0 {} //~ ERROR `X..` range patterns are not supported
+    //~^ ERROR only char and numeric types
+    if let .0.. = 0 {} //~ ERROR `X..` range patterns are not supported
+    //~^ ERROR float literals must have an integer part
+    //~| ERROR mismatched types
+}
+
+fn inclusive_from() {
+    if let 0..= = 0 {} //~ ERROR `X..=` range patterns are not supported
+    if let X..= = 0 {} //~ ERROR `X..=` range patterns are not supported
+    if let true..= = 0 {} //~ ERROR `X..=` range patterns are not supported
+    //~| ERROR only char and numeric types
+    if let .0..= = 0 {} //~ ERROR `X..=` range patterns are not supported
+    //~^ ERROR float literals must have an integer part
+    //~| ERROR mismatched types
+}
+
+fn inclusive2_from() {
+    if let 0... = 0 {} //~ ERROR `X...` range patterns are not supported
+    //~^ ERROR `...` range patterns are deprecated
+    if let X... = 0 {} //~ ERROR `X...` range patterns are not supported
+    //~^ ERROR `...` range patterns are deprecated
+    if let true... = 0 {} //~ ERROR `X...` range patterns are not supported
+    //~^ ERROR `...` range patterns are deprecated
+    //~| ERROR only char and numeric types
+    if let .0... = 0 {} //~ ERROR `X...` range patterns are not supported
+    //~^ ERROR float literals must have an integer part
+    //~| ERROR `...` range patterns are deprecated
+    //~| ERROR mismatched types
+}
+
+fn exclusive_to() {
+    if let ..0 = 0 {} //~ ERROR `..X` range patterns are not supported
+    if let ..Y = 0 {} //~ ERROR `..X` range patterns are not supported
+    if let ..true = 0 {} //~ ERROR `..X` range patterns are not supported
+    //~| ERROR only char and numeric types
+    if let .. .0 = 0 {} //~ ERROR `..X` range patterns are not supported
+    //~^ ERROR float literals must have an integer part
+    //~| ERROR mismatched types
+}
+
+fn inclusive_to() {
+    if let ..=3 = 0 {} //~ ERROR `..=X` range patterns are not supported
+    if let ..=Y = 0 {} //~ ERROR `..=X` range patterns are not supported
+    if let ..=true = 0 {} //~ ERROR `..=X` range patterns are not supported
+    //~| ERROR only char and numeric types
+    if let ..=.0 = 0 {} //~ ERROR `..=X` range patterns are not supported
+    //~^ ERROR float literals must have an integer part
+    //~| ERROR mismatched types
+}
+
+fn inclusive2_to() {
+    if let ...3 = 0 {} //~ ERROR `...X` range patterns are not supported
+    //~^ ERROR `...` range patterns are deprecated
+    if let ...Y = 0 {} //~ ERROR `...X` range patterns are not supported
+    //~^ ERROR `...` range patterns are deprecated
+    if let ...true = 0 {} //~ ERROR `...X` range patterns are not supported
+    //~^ ERROR `...` range patterns are deprecated
+    //~| ERROR only char and numeric types
+    if let ....3 = 0 {} //~ ERROR `...X` range patterns are not supported
+    //~^ ERROR float literals must have an integer part
+    //~| ERROR `...` range patterns are deprecated
+    //~| ERROR mismatched types
+}
diff --git a/src/test/ui/parser/recover-range-pats.stderr b/src/test/ui/parser/recover-range-pats.stderr
new file mode 100644
index 0000000000000..c50d5e6eb6153
--- /dev/null
+++ b/src/test/ui/parser/recover-range-pats.stderr
@@ -0,0 +1,538 @@
+error: float literals must have an integer part
+  --> $DIR/recover-range-pats.rs:21:12
+   |
+LL |     if let .0..Y = 0 {}
+   |            ^^ help: must have an integer part: `0.0`
+
+error: float literals must have an integer part
+  --> $DIR/recover-range-pats.rs:23:16
+   |
+LL |     if let X.. .0 = 0 {}
+   |                ^^ help: must have an integer part: `0.0`
+
+error: float literals must have an integer part
+  --> $DIR/recover-range-pats.rs:34:12
+   |
+LL |     if let .0..=Y = 0 {}
+   |            ^^ help: must have an integer part: `0.0`
+
+error: float literals must have an integer part
+  --> $DIR/recover-range-pats.rs:36:16
+   |
+LL |     if let X..=.0 = 0 {}
+   |                ^^ help: must have an integer part: `0.0`
+
+error: float literals must have an integer part
+  --> $DIR/recover-range-pats.rs:49:12
+   |
+LL |     if let .0...Y = 0 {}
+   |            ^^ help: must have an integer part: `0.0`
+
+error: float literals must have an integer part
+  --> $DIR/recover-range-pats.rs:52:17
+   |
+LL |     if let X... .0 = 0 {}
+   |                 ^^ help: must have an integer part: `0.0`
+
+error: `X..` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:58:12
+   |
+LL |     if let 0.. = 0 {}
+   |            ^^^ help: try using the maximum value for the type: `0..MAX`
+
+error: `X..` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:59:12
+   |
+LL |     if let X.. = 0 {}
+   |            ^^^ help: try using the maximum value for the type: `X..MAX`
+
+error: `X..` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:60:12
+   |
+LL |     if let true.. = 0 {}
+   |            ^^^^^^ help: try using the maximum value for the type: `true..MAX`
+
+error: float literals must have an integer part
+  --> $DIR/recover-range-pats.rs:62:12
+   |
+LL |     if let .0.. = 0 {}
+   |            ^^ help: must have an integer part: `0.0`
+
+error: `X..` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:62:12
+   |
+LL |     if let .0.. = 0 {}
+   |            ^^^^ help: try using the maximum value for the type: `0.0..MAX`
+
+error: `X..=` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:68:12
+   |
+LL |     if let 0..= = 0 {}
+   |            ^^^^ help: try using the maximum value for the type: `0..=MAX`
+
+error: `X..=` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:69:12
+   |
+LL |     if let X..= = 0 {}
+   |            ^^^^ help: try using the maximum value for the type: `X..=MAX`
+
+error: `X..=` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:70:12
+   |
+LL |     if let true..= = 0 {}
+   |            ^^^^^^^ help: try using the maximum value for the type: `true..=MAX`
+
+error: float literals must have an integer part
+  --> $DIR/recover-range-pats.rs:72:12
+   |
+LL |     if let .0..= = 0 {}
+   |            ^^ help: must have an integer part: `0.0`
+
+error: `X..=` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:72:12
+   |
+LL |     if let .0..= = 0 {}
+   |            ^^^^^ help: try using the maximum value for the type: `0.0..=MAX`
+
+error: `X...` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:78:12
+   |
+LL |     if let 0... = 0 {}
+   |            ^^^^ help: try using the maximum value for the type: `0...MAX`
+
+error: `X...` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:80:12
+   |
+LL |     if let X... = 0 {}
+   |            ^^^^ help: try using the maximum value for the type: `X...MAX`
+
+error: `X...` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:82:12
+   |
+LL |     if let true... = 0 {}
+   |            ^^^^^^^ help: try using the maximum value for the type: `true...MAX`
+
+error: float literals must have an integer part
+  --> $DIR/recover-range-pats.rs:85:12
+   |
+LL |     if let .0... = 0 {}
+   |            ^^ help: must have an integer part: `0.0`
+
+error: `X...` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:85:12
+   |
+LL |     if let .0... = 0 {}
+   |            ^^^^^ help: try using the maximum value for the type: `0.0...MAX`
+
+error: `..X` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:92:12
+   |
+LL |     if let ..0 = 0 {}
+   |            ^^^ help: try using the minimum value for the type: `MIN..0`
+
+error: `..X` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:93:12
+   |
+LL |     if let ..Y = 0 {}
+   |            ^^^ help: try using the minimum value for the type: `MIN..Y`
+
+error: `..X` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:94:12
+   |
+LL |     if let ..true = 0 {}
+   |            ^^^^^^ help: try using the minimum value for the type: `MIN..true`
+
+error: float literals must have an integer part
+  --> $DIR/recover-range-pats.rs:96:15
+   |
+LL |     if let .. .0 = 0 {}
+   |               ^^ help: must have an integer part: `0.0`
+
+error: `..X` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:96:12
+   |
+LL |     if let .. .0 = 0 {}
+   |            ^^^^^ help: try using the minimum value for the type: `MIN..0.0`
+
+error: `..=X` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:102:12
+   |
+LL |     if let ..=3 = 0 {}
+   |            ^^^^ help: try using the minimum value for the type: `MIN..=3`
+
+error: `..=X` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:103:12
+   |
+LL |     if let ..=Y = 0 {}
+   |            ^^^^ help: try using the minimum value for the type: `MIN..=Y`
+
+error: `..=X` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:104:12
+   |
+LL |     if let ..=true = 0 {}
+   |            ^^^^^^^ help: try using the minimum value for the type: `MIN..=true`
+
+error: float literals must have an integer part
+  --> $DIR/recover-range-pats.rs:106:15
+   |
+LL |     if let ..=.0 = 0 {}
+   |               ^^ help: must have an integer part: `0.0`
+
+error: `..=X` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:106:12
+   |
+LL |     if let ..=.0 = 0 {}
+   |            ^^^^^ help: try using the minimum value for the type: `MIN..=0.0`
+
+error: `...X` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:112:12
+   |
+LL |     if let ...3 = 0 {}
+   |            ^^^^ help: try using the minimum value for the type: `MIN...3`
+
+error: `...X` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:114:12
+   |
+LL |     if let ...Y = 0 {}
+   |            ^^^^ help: try using the minimum value for the type: `MIN...Y`
+
+error: `...X` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:116:12
+   |
+LL |     if let ...true = 0 {}
+   |            ^^^^^^^ help: try using the minimum value for the type: `MIN...true`
+
+error: float literals must have an integer part
+  --> $DIR/recover-range-pats.rs:119:15
+   |
+LL |     if let ....3 = 0 {}
+   |               ^^ help: must have an integer part: `0.3`
+
+error: `...X` range patterns are not supported
+  --> $DIR/recover-range-pats.rs:119:12
+   |
+LL |     if let ....3 = 0 {}
+   |            ^^^^^ help: try using the minimum value for the type: `MIN...0.3`
+
+error: `...` range patterns are deprecated
+  --> $DIR/recover-range-pats.rs:41:13
+   |
+LL |     if let 0...3 = 0 {}
+   |             ^^^ help: use `..=` for an inclusive range
+   |
+note: lint level defined here
+  --> $DIR/recover-range-pats.rs:7:9
+   |
+LL | #![deny(ellipsis_inclusive_range_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `...` range patterns are deprecated
+  --> $DIR/recover-range-pats.rs:42:13
+   |
+LL |     if let 0...Y = 0 {}
+   |             ^^^ help: use `..=` for an inclusive range
+
+error: `...` range patterns are deprecated
+  --> $DIR/recover-range-pats.rs:43:13
+   |
+LL |     if let X...3 = 0 {}
+   |             ^^^ help: use `..=` for an inclusive range
+
+error: `...` range patterns are deprecated
+  --> $DIR/recover-range-pats.rs:44:13
+   |
+LL |     if let X...Y = 0 {}
+   |             ^^^ help: use `..=` for an inclusive range
+
+error: `...` range patterns are deprecated
+  --> $DIR/recover-range-pats.rs:45:16
+   |
+LL |     if let true...Y = 0 {}
+   |                ^^^ help: use `..=` for an inclusive range
+
+error: `...` range patterns are deprecated
+  --> $DIR/recover-range-pats.rs:47:13
+   |
+LL |     if let X...true = 0 {}
+   |             ^^^ help: use `..=` for an inclusive range
+
+error: `...` range patterns are deprecated
+  --> $DIR/recover-range-pats.rs:49:14
+   |
+LL |     if let .0...Y = 0 {}
+   |              ^^^ help: use `..=` for an inclusive range
+
+error: `...` range patterns are deprecated
+  --> $DIR/recover-range-pats.rs:52:13
+   |
+LL |     if let X... .0 = 0 {}
+   |             ^^^ help: use `..=` for an inclusive range
+
+error: `...` range patterns are deprecated
+  --> $DIR/recover-range-pats.rs:78:13
+   |
+LL |     if let 0... = 0 {}
+   |             ^^^ help: use `..=` for an inclusive range
+
+error: `...` range patterns are deprecated
+  --> $DIR/recover-range-pats.rs:80:13
+   |
+LL |     if let X... = 0 {}
+   |             ^^^ help: use `..=` for an inclusive range
+
+error: `...` range patterns are deprecated
+  --> $DIR/recover-range-pats.rs:82:16
+   |
+LL |     if let true... = 0 {}
+   |                ^^^ help: use `..=` for an inclusive range
+
+error: `...` range patterns are deprecated
+  --> $DIR/recover-range-pats.rs:85:14
+   |
+LL |     if let .0... = 0 {}
+   |              ^^^ help: use `..=` for an inclusive range
+
+error: `...` range patterns are deprecated
+  --> $DIR/recover-range-pats.rs:112:12
+   |
+LL |     if let ...3 = 0 {}
+   |            ^^^ help: use `..=` for an inclusive range
+
+error: `...` range patterns are deprecated
+  --> $DIR/recover-range-pats.rs:114:12
+   |
+LL |     if let ...Y = 0 {}
+   |            ^^^ help: use `..=` for an inclusive range
+
+error: `...` range patterns are deprecated
+  --> $DIR/recover-range-pats.rs:116:12
+   |
+LL |     if let ...true = 0 {}
+   |            ^^^ help: use `..=` for an inclusive range
+
+error: `...` range patterns are deprecated
+  --> $DIR/recover-range-pats.rs:119:12
+   |
+LL |     if let ....3 = 0 {}
+   |            ^^^ help: use `..=` for an inclusive range
+
+error[E0029]: only char and numeric types are allowed in range patterns
+  --> $DIR/recover-range-pats.rs:19:12
+   |
+LL |     if let true..Y = 0 {}
+   |            ^^^^ ranges require char or numeric types
+   |
+   = note: start type: bool
+   = note: end type: u8
+
+error[E0029]: only char and numeric types are allowed in range patterns
+  --> $DIR/recover-range-pats.rs:20:15
+   |
+LL |     if let X..true = 0 {}
+   |               ^^^^ ranges require char or numeric types
+   |
+   = note: start type: u8
+   = note: end type: bool
+
+error[E0308]: mismatched types
+  --> $DIR/recover-range-pats.rs:21:12
+   |
+LL |     if let .0..Y = 0 {}
+   |            ^^^^^ expected integer, found floating-point number
+   |
+   = note: expected type `{integer}`
+              found type `{float}`
+
+error[E0308]: mismatched types
+  --> $DIR/recover-range-pats.rs:23:12
+   |
+LL |     if let X.. .0 = 0 {}
+   |            ^^^^^^ expected integer, found floating-point number
+   |
+   = note: expected type `u8`
+              found type `{float}`
+
+error[E0029]: only char and numeric types are allowed in range patterns
+  --> $DIR/recover-range-pats.rs:32:12
+   |
+LL |     if let true..=Y = 0 {}
+   |            ^^^^ ranges require char or numeric types
+   |
+   = note: start type: bool
+   = note: end type: u8
+
+error[E0029]: only char and numeric types are allowed in range patterns
+  --> $DIR/recover-range-pats.rs:33:16
+   |
+LL |     if let X..=true = 0 {}
+   |                ^^^^ ranges require char or numeric types
+   |
+   = note: start type: u8
+   = note: end type: bool
+
+error[E0308]: mismatched types
+  --> $DIR/recover-range-pats.rs:34:12
+   |
+LL |     if let .0..=Y = 0 {}
+   |            ^^^^^^ expected integer, found floating-point number
+   |
+   = note: expected type `{integer}`
+              found type `{float}`
+
+error[E0308]: mismatched types
+  --> $DIR/recover-range-pats.rs:36:12
+   |
+LL |     if let X..=.0 = 0 {}
+   |            ^^^^^^ expected integer, found floating-point number
+   |
+   = note: expected type `u8`
+              found type `{float}`
+
+error[E0029]: only char and numeric types are allowed in range patterns
+  --> $DIR/recover-range-pats.rs:45:12
+   |
+LL |     if let true...Y = 0 {}
+   |            ^^^^ ranges require char or numeric types
+   |
+   = note: start type: bool
+   = note: end type: u8
+
+error[E0029]: only char and numeric types are allowed in range patterns
+  --> $DIR/recover-range-pats.rs:47:16
+   |
+LL |     if let X...true = 0 {}
+   |                ^^^^ ranges require char or numeric types
+   |
+   = note: start type: u8
+   = note: end type: bool
+
+error[E0308]: mismatched types
+  --> $DIR/recover-range-pats.rs:49:12
+   |
+LL |     if let .0...Y = 0 {}
+   |            ^^^^^^ expected integer, found floating-point number
+   |
+   = note: expected type `{integer}`
+              found type `{float}`
+
+error[E0308]: mismatched types
+  --> $DIR/recover-range-pats.rs:52:12
+   |
+LL |     if let X... .0 = 0 {}
+   |            ^^^^^^^ expected integer, found floating-point number
+   |
+   = note: expected type `u8`
+              found type `{float}`
+
+error[E0029]: only char and numeric types are allowed in range patterns
+  --> $DIR/recover-range-pats.rs:60:12
+   |
+LL |     if let true.. = 0 {}
+   |            ^^^^ ranges require char or numeric types
+   |
+   = note: start type: bool
+   = note: end type: [type error]
+
+error[E0308]: mismatched types
+  --> $DIR/recover-range-pats.rs:62:12
+   |
+LL |     if let .0.. = 0 {}
+   |            ^^^^ expected integer, found floating-point number
+   |
+   = note: expected type `{integer}`
+              found type `{float}`
+
+error[E0029]: only char and numeric types are allowed in range patterns
+  --> $DIR/recover-range-pats.rs:70:12
+   |
+LL |     if let true..= = 0 {}
+   |            ^^^^ ranges require char or numeric types
+   |
+   = note: start type: bool
+   = note: end type: [type error]
+
+error[E0308]: mismatched types
+  --> $DIR/recover-range-pats.rs:72:12
+   |
+LL |     if let .0..= = 0 {}
+   |            ^^^^^ expected integer, found floating-point number
+   |
+   = note: expected type `{integer}`
+              found type `{float}`
+
+error[E0029]: only char and numeric types are allowed in range patterns
+  --> $DIR/recover-range-pats.rs:82:12
+   |
+LL |     if let true... = 0 {}
+   |            ^^^^ ranges require char or numeric types
+   |
+   = note: start type: bool
+   = note: end type: [type error]
+
+error[E0308]: mismatched types
+  --> $DIR/recover-range-pats.rs:85:12
+   |
+LL |     if let .0... = 0 {}
+   |            ^^^^^ expected integer, found floating-point number
+   |
+   = note: expected type `{integer}`
+              found type `{float}`
+
+error[E0029]: only char and numeric types are allowed in range patterns
+  --> $DIR/recover-range-pats.rs:94:14
+   |
+LL |     if let ..true = 0 {}
+   |              ^^^^ ranges require char or numeric types
+   |
+   = note: start type: [type error]
+   = note: end type: bool
+
+error[E0308]: mismatched types
+  --> $DIR/recover-range-pats.rs:96:12
+   |
+LL |     if let .. .0 = 0 {}
+   |            ^^^^^ expected integer, found floating-point number
+   |
+   = note: expected type `{integer}`
+              found type `{float}`
+
+error[E0029]: only char and numeric types are allowed in range patterns
+  --> $DIR/recover-range-pats.rs:104:15
+   |
+LL |     if let ..=true = 0 {}
+   |               ^^^^ ranges require char or numeric types
+   |
+   = note: start type: [type error]
+   = note: end type: bool
+
+error[E0308]: mismatched types
+  --> $DIR/recover-range-pats.rs:106:12
+   |
+LL |     if let ..=.0 = 0 {}
+   |            ^^^^^ expected integer, found floating-point number
+   |
+   = note: expected type `{integer}`
+              found type `{float}`
+
+error[E0029]: only char and numeric types are allowed in range patterns
+  --> $DIR/recover-range-pats.rs:116:15
+   |
+LL |     if let ...true = 0 {}
+   |               ^^^^ ranges require char or numeric types
+   |
+   = note: start type: [type error]
+   = note: end type: bool
+
+error[E0308]: mismatched types
+  --> $DIR/recover-range-pats.rs:119:12
+   |
+LL |     if let ....3 = 0 {}
+   |            ^^^^^ expected integer, found floating-point number
+   |
+   = note: expected type `{integer}`
+              found type `{float}`
+
+error: aborting due to 76 previous errors
+
+Some errors have detailed explanations: E0029, E0308.
+For more information about an error, try `rustc --explain E0029`.
diff --git a/src/test/ui/parser/recover-tuple-pat.rs b/src/test/ui/parser/recover-tuple-pat.rs
index 488e8db6b8789..7fded752d675e 100644
--- a/src/test/ui/parser/recover-tuple-pat.rs
+++ b/src/test/ui/parser/recover-tuple-pat.rs
@@ -1,12 +1,12 @@
+// NOTE: This doesn't recover anymore.
+
 fn main() {
     let x = (1, 2, 3, 4);
     match x {
         (1, .., 4) => {}
         (1, .=., 4) => { let _: usize = ""; }
         //~^ ERROR expected pattern, found `.`
-        //~| ERROR mismatched types
         (.=., 4) => {}
-        //~^ ERROR expected pattern, found `.`
         (1, 2, 3, 4) => {}
     }
 }
diff --git a/src/test/ui/parser/recover-tuple-pat.stderr b/src/test/ui/parser/recover-tuple-pat.stderr
index 5919aa72355ac..93a6a66a63082 100644
--- a/src/test/ui/parser/recover-tuple-pat.stderr
+++ b/src/test/ui/parser/recover-tuple-pat.stderr
@@ -1,24 +1,8 @@
 error: expected pattern, found `.`
-  --> $DIR/recover-tuple-pat.rs:5:13
+  --> $DIR/recover-tuple-pat.rs:7:13
    |
 LL |         (1, .=., 4) => { let _: usize = ""; }
    |             ^ expected pattern
 
-error: expected pattern, found `.`
-  --> $DIR/recover-tuple-pat.rs:8:10
-   |
-LL |         (.=., 4) => {}
-   |          ^ expected pattern
-
-error[E0308]: mismatched types
-  --> $DIR/recover-tuple-pat.rs:5:41
-   |
-LL |         (1, .=., 4) => { let _: usize = ""; }
-   |                                         ^^ expected usize, found reference
-   |
-   = note: expected type `usize`
-              found type `&'static str`
-
-error: aborting due to 3 previous errors
+error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/rfc-2005-default-binding-mode/slice.rs b/src/test/ui/rfc-2005-default-binding-mode/slice.rs
index fd85bf7f16007..1484b8c4a1f13 100644
--- a/src/test/ui/rfc-2005-default-binding-mode/slice.rs
+++ b/src/test/ui/rfc-2005-default-binding-mode/slice.rs
@@ -4,6 +4,6 @@ pub fn main() {
     let sl: &[u8] = b"foo";
 
     match sl { //~ ERROR non-exhaustive patterns
-        [first, remainder..] => {},
+        [first, remainder @ ..] => {},
     };
 }
diff --git a/src/test/ui/rfcs/rfc-2005-default-binding-mode/slice.rs b/src/test/ui/rfcs/rfc-2005-default-binding-mode/slice.rs
index 939a3c4a1fd7a..38b0941aad0a6 100644
--- a/src/test/ui/rfcs/rfc-2005-default-binding-mode/slice.rs
+++ b/src/test/ui/rfcs/rfc-2005-default-binding-mode/slice.rs
@@ -5,7 +5,7 @@ fn slice_pat() {
     let sl: &[u8] = b"foo";
 
     match sl {
-        [first, remainder..] => {
+        [first, remainder @ ..] => {
             let _: &u8 = first;
             assert_eq!(first, &b'f');
             assert_eq!(remainder, b"oo");
diff --git a/src/test/ui/trailing-comma.rs b/src/test/ui/trailing-comma.rs
index f34e6b7606dd7..929c35a9e1122 100644
--- a/src/test/ui/trailing-comma.rs
+++ b/src/test/ui/trailing-comma.rs
@@ -25,7 +25,7 @@ pub fn main() {
     let (_, _,) = (1, 1,);
     let [_, _,] = [1, 1,];
     let [_, _, .., _,] = [1, 1, 1, 1,];
-    let [_, _, _.., _,] = [1, 1, 1, 1,];
+    let [_, _, _, ..,] = [1, 1, 1, 1,];
 
     let x: Foo<isize,> = Foo::<isize,>(1);