From d69aeaf662c637b454e8c7a5ddbd69b4978ec211 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 6 Mar 2016 15:54:44 +0300 Subject: [PATCH] Implement `..` in tuple (struct) patterns --- src/doc/reference.md | 2 + src/librustc/cfg/construct.rs | 5 +- src/librustc/hir/fold.rs | 8 +- src/librustc/hir/intravisit.rs | 8 +- src/librustc/hir/lowering.rs | 12 +- src/librustc/hir/mod.rs | 16 +- src/librustc/hir/pat_util.rs | 22 ++ src/librustc/hir/print.rs | 41 +++- src/librustc/lib.rs | 2 + src/librustc/middle/expr_use_visitor.rs | 2 +- src/librustc/middle/mem_categorization.rs | 33 ++- src/librustc/middle/region.rs | 4 +- src/librustc/middle/stability.rs | 9 +- src/librustc_const_eval/check_match.rs | 33 ++- src/librustc_const_eval/eval.rs | 9 +- src/librustc_const_eval/lib.rs | 1 + src/librustc_mir/hair/cx/pattern.rs | 45 ++-- src/librustc_privacy/lib.rs | 13 +- src/librustc_resolve/lib.rs | 2 +- src/librustc_save_analysis/lib.rs | 2 +- src/librustc_trans/_match.rs | 79 +++---- .../debuginfo/create_scope_map.rs | 10 +- src/librustc_typeck/check/_match.rs | 92 ++++---- src/librustc_typeck/diagnostics.rs | 25 --- src/librustc_typeck/lib.rs | 3 +- src/librustdoc/clean/mod.rs | 4 +- src/libsyntax/ast.rs | 16 +- src/libsyntax/ext/build.rs | 4 +- src/libsyntax/feature_gate.rs | 24 ++- src/libsyntax/fold.rs | 8 +- src/libsyntax/parse/parser.rs | 65 ++---- src/libsyntax/print/pprust.rs | 44 ++-- src/libsyntax/visit.rs | 8 +- src/test/compile-fail/issue-32004.rs | 4 +- .../match-pattern-field-mismatch-2.rs | 2 +- src/test/compile-fail/pat-tuple-bad-type.rs | 27 +++ .../compile-fail/pat-tuple-feature-gate.rs | 17 ++ src/test/compile-fail/pat-tuple-overfield.rs | 28 +++ .../compile-fail/pattern-error-continue.rs | 2 +- src/test/parse-fail/pat-lt-bracket-6.rs | 3 +- src/test/parse-fail/pat-lt-bracket-7.rs | 3 +- .../E0024.rs => parse-fail/pat-tuple-1.rs} | 11 +- src/test/parse-fail/pat-tuple-2.rs | 17 ++ src/test/parse-fail/pat-tuple-3.rs | 17 ++ src/test/parse-fail/pat-tuple-4.rs | 17 ++ src/test/parse-fail/pat-tuple-5.rs | 17 ++ src/test/parse-fail/pat-tuple-6.rs | 17 ++ src/test/run-pass/pat-tuple.rs | 202 ++++++++++++++++++ 48 files changed, 736 insertions(+), 299 deletions(-) create mode 100644 src/test/compile-fail/pat-tuple-bad-type.rs create mode 100644 src/test/compile-fail/pat-tuple-feature-gate.rs create mode 100644 src/test/compile-fail/pat-tuple-overfield.rs rename src/test/{compile-fail/E0024.rs => parse-fail/pat-tuple-1.rs} (74%) create mode 100644 src/test/parse-fail/pat-tuple-2.rs create mode 100644 src/test/parse-fail/pat-tuple-3.rs create mode 100644 src/test/parse-fail/pat-tuple-4.rs create mode 100644 src/test/parse-fail/pat-tuple-5.rs create mode 100644 src/test/parse-fail/pat-tuple-6.rs create mode 100644 src/test/run-pass/pat-tuple.rs diff --git a/src/doc/reference.md b/src/doc/reference.md index ebb111a2e2e73..810138e5a2983 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -2433,6 +2433,8 @@ The currently implemented features of the reference compiler are: * - `abi_vectorcall` - Allows the usage of the vectorcall calling convention (e.g. `extern "vectorcall" func fn_();`) +* - `dotdot_in_tuple_patterns` - Allows `..` in tuple (struct) patterns. + If a feature is promoted to a language feature, then all existing programs will start to receive compilation warnings about `#![feature]` directives which enabled the new feature (because the directive is no longer necessary). However, if a diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs index 76699f13959ea..af47617ea92fd 100644 --- a/src/librustc/cfg/construct.rs +++ b/src/librustc/cfg/construct.rs @@ -100,7 +100,6 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { fn pat(&mut self, pat: &hir::Pat, pred: CFGIndex) -> CFGIndex { match pat.node { PatKind::Ident(_, _, None) | - PatKind::TupleStruct(_, None) | PatKind::Path(..) | PatKind::QPath(..) | PatKind::Lit(..) | @@ -116,8 +115,8 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { self.add_ast_node(pat.id, &[subpat_exit]) } - PatKind::TupleStruct(_, Some(ref subpats)) | - PatKind::Tup(ref subpats) => { + PatKind::TupleStruct(_, ref subpats, _) | + PatKind::Tuple(ref subpats, _) => { let pats_exit = self.pats_all(subpats.iter(), pred); self.add_ast_node(pat.id, &[pats_exit]) } diff --git a/src/librustc/hir/fold.rs b/src/librustc/hir/fold.rs index a91d16f25a2b8..9cba790c54b86 100644 --- a/src/librustc/hir/fold.rs +++ b/src/librustc/hir/fold.rs @@ -923,9 +923,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)) @@ -948,7 +948,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/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index 2e9e433b830fc..69e38c9646c92 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -454,11 +454,9 @@ pub fn walk_assoc_type_binding<'v, V: Visitor<'v>>(visitor: &mut V, pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) { match pattern.node { - PatKind::TupleStruct(ref path, ref opt_children) => { + PatKind::TupleStruct(ref path, ref children, _) => { visitor.visit_path(path, pattern.id); - if let Some(ref children) = *opt_children { - walk_list!(visitor, visit_pat, children); - } + walk_list!(visitor, visit_pat, children); } PatKind::Path(ref path) => { visitor.visit_path(path, pattern.id); @@ -474,7 +472,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) { visitor.visit_pat(&field.node.pat) } } - PatKind::Tup(ref tuple_elements) => { + PatKind::Tuple(ref tuple_elements, _) => { walk_list!(visitor, visit_pat, tuple_elements); } PatKind::Box(ref subpattern) | diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 28506fd20fe53..71153a459bd6b 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -872,10 +872,10 @@ impl<'a> LoweringContext<'a> { }) } PatKind::Lit(ref e) => hir::PatKind::Lit(self.lower_expr(e)), - PatKind::TupleStruct(ref pth, ref pats) => { + PatKind::TupleStruct(ref pth, ref pats, ddpos) => { hir::PatKind::TupleStruct(self.lower_path(pth), - pats.as_ref() - .map(|pats| pats.iter().map(|x| self.lower_pat(x)).collect())) + pats.iter().map(|x| self.lower_pat(x)).collect(), + ddpos) } PatKind::Path(ref pth) => { hir::PatKind::Path(self.lower_path(pth)) @@ -903,8 +903,8 @@ impl<'a> LoweringContext<'a> { .collect(); hir::PatKind::Struct(pth, fs, etc) } - PatKind::Tup(ref elts) => { - hir::PatKind::Tup(elts.iter().map(|x| self.lower_pat(x)).collect()) + PatKind::Tuple(ref elts, ddpos) => { + hir::PatKind::Tuple(elts.iter().map(|x| self.lower_pat(x)).collect(), ddpos) } PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)), PatKind::Ref(ref inner, mutbl) => { @@ -1857,7 +1857,7 @@ impl<'a> LoweringContext<'a> { let pt = if subpats.is_empty() { hir::PatKind::Path(path) } else { - hir::PatKind::TupleStruct(path, Some(subpats)) + hir::PatKind::TupleStruct(path, subpats, None) }; let pat = self.pat(span, pt); self.resolver.record_resolution(pat.id, def); diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 39a6ec9f3af27..961885d1b86d6 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -470,7 +470,7 @@ impl Pat { PatKind::Struct(_, ref fields, _) => { fields.iter().all(|field| field.node.pat.walk_(it)) } - PatKind::TupleStruct(_, Some(ref s)) | PatKind::Tup(ref s) => { + PatKind::TupleStruct(_, ref s, _) | PatKind::Tuple(ref s, _) => { s.iter().all(|p| p.walk_(it)) } PatKind::Box(ref s) | PatKind::Ref(ref s, _) => { @@ -485,7 +485,6 @@ impl Pat { PatKind::Lit(_) | PatKind::Range(_, _) | PatKind::Ident(_, _, _) | - PatKind::TupleStruct(..) | PatKind::Path(..) | PatKind::QPath(_, _) => { true @@ -539,9 +538,10 @@ pub enum PatKind { /// The `bool` is `true` in the presence of a `..`. Struct(Path, HirVec>, 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. @@ -553,8 +553,10 @@ pub enum PatKind { /// PatKind::Path, and the resolver will have to sort that out. QPath(QSelf, Path), - /// A tuple pattern `(a, b)` - Tup(HirVec>), + /// 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/hir/pat_util.rs b/src/librustc/hir/pat_util.rs index 15f2310607ffe..f41c4b0840d58 100644 --- a/src/librustc/hir/pat_util.rs +++ b/src/librustc/hir/pat_util.rs @@ -21,6 +21,28 @@ use std::cell::RefCell; pub type PatIdMap = FnvHashMap; +#[derive(Clone, Copy)] +pub struct AjustPos { + gap_pos: usize, + gap_len: usize, +} + +impl FnOnce<(usize,)> for AjustPos { + type Output = usize; + extern "rust-call" fn call_once(self, (i,): (usize,)) -> usize { + if i < self.gap_pos { i } else { i + self.gap_len } + } +} + +// Returns a functional object used to adjust tuple pattern indexes. Example: for 5-tuple and +// pattern (a, b, .., c) expected_len is 5, actual_len is 3 and gap_pos is Some(2). +pub fn pat_adjust_pos(expected_len: usize, actual_len: usize, gap_pos: Option) -> AjustPos { + AjustPos { + gap_pos: if let Some(gap_pos) = gap_pos { gap_pos } else { expected_len }, + gap_len: expected_len - actual_len, + } +} + // This is used because same-named variables in alternative patterns need to // use the NodeId of their namesake in the first pattern. pub fn pat_id_map(dm: &RefCell, pat: &hir::Pat) -> PatIdMap { diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 4455c7da3ba3f..d105a72b986fd 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -1736,16 +1736,23 @@ impl<'a> State<'a> { None => (), } } - PatKind::TupleStruct(ref path, ref args_) => { + PatKind::TupleStruct(ref path, ref elts, ddpos) => { self.print_path(path, true, 0)?; - match *args_ { - None => word(&mut self.s, "(..)")?, - Some(ref args) => { - self.popen()?; - self.commasep(Inconsistent, &args[..], |s, p| s.print_pat(&p))?; - self.pclose()?; + self.popen()?; + if let Some(ddpos) = ddpos { + self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p))?; + if ddpos != 0 { + self.word_space(",")?; + } + word(&mut self.s, "..")?; + if ddpos != elts.len() { + word(&mut self.s, ",")?; + self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p))?; } + } else { + try!(self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p))); } + try!(self.pclose()); } PatKind::Path(ref path) => { self.print_path(path, true, 0)?; @@ -1778,11 +1785,23 @@ impl<'a> State<'a> { space(&mut self.s)?; word(&mut self.s, "}")?; } - PatKind::Tup(ref elts) => { + PatKind::Tuple(ref elts, ddpos) => { self.popen()?; - self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p))?; - if elts.len() == 1 { - word(&mut self.s, ",")?; + if let Some(ddpos) = ddpos { + self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p))?; + if ddpos != 0 { + self.word_space(",")?; + } + word(&mut self.s, "..")?; + if ddpos != elts.len() { + word(&mut self.s, ",")?; + self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p))?; + } + } else { + self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p))?; + if elts.len() == 1 { + word(&mut self.s, ",")?; + } } self.pclose()?; } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index e1fb701e641bf..4ecad7f93a5a4 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -29,6 +29,7 @@ #![feature(collections)] #![feature(const_fn)] #![feature(enumset)] +#![feature(fn_traits)] #![feature(iter_arith)] #![feature(libc)] #![feature(nonzero)] @@ -38,6 +39,7 @@ #![feature(slice_patterns)] #![feature(staged_api)] #![feature(question_mark)] +#![feature(unboxed_closures)] #![cfg_attr(test, feature(test))] extern crate arena; diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 4cee8c5d89ae6..b0add5a23dc46 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -1127,7 +1127,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> { // will visit the substructure recursively. } - PatKind::Wild | PatKind::Tup(..) | PatKind::Box(..) | + PatKind::Wild | PatKind::Tuple(..) | PatKind::Box(..) | PatKind::Ref(..) | PatKind::Lit(..) | PatKind::Range(..) | PatKind::Vec(..) => { // Similarly, each of these cases does not diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 3999b02425de6..da3df7ad3e9ad 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -80,6 +80,7 @@ use ty::adjustment; use ty::{self, Ty, TyCtxt}; use hir::{MutImmutable, MutMutable, PatKind}; +use hir::pat_util::pat_adjust_pos; use hir; use syntax::ast; use syntax::codemap::Span; @@ -1225,31 +1226,40 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { // _ } - PatKind::TupleStruct(_, None) => { - // variant(..) - } - PatKind::TupleStruct(_, Some(ref subpats)) => { + PatKind::TupleStruct(_, ref subpats, ddpos) => { match opt_def { - Some(Def::Variant(..)) => { + Some(Def::Variant(enum_def, def_id)) => { // variant(x, y, z) + let variant = self.tcx().lookup_adt_def(enum_def).variant_with_id(def_id); + let adjust = pat_adjust_pos(variant.fields.len(), subpats.len(), ddpos); for (i, subpat) in subpats.iter().enumerate() { let subpat_ty = self.pat_ty(&subpat)?; // see (*2) let subcmt = self.cat_imm_interior( pat, cmt.clone(), subpat_ty, - InteriorField(PositionalField(i))); + InteriorField(PositionalField(adjust(i)))); self.cat_pattern_(subcmt, &subpat, op)?; } } Some(Def::Struct(..)) => { + let expected_len = match self.pat_ty(&pat) { + Ok(&ty::TyS{sty: ty::TyStruct(adt_def, _), ..}) => { + adt_def.struct_variant().fields.len() + } + ref ty => { + span_bug!(pat.span, "tuple struct pattern unexpected type {:?}", ty); + } + }; + + let adjust = pat_adjust_pos(expected_len, subpats.len(), ddpos); for (i, subpat) in subpats.iter().enumerate() { let subpat_ty = self.pat_ty(&subpat)?; // see (*2) let cmt_field = self.cat_imm_interior( pat, cmt.clone(), subpat_ty, - InteriorField(PositionalField(i))); + InteriorField(PositionalField(adjust(i)))); self.cat_pattern_(cmt_field, &subpat, op)?; } } @@ -1284,14 +1294,19 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { } } - PatKind::Tup(ref subpats) => { + PatKind::Tuple(ref subpats, ddpos) => { // (p1, ..., pN) + let expected_len = match self.pat_ty(&pat) { + Ok(&ty::TyS{sty: ty::TyTuple(ref tys), ..}) => tys.len(), + ref ty => span_bug!(pat.span, "tuple pattern unexpected type {:?}", ty), + }; + let adjust = pat_adjust_pos(expected_len, subpats.len(), ddpos); for (i, subpat) in subpats.iter().enumerate() { let subpat_ty = self.pat_ty(&subpat)?; // see (*2) let subcmt = self.cat_imm_interior( pat, cmt.clone(), subpat_ty, - InteriorField(PositionalField(i))); + InteriorField(PositionalField(adjust(i)))); self.cat_pattern_(subcmt, &subpat, op)?; } } diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 56b4036b7d785..6b2c2dfcd72b4 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 c2db6de03700e..50b6d661fa8b3 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -33,6 +33,7 @@ use util::nodemap::{DefIdMap, FnvHashSet, FnvHashMap}; use hir; use hir::{Item, Generics, StructField, Variant, PatKind}; use hir::intravisit::{self, Visitor}; +use hir::pat_util::pat_adjust_pos; use std::mem::replace; use std::cmp::Ordering; @@ -614,10 +615,10 @@ pub fn check_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, pat: &hir::Pat, }; match pat.node { // Foo(a, b, c) - // A Variant(..) pattern `PatKind::TupleStruct(_, None)` doesn't have to be recursed into. - PatKind::TupleStruct(_, Some(ref pat_fields)) => { - for (field, struct_field) in pat_fields.iter().zip(&v.fields) { - maybe_do_stability_check(tcx, struct_field.did, field.span, cb) + PatKind::TupleStruct(_, ref pat_fields, ddpos) => { + let adjust = pat_adjust_pos(v.fields.len(), pat_fields.len(), ddpos); + for (i, field) in pat_fields.iter().enumerate() { + maybe_do_stability_check(tcx, v.fields[adjust(i)].did, field.span, cb) } } // Foo { a, b, c } diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index 2fb5d796589b2..f98734f21add5 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -374,7 +374,7 @@ fn pat_is_catchall(dm: &DefMap, p: &Pat) -> bool { PatKind::Ident(_, _, None) => pat_is_binding(dm, p), PatKind::Ident(_, _, Some(ref s)) => pat_is_catchall(dm, &s), PatKind::Ref(ref s, _) => pat_is_catchall(dm, &s), - PatKind::Tup(ref v) => v.iter().all(|p| pat_is_catchall(dm, &p)), + PatKind::Tuple(ref v, _) => v.iter().all(|p| pat_is_catchall(dm, &p)), _ => false } } @@ -398,7 +398,7 @@ fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, matrix: &Matrix, source: hir: hir::MatchSource::ForLoopDesugar => { // `witnesses[0]` has the form `Some()`, peel off the `Some` let witness = match witnesses[0].node { - PatKind::TupleStruct(_, Some(ref pats)) => match &pats[..] { + PatKind::TupleStruct(_, ref pats, _) => match &pats[..] { [ref pat] => &**pat, _ => bug!(), }, @@ -559,7 +559,7 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor, let pats_len = pats.len(); let mut pats = pats.into_iter().map(|p| P((*p).clone())); let pat = match left_ty.sty { - ty::TyTuple(_) => PatKind::Tup(pats.collect()), + ty::TyTuple(..) => PatKind::Tuple(pats.collect(), None), ty::TyEnum(adt, _) | ty::TyStruct(adt, _) => { let v = ctor.variant_for_adt(adt); @@ -580,7 +580,7 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor, PatKind::Struct(def_to_path(cx.tcx, v.did), field_pats, has_more_fields) } VariantKind::Tuple => { - PatKind::TupleStruct(def_to_path(cx.tcx, v.did), Some(pats.collect())) + PatKind::TupleStruct(def_to_path(cx.tcx, v.did), pats.collect(), None) } VariantKind::Unit => { PatKind::Path(def_to_path(cx.tcx, v.did)) @@ -832,7 +832,7 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: &Pat, vec!(Slice(before.len() + after.len())) } }, - PatKind::Box(_) | PatKind::Tup(_) | PatKind::Ref(..) => + PatKind::Box(..) | PatKind::Tuple(..) | PatKind::Ref(..) => vec!(Single), PatKind::Wild => vec!(), @@ -914,7 +914,7 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat], } } - PatKind::TupleStruct(_, ref args) => { + PatKind::TupleStruct(_, ref args, ddpos) => { let def = cx.tcx.def_map.borrow().get(&pat_id).unwrap().full_def(); match def { Def::Const(..) | Def::AssociatedConst(..) => @@ -922,10 +922,15 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat], been rewritten"), Def::Variant(_, id) if *constructor != Variant(id) => None, Def::Variant(..) | Def::Struct(..) => { - Some(match args { - &Some(ref args) => args.iter().map(|p| &**p).collect(), - &None => vec![DUMMY_WILD_PAT; arity], - }) + match ddpos { + Some(ddpos) => { + let mut pats = args[..ddpos].iter().map(|p| &**p).collect(): Vec<_>; + pats.extend(repeat(DUMMY_WILD_PAT).take(arity - args.len())); + pats.extend(args[ddpos..].iter().map(|p| &**p)); + Some(pats) + } + None => Some(args.iter().map(|p| &**p).collect()) + } } _ => None } @@ -952,7 +957,13 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat], } } - PatKind::Tup(ref args) => + PatKind::Tuple(ref args, Some(ddpos)) => { + let mut pats = args[..ddpos].iter().map(|p| &**p).collect(): Vec<_>; + pats.extend(repeat(DUMMY_WILD_PAT).take(arity - args.len())); + pats.extend(args[ddpos..].iter().map(|p| &**p)); + Some(pats) + } + PatKind::Tuple(ref args, None) => Some(args.iter().map(|p| &**p).collect()), PatKind::Box(ref inner) | PatKind::Ref(ref inner, _) => diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 9db24fa4770fe..b727b778fcd79 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -271,10 +271,9 @@ pub fn const_expr_to_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } let pat = match expr.node { hir::ExprTup(ref exprs) => - PatKind::Tup(try!(exprs.iter() - .map(|expr| const_expr_to_pat(tcx, &expr, - pat_id, span)) - .collect())), + PatKind::Tuple(try!(exprs.iter() + .map(|expr| const_expr_to_pat(tcx, &expr, pat_id, span)) + .collect()), None), hir::ExprCall(ref callee, ref args) => { let def = *tcx.def_map.borrow().get(&callee.id).unwrap(); @@ -295,7 +294,7 @@ pub fn const_expr_to_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, .map(|expr| const_expr_to_pat(tcx, &**expr, pat_id, span)) .collect()); - PatKind::TupleStruct(path, Some(pats)) + PatKind::TupleStruct(path, pats, None) } hir::ExprStruct(ref path, ref fields, None) => { diff --git a/src/librustc_const_eval/lib.rs b/src/librustc_const_eval/lib.rs index 9ab6a437a5ab2..2c796690df435 100644 --- a/src/librustc_const_eval/lib.rs +++ b/src/librustc_const_eval/lib.rs @@ -31,6 +31,7 @@ #![feature(question_mark)] #![feature(box_patterns)] #![feature(box_syntax)] +#![feature(type_ascription)] #[macro_use] extern crate syntax; #[macro_use] extern crate log; diff --git a/src/librustc_mir/hair/cx/pattern.rs b/src/librustc_mir/hair/cx/pattern.rs index 0118b97dd7f3e..494d8a5c0356d 100644 --- a/src/librustc_mir/hair/cx/pattern.rs +++ b/src/librustc_mir/hair/cx/pattern.rs @@ -13,7 +13,7 @@ use hair::cx::Cx; use rustc_data_structures::fnv::FnvHashMap; use rustc_const_eval as const_eval; use rustc::hir::def::Def; -use rustc::hir::pat_util::{pat_is_resolved_const, pat_is_binding}; +use rustc::hir::pat_util::{pat_adjust_pos, pat_is_resolved_const, pat_is_binding}; use rustc::ty::{self, Ty}; use rustc::mir::repr::*; use rustc::hir::{self, PatKind}; @@ -148,17 +148,24 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> { } } - PatKind::Tup(ref subpatterns) => { - let subpatterns = - subpatterns.iter() - .enumerate() - .map(|(i, subpattern)| FieldPattern { - field: Field::new(i), - pattern: self.to_pattern(subpattern), - }) - .collect(); + PatKind::Tuple(ref subpatterns, ddpos) => { + match self.cx.tcx.node_id_to_type(pat.id).sty { + ty::TyTuple(ref tys) => { + let adjust = pat_adjust_pos(tys.len(), subpatterns.len(), ddpos); + let subpatterns = + subpatterns.iter() + .enumerate() + .map(|(i, subpattern)| FieldPattern { + field: Field::new(adjust(i)), + pattern: self.to_pattern(subpattern), + }) + .collect(); + + PatternKind::Leaf { subpatterns: subpatterns } + } - PatternKind::Leaf { subpatterns: subpatterns } + ref sty => span_bug!(pat.span, "unexpected type for tuple pattern: {:?}", sty), + } } PatKind::Ident(bm, ref ident, ref sub) @@ -208,13 +215,21 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> { self.variant_or_leaf(pat, vec![]) } - PatKind::TupleStruct(_, ref opt_subpatterns) => { + PatKind::TupleStruct(_, ref subpatterns, ddpos) => { + let pat_ty = self.cx.tcx.node_id_to_type(pat.id); + let adt_def = match pat_ty.sty { + ty::TyStruct(adt_def, _) | ty::TyEnum(adt_def, _) => adt_def, + _ => span_bug!(pat.span, "tuple struct pattern not applied to struct or enum"), + }; + let def = self.cx.tcx.def_map.borrow().get(&pat.id).unwrap().full_def(); + let variant_def = adt_def.variant_of_def(def); + + let adjust = pat_adjust_pos(variant_def.fields.len(), subpatterns.len(), ddpos); let subpatterns = - opt_subpatterns.iter() - .flat_map(|v| v.iter()) + subpatterns.iter() .enumerate() .map(|(i, field)| FieldPattern { - field: Field::new(i), + field: Field::new(adjust(i)), pattern: self.to_pattern(field), }) .collect(); diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index f1e744098b960..953bcf457b803 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -31,7 +31,7 @@ use std::mem::replace; use rustc::hir::{self, PatKind}; use rustc::hir::intravisit::{self, Visitor}; - +use rustc::hir::pat_util::pat_adjust_pos; use rustc::dep_graph::DepNode; use rustc::lint; use rustc::hir::def::{self, Def}; @@ -488,17 +488,17 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivacyVisitor<'a, 'tcx> { self.check_field(pattern.span, adt, variant.field_named(field.node.name)); } } - - // Patterns which bind no fields are allowable (the path is check - // elsewhere). - PatKind::TupleStruct(_, Some(ref fields)) => { + PatKind::TupleStruct(_, ref fields, ddpos) => { match self.tcx.pat_ty(pattern).sty { ty::TyStruct(def, _) => { + let adjust = pat_adjust_pos(def.struct_variant().fields.len(), + fields.len(), ddpos); for (i, field) in fields.iter().enumerate() { if let PatKind::Wild = field.node { continue } - self.check_field(field.span, def, &def.struct_variant().fields[i]); + self.check_field(field.span, def, + &def.struct_variant().fields[adjust(i)]); } } ty::TyEnum(..) => { @@ -506,7 +506,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivacyVisitor<'a, 'tcx> { } _ => {} } - } _ => {} } diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 41cc546281649..e0aa46eda183a 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -2360,7 +2360,7 @@ impl<'a> Resolver<'a> { } } - PatKind::TupleStruct(ref path, _) | PatKind::Path(ref path) => { + PatKind::TupleStruct(ref path, _, _) | PatKind::Path(ref path) => { // This must be an enum variant, struct or const. let resolution = match self.resolve_possibly_assoc_item(pat_id, None, diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 8c00a56999398..7b0ae100f058f 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -696,7 +696,7 @@ impl<'v> Visitor<'v> for PathCollector { self.collected_paths.push((p.id, path.clone(), ast::Mutability::Mutable, recorder::TypeRef)); } - PatKind::TupleStruct(ref path, _) | + PatKind::TupleStruct(ref path, _, _) | PatKind::Path(ref path) | PatKind::QPath(_, ref path) => { self.collected_paths.push((p.id, path.clone(), diff --git a/src/librustc_trans/_match.rs b/src/librustc_trans/_match.rs index dbc277f243267..9b168301f7325 100644 --- a/src/librustc_trans/_match.rs +++ b/src/librustc_trans/_match.rs @@ -792,7 +792,7 @@ fn any_irrefutable_adt_pat(tcx: TyCtxt, m: &[Match], col: usize) -> bool { m.iter().any(|br| { let pat = br.pats[col]; match pat.node { - PatKind::Tup(_) => true, + PatKind::Tuple(..) => true, PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..) | PatKind::Ident(_, _, None) => { match tcx.def_map.borrow().get(&pat.id).unwrap().full_def() { @@ -1833,7 +1833,7 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, bcx = bind_irrefutable_pat(bcx, &inner_pat, val, cleanup_scope); } } - PatKind::TupleStruct(_, ref sub_pats) => { + PatKind::TupleStruct(_, ref sub_pats, ddpos) => { let opt_def = bcx.tcx().def_map.borrow().get(&pat.id).map(|d| d.full_def()); match opt_def { Some(Def::Variant(enum_id, var_id)) => { @@ -1843,35 +1843,36 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, &repr, Disr::from(vinfo.disr_val), val); - if let Some(ref sub_pat) = *sub_pats { - for (i, &argval) in args.vals.iter().enumerate() { - bcx = bind_irrefutable_pat( - bcx, - &sub_pat[i], - MatchInput::from_val(argval), - cleanup_scope); - } + let adjust = pat_adjust_pos(vinfo.fields.len(), sub_pats.len(), ddpos); + for (i, subpat) in sub_pats.iter().enumerate() { + bcx = bind_irrefutable_pat( + bcx, + subpat, + MatchInput::from_val(args.vals[adjust(i)]), + cleanup_scope); } } Some(Def::Struct(..)) => { - match *sub_pats { - None => { - // This is a unit-like struct. Nothing to do here. + let expected_len = match *ccx.tcx().pat_ty(&pat) { + ty::TyS{sty: ty::TyStruct(adt_def, _), ..} => { + adt_def.struct_variant().fields.len() } - Some(ref elems) => { - // This is the tuple struct case. - let repr = adt::represent_node(bcx, pat.id); - let val = adt::MaybeSizedValue::sized(val.val); - for (i, elem) in elems.iter().enumerate() { - let fldptr = adt::trans_field_ptr(bcx, &repr, - val, Disr(0), i); - bcx = bind_irrefutable_pat( - bcx, - &elem, - MatchInput::from_val(fldptr), - cleanup_scope); - } + ref ty => { + span_bug!(pat.span, "tuple struct pattern unexpected type {:?}", ty); } + }; + + let adjust = pat_adjust_pos(expected_len, sub_pats.len(), ddpos); + let repr = adt::represent_node(bcx, pat.id); + let val = adt::MaybeSizedValue::sized(val.val); + for (i, elem) in sub_pats.iter().enumerate() { + let fldptr = adt::trans_field_ptr(bcx, &repr, + val, Disr(0), adjust(i)); + bcx = bind_irrefutable_pat( + bcx, + &elem, + MatchInput::from_val(fldptr), + cleanup_scope); } } _ => { @@ -1919,16 +1920,22 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, cleanup_scope); } } - PatKind::Tup(ref elems) => { - let repr = adt::represent_node(bcx, pat.id); - let val = adt::MaybeSizedValue::sized(val.val); - for (i, elem) in elems.iter().enumerate() { - let fldptr = adt::trans_field_ptr(bcx, &repr, val, Disr(0), i); - bcx = bind_irrefutable_pat( - bcx, - &elem, - MatchInput::from_val(fldptr), - cleanup_scope); + PatKind::Tuple(ref elems, ddpos) => { + match tcx.node_id_to_type(pat.id).sty { + ty::TyTuple(ref tys) => { + let adjust = pat_adjust_pos(tys.len(), elems.len(), ddpos); + let repr = adt::represent_node(bcx, pat.id); + let val = adt::MaybeSizedValue::sized(val.val); + for (i, elem) in elems.iter().enumerate() { + let fldptr = adt::trans_field_ptr(bcx, &repr, val, Disr(0), adjust(i)); + bcx = bind_irrefutable_pat( + bcx, + &elem, + MatchInput::from_val(fldptr), + cleanup_scope); + } + } + ref sty => span_bug!(pat.span, "unexpected type for tuple pattern: {:?}", sty), } } PatKind::Box(ref inner) => { diff --git a/src/librustc_trans/debuginfo/create_scope_map.rs b/src/librustc_trans/debuginfo/create_scope_map.rs index ba592382d1a79..aec43e69e5182 100644 --- a/src/librustc_trans/debuginfo/create_scope_map.rs +++ b/src/librustc_trans/debuginfo/create_scope_map.rs @@ -318,13 +318,11 @@ fn walk_pattern(cx: &CrateContext, scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); } - PatKind::TupleStruct(_, ref sub_pats_opt) => { + PatKind::TupleStruct(_, ref sub_pats, _) => { scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - if let Some(ref sub_pats) = *sub_pats_opt { - for p in sub_pats { - walk_pattern(cx, &p, scope_stack, scope_map); - } + for p in sub_pats { + walk_pattern(cx, &p, scope_stack, scope_map); } } @@ -343,7 +341,7 @@ fn walk_pattern(cx: &CrateContext, } } - PatKind::Tup(ref sub_pats) => { + PatKind::Tuple(ref sub_pats, _) => { scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); for sub_pat in sub_pats { diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 10c8ea84bfd64..ce4ac4e815c78 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -11,7 +11,7 @@ use hir::def::{self, Def}; use rustc::infer::{self, InferOk, TypeOrigin}; use hir::pat_util::{PatIdMap, pat_id_map, pat_is_binding}; -use hir::pat_util::pat_is_resolved_const; +use hir::pat_util::{pat_adjust_pos, pat_is_resolved_const}; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty, TypeFoldable, LvaluePreference}; use check::{FnCtxt, Expectation}; @@ -213,13 +213,13 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> { } PatKind::Ident(_, ref path, _) => { let path = hir::Path::from_name(path.span, path.node); - self.check_pat_enum(pat, &path, Some(&[]), expected, false); + self.check_pat_enum(pat, &path, &[], None, expected, false); } - PatKind::TupleStruct(ref path, ref subpats) => { - self.check_pat_enum(pat, path, subpats.as_ref().map(|v| &v[..]), expected, true); + PatKind::TupleStruct(ref path, ref subpats, ddpos) => { + self.check_pat_enum(pat, path, &subpats, ddpos, expected, true); } PatKind::Path(ref path) => { - self.check_pat_enum(pat, path, Some(&[]), expected, false); + self.check_pat_enum(pat, path, &[], None, expected, false); } PatKind::QPath(ref qself, ref path) => { let self_ty = self.to_ty(&qself.ty); @@ -260,14 +260,24 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> { PatKind::Struct(ref path, ref fields, etc) => { self.check_pat_struct(pat, path, fields, etc, expected); } - PatKind::Tup(ref elements) => { - let element_tys: Vec<_> = - (0..elements.len()).map(|_| self.next_ty_var()).collect(); + PatKind::Tuple(ref elements, ddpos) => { + let mut expected_len = elements.len(); + if ddpos.is_some() { + // Require known type only when `..` is present + if let ty::TyTuple(ref tys) = + self.structurally_resolved_type(pat.span, expected).sty { + expected_len = tys.len(); + } + } + let max_len = cmp::max(expected_len, elements.len()); + + let element_tys = (0 .. max_len).map(|_| self.next_ty_var()).collect(): Vec<_>; let pat_ty = tcx.mk_tup(element_tys.clone()); self.write_ty(pat.id, pat_ty); self.demand_eqtype(pat.span, expected, pat_ty); - for (element_pat, element_ty) in elements.iter().zip(element_tys) { - self.check_pat(&element_pat, element_ty); + let adjust = pat_adjust_pos(expected_len, elements.len(), ddpos); + for i in 0 .. elements.len() { + self.check_pat(&elements[i], &element_tys[adjust(i)]); } } PatKind::Box(ref inner) => { @@ -615,7 +625,8 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> { fn check_pat_enum(&self, pat: &hir::Pat, path: &hir::Path, - subpats: Option<&'gcx [P]>, + subpats: &'gcx [P], + ddpos: Option, expected: Ty<'tcx>, is_tuple_struct_pat: bool) { @@ -628,12 +639,9 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> { self.set_tainted_by_errors(); self.write_error(pat.id); - if let Some(subpats) = subpats { - for pat in subpats { - self.check_pat(&pat, tcx.types.err); - } + for pat in subpats { + self.check_pat(&pat, tcx.types.err); } - return; } }; @@ -670,15 +678,12 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> { }; self.instantiate_path(segments, path_scheme, &ctor_predicates, opt_ty, def, pat.span, pat.id); - let report_bad_struct_kind = |is_warning| { bad_struct_kind_err(tcx.sess, pat, path, is_warning); if is_warning { return; } self.write_error(pat.id); - if let Some(subpats) = subpats { - for pat in subpats { - self.check_pat(&pat, tcx.types.err); - } + for pat in subpats { + self.check_pat(&pat, tcx.types.err); } }; @@ -715,11 +720,13 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> { }; match (is_tuple_struct_pat, variant.kind()) { - (true, ty::VariantKind::Unit) => { + (true, ty::VariantKind::Unit) if subpats.is_empty() && ddpos.is_some() => { // Matching unit structs with tuple variant patterns (`UnitVariant(..)`) // is allowed for backward compatibility. report_bad_struct_kind(true); } + (true, ty::VariantKind::Unit) | + (false, ty::VariantKind::Tuple) | (_, ty::VariantKind::Struct) => { report_bad_struct_kind(false); return @@ -727,30 +734,23 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> { _ => {} } - if let Some(subpats) = subpats { - if subpats.len() == variant.fields.len() { - for (subpat, field) in subpats.iter().zip(&variant.fields) { - let field_ty = self.field_ty(subpat.span, field, expected_substs); - self.check_pat(&subpat, field_ty); - } - } else if variant.fields.is_empty() { - span_err!(tcx.sess, pat.span, E0024, - "this pattern has {} field{}, but the corresponding {} has no fields", - subpats.len(), if subpats.len() == 1 {""} else {"s"}, kind_name); - - for pat in subpats { - self.check_pat(&pat, tcx.types.err); - } - } else { - span_err!(tcx.sess, pat.span, E0023, - "this pattern has {} field{}, but the corresponding {} has {} field{}", - subpats.len(), if subpats.len() == 1 {""} else {"s"}, - kind_name, - variant.fields.len(), if variant.fields.len() == 1 {""} else {"s"}); - - for pat in subpats { - self.check_pat(&pat, tcx.types.err); - } + let adjust = pat_adjust_pos(variant.fields.len(), subpats.len(), ddpos); + if subpats.len() == variant.fields.len() || + subpats.len() < variant.fields.len() && ddpos.is_some() { + for (i, subpat) in subpats.iter().enumerate() { + let field_ty = self.field_ty(subpat.span, + &variant.fields[adjust(i)], expected_substs); + self.check_pat(&subpat, field_ty); + } + } else { + span_err!(tcx.sess, pat.span, E0023, + "this pattern has {} field{}, but the corresponding {} has {} field{}", + subpats.len(), if subpats.len() == 1 {""} else {"s"}, + kind_name, + variant.fields.len(), if variant.fields.len() == 1 {""} else {"s"}); + + for pat in subpats { + self.check_pat(&pat, tcx.types.err); } } } diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 40b9b0e01ab9b..77ff818aa881a 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -62,31 +62,6 @@ Check how many fields the enum was declared with and ensure that your pattern uses the same number. "##, -E0024: r##" -This error indicates that a pattern attempted to extract the fields of an enum -variant with no fields. Here's a tiny example of this error: - -```compile_fail -// This enum has two variants. -enum Number { - // This variant has no fields. - Zero, - // This variant has one field. - One(u32) -} - -// Assuming x is a Number we can pattern match on its contents. -match x { - Number::Zero(inside) => {}, - Number::One(inside) => {}, -} -``` - -The pattern match `Zero(inside)` is incorrect because the `Zero` variant -contains no fields, yet the `inside` name attempts to bind the first field of -the enum. -"##, - E0025: r##" Each field of a struct can only be bound once in a pattern. Erroneous code example: diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 0b23951db3661..282b7582393ec 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -77,11 +77,12 @@ This API is completely unstable and subject to change. #![feature(box_patterns)] #![feature(box_syntax)] #![feature(iter_arith)] +#![feature(question_mark)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] #![feature(rustc_private)] #![feature(staged_api)] -#![feature(question_mark)] +#![feature(type_ascription)] #[macro_use] extern crate log; #[macro_use] extern crate syntax; diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index ca138168b2954..4831ee9a2e041 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2579,7 +2579,7 @@ fn name_from_pat(p: &hir::Pat) -> String { match p.node { PatKind::Wild => "_".to_string(), PatKind::Ident(_, ref p, _) => p.node.to_string(), - PatKind::TupleStruct(ref p, _) | PatKind::Path(ref p) => path_to_string(p), + PatKind::TupleStruct(ref p, _, _) | PatKind::Path(ref p) => path_to_string(p), PatKind::QPath(..) => panic!("tried to get argument name from PatKind::QPath, \ which is not allowed in function arguments"), PatKind::Struct(ref name, ref fields, etc) => { @@ -2590,7 +2590,7 @@ fn name_from_pat(p: &hir::Pat) -> String { if etc { ", ..." } else { "" } ) }, - PatKind::Tup(ref elts) => format!("({})", elts.iter().map(|p| name_from_pat(&**p)) + PatKind::Tuple(ref elts, _) => format!("({})", elts.iter().map(|p| name_from_pat(&**p)) .collect::>().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 d9409d3bbd921..6eb588767c453 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -567,7 +567,7 @@ impl Pat { PatKind::Struct(_, ref fields, _) => { fields.iter().all(|field| field.node.pat.walk(it)) } - PatKind::TupleStruct(_, Some(ref s)) | PatKind::Tup(ref s) => { + PatKind::TupleStruct(_, ref s, _) | PatKind::Tuple(ref s, _) => { s.iter().all(|p| p.walk(it)) } PatKind::Box(ref s) | PatKind::Ref(ref s, _) => { @@ -582,7 +582,6 @@ impl Pat { PatKind::Lit(_) | PatKind::Range(_, _) | PatKind::Ident(_, _, _) | - PatKind::TupleStruct(..) | PatKind::Path(..) | PatKind::QPath(_, _) | PatKind::Mac(_) => { @@ -631,9 +630,10 @@ pub enum PatKind { /// The `bool` is `true` in the presence of a `..`. Struct(Path, Vec>, 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. @@ -645,8 +645,10 @@ pub enum PatKind { /// PatKind::Path, and the resolver will have to sort that out. QPath(QSelf, Path), - /// A tuple pattern `(a, b)` - Tup(Vec>), + /// 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 7958162986cfc..3a1cdae9bfbd0 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -832,7 +832,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { let pat = if subpats.is_empty() { PatKind::Path(path) } else { - PatKind::TupleStruct(path, Some(subpats)) + PatKind::TupleStruct(path, subpats, None) }; self.pat(span, pat) } @@ -842,7 +842,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { self.pat(span, pat) } fn pat_tuple(&self, span: Span, pats: Vec>) -> P { - 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 acef98f2afc1b..b5bab09c70e0c 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -274,7 +274,10 @@ declare_features! ( (active, drop_types_in_const, "1.9.0", Some(33156)), // Allows cfg(target_has_atomic = "..."). - (active, cfg_target_has_atomic, "1.9.0", Some(32976)) + (active, cfg_target_has_atomic, "1.9.0", Some(32976)), + + // Allows `..` in tuple (struct) patterns + (active, dotdot_in_tuple_patterns, "1.10.0", Some(33627)) ); declare_features! ( @@ -315,7 +318,6 @@ declare_features! ( // Allows `#[deprecated]` attribute (accepted, deprecated, "1.9.0", Some(29935)) ); - // (changing above list without updating src/doc/reference.md makes @cmr sad) #[derive(PartialEq, Copy, Clone, Debug)] @@ -1021,6 +1023,24 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { pattern.span, "box pattern syntax is experimental"); } + PatKind::Tuple(_, ddpos) + if ddpos.is_some() => { + gate_feature_post!(&self, dotdot_in_tuple_patterns, + pattern.span, + "`..` in tuple patterns is experimental"); + } + PatKind::TupleStruct(_, ref fields, ddpos) + if ddpos.is_some() && !fields.is_empty() => { + gate_feature_post!(&self, dotdot_in_tuple_patterns, + pattern.span, + "`..` in tuple struct patterns is experimental"); + } + PatKind::TupleStruct(_, ref fields, ddpos) + if ddpos.is_none() && fields.is_empty() => { + self.context.span_handler.struct_span_err(pattern.span, + "nullary enum variants are written with \ + no trailing `( )`").emit(); + } _ => {} } visit::walk_pat(self, pattern) diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 2c325080c0c26..c9d23427657a5 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1115,9 +1115,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)) @@ -1138,7 +1138,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 fc62cee92fdbc..a4f12769b5c09 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -954,25 +954,6 @@ impl<'a> Parser<'a> { Ok(result) } - /// Parse a sequence parameter of enum variant. For consistency purposes, - /// these should not be empty. - pub fn parse_enum_variant_seq(&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 = 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, @@ -3433,21 +3414,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(self.parse_pat()?); - if self.look_ahead(1, |t| *t != token::CloseDelim(token::Paren)) { - while self.eat(&token::Comma) && - !self.check(&token::CloseDelim(token::Paren)) { + let mut ddpos = None; + + while !self.check(&token::CloseDelim(token::Paren)) { + if ddpos.is_none() && self.eat(&token::DotDot) { + ddpos = Some(fields.len()); + if self.eat(&token::Comma) { + // `..` needs to be followed by `)` or `, pat`, `..,)` is disallowed. fields.push(self.parse_pat()?); } + } else { + fields.push(self.parse_pat()?); } - if fields.len() == 1 { + + if !self.check(&token::CloseDelim(token::Paren)) || + (unary_needs_comma && fields.len() == 1 && ddpos.is_none()) { self.expect(&token::Comma)?; } } - Ok(fields) + + Ok((fields, ddpos)) } fn parse_pat_vec_elements( @@ -3626,9 +3615,9 @@ impl<'a> Parser<'a> { token::OpenDelim(token::Paren) => { // Parse (pat,pat,pat,...) as tuple pattern self.bump(); - let fields = self.parse_pat_tuple_elements()?; + let (fields, ddpos) = self.parse_pat_tuple_elements(true)?; self.expect(&token::CloseDelim(token::Paren))?; - pat = PatKind::Tup(fields); + pat = PatKind::Tuple(fields, ddpos); } token::OpenDelim(token::Bracket) => { // Parse [pat,pat,...] as slice pattern @@ -3713,20 +3702,10 @@ impl<'a> Parser<'a> { return Err(self.fatal("unexpected `(` after qualified path")); } // Parse tuple struct or enum pattern - if self.look_ahead(1, |t| *t == token::DotDot) { - // This is a "top constructor only" pat - self.bump(); - self.bump(); - self.expect(&token::CloseDelim(token::Paren))?; - pat = PatKind::TupleStruct(path, None); - } else { - let args = self.parse_enum_variant_seq( - &token::OpenDelim(token::Paren), - &token::CloseDelim(token::Paren), - SeqSep::trailing_allowed(token::Comma), - |p| p.parse_pat())?; - pat = PatKind::TupleStruct(path, Some(args)); - } + self.bump(); + let (fields, ddpos) = self.parse_pat_tuple_elements(false)?; + self.expect(&token::CloseDelim(token::Paren))?; + pat = PatKind::TupleStruct(path, fields, ddpos) } _ => { pat = match qself { diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index ebb4927d69c0b..6c3f3e28727c7 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -2472,17 +2472,23 @@ impl<'a> State<'a> { None => () } } - PatKind::TupleStruct(ref path, ref args_) => { + PatKind::TupleStruct(ref path, ref elts, ddpos) => { self.print_path(path, true, 0)?; - match *args_ { - None => word(&mut self.s, "(..)")?, - Some(ref args) => { - self.popen()?; - self.commasep(Inconsistent, &args[..], - |s, p| s.print_pat(&p))?; - self.pclose()?; + self.popen()?; + if let Some(ddpos) = ddpos { + self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p))?; + if ddpos != 0 { + self.word_space(",")?; + } + word(&mut self.s, "..")?; + if ddpos != elts.len() { + word(&mut self.s, ",")?; + self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p))?; } + } else { + self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p))?; } + self.pclose()?; } PatKind::Path(ref path) => { self.print_path(path, true, 0)?; @@ -2513,13 +2519,23 @@ impl<'a> State<'a> { space(&mut self.s)?; word(&mut self.s, "}")?; } - PatKind::Tup(ref elts) => { + PatKind::Tuple(ref elts, ddpos) => { self.popen()?; - self.commasep(Inconsistent, - &elts[..], - |s, p| s.print_pat(&p))?; - if elts.len() == 1 { - word(&mut self.s, ",")?; + if let Some(ddpos) = ddpos { + self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p))?; + if ddpos != 0 { + self.word_space(",")?; + } + word(&mut self.s, "..")?; + if ddpos != elts.len() { + word(&mut self.s, ",")?; + self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p))?; + } + } else { + self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p))?; + if elts.len() == 1 { + word(&mut self.s, ",")?; + } } self.pclose()?; } diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index f50a480e5e55a..d41d170c084d5 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -423,11 +423,9 @@ pub fn walk_assoc_type_binding<'v, V: Visitor<'v>>(visitor: &mut V, pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) { match pattern.node { - PatKind::TupleStruct(ref path, ref opt_children) => { + PatKind::TupleStruct(ref path, ref children, _) => { visitor.visit_path(path, pattern.id); - if let Some(ref children) = *opt_children { - walk_list!(visitor, visit_pat, children); - } + walk_list!(visitor, visit_pat, children); } PatKind::Path(ref path) => { visitor.visit_path(path, pattern.id); @@ -443,7 +441,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) { visitor.visit_pat(&field.node.pat) } } - PatKind::Tup(ref tuple_elements) => { + PatKind::Tuple(ref tuple_elements, _) => { walk_list!(visitor, visit_pat, tuple_elements); } PatKind::Box(ref subpattern) | diff --git a/src/test/compile-fail/issue-32004.rs b/src/test/compile-fail/issue-32004.rs index 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-bad-type.rs b/src/test/compile-fail/pat-tuple-bad-type.rs new file mode 100644 index 0000000000000..0d50a30dd0526 --- /dev/null +++ b/src/test/compile-fail/pat-tuple-bad-type.rs @@ -0,0 +1,27 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 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 main() { + let x; + + match x { + (..) => {} //~ ERROR the type of this value must be known in this context + _ => {} + } + + match 0u8 { + (..) => {} //~ ERROR mismatched types + _ => {} + } + + x = 10; +} diff --git a/src/test/compile-fail/pat-tuple-feature-gate.rs b/src/test/compile-fail/pat-tuple-feature-gate.rs new file mode 100644 index 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 d9f3bb3c40f8d..507012e8c5c06 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 5ed8f6dee8cd8..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 00681e6149785..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 one of `@` or `in`, found `[` + for thing(x[]) in foo {} //~ ERROR: expected one of `)`, `,`, or `@`, found `[` } diff --git a/src/test/compile-fail/E0024.rs b/src/test/parse-fail/pat-tuple-1.rs similarity index 74% rename from src/test/compile-fail/E0024.rs rename to src/test/parse-fail/pat-tuple-1.rs index 18f4dcf19d706..945d0740654e8 100644 --- a/src/test/compile-fail/E0024.rs +++ b/src/test/parse-fail/pat-tuple-1.rs @@ -8,15 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -enum Number { - Zero, - One(u32) -} +// compile-flags: -Z parse-only fn main() { - let x = Number::Zero; - match x { - Number::Zero(inside) => {}, //~ ERROR E0024 - Number::One(inside) => {}, + match 0 { + (, ..) => {} //~ ERROR expected pattern, found `,` } } diff --git a/src/test/parse-fail/pat-tuple-2.rs b/src/test/parse-fail/pat-tuple-2.rs new file mode 100644 index 0000000000000..ad52fa5787007 --- /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 expected pattern, found `)` + } +} diff --git a/src/test/parse-fail/pat-tuple-3.rs b/src/test/parse-fail/pat-tuple-3.rs new file mode 100644 index 0000000000000..95e44ae134c07 --- /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 expected pattern, found `..` + } +} diff --git a/src/test/parse-fail/pat-tuple-4.rs b/src/test/parse-fail/pat-tuple-4.rs new file mode 100644 index 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(); +}