diff --git a/src/doc/reference.md b/src/doc/reference.md index c2b22ef3e72c2..4de8f29c31320 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -2398,6 +2398,8 @@ The currently implemented features of the reference compiler are: * - `abi_vectorcall` - Allows the usage of the vectorcall calling convention (e.g. `extern "vectorcall" func fn_();`) +* - `dotdot_in_tuple_patterns` - Allows `..` in tuple (struct) patterns. + If a feature is promoted to a language feature, then all existing programs will start to receive compilation warnings about `#![feature]` directives which enabled the new feature (because the directive is no longer necessary). However, if a diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 4ff3b21dc83f4..9c3e2325113a8 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -39,6 +39,7 @@ #![feature(slice_patterns)] #![feature(staged_api)] #![feature(str_char)] +#![feature(type_ascription)] #![cfg_attr(test, feature(test))] extern crate arena; diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index 1fb27261c4dbf..b7558e3d458fe 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -100,7 +100,6 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { fn pat(&mut self, pat: &hir::Pat, pred: CFGIndex) -> CFGIndex { match pat.node { PatKind::Ident(_, _, None) | - PatKind::TupleStruct(_, None) | PatKind::Path(..) | PatKind::QPath(..) | PatKind::Lit(..) | @@ -116,8 +115,8 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { self.add_ast_node(pat.id, &[subpat_exit]) } - PatKind::TupleStruct(_, Some(ref subpats)) | - PatKind::Tup(ref subpats) => { + PatKind::TupleStruct(_, ref subpats, _) | + PatKind::Tuple(ref subpats, _) => { let pats_exit = self.pats_all(subpats.iter(), pred); self.add_ast_node(pat.id, &[pats_exit]) } diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index be8793bac5dbf..0328f64839ae8 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -377,7 +377,7 @@ fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, matrix: &Matrix, source: hir: hir::MatchSource::ForLoopDesugar => { // `witnesses[0]` has the form `Some()`, peel off the `Some` let witness = match witnesses[0].node { - PatKind::TupleStruct(_, Some(ref pats)) => match &pats[..] { + PatKind::TupleStruct(_, ref pats, _) => match &pats[..] { [ref pat] => &**pat, _ => unreachable!(), }, @@ -530,7 +530,7 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor, let pats_len = pats.len(); let mut pats = pats.into_iter().map(|p| P((*p).clone())); let pat = match left_ty.sty { - ty::TyTuple(_) => PatKind::Tup(pats.collect()), + ty::TyTuple(..) => PatKind::Tuple(pats.collect(), None), ty::TyEnum(adt, _) | ty::TyStruct(adt, _) => { let v = adt.variant_of_ctor(ctor); @@ -551,7 +551,7 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor, PatKind::Struct(def_to_path(cx.tcx, v.did), field_pats, has_more_fields) } VariantKind::Tuple => { - PatKind::TupleStruct(def_to_path(cx.tcx, v.did), Some(pats.collect())) + PatKind::TupleStruct(def_to_path(cx.tcx, v.did), pats.collect(), None) } VariantKind::Unit => { PatKind::Path(def_to_path(cx.tcx, v.did)) @@ -804,7 +804,7 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: &Pat, vec!(Slice(before.len() + after.len())) } }, - PatKind::Box(_) | PatKind::Tup(_) | PatKind::Ref(..) => + PatKind::Box(..) | PatKind::Tuple(..) | PatKind::Ref(..) => vec!(Single), PatKind::Wild => vec!(), @@ -886,7 +886,7 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat], } } - PatKind::TupleStruct(_, ref args) => { + PatKind::TupleStruct(_, ref args, ddpos) => { let def = cx.tcx.def_map.borrow().get(&pat_id).unwrap().full_def(); match def { Def::Const(..) | Def::AssociatedConst(..) => @@ -894,10 +894,17 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat], been rewritten"), Def::Variant(_, id) if *constructor != Variant(id) => None, Def::Variant(..) | Def::Struct(..) => { - Some(match args { - &Some(ref args) => args.iter().map(|p| &**p).collect(), - &None => vec![DUMMY_WILD_PAT; arity], - }) + match ddpos { + Some(ddpos) => { + let mut pats = args[..ddpos].iter().map(|p| &**p).collect(): Vec<_>; + pats.extend(repeat(DUMMY_WILD_PAT).take(arity - args.len())); + if ddpos != args.len() { + pats.extend(args[ddpos..].iter().map(|p| &**p)); + } + Some(pats) + } + None => Some(args.iter().map(|p| &**p).collect()) + } } _ => None } @@ -925,7 +932,15 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat], } } - PatKind::Tup(ref args) => + PatKind::Tuple(ref args, Some(ddpos)) => { + let mut pats = args[..ddpos].iter().map(|p| &**p).collect(): Vec<_>; + pats.extend(repeat(DUMMY_WILD_PAT).take(arity - args.len())); + if ddpos != args.len() { + pats.extend(args[ddpos..].iter().map(|p| &**p)); + } + Some(pats) + } + PatKind::Tuple(ref args, None) => Some(args.iter().map(|p| &**p).collect()), PatKind::Box(ref inner) | PatKind::Ref(ref inner, _) => diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 06030c0021171..6d0934b37126f 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -325,7 +325,9 @@ impl ConstVal { pub fn const_expr_to_pat(tcx: &TyCtxt, expr: &Expr, span: Span) -> P { let pat = match expr.node { hir::ExprTup(ref exprs) => - PatKind::Tup(exprs.iter().map(|expr| const_expr_to_pat(tcx, &expr, span)).collect()), + PatKind::Tuple(exprs.iter() + .map(|expr| const_expr_to_pat(tcx, &expr, span)) + .collect(), None), hir::ExprCall(ref callee, ref args) => { let def = *tcx.def_map.borrow().get(&callee.id).unwrap(); @@ -343,7 +345,7 @@ pub fn const_expr_to_pat(tcx: &TyCtxt, expr: &Expr, span: Span) -> P { _ => unreachable!() }; let pats = args.iter().map(|expr| const_expr_to_pat(tcx, &expr, span)).collect(); - PatKind::TupleStruct(path, Some(pats)) + PatKind::TupleStruct(path, pats, None) } hir::ExprStruct(ref path, ref fields, None) => { diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 5ddea6917435e..dca90357183a5 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -1135,7 +1135,7 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> { // will visit the substructure recursively. } - PatKind::Wild | PatKind::Tup(..) | PatKind::Box(..) | + PatKind::Wild | PatKind::Tuple(..) | PatKind::Box(..) | PatKind::Ref(..) | PatKind::Lit(..) | PatKind::Range(..) | PatKind::Vec(..) => { // Similarly, each of these cases does not diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index ab894a55b44fe..22e6ec504c4a1 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -1227,31 +1227,45 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> { // _ } - PatKind::TupleStruct(_, None) => { - // variant(..) - } - PatKind::TupleStruct(_, Some(ref subpats)) => { + PatKind::TupleStruct(_, ref subpats, ddpos) => { match opt_def { - Some(Def::Variant(..)) => { + Some(Def::Variant(enum_def, def_id)) => { // variant(x, y, z) + let variant = self.tcx().lookup_adt_def(enum_def).variant_with_id(def_id); + let adjust = |i| { + let gap = variant.fields.len() - subpats.len(); + if ddpos.is_none() || ddpos.unwrap() > i { i } else { i + gap } + }; for (i, subpat) in subpats.iter().enumerate() { let subpat_ty = try!(self.pat_ty(&subpat)); // see (*2) let subcmt = self.cat_imm_interior( pat, cmt.clone(), subpat_ty, - InteriorField(PositionalField(i))); + InteriorField(PositionalField(adjust(i)))); try!(self.cat_pattern_(subcmt, &subpat, op)); } } Some(Def::Struct(..)) => { + let expected_len = match self.pat_ty(&pat) { + Ok(&ty::TyS{sty: ty::TyStruct(adt_def, _), ..}) => { + adt_def.struct_variant().fields.len() + } + ref ty => self.tcx().sess.span_bug(pat.span, + &format!("tuple struct pattern unexpected type {:?}", ty)), + }; + + let adjust = |i| { + let gap = expected_len - subpats.len(); + if ddpos.is_none() || ddpos.unwrap() > i { i } else { i + gap } + }; for (i, subpat) in subpats.iter().enumerate() { let subpat_ty = try!(self.pat_ty(&subpat)); // see (*2) let cmt_field = self.cat_imm_interior( pat, cmt.clone(), subpat_ty, - InteriorField(PositionalField(i))); + InteriorField(PositionalField(adjust(i)))); try!(self.cat_pattern_(cmt_field, &subpat, op)); } } @@ -1285,14 +1299,23 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> { } } - PatKind::Tup(ref subpats) => { + PatKind::Tuple(ref subpats, ddpos) => { // (p1, ..., pN) + let expected_len = match self.pat_ty(&pat) { + Ok(&ty::TyS{sty: ty::TyTuple(ref tys), ..}) => tys.len(), + ref ty => self.tcx().sess.span_bug(pat.span, + &format!("tuple pattern unexpected type {:?}", ty)), + }; + let adjust = |i| { + let gap = expected_len - subpats.len(); + if ddpos.is_none() || ddpos.unwrap() > i { i } else { i + gap } + }; for (i, subpat) in subpats.iter().enumerate() { let subpat_ty = try!(self.pat_ty(&subpat)); // see (*2) let subcmt = self.cat_imm_interior( pat, cmt.clone(), subpat_ty, - InteriorField(PositionalField(i))); + InteriorField(PositionalField(adjust(i)))); try!(self.cat_pattern_(subcmt, &subpat, op)); } } diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index b39964e2861e3..be31c757ae8fa 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -970,8 +970,8 @@ fn resolve_local(visitor: &mut RegionResolutionVisitor, local: &hir::Local) { pats3.iter().any(|p| is_binding_pat(&p)) } - PatKind::TupleStruct(_, Some(ref subpats)) | - PatKind::Tup(ref subpats) => { + PatKind::TupleStruct(_, ref subpats, _) | + PatKind::Tuple(ref subpats, _) => { subpats.iter().any(|p| is_binding_pat(&p)) } diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 2d92742ed0f20..6f1c8571636d1 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -598,10 +598,13 @@ pub fn check_pat(tcx: &TyCtxt, pat: &hir::Pat, }; match pat.node { // Foo(a, b, c) - // A Variant(..) pattern `PatKind::TupleStruct(_, None)` doesn't have to be recursed into. - PatKind::TupleStruct(_, Some(ref pat_fields)) => { - for (field, struct_field) in pat_fields.iter().zip(&v.fields) { - maybe_do_stability_check(tcx, struct_field.did, field.span, cb) + PatKind::TupleStruct(_, ref pat_fields, ddpos) => { + let adjust = |i| { + let gap = v.fields.len() - pat_fields.len(); + if ddpos.is_none() || ddpos.unwrap() > i { i } else { i + gap } + }; + for (i, field) in pat_fields.iter().enumerate() { + maybe_do_stability_check(tcx, v.fields[adjust(i)].did, field.span, cb) } } // Foo { a, b, c } diff --git a/src/librustc_front/fold.rs b/src/librustc_front/fold.rs index beedb3d70b699..705daeb8bf71b 100644 --- a/src/librustc_front/fold.rs +++ b/src/librustc_front/fold.rs @@ -970,9 +970,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)) @@ -995,7 +995,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/librustc_front/hir.rs b/src/librustc_front/hir.rs index 44e7fa05073a1..5109b14b73f97 100644 --- a/src/librustc_front/hir.rs +++ b/src/librustc_front/hir.rs @@ -522,9 +522,10 @@ pub enum PatKind { /// The `bool` is `true` in the presence of a `..`. Struct(Path, HirVec>, 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, 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(); +}