` denotes its position.
+ /// 0 <= position <= subpats.len()
+ TupleStruct(Path, HirVec>, Option),
/// A path pattern.
/// Such pattern can be resolved to a unit struct/variant or a constant.
@@ -536,8 +537,10 @@ pub enum PatKind {
/// PatKind::Path, and the resolver will have to sort that out.
QPath(QSelf, Path),
- /// A tuple pattern `(a, b)`
- Tup(HirVec>),
+ /// A tuple pattern `(a, b)`.
+ /// If the `..` pattern fragment presents, then `Option` denotes its position.
+ /// 0 <= position <= subpats.len()
+ Tuple(HirVec>, Option),
/// A `box` pattern
Box(P),
/// A reference pattern, e.g. `&mut (a, b)`
diff --git a/src/librustc_front/intravisit.rs b/src/librustc_front/intravisit.rs
index d71e392f521e7..2f185e33ec9aa 100644
--- a/src/librustc_front/intravisit.rs
+++ b/src/librustc_front/intravisit.rs
@@ -468,11 +468,9 @@ pub fn walk_assoc_type_binding<'v, V: Visitor<'v>>(visitor: &mut V,
pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
match pattern.node {
- PatKind::TupleStruct(ref path, ref opt_children) => {
+ PatKind::TupleStruct(ref path, ref children, _) => {
visitor.visit_path(path, pattern.id);
- if let Some(ref children) = *opt_children {
- walk_list!(visitor, visit_pat, children);
- }
+ walk_list!(visitor, visit_pat, children);
}
PatKind::Path(ref path) => {
visitor.visit_path(path, pattern.id);
@@ -488,7 +486,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
visitor.visit_pat(&field.node.pat)
}
}
- PatKind::Tup(ref tuple_elements) => {
+ PatKind::Tuple(ref tuple_elements, _) => {
walk_list!(visitor, visit_pat, tuple_elements);
}
PatKind::Box(ref subpattern) |
diff --git a/src/librustc_front/lowering.rs b/src/librustc_front/lowering.rs
index acc7c6164b54a..91b62bf6e5448 100644
--- a/src/librustc_front/lowering.rs
+++ b/src/librustc_front/lowering.rs
@@ -925,10 +925,9 @@ pub fn lower_pat(lctx: &LoweringContext, p: &Pat) -> P {
sub.as_ref().map(|x| lower_pat(lctx, x)))
}
PatKind::Lit(ref e) => hir::PatKind::Lit(lower_expr(lctx, e)),
- PatKind::TupleStruct(ref pth, ref pats) => {
+ PatKind::TupleStruct(ref pth, ref pats, ddpos) => {
hir::PatKind::TupleStruct(lower_path(lctx, pth),
- pats.as_ref()
- .map(|pats| pats.iter().map(|x| lower_pat(lctx, x)).collect()))
+ pats.iter().map(|x| lower_pat(lctx, x)).collect(), ddpos)
}
PatKind::Path(ref pth) => {
hir::PatKind::Path(lower_path(lctx, pth))
@@ -956,8 +955,8 @@ pub fn lower_pat(lctx: &LoweringContext, p: &Pat) -> P {
.collect();
hir::PatKind::Struct(pth, fs, etc)
}
- PatKind::Tup(ref elts) => {
- hir::PatKind::Tup(elts.iter().map(|x| lower_pat(lctx, x)).collect())
+ PatKind::Tuple(ref elts, ddpos) => {
+ hir::PatKind::Tuple(elts.iter().map(|x| lower_pat(lctx, x)).collect(), ddpos)
}
PatKind::Box(ref inner) => hir::PatKind::Box(lower_pat(lctx, inner)),
PatKind::Ref(ref inner, mutbl) => {
@@ -1839,7 +1838,7 @@ fn pat_enum(lctx: &LoweringContext,
let pt = if subpats.is_empty() {
hir::PatKind::Path(path)
} else {
- hir::PatKind::TupleStruct(path, Some(subpats))
+ hir::PatKind::TupleStruct(path, subpats, None)
};
pat(lctx, span, pt)
}
diff --git a/src/librustc_front/print/pprust.rs b/src/librustc_front/print/pprust.rs
index 143dfce09b602..f1ef7a06c6ee0 100644
--- a/src/librustc_front/print/pprust.rs
+++ b/src/librustc_front/print/pprust.rs
@@ -1729,16 +1729,23 @@ impl<'a> State<'a> {
None => (),
}
}
- PatKind::TupleStruct(ref path, ref args_) => {
+ PatKind::TupleStruct(ref path, ref elts, ddpos) => {
try!(self.print_path(path, true, 0));
- match *args_ {
- None => try!(word(&mut self.s, "(..)")),
- Some(ref args) => {
- try!(self.popen());
- try!(self.commasep(Inconsistent, &args[..], |s, p| s.print_pat(&p)));
- try!(self.pclose());
+ try!(self.popen());
+ if let Some(ddpos) = ddpos {
+ try!(self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p)));
+ if ddpos != 0 {
+ try!(self.word_space(","));
+ }
+ try!(word(&mut self.s, ".."));
+ if ddpos != elts.len() {
+ try!(word(&mut self.s, ","));
+ try!(self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p)));
}
+ } else {
+ try!(self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p)));
}
+ try!(self.pclose());
}
PatKind::Path(ref path) => {
try!(self.print_path(path, true, 0));
@@ -1771,11 +1778,23 @@ impl<'a> State<'a> {
try!(space(&mut self.s));
try!(word(&mut self.s, "}"));
}
- PatKind::Tup(ref elts) => {
+ PatKind::Tuple(ref elts, ddpos) => {
try!(self.popen());
- try!(self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p)));
- if elts.len() == 1 {
- try!(word(&mut self.s, ","));
+ if let Some(ddpos) = ddpos {
+ try!(self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p)));
+ if ddpos != 0 {
+ try!(self.word_space(","));
+ }
+ try!(word(&mut self.s, ".."));
+ if ddpos != elts.len() {
+ try!(word(&mut self.s, ","));
+ try!(self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p)));
+ }
+ } else {
+ try!(self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p)));
+ if elts.len() == 1 {
+ try!(word(&mut self.s, ","));
+ }
}
try!(self.pclose());
}
diff --git a/src/librustc_front/util.rs b/src/librustc_front/util.rs
index 2c86c713b1b7a..7c40900b98ad4 100644
--- a/src/librustc_front/util.rs
+++ b/src/librustc_front/util.rs
@@ -32,7 +32,7 @@ pub fn walk_pat(pat: &Pat, mut it: F) -> bool
PatKind::Struct(_, ref fields, _) => {
fields.iter().all(|field| walk_pat_(&field.node.pat, it))
}
- PatKind::TupleStruct(_, Some(ref s)) | PatKind::Tup(ref s) => {
+ PatKind::TupleStruct(_, ref s, _) | PatKind::Tuple(ref s, _) => {
s.iter().all(|p| walk_pat_(&p, it))
}
PatKind::Box(ref s) | PatKind::Ref(ref s, _) => {
@@ -47,7 +47,6 @@ pub fn walk_pat(pat: &Pat, mut it: F) -> bool
PatKind::Lit(_) |
PatKind::Range(_, _) |
PatKind::Ident(_, _, _) |
- PatKind::TupleStruct(..) |
PatKind::Path(..) |
PatKind::QPath(_, _) => {
true
diff --git a/src/librustc_mir/hair/cx/pattern.rs b/src/librustc_mir/hair/cx/pattern.rs
index 6f4375d53ec4b..efc8f380e7891 100644
--- a/src/librustc_mir/hair/cx/pattern.rs
+++ b/src/librustc_mir/hair/cx/pattern.rs
@@ -130,21 +130,34 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
ref sty =>
self.cx.tcx.sess.span_bug(
pat.span,
- &format!("unexpanded type for vector pattern: {:?}", sty)),
+ &format!("unexpected type for vector pattern: {:?}", sty)),
}
}
- PatKind::Tup(ref subpatterns) => {
- let subpatterns =
- subpatterns.iter()
- .enumerate()
- .map(|(i, subpattern)| FieldPattern {
- field: Field::new(i),
- pattern: self.to_pattern(subpattern),
- })
- .collect();
+ PatKind::Tuple(ref subpatterns, ddpos) => {
+ match self.cx.tcx.node_id_to_type(pat.id).sty {
+ ty::TyTuple(ref tys) => {
+ let adjust = |i| {
+ let gap = tys.len() - subpatterns.len();
+ if ddpos.is_none() || ddpos.unwrap() > i { i } else { i + gap }
+ };
+ let subpatterns =
+ subpatterns.iter()
+ .enumerate()
+ .map(|(i, subpattern)| FieldPattern {
+ field: Field::new(adjust(i)),
+ pattern: self.to_pattern(subpattern),
+ })
+ .collect();
+
+ PatternKind::Leaf { subpatterns: subpatterns }
+ }
- PatternKind::Leaf { subpatterns: subpatterns }
+ ref sty =>
+ self.cx.tcx.sess.span_bug(
+ pat.span,
+ &format!("unexpected type for tuple pattern: {:?}", sty)),
+ }
}
PatKind::Ident(bm, ref ident, ref sub)
@@ -183,13 +196,28 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
self.variant_or_leaf(pat, vec![])
}
- PatKind::TupleStruct(_, ref opt_subpatterns) => {
+ PatKind::TupleStruct(_, ref subpatterns, ddpos) => {
+ let pat_ty = self.cx.tcx.node_id_to_type(pat.id);
+ let adt_def = match pat_ty.sty {
+ ty::TyStruct(adt_def, _) | ty::TyEnum(adt_def, _) => adt_def,
+ _ => {
+ self.cx.tcx.sess.span_bug(
+ pat.span,
+ "tuple struct pattern not applied to struct or enum");
+ }
+ };
+ let def = self.cx.tcx.def_map.borrow().get(&pat.id).unwrap().full_def();
+ let variant_def = adt_def.variant_of_def(def);
+
+ let adjust = |i| {
+ let gap = variant_def.fields.len() - subpatterns.len();
+ if ddpos.is_none() || ddpos.unwrap() > i { i } else { i + gap }
+ };
let subpatterns =
- opt_subpatterns.iter()
- .flat_map(|v| v.iter())
+ subpatterns.iter()
.enumerate()
.map(|(i, field)| FieldPattern {
- field: Field::new(i),
+ field: Field::new(adjust(i)),
pattern: self.to_pattern(field),
})
.collect();
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index 214ac81ee5092..c98da9964625b 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -895,12 +895,13 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivacyVisitor<'a, 'tcx> {
NamedField(field.node.name));
}
}
-
- // Patterns which bind no fields are allowable (the path is check
- // elsewhere).
- PatKind::TupleStruct(_, Some(ref fields)) => {
+ PatKind::TupleStruct(_, ref fields, ddpos) => {
match self.tcx.pat_ty(pattern).sty {
ty::TyStruct(def, _) => {
+ let adjust = |i| {
+ let gap = def.struct_variant().fields.len() - fields.len();
+ if ddpos.is_none() || ddpos.unwrap() > i { i } else { i + gap }
+ };
for (i, field) in fields.iter().enumerate() {
if let PatKind::Wild = field.node {
continue
@@ -908,7 +909,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivacyVisitor<'a, 'tcx> {
self.check_field(field.span,
def,
def.struct_variant(),
- UnnamedField(i));
+ UnnamedField(adjust(i)));
}
}
ty::TyEnum(..) => {
@@ -916,7 +917,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivacyVisitor<'a, 'tcx> {
}
_ => {}
}
-
}
_ => {}
}
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index a205bfb98acfe..2ae843c0515cc 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -2431,7 +2431,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
}
- PatKind::TupleStruct(ref path, _) | PatKind::Path(ref path) => {
+ PatKind::TupleStruct(ref path, _, _) | PatKind::Path(ref path) => {
// This must be an enum variant, struct or const.
let resolution = match self.resolve_possibly_assoc_item(pat_id,
None,
diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs
index 7f9f876fad1fa..e31e81bd9ec98 100644
--- a/src/librustc_trans/save/mod.rs
+++ b/src/librustc_trans/save/mod.rs
@@ -762,7 +762,7 @@ impl<'v> Visitor<'v> for PathCollector {
self.collected_paths.push((p.id, path.clone(),
ast::Mutability::Mutable, recorder::TypeRef));
}
- PatKind::TupleStruct(ref path, _) |
+ PatKind::TupleStruct(ref path, _, _) |
PatKind::Path(ref path) |
PatKind::QPath(_, ref path) => {
self.collected_paths.push((p.id, path.clone(),
diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs
index d1567cc6fa543..bb64569895bbe 100644
--- a/src/librustc_trans/trans/_match.rs
+++ b/src/librustc_trans/trans/_match.rs
@@ -798,7 +798,7 @@ fn any_irrefutable_adt_pat(tcx: &TyCtxt, m: &[Match], col: usize) -> bool {
m.iter().any(|br| {
let pat = br.pats[col];
match pat.node {
- PatKind::Tup(_) => true,
+ PatKind::Tuple(..) => true,
PatKind::Struct(..) | PatKind::TupleStruct(..) |
PatKind::Path(..) | PatKind::Ident(_, _, None) => {
match tcx.def_map.borrow().get(&pat.id).unwrap().full_def() {
@@ -1845,7 +1845,7 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
bcx = bind_irrefutable_pat(bcx, &inner_pat, val, cleanup_scope);
}
}
- PatKind::TupleStruct(_, ref sub_pats) => {
+ PatKind::TupleStruct(_, ref sub_pats, ddpos) => {
let opt_def = bcx.tcx().def_map.borrow().get(&pat.id).map(|d| d.full_def());
match opt_def {
Some(Def::Variant(enum_id, var_id)) => {
@@ -1855,35 +1855,41 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
&repr,
Disr::from(vinfo.disr_val),
val);
- if let Some(ref sub_pat) = *sub_pats {
- for (i, &argval) in args.vals.iter().enumerate() {
- bcx = bind_irrefutable_pat(
- bcx,
- &sub_pat[i],
- MatchInput::from_val(argval),
- cleanup_scope);
- }
+ let adjust = |i| {
+ let gap = vinfo.fields.len() - sub_pats.len();
+ if ddpos.is_none() || ddpos.unwrap() > i { i } else { i + gap }
+ };
+ for (i, subpat) in sub_pats.iter().enumerate() {
+ bcx = bind_irrefutable_pat(
+ bcx,
+ subpat,
+ MatchInput::from_val(args.vals[adjust(i)]),
+ cleanup_scope);
}
}
Some(Def::Struct(..)) => {
- match *sub_pats {
- None => {
- // This is a unit-like struct. Nothing to do here.
- }
- Some(ref elems) => {
- // This is the tuple struct case.
- let repr = adt::represent_node(bcx, pat.id);
- let val = adt::MaybeSizedValue::sized(val.val);
- for (i, elem) in elems.iter().enumerate() {
- let fldptr = adt::trans_field_ptr(bcx, &repr,
- val, Disr(0), i);
- bcx = bind_irrefutable_pat(
- bcx,
- &elem,
- MatchInput::from_val(fldptr),
- cleanup_scope);
- }
+ let expected_len = match *ccx.tcx().pat_ty(&pat) {
+ ty::TyS{sty: ty::TyStruct(adt_def, _), ..} => {
+ adt_def.struct_variant().fields.len()
}
+ ref ty => ccx.tcx().sess.span_bug(pat.span,
+ &format!("tuple struct pattern unexpected type {:?}", ty)),
+ };
+
+ let adjust = |i| {
+ let gap = expected_len - sub_pats.len();
+ if ddpos.is_none() || ddpos.unwrap() > i { i } else { i + gap }
+ };
+ let repr = adt::represent_node(bcx, pat.id);
+ let val = adt::MaybeSizedValue::sized(val.val);
+ for (i, elem) in sub_pats.iter().enumerate() {
+ let fldptr = adt::trans_field_ptr(bcx, &repr,
+ val, Disr(0), adjust(i));
+ bcx = bind_irrefutable_pat(
+ bcx,
+ &elem,
+ MatchInput::from_val(fldptr),
+ cleanup_scope);
}
}
_ => {
@@ -1931,16 +1937,28 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
cleanup_scope);
}
}
- PatKind::Tup(ref elems) => {
- let repr = adt::represent_node(bcx, pat.id);
- let val = adt::MaybeSizedValue::sized(val.val);
- for (i, elem) in elems.iter().enumerate() {
- let fldptr = adt::trans_field_ptr(bcx, &repr, val, Disr(0), i);
- bcx = bind_irrefutable_pat(
- bcx,
- &elem,
- MatchInput::from_val(fldptr),
- cleanup_scope);
+ PatKind::Tuple(ref elems, ddpos) => {
+ match tcx.node_id_to_type(pat.id).sty {
+ ty::TyTuple(ref tys) => {
+ let adjust = |i| {
+ let gap = tys.len() - elems.len();
+ if ddpos.is_none() || ddpos.unwrap() > i { i } else { i + gap }
+ };
+ let repr = adt::represent_node(bcx, pat.id);
+ let val = adt::MaybeSizedValue::sized(val.val);
+ for (i, elem) in elems.iter().enumerate() {
+ let fldptr = adt::trans_field_ptr(bcx, &repr, val, Disr(0), adjust(i));
+ bcx = bind_irrefutable_pat(
+ bcx,
+ &elem,
+ MatchInput::from_val(fldptr),
+ cleanup_scope);
+ }
+ }
+ ref sty => {
+ tcx.sess.span_bug(pat.span,
+ &format!("unexpected type for tuple pattern: {:?}", sty))
+ }
}
}
PatKind::Box(ref inner) => {
diff --git a/src/librustc_trans/trans/debuginfo/create_scope_map.rs b/src/librustc_trans/trans/debuginfo/create_scope_map.rs
index 4ba103c0c0d08..ac3a4b946759e 100644
--- a/src/librustc_trans/trans/debuginfo/create_scope_map.rs
+++ b/src/librustc_trans/trans/debuginfo/create_scope_map.rs
@@ -239,13 +239,11 @@ fn walk_pattern(cx: &CrateContext,
scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
}
- PatKind::TupleStruct(_, ref sub_pats_opt) => {
+ PatKind::TupleStruct(_, ref sub_pats, _) => {
scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
- if let Some(ref sub_pats) = *sub_pats_opt {
- for p in sub_pats {
- walk_pattern(cx, &p, scope_stack, scope_map);
- }
+ for p in sub_pats {
+ walk_pattern(cx, &p, scope_stack, scope_map);
}
}
@@ -264,7 +262,7 @@ fn walk_pattern(cx: &CrateContext,
}
}
- PatKind::Tup(ref sub_pats) => {
+ PatKind::Tuple(ref sub_pats, _) => {
scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
for sub_pat in sub_pats {
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index 548f5ee5e949d..f537494afab2e 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -13,7 +13,7 @@ use middle::infer::{self, TypeOrigin};
use middle::pat_util::{PatIdMap, pat_id_map, pat_is_binding};
use middle::pat_util::pat_is_resolved_const;
use middle::subst::Substs;
-use middle::ty::{self, Ty, TypeFoldable, LvaluePreference};
+use middle::ty::{self, Ty, TypeFoldable, LvaluePreference, TyTuple};
use check::{check_expr, check_expr_has_type, check_expr_with_expectation};
use check::{check_expr_coercable_to_type, demand, FnCtxt, Expectation};
use check::{check_expr_with_lvalue_pref};
@@ -197,13 +197,13 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
}
PatKind::Ident(_, ref path, _) => {
let path = hir_util::ident_to_path(path.span, path.node);
- check_pat_enum(pcx, pat, &path, Some(&[]), expected, false);
+ check_pat_enum(pcx, pat, &path, &[], None, expected, false);
}
- PatKind::TupleStruct(ref path, ref subpats) => {
- check_pat_enum(pcx, pat, path, subpats.as_ref().map(|v| &v[..]), expected, true);
+ PatKind::TupleStruct(ref path, ref subpats, ddpos) => {
+ check_pat_enum(pcx, pat, path, &subpats, ddpos, expected, true);
}
PatKind::Path(ref path) => {
- check_pat_enum(pcx, pat, path, Some(&[]), expected, false);
+ check_pat_enum(pcx, pat, path, &[], None, expected, false);
}
PatKind::QPath(ref qself, ref path) => {
let self_ty = fcx.to_ty(&qself.ty);
@@ -244,15 +244,26 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
PatKind::Struct(ref path, ref fields, etc) => {
check_pat_struct(pcx, pat, path, fields, etc, expected);
}
- PatKind::Tup(ref elements) => {
- let element_tys: Vec<_> =
- (0..elements.len()).map(|_| fcx.infcx().next_ty_var())
- .collect();
+ PatKind::Tuple(ref elements, ddpos) => {
+ let mut expected_len = elements.len();
+ if ddpos.is_some() {
+ // Require known type only when `..` is present
+ if let TyTuple(ref tys) = structurally_resolved_type(fcx, pat.span, expected).sty {
+ expected_len = tys.len();
+ }
+ }
+ let max_len = cmp::max(expected_len, elements.len());
+
+ let element_tys = (0 .. max_len).map(|_| fcx.infcx().next_ty_var()).collect(): Vec<_>;
let pat_ty = tcx.mk_tup(element_tys.clone());
fcx.write_ty(pat.id, pat_ty);
demand::eqtype(fcx, pat.span, expected, pat_ty);
- for (element_pat, element_ty) in elements.iter().zip(element_tys) {
- check_pat(pcx, &element_pat, element_ty);
+ let adjust = |i| {
+ let gap = expected_len - elements.len();
+ if ddpos.is_none() || ddpos.unwrap() > i { i } else { i + gap }
+ };
+ for i in 0 .. elements.len() {
+ check_pat(pcx, &elements[i], &element_tys[adjust(i)]);
}
}
PatKind::Box(ref inner) => {
@@ -600,7 +611,8 @@ fn bad_struct_kind_err(sess: &Session, pat: &hir::Pat, path: &hir::Path, lint: b
fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
pat: &hir::Pat,
path: &hir::Path,
- subpats: Option<&'tcx [P]>,
+ subpats: &'tcx [P],
+ ddpos: Option,
expected: Ty<'tcx>,
is_tuple_struct_pat: bool)
{
@@ -612,13 +624,9 @@ fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
Some(&path_res) if path_res.base_def != Def::Err => path_res,
_ => {
fcx.write_error(pat.id);
-
- if let Some(subpats) = subpats {
- for pat in subpats {
- check_pat(pcx, &pat, tcx.types.err);
- }
+ for pat in subpats {
+ check_pat(pcx, &pat, tcx.types.err);
}
-
return;
}
};
@@ -661,10 +669,8 @@ fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
bad_struct_kind_err(tcx.sess, pat, path, is_warning);
if is_warning { return; }
fcx.write_error(pat.id);
- if let Some(subpats) = subpats {
- for pat in subpats {
- check_pat(pcx, &pat, tcx.types.err);
- }
+ for pat in subpats {
+ check_pat(pcx, &pat, tcx.types.err);
}
};
@@ -701,11 +707,13 @@ fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
};
match (is_tuple_struct_pat, variant.kind()) {
- (true, ty::VariantKind::Unit) => {
+ (true, ty::VariantKind::Unit) if subpats.is_empty() && ddpos.is_some() => {
// Matching unit structs with tuple variant patterns (`UnitVariant(..)`)
// is allowed for backward compatibility.
report_bad_struct_kind(true);
}
+ (true, ty::VariantKind::Unit) |
+ (false, ty::VariantKind::Tuple) |
(_, ty::VariantKind::Struct) => {
report_bad_struct_kind(false);
return
@@ -713,30 +721,25 @@ fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
_ => {}
}
- if let Some(subpats) = subpats {
- if subpats.len() == variant.fields.len() {
- for (subpat, field) in subpats.iter().zip(&variant.fields) {
- let field_ty = fcx.field_ty(subpat.span, field, expected_substs);
- check_pat(pcx, &subpat, field_ty);
- }
- } else if variant.fields.is_empty() {
- span_err!(tcx.sess, pat.span, E0024,
- "this pattern has {} field{}, but the corresponding {} has no fields",
- subpats.len(), if subpats.len() == 1 {""} else {"s"}, kind_name);
-
- for pat in subpats {
- check_pat(pcx, &pat, tcx.types.err);
- }
- } else {
- span_err!(tcx.sess, pat.span, E0023,
- "this pattern has {} field{}, but the corresponding {} has {} field{}",
- subpats.len(), if subpats.len() == 1 {""} else {"s"},
- kind_name,
- variant.fields.len(), if variant.fields.len() == 1 {""} else {"s"});
-
- for pat in subpats {
- check_pat(pcx, &pat, tcx.types.err);
- }
+ let adjust = |i| {
+ let gap = variant.fields.len() - subpats.len();
+ if ddpos.is_none() || ddpos.unwrap() > i { i } else { i + gap }
+ };
+ if subpats.len() == variant.fields.len() ||
+ subpats.len() < variant.fields.len() && ddpos.is_some() {
+ for (i, subpat) in subpats.iter().enumerate() {
+ let field_ty = fcx.field_ty(subpat.span, &variant.fields[adjust(i)], expected_substs);
+ check_pat(pcx, &subpat, field_ty);
+ }
+ } else {
+ span_err!(tcx.sess, pat.span, E0023,
+ "this pattern has {} field{}, but the corresponding {} has {} field{}",
+ subpats.len(), if subpats.len() == 1 {""} else {"s"},
+ kind_name,
+ variant.fields.len(), if variant.fields.len() == 1 {""} else {"s"});
+
+ for pat in subpats {
+ check_pat(pcx, &pat, tcx.types.err);
}
}
}
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index a103cbc928b49..f7ab68e707b59 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -81,6 +81,7 @@ This API is completely unstable and subject to change.
#![feature(rustc_diagnostic_macros)]
#![feature(rustc_private)]
#![feature(staged_api)]
+#![feature(type_ascription)]
#[macro_use] extern crate log;
#[macro_use] extern crate syntax;
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 6c9ee52878201..17c87f010bcf0 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -2535,7 +2535,7 @@ fn name_from_pat(p: &hir::Pat) -> String {
match p.node {
PatKind::Wild => "_".to_string(),
PatKind::Ident(_, ref p, _) => p.node.to_string(),
- PatKind::TupleStruct(ref p, _) | PatKind::Path(ref p) => path_to_string(p),
+ PatKind::TupleStruct(ref p, _, _) | PatKind::Path(ref p) => path_to_string(p),
PatKind::QPath(..) => panic!("tried to get argument name from PatKind::QPath, \
which is not allowed in function arguments"),
PatKind::Struct(ref name, ref fields, etc) => {
@@ -2546,7 +2546,7 @@ fn name_from_pat(p: &hir::Pat) -> String {
if etc { ", ..." } else { "" }
)
},
- PatKind::Tup(ref elts) => format!("({})", elts.iter().map(|p| name_from_pat(&**p))
+ PatKind::Tuple(ref elts, _) => format!("({})", elts.iter().map(|p| name_from_pat(&**p))
.collect::>().join(", ")),
PatKind::Box(ref p) => name_from_pat(&**p),
PatKind::Ref(ref p, _) => name_from_pat(&**p),
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 0dbfb2c7be654..927dc93de0bab 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -569,9 +569,10 @@ pub enum PatKind {
/// The `bool` is `true` in the presence of a `..`.
Struct(Path, Vec>, bool),
- /// A tuple struct/variant pattern `Variant(x, y, z)`.
- /// "None" means a `Variant(..)` pattern where we don't bind the fields to names.
- TupleStruct(Path, Option>>),
+ /// A tuple struct/variant pattern `Variant(x, y, .., z)`.
+ /// If the `..` pattern fragment presents, then `Option` denotes its position.
+ /// 0 <= position <= subpats.len()
+ TupleStruct(Path, Vec>, Option),
/// A path pattern.
/// Such pattern can be resolved to a unit struct/variant or a constant.
@@ -583,8 +584,10 @@ pub enum PatKind {
/// PatKind::Path, and the resolver will have to sort that out.
QPath(QSelf, Path),
- /// A tuple pattern `(a, b)`
- Tup(Vec>),
+ /// A tuple pattern `(a, b)`.
+ /// If the `..` pattern fragment presents, then `Option` denotes its position.
+ /// 0 <= position <= subpats.len()
+ Tuple(Vec>, Option),
/// A `box` pattern
Box(P),
/// A reference pattern, e.g. `&mut (a, b)`
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index 0eb42f17f68b4..ad0272b3c11b1 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -830,7 +830,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
let pat = if subpats.is_empty() {
PatKind::Path(path)
} else {
- PatKind::TupleStruct(path, Some(subpats))
+ PatKind::TupleStruct(path, subpats, None)
};
self.pat(span, pat)
}
@@ -840,7 +840,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
self.pat(span, pat)
}
fn pat_tuple(&self, span: Span, pats: Vec>) -> P {
- self.pat(span, PatKind::Tup(pats))
+ self.pat(span, PatKind::Tuple(pats, None))
}
fn pat_some(&self, span: Span, pat: P) -> P {
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 2302548914223..e856c2f99cb57 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -245,6 +245,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option, Status
// a...b and ...b
("inclusive_range_syntax", "1.7.0", Some(28237), Active),
+
+ // Allows `..` in tuple (struct) patterns
+ ("dotdot_in_tuple_patterns", "1.9.0", None, Active),
];
// (changing above list without updating src/doc/reference.md makes @cmr sad)
@@ -1025,6 +1028,24 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
pattern.span,
"box pattern syntax is experimental");
}
+ PatKind::Tuple(_, ddpos)
+ if ddpos.is_some() => {
+ self.gate_feature("dotdot_in_tuple_patterns",
+ pattern.span,
+ "`..` in tuple patterns is experimental");
+ }
+ PatKind::TupleStruct(_, ref fields, ddpos)
+ if ddpos.is_some() && !fields.is_empty() => {
+ self.gate_feature("dotdot_in_tuple_patterns",
+ pattern.span,
+ "`..` in tuple struct patterns is experimental");
+ }
+ PatKind::TupleStruct(_, ref fields, ddpos)
+ if ddpos.is_none() && fields.is_empty() => {
+ self.context.span_handler.struct_span_err(pattern.span,
+ "nullary enum variants are written with \
+ no trailing `( )`").emit();
+ }
_ => {}
}
visit::walk_pat(self, pattern)
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 591c1295d6641..6680ec03836a8 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -1127,9 +1127,9 @@ pub fn noop_fold_pat(p: P, folder: &mut T) -> P {
sub.map(|x| folder.fold_pat(x)))
}
PatKind::Lit(e) => PatKind::Lit(folder.fold_expr(e)),
- PatKind::TupleStruct(pth, pats) => {
+ PatKind::TupleStruct(pth, pats, ddpos) => {
PatKind::TupleStruct(folder.fold_path(pth),
- pats.map(|pats| pats.move_map(|x| folder.fold_pat(x))))
+ pats.move_map(|x| folder.fold_pat(x)), ddpos)
}
PatKind::Path(pth) => {
PatKind::Path(folder.fold_path(pth))
@@ -1150,7 +1150,9 @@ pub fn noop_fold_pat(p: P, folder: &mut T) -> P {
});
PatKind::Struct(pth, fs, etc)
}
- PatKind::Tup(elts) => PatKind::Tup(elts.move_map(|x| folder.fold_pat(x))),
+ PatKind::Tuple(elts, ddpos) => {
+ PatKind::Tuple(elts.move_map(|x| folder.fold_pat(x)), ddpos)
+ }
PatKind::Box(inner) => PatKind::Box(folder.fold_pat(inner)),
PatKind::Ref(inner, mutbl) => PatKind::Ref(folder.fold_pat(inner), mutbl),
PatKind::Range(e1, e2) => {
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index d9714cc1e25e0..6a457695e280e 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -948,25 +948,6 @@ impl<'a> Parser<'a> {
Ok(result)
}
- /// Parse a sequence parameter of enum variant. For consistency purposes,
- /// these should not be empty.
- pub fn parse_enum_variant_seq(&mut self,
- bra: &token::Token,
- ket: &token::Token,
- sep: SeqSep,
- f: F)
- -> PResult<'a, Vec> where
- F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
- {
- let result = try!(self.parse_unspanned_seq(bra, ket, sep, f));
- if result.is_empty() {
- let last_span = self.last_span;
- self.span_err(last_span,
- "nullary enum variants are written with no trailing `( )`");
- }
- Ok(result)
- }
-
// NB: Do not use this function unless you actually plan to place the
// spanned list in the AST.
pub fn parse_seq(&mut self,
@@ -3358,21 +3339,29 @@ impl<'a> Parser<'a> {
};
}
- fn parse_pat_tuple_elements(&mut self) -> PResult<'a, Vec>> {
+ fn parse_pat_tuple_elements(&mut self, unary_needs_comma: bool)
+ -> PResult<'a, (Vec
>, Option)> {
let mut fields = vec![];
- if !self.check(&token::CloseDelim(token::Paren)) {
- fields.push(try!(self.parse_pat()));
- if self.look_ahead(1, |t| *t != token::CloseDelim(token::Paren)) {
- while self.eat(&token::Comma) &&
- !self.check(&token::CloseDelim(token::Paren)) {
+ let mut ddpos = None;
+
+ while !self.check(&token::CloseDelim(token::Paren)) {
+ if ddpos.is_none() && self.eat(&token::DotDot) {
+ ddpos = Some(fields.len());
+ if self.eat(&token::Comma) {
+ // `..` needs to be followed by `)` or `, pat`, `..,)` is disallowed.
fields.push(try!(self.parse_pat()));
}
+ } else {
+ fields.push(try!(self.parse_pat()));
}
- if fields.len() == 1 {
+
+ if !self.check(&token::CloseDelim(token::Paren)) ||
+ (unary_needs_comma && fields.len() == 1 && ddpos.is_none()) {
try!(self.expect(&token::Comma));
}
}
- Ok(fields)
+
+ Ok((fields, ddpos))
}
fn parse_pat_vec_elements(
@@ -3557,9 +3546,9 @@ impl<'a> Parser<'a> {
token::OpenDelim(token::Paren) => {
// Parse (pat,pat,pat,...) as tuple pattern
self.bump();
- let fields = try!(self.parse_pat_tuple_elements());
+ let (fields, ddpos) = try!(self.parse_pat_tuple_elements(true));
try!(self.expect(&token::CloseDelim(token::Paren)));
- pat = PatKind::Tup(fields);
+ pat = PatKind::Tuple(fields, ddpos);
}
token::OpenDelim(token::Bracket) => {
// Parse [pat,pat,...] as slice pattern
@@ -3646,20 +3635,10 @@ impl<'a> Parser<'a> {
return Err(self.fatal("unexpected `(` after qualified path"));
}
// Parse tuple struct or enum pattern
- if self.look_ahead(1, |t| *t == token::DotDot) {
- // This is a "top constructor only" pat
- self.bump();
- self.bump();
- try!(self.expect(&token::CloseDelim(token::Paren)));
- pat = PatKind::TupleStruct(path, None);
- } else {
- let args = try!(self.parse_enum_variant_seq(
- &token::OpenDelim(token::Paren),
- &token::CloseDelim(token::Paren),
- SeqSep::trailing_allowed(token::Comma),
- |p| p.parse_pat()));
- pat = PatKind::TupleStruct(path, Some(args));
- }
+ self.bump();
+ let (fields, ddpos) = try!(self.parse_pat_tuple_elements(false));
+ try!(self.expect(&token::CloseDelim(token::Paren)));
+ pat = PatKind::TupleStruct(path, fields, ddpos)
}
_ => {
pat = match qself {
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 55c1af44cab85..c827a056631ef 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -2482,17 +2482,23 @@ impl<'a> State<'a> {
None => ()
}
}
- PatKind::TupleStruct(ref path, ref args_) => {
+ PatKind::TupleStruct(ref path, ref elts, ddpos) => {
try!(self.print_path(path, true, 0));
- match *args_ {
- None => try!(word(&mut self.s, "(..)")),
- Some(ref args) => {
- try!(self.popen());
- try!(self.commasep(Inconsistent, &args[..],
- |s, p| s.print_pat(&p)));
- try!(self.pclose());
+ try!(self.popen());
+ if let Some(ddpos) = ddpos {
+ try!(self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p)));
+ if ddpos != 0 {
+ try!(self.word_space(","));
}
+ try!(word(&mut self.s, ".."));
+ if ddpos != elts.len() {
+ try!(word(&mut self.s, ","));
+ try!(self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p)));
+ }
+ } else {
+ try!(self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p)));
}
+ try!(self.pclose());
}
PatKind::Path(ref path) => {
try!(self.print_path(path, true, 0));
@@ -2523,13 +2529,23 @@ impl<'a> State<'a> {
try!(space(&mut self.s));
try!(word(&mut self.s, "}"));
}
- PatKind::Tup(ref elts) => {
+ PatKind::Tuple(ref elts, ddpos) => {
try!(self.popen());
- try!(self.commasep(Inconsistent,
- &elts[..],
- |s, p| s.print_pat(&p)));
- if elts.len() == 1 {
- try!(word(&mut self.s, ","));
+ if let Some(ddpos) = ddpos {
+ try!(self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p)));
+ if ddpos != 0 {
+ try!(self.word_space(","));
+ }
+ try!(word(&mut self.s, ".."));
+ if ddpos != elts.len() {
+ try!(word(&mut self.s, ","));
+ try!(self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p)));
+ }
+ } else {
+ try!(self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p)));
+ if elts.len() == 1 {
+ try!(word(&mut self.s, ","));
+ }
}
try!(self.pclose());
}
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 73ad488e55c93..00cbf05d029ec 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -419,11 +419,9 @@ pub fn walk_assoc_type_binding<'v, V: Visitor<'v>>(visitor: &mut V,
pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
match pattern.node {
- PatKind::TupleStruct(ref path, ref opt_children) => {
+ PatKind::TupleStruct(ref path, ref children, _) => {
visitor.visit_path(path, pattern.id);
- if let Some(ref children) = *opt_children {
- walk_list!(visitor, visit_pat, children);
- }
+ walk_list!(visitor, visit_pat, children);
}
PatKind::Path(ref path) => {
visitor.visit_path(path, pattern.id);
@@ -439,7 +437,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
visitor.visit_pat(&field.node.pat)
}
}
- PatKind::Tup(ref tuple_elements) => {
+ PatKind::Tuple(ref tuple_elements, _) => {
walk_list!(visitor, visit_pat, tuple_elements);
}
PatKind::Box(ref subpattern) |
diff --git a/src/test/compile-fail/issue-32004.rs b/src/test/compile-fail/issue-32004.rs
index 0227a80fd75d3..8d74154655fce 100644
--- a/src/test/compile-fail/issue-32004.rs
+++ b/src/test/compile-fail/issue-32004.rs
@@ -18,12 +18,12 @@ struct S;
fn main() {
match Foo::Baz {
Foo::Bar => {}
- //~^ ERROR this pattern has 0 fields, but the corresponding variant
+ //~^ ERROR `Foo::Bar` does not name a tuple variant or a tuple struct
_ => {}
}
match S {
S(()) => {}
- //~^ ERROR this pattern has 1 field, but the corresponding struct
+ //~^ ERROR `S` does not name a tuple variant or a tuple struct
}
}
diff --git a/src/test/compile-fail/match-pattern-field-mismatch-2.rs b/src/test/compile-fail/match-pattern-field-mismatch-2.rs
index e63ddf6c7fd9b..a4ba93ea17333 100644
--- a/src/test/compile-fail/match-pattern-field-mismatch-2.rs
+++ b/src/test/compile-fail/match-pattern-field-mismatch-2.rs
@@ -20,7 +20,7 @@ fn main() {
color::rgb(_, _, _) => { }
color::cmyk(_, _, _, _) => { }
color::no_color(_) => { }
- //~^ ERROR this pattern has 1 field, but the corresponding variant has no fields
+ //~^ ERROR `color::no_color` does not name a tuple variant or a tuple struct
}
}
}
diff --git a/src/test/compile-fail/pat-tuple-feature-gate.rs b/src/test/compile-fail/pat-tuple-feature-gate.rs
new file mode 100644
index 0000000000000..55ca05bdef381
--- /dev/null
+++ b/src/test/compile-fail/pat-tuple-feature-gate.rs
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+ match 0 {
+ (..) => {} //~ ERROR `..` in tuple patterns is experimental
+ (pat, ..) => {} //~ ERROR `..` in tuple patterns is experimental
+ S(pat, ..) => {} //~ ERROR `..` in tuple struct patterns is experimental
+ }
+}
diff --git a/src/test/compile-fail/pat-tuple-overfield.rs b/src/test/compile-fail/pat-tuple-overfield.rs
new file mode 100644
index 0000000000000..034ef4a72e21c
--- /dev/null
+++ b/src/test/compile-fail/pat-tuple-overfield.rs
@@ -0,0 +1,28 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(dotdot_in_tuple_patterns)]
+
+struct S(u8, u8, u8);
+
+fn main() {
+ match (1, 2, 3) {
+ (1, 2, 3, 4) => {} //~ ERROR mismatched types
+ (1, 2, .., 3, 4) => {} //~ ERROR mismatched types
+ _ => {}
+ }
+ match S(1, 2, 3) {
+ S(1, 2, 3, 4) => {}
+ //~^ ERROR this pattern has 4 fields, but the corresponding struct has 3 fields
+ S(1, 2, .., 3, 4) => {}
+ //~^ ERROR this pattern has 4 fields, but the corresponding struct has 3 fields
+ _ => {}
+ }
+}
diff --git a/src/test/compile-fail/pattern-error-continue.rs b/src/test/compile-fail/pattern-error-continue.rs
index 9ebdcf1a9ecb0..bbba5c56b5615 100644
--- a/src/test/compile-fail/pattern-error-continue.rs
+++ b/src/test/compile-fail/pattern-error-continue.rs
@@ -25,7 +25,7 @@ fn f(_c: char) {}
fn main() {
match A::B(1, 2) {
A::B(_, _, _) => (), //~ ERROR this pattern has 3 fields, but
- A::D(_) => (), //~ ERROR this pattern has 1 field, but
+ A::D(_) => (), //~ ERROR `A::D` does not name a tuple variant or a tuple struct
_ => ()
}
match 'c' {
diff --git a/src/test/parse-fail/pat-lt-bracket-6.rs b/src/test/parse-fail/pat-lt-bracket-6.rs
index bc27aedb627ee..fb78e558a951a 100644
--- a/src/test/parse-fail/pat-lt-bracket-6.rs
+++ b/src/test/parse-fail/pat-lt-bracket-6.rs
@@ -9,6 +9,5 @@
// except according to those terms.
fn main() {
- let Test(&desc[..]) = x; //~ error: expected one of `,` or `@`, found `[`
- //~^ ERROR expected one of `:`, `;`, or `=`, found `..`
+ let Test(&desc[..]) = x; //~ ERROR: expected one of `)`, `,`, or `@`, found `[`
}
diff --git a/src/test/parse-fail/pat-lt-bracket-7.rs b/src/test/parse-fail/pat-lt-bracket-7.rs
index 3e9478da44de5..d75589d8889e3 100644
--- a/src/test/parse-fail/pat-lt-bracket-7.rs
+++ b/src/test/parse-fail/pat-lt-bracket-7.rs
@@ -9,6 +9,5 @@
// except according to those terms.
fn main() {
- for thing(x[]) in foo {} //~ error: expected one of `,` or `@`, found `[`
- //~^ ERROR: expected `in`, found `]`
+ for thing(x[]) in foo {} //~ ERROR: expected one of `)`, `,`, or `@`, found `[`
}
diff --git a/src/test/parse-fail/pat-tuple-1.rs b/src/test/parse-fail/pat-tuple-1.rs
new file mode 100644
index 0000000000000..edda960f14f06
--- /dev/null
+++ b/src/test/parse-fail/pat-tuple-1.rs
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -Z parse-only
+
+fn main() {
+ match 0 {
+ (, ..) => {} //~ ERROR unexpected token: `,`
+ }
+}
diff --git a/src/test/parse-fail/pat-tuple-2.rs b/src/test/parse-fail/pat-tuple-2.rs
new file mode 100644
index 0000000000000..cd4e6cf4f9ebb
--- /dev/null
+++ b/src/test/parse-fail/pat-tuple-2.rs
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -Z parse-only
+
+fn main() {
+ match 0 {
+ (pat, ..,) => {} //~ ERROR unexpected token: `)`
+ }
+}
diff --git a/src/test/parse-fail/pat-tuple-3.rs b/src/test/parse-fail/pat-tuple-3.rs
new file mode 100644
index 0000000000000..eb9356dcdd9e3
--- /dev/null
+++ b/src/test/parse-fail/pat-tuple-3.rs
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -Z parse-only
+
+fn main() {
+ match 0 {
+ (.., pat, ..) => {} //~ ERROR unexpected token: `..`
+ }
+}
diff --git a/src/test/parse-fail/pat-tuple-4.rs b/src/test/parse-fail/pat-tuple-4.rs
new file mode 100644
index 0000000000000..f4c3afa07f106
--- /dev/null
+++ b/src/test/parse-fail/pat-tuple-4.rs
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -Z parse-only
+
+fn main() {
+ match 0 {
+ (.. pat) => {} //~ ERROR expected one of `)` or `,`, found `pat`
+ }
+}
diff --git a/src/test/parse-fail/pat-tuple-5.rs b/src/test/parse-fail/pat-tuple-5.rs
new file mode 100644
index 0000000000000..145d1f9d8ec76
--- /dev/null
+++ b/src/test/parse-fail/pat-tuple-5.rs
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -Z parse-only
+
+fn main() {
+ match 0 {
+ (pat ..) => {} //~ ERROR expected one of `)`, `,`, or `@`, found `..`
+ }
+}
diff --git a/src/test/parse-fail/pat-tuple-6.rs b/src/test/parse-fail/pat-tuple-6.rs
new file mode 100644
index 0000000000000..3252d92fe1b50
--- /dev/null
+++ b/src/test/parse-fail/pat-tuple-6.rs
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -Z parse-only
+
+fn main() {
+ match 0 {
+ (pat) => {} //~ ERROR expected one of `,` or `@`, found `)`
+ }
+}
diff --git a/src/test/run-pass/pat-tuple.rs b/src/test/run-pass/pat-tuple.rs
new file mode 100644
index 0000000000000..ccea068f715a0
--- /dev/null
+++ b/src/test/run-pass/pat-tuple.rs
@@ -0,0 +1,202 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(dotdot_in_tuple_patterns)]
+
+fn b() {
+ let x = (1, 2, 3);
+ match x {
+ (a, b, ..) => {
+ assert_eq!(a, 1);
+ assert_eq!(b, 2);
+ }
+ }
+ match x {
+ (.., b, c) => {
+ assert_eq!(b, 2);
+ assert_eq!(c, 3);
+ }
+ }
+ match x {
+ (a, .., c) => {
+ assert_eq!(a, 1);
+ assert_eq!(c, 3);
+ }
+ }
+ match x {
+ (a, b, c) => {
+ assert_eq!(a, 1);
+ assert_eq!(b, 2);
+ assert_eq!(c, 3);
+ }
+ }
+}
+
+fn bs() {
+ struct S(u8, u8, u8);
+
+ let x = S(1, 2, 3);
+ match x {
+ S(a, b, ..) => {
+ assert_eq!(a, 1);
+ assert_eq!(b, 2);
+ }
+ }
+ match x {
+ S(.., b, c) => {
+ assert_eq!(b, 2);
+ assert_eq!(c, 3);
+ }
+ }
+ match x {
+ S(a, .., c) => {
+ assert_eq!(a, 1);
+ assert_eq!(c, 3);
+ }
+ }
+ match x {
+ S(a, b, c) => {
+ assert_eq!(a, 1);
+ assert_eq!(b, 2);
+ assert_eq!(c, 3);
+ }
+ }
+}
+
+fn c() {
+ let x = (1,);
+ match x {
+ (2, ..) => panic!(),
+ (..) => ()
+ }
+}
+
+fn cs() {
+ struct S(u8);
+
+ let x = S(1);
+ match x {
+ S(2, ..) => panic!(),
+ S(..) => ()
+ }
+}
+
+fn d() {
+ let x = (1, 2, 3);
+ let branch = match x {
+ (1, 1, ..) => 0,
+ (1, 2, 3, ..) => 1,
+ (1, 2, ..) => 2,
+ _ => 3
+ };
+ assert_eq!(branch, 1);
+}
+
+fn ds() {
+ struct S(u8, u8, u8);
+
+ let x = S(1, 2, 3);
+ let branch = match x {
+ S(1, 1, ..) => 0,
+ S(1, 2, 3, ..) => 1,
+ S(1, 2, ..) => 2,
+ _ => 3
+ };
+ assert_eq!(branch, 1);
+}
+
+fn f() {
+ let x = (1, 2, 3);
+ match x {
+ (1, 2, 4) => unreachable!(),
+ (0, 2, 3, ..) => unreachable!(),
+ (0, .., 3) => unreachable!(),
+ (0, ..) => unreachable!(),
+ (1, 2, 3) => (),
+ (_, _, _) => unreachable!(),
+ }
+ match x {
+ (..) => (),
+ }
+ match x {
+ (_, _, _, ..) => (),
+ }
+ match x {
+ (a, b, c) => {
+ assert_eq!(1, a);
+ assert_eq!(2, b);
+ assert_eq!(3, c);
+ }
+ }
+}
+
+fn fs() {
+ struct S(u8, u8, u8);
+
+ let x = S(1, 2, 3);
+ match x {
+ S(1, 2, 4) => unreachable!(),
+ S(0, 2, 3, ..) => unreachable!(),
+ S(0, .., 3) => unreachable!(),
+ S(0, ..) => unreachable!(),
+ S(1, 2, 3) => (),
+ S(_, _, _) => unreachable!(),
+ }
+ match x {
+ S(..) => (),
+ }
+ match x {
+ S(_, _, _, ..) => (),
+ }
+ match x {
+ S(a, b, c) => {
+ assert_eq!(1, a);
+ assert_eq!(2, b);
+ assert_eq!(3, c);
+ }
+ }
+}
+
+fn g() {
+ struct S;
+ struct Z;
+ struct W;
+ let x = (S, Z, W);
+ match x { (S, ..) => {} }
+ match x { (.., W) => {} }
+ match x { (S, .., W) => {} }
+ match x { (.., Z, _) => {} }
+}
+
+fn gs() {
+ struct SS(S, Z, W);
+
+ struct S;
+ struct Z;
+ struct W;
+ let x = SS(S, Z, W);
+ match x { SS(S, ..) => {} }
+ match x { SS(.., W) => {} }
+ match x { SS(S, .., W) => {} }
+ match x { SS(.., Z, _) => {} }
+}
+
+fn main() {
+ b();
+ bs();
+ c();
+ cs();
+ d();
+ ds();
+ f();
+ fs();
+ g();
+ gs();
+}