Skip to content

Implement .. in tuple (struct) patterns (RFC 1492) #33639

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 27, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/doc/reference.md
Original file line number Diff line number Diff line change
@@ -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
5 changes: 2 additions & 3 deletions src/librustc/cfg/construct.rs
Original file line number Diff line number Diff line change
@@ -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])
}
8 changes: 5 additions & 3 deletions src/librustc/hir/fold.rs
Original file line number Diff line number Diff line change
@@ -923,9 +923,9 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
sub.map(|x| folder.fold_pat(x)))
}
PatKind::Lit(e) => PatKind::Lit(folder.fold_expr(e)),
PatKind::TupleStruct(pth, pats) => {
PatKind::TupleStruct(pth, pats, ddpos) => {
PatKind::TupleStruct(folder.fold_path(pth),
pats.map(|pats| pats.move_map(|x| folder.fold_pat(x))))
pats.move_map(|x| folder.fold_pat(x)), ddpos)
}
PatKind::Path(pth) => {
PatKind::Path(folder.fold_path(pth))
@@ -948,7 +948,9 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
});
PatKind::Struct(pth, fs, etc)
}
PatKind::Tup(elts) => PatKind::Tup(elts.move_map(|x| folder.fold_pat(x))),
PatKind::Tuple(elts, ddpos) => {
PatKind::Tuple(elts.move_map(|x| folder.fold_pat(x)), ddpos)
}
PatKind::Box(inner) => PatKind::Box(folder.fold_pat(inner)),
PatKind::Ref(inner, mutbl) => PatKind::Ref(folder.fold_pat(inner), mutbl),
PatKind::Range(e1, e2) => {
8 changes: 3 additions & 5 deletions src/librustc/hir/intravisit.rs
Original file line number Diff line number Diff line change
@@ -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) |
12 changes: 6 additions & 6 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
@@ -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);
16 changes: 9 additions & 7 deletions src/librustc/hir/mod.rs
Original file line number Diff line number Diff line change
@@ -470,7 +470,7 @@ impl Pat {
PatKind::Struct(_, ref fields, _) => {
fields.iter().all(|field| field.node.pat.walk_(it))
}
PatKind::TupleStruct(_, Some(ref s)) | PatKind::Tup(ref s) => {
PatKind::TupleStruct(_, ref s, _) | PatKind::Tuple(ref s, _) => {
s.iter().all(|p| p.walk_(it))
}
PatKind::Box(ref s) | PatKind::Ref(ref s, _) => {
@@ -485,7 +485,6 @@ impl Pat {
PatKind::Lit(_) |
PatKind::Range(_, _) |
PatKind::Ident(_, _, _) |
PatKind::TupleStruct(..) |
PatKind::Path(..) |
PatKind::QPath(_, _) => {
true
@@ -539,9 +538,10 @@ pub enum PatKind {
/// The `bool` is `true` in the presence of a `..`.
Struct(Path, HirVec<Spanned<FieldPat>>, bool),

/// A tuple struct/variant pattern `Variant(x, y, z)`.
/// "None" means a `Variant(..)` pattern where we don't bind the fields to names.
TupleStruct(Path, Option<HirVec<P<Pat>>>),
/// A tuple struct/variant pattern `Variant(x, y, .., z)`.
/// If the `..` pattern fragment is present, then `Option<usize>` denotes its position.
/// 0 <= position <= subpats.len()
TupleStruct(Path, HirVec<P<Pat>>, Option<usize>),

/// A path pattern.
/// Such pattern can be resolved to a unit struct/variant or a constant.
@@ -553,8 +553,10 @@ pub enum PatKind {
/// PatKind::Path, and the resolver will have to sort that out.
QPath(QSelf, Path),

/// A tuple pattern `(a, b)`
Tup(HirVec<P<Pat>>),
/// A tuple pattern `(a, b)`.
/// If the `..` pattern fragment is present, then `Option<usize>` denotes its position.
/// 0 <= position <= subpats.len()
Tuple(HirVec<P<Pat>>, Option<usize>),
/// A `box` pattern
Box(P<Pat>),
/// A reference pattern, e.g. `&mut (a, b)`
34 changes: 34 additions & 0 deletions src/librustc/hir/pat_util.rs
Original file line number Diff line number Diff line change
@@ -18,9 +18,43 @@ use hir::{self, PatKind};
use syntax::codemap::{respan, Span, Spanned, DUMMY_SP};

use std::cell::RefCell;
use std::iter::{Enumerate, ExactSizeIterator};

pub type PatIdMap = FnvHashMap<ast::Name, ast::NodeId>;

pub struct EnumerateAndAdjust<I> {
enumerate: Enumerate<I>,
gap_pos: usize,
gap_len: usize,
}

impl<I> Iterator for EnumerateAndAdjust<I> where I: Iterator {
type Item = (usize, <I as Iterator>::Item);

fn next(&mut self) -> Option<(usize, <I as Iterator>::Item)> {
self.enumerate.next().map(|(i, elem)| {
(if i < self.gap_pos { i } else { i + self.gap_len }, elem)
})
}
}

pub trait EnumerateAndAdjustIterator {
fn enumerate_and_adjust(self, expected_len: usize, gap_pos: Option<usize>)
-> EnumerateAndAdjust<Self> where Self: Sized;
}

impl<T: ExactSizeIterator> EnumerateAndAdjustIterator for T {
fn enumerate_and_adjust(self, expected_len: usize, gap_pos: Option<usize>)
-> EnumerateAndAdjust<Self> where Self: Sized {
let actual_len = self.len();
EnumerateAndAdjust {
enumerate: self.enumerate(),
gap_pos: if let Some(gap_pos) = gap_pos { gap_pos } else { expected_len },
gap_len: expected_len - actual_len,
}
}
}

// This is used because same-named variables in alternative patterns need to
// use the NodeId of their namesake in the first pattern.
pub fn pat_id_map(dm: &RefCell<DefMap>, pat: &hir::Pat) -> PatIdMap {
41 changes: 30 additions & 11 deletions src/librustc/hir/print.rs
Original file line number Diff line number Diff line change
@@ -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()?;
}
2 changes: 1 addition & 1 deletion src/librustc/middle/expr_use_visitor.rs
Original file line number Diff line number Diff line change
@@ -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
31 changes: 22 additions & 9 deletions src/librustc/middle/mem_categorization.rs
Original file line number Diff line number Diff line change
@@ -80,6 +80,7 @@ use ty::adjustment;
use ty::{self, Ty, TyCtxt};

use hir::{MutImmutable, MutMutable, PatKind};
use hir::pat_util::EnumerateAndAdjustIterator;
use hir;
use syntax::ast;
use syntax::codemap::Span;
@@ -1225,14 +1226,13 @@ 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)
for (i, subpat) in subpats.iter().enumerate() {
let expected_len = self.tcx().lookup_adt_def(enum_def)
.variant_with_id(def_id).fields.len();
for (i, subpat) in subpats.iter().enumerate_and_adjust(expected_len, ddpos) {
let subpat_ty = self.pat_ty(&subpat)?; // see (*2)

let subcmt =
@@ -1244,7 +1244,16 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
}
}
Some(Def::Struct(..)) => {
for (i, subpat) in subpats.iter().enumerate() {
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);
}
};

for (i, subpat) in subpats.iter().enumerate_and_adjust(expected_len, ddpos) {
let subpat_ty = self.pat_ty(&subpat)?; // see (*2)
let cmt_field =
self.cat_imm_interior(
@@ -1284,9 +1293,13 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
}
}

PatKind::Tup(ref subpats) => {
PatKind::Tuple(ref subpats, ddpos) => {
// (p1, ..., pN)
for (i, subpat) in subpats.iter().enumerate() {
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),
};
for (i, subpat) in subpats.iter().enumerate_and_adjust(expected_len, ddpos) {
let subpat_ty = self.pat_ty(&subpat)?; // see (*2)
let subcmt =
self.cat_imm_interior(
4 changes: 2 additions & 2 deletions src/librustc/middle/region.rs
Original file line number Diff line number Diff line change
@@ -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))
}

8 changes: 4 additions & 4 deletions src/librustc/middle/stability.rs
Original file line number Diff line number Diff line change
@@ -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::EnumerateAndAdjustIterator;

use std::mem::replace;
use std::cmp::Ordering;
@@ -614,10 +615,9 @@ 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) => {
for (i, field) in pat_fields.iter().enumerate_and_adjust(v.fields.len(), ddpos) {
maybe_do_stability_check(tcx, v.fields[i].did, field.span, cb)
}
}
// Foo { a, b, c }
Loading