Skip to content

Commit

Permalink
Implement .. in tuple (struct) patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
petrochenkov committed May 26, 2016
1 parent d5759a3 commit d69aeaf
Show file tree
Hide file tree
Showing 48 changed files with 736 additions and 299 deletions.
2 changes: 2 additions & 0 deletions src/doc/reference.md
Expand Up @@ -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
Expand Down
5 changes: 2 additions & 3 deletions src/librustc/cfg/construct.rs
Expand Up @@ -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(..) |
Expand All @@ -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])
}
Expand Down
8 changes: 5 additions & 3 deletions src/librustc/hir/fold.rs
Expand Up @@ -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))
Expand All @@ -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) => {
Expand Down
8 changes: 3 additions & 5 deletions src/librustc/hir/intravisit.rs
Expand Up @@ -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);
Expand All @@ -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) |
Expand Down
12 changes: 6 additions & 6 deletions src/librustc/hir/lowering.rs
Expand Up @@ -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))
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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);
Expand Down
16 changes: 9 additions & 7 deletions src/librustc/hir/mod.rs
Expand Up @@ -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, _) => {
Expand All @@ -485,7 +485,6 @@ impl Pat {
PatKind::Lit(_) |
PatKind::Range(_, _) |
PatKind::Ident(_, _, _) |
PatKind::TupleStruct(..) |
PatKind::Path(..) |
PatKind::QPath(_, _) => {
true
Expand Down Expand Up @@ -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 presents, 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.
Expand All @@ -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 presents, 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)`
Expand Down
22 changes: 22 additions & 0 deletions src/librustc/hir/pat_util.rs
Expand Up @@ -21,6 +21,28 @@ use std::cell::RefCell;

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

#[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<usize>) -> 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<DefMap>, pat: &hir::Pat) -> PatIdMap {
Expand Down
41 changes: 30 additions & 11 deletions src/librustc/hir/print.rs
Expand Up @@ -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)?;
Expand Down Expand Up @@ -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()?;
}
Expand Down
2 changes: 2 additions & 0 deletions src/librustc/lib.rs
Expand Up @@ -29,6 +29,7 @@
#![feature(collections)]
#![feature(const_fn)]
#![feature(enumset)]
#![feature(fn_traits)]
#![feature(iter_arith)]
#![feature(libc)]
#![feature(nonzero)]
Expand All @@ -38,6 +39,7 @@
#![feature(slice_patterns)]
#![feature(staged_api)]
#![feature(question_mark)]
#![feature(unboxed_closures)]
#![cfg_attr(test, feature(test))]

extern crate arena;
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/expr_use_visitor.rs
Expand Up @@ -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
Expand Down
33 changes: 24 additions & 9 deletions src/librustc/middle/mem_categorization.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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)?;
}
}
Expand Down Expand Up @@ -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)?;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/middle/region.rs
Expand Up @@ -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))
}

Expand Down
9 changes: 5 additions & 4 deletions src/librustc/middle/stability.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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 }
Expand Down

0 comments on commit d69aeaf

Please sign in to comment.