Skip to content
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

[RFC] Implement .. in tuple (struct) patterns #32079

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
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
Expand Up @@ -2398,6 +2398,8 @@ The currently implemented features of the reference compiler are:
* - `abi_vectorcall` - Allows the usage of the vectorcall calling convention
(e.g. `extern "vectorcall" func fn_();`)

* - `dotdot_in_tuple_patterns` - Allows `..` in tuple (struct) patterns.

If a feature is promoted to a language feature, then all existing programs will
start to receive compilation warnings about `#![feature]` directives which enabled
the new feature (because the directive is no longer necessary). However, if a
Expand Down
1 change: 1 addition & 0 deletions src/librustc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#![feature(slice_patterns)]
#![feature(staged_api)]
#![feature(str_char)]
#![feature(type_ascription)]
#![cfg_attr(test, feature(test))]

extern crate arena;
Expand Down
5 changes: 2 additions & 3 deletions src/librustc/middle/cfg/construct.rs
Original file line number Diff line number Diff line change
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
35 changes: 25 additions & 10 deletions src/librustc/middle/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, matrix: &Matrix, source: hir:
hir::MatchSource::ForLoopDesugar => {
// `witnesses[0]` has the form `Some(<head>)`, peel off the `Some`
let witness = match witnesses[0].node {
PatKind::TupleStruct(_, Some(ref pats)) => match &pats[..] {
PatKind::TupleStruct(_, ref pats, _) => match &pats[..] {
[ref pat] => &**pat,
_ => unreachable!(),
},
Expand Down Expand Up @@ -530,7 +530,7 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor,
let pats_len = pats.len();
let mut pats = pats.into_iter().map(|p| P((*p).clone()));
let pat = match left_ty.sty {
ty::TyTuple(_) => PatKind::Tup(pats.collect()),
ty::TyTuple(..) => PatKind::Tuple(pats.collect(), None),

ty::TyEnum(adt, _) | ty::TyStruct(adt, _) => {
let v = adt.variant_of_ctor(ctor);
Expand All @@ -551,7 +551,7 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor,
PatKind::Struct(def_to_path(cx.tcx, v.did), field_pats, has_more_fields)
}
VariantKind::Tuple => {
PatKind::TupleStruct(def_to_path(cx.tcx, v.did), Some(pats.collect()))
PatKind::TupleStruct(def_to_path(cx.tcx, v.did), pats.collect(), None)
}
VariantKind::Unit => {
PatKind::Path(def_to_path(cx.tcx, v.did))
Expand Down Expand Up @@ -804,7 +804,7 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: &Pat,
vec!(Slice(before.len() + after.len()))
}
},
PatKind::Box(_) | PatKind::Tup(_) | PatKind::Ref(..) =>
PatKind::Box(..) | PatKind::Tuple(..) | PatKind::Ref(..) =>
vec!(Single),
PatKind::Wild =>
vec!(),
Expand Down Expand Up @@ -886,18 +886,25 @@ 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(..) =>
cx.tcx.sess.span_bug(pat_span, "const pattern should've \
been rewritten"),
Def::Variant(_, id) if *constructor != Variant(id) => None,
Def::Variant(..) | Def::Struct(..) => {
Some(match args {
&Some(ref args) => args.iter().map(|p| &**p).collect(),
&None => vec![DUMMY_WILD_PAT; arity],
})
match ddpos {
Some(ddpos) => {
let mut pats = args[..ddpos].iter().map(|p| &**p).collect(): Vec<_>;
pats.extend(repeat(DUMMY_WILD_PAT).take(arity - args.len()));
if ddpos != args.len() {
pats.extend(args[ddpos..].iter().map(|p| &**p));
}
Some(pats)
}
None => Some(args.iter().map(|p| &**p).collect())
}
}
_ => None
}
Expand Down Expand Up @@ -925,7 +932,15 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
}
}

PatKind::Tup(ref args) =>
PatKind::Tuple(ref args, Some(ddpos)) => {
let mut pats = args[..ddpos].iter().map(|p| &**p).collect(): Vec<_>;
pats.extend(repeat(DUMMY_WILD_PAT).take(arity - args.len()));
if ddpos != args.len() {
pats.extend(args[ddpos..].iter().map(|p| &**p));
}
Some(pats)
}
PatKind::Tuple(ref args, None) =>
Some(args.iter().map(|p| &**p).collect()),

PatKind::Box(ref inner) | PatKind::Ref(ref inner, _) =>
Expand Down
6 changes: 4 additions & 2 deletions src/librustc/middle/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,9 @@ impl ConstVal {
pub fn const_expr_to_pat(tcx: &TyCtxt, expr: &Expr, span: Span) -> P<hir::Pat> {
let pat = match expr.node {
hir::ExprTup(ref exprs) =>
PatKind::Tup(exprs.iter().map(|expr| const_expr_to_pat(tcx, &expr, span)).collect()),
PatKind::Tuple(exprs.iter()
.map(|expr| const_expr_to_pat(tcx, &expr, span))
.collect(), None),

hir::ExprCall(ref callee, ref args) => {
let def = *tcx.def_map.borrow().get(&callee.id).unwrap();
Expand All @@ -343,7 +345,7 @@ pub fn const_expr_to_pat(tcx: &TyCtxt, expr: &Expr, span: Span) -> P<hir::Pat> {
_ => unreachable!()
};
let pats = args.iter().map(|expr| const_expr_to_pat(tcx, &expr, span)).collect();
PatKind::TupleStruct(path, Some(pats))
PatKind::TupleStruct(path, pats, None)
}

hir::ExprStruct(ref path, ref fields, None) => {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1135,7 +1135,7 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
// will visit the substructure recursively.
}

PatKind::Wild | PatKind::Tup(..) | PatKind::Box(..) |
PatKind::Wild | PatKind::Tuple(..) | PatKind::Box(..) |
PatKind::Ref(..) | PatKind::Lit(..) | PatKind::Range(..) |
PatKind::Vec(..) => {
// Similarly, each of these cases does not
Expand Down
41 changes: 32 additions & 9 deletions src/librustc/middle/mem_categorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1227,31 +1227,45 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> {
// _
}

PatKind::TupleStruct(_, None) => {
// variant(..)
}
PatKind::TupleStruct(_, Some(ref subpats)) => {
PatKind::TupleStruct(_, ref subpats, ddpos) => {
match opt_def {
Some(Def::Variant(..)) => {
Some(Def::Variant(enum_def, def_id)) => {
// variant(x, y, z)
let variant = self.tcx().lookup_adt_def(enum_def).variant_with_id(def_id);
let adjust = |i| {
let gap = variant.fields.len() - subpats.len();
if ddpos.is_none() || ddpos.unwrap() > i { i } else { i + gap }
};
for (i, subpat) in subpats.iter().enumerate() {
let subpat_ty = try!(self.pat_ty(&subpat)); // see (*2)

let subcmt =
self.cat_imm_interior(
pat, cmt.clone(), subpat_ty,
InteriorField(PositionalField(i)));
InteriorField(PositionalField(adjust(i))));

try!(self.cat_pattern_(subcmt, &subpat, op));
}
}
Some(Def::Struct(..)) => {
let expected_len = match self.pat_ty(&pat) {
Ok(&ty::TyS{sty: ty::TyStruct(adt_def, _), ..}) => {
adt_def.struct_variant().fields.len()
}
ref ty => self.tcx().sess.span_bug(pat.span,
&format!("tuple struct pattern unexpected type {:?}", ty)),
};

let adjust = |i| {
let gap = expected_len - subpats.len();
if ddpos.is_none() || ddpos.unwrap() > i { i } else { i + gap }
};
for (i, subpat) in subpats.iter().enumerate() {
let subpat_ty = try!(self.pat_ty(&subpat)); // see (*2)
let cmt_field =
self.cat_imm_interior(
pat, cmt.clone(), subpat_ty,
InteriorField(PositionalField(i)));
InteriorField(PositionalField(adjust(i))));
try!(self.cat_pattern_(cmt_field, &subpat, op));
}
}
Expand Down Expand Up @@ -1285,14 +1299,23 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> {
}
}

PatKind::Tup(ref subpats) => {
PatKind::Tuple(ref subpats, ddpos) => {
// (p1, ..., pN)
let expected_len = match self.pat_ty(&pat) {
Ok(&ty::TyS{sty: ty::TyTuple(ref tys), ..}) => tys.len(),
ref ty => self.tcx().sess.span_bug(pat.span,
&format!("tuple pattern unexpected type {:?}", ty)),
};
let adjust = |i| {
let gap = expected_len - subpats.len();
if ddpos.is_none() || ddpos.unwrap() > i { i } else { i + gap }
};
for (i, subpat) in subpats.iter().enumerate() {
let subpat_ty = try!(self.pat_ty(&subpat)); // see (*2)
let subcmt =
self.cat_imm_interior(
pat, cmt.clone(), subpat_ty,
InteriorField(PositionalField(i)));
InteriorField(PositionalField(adjust(i))));
try!(self.cat_pattern_(subcmt, &subpat, op));
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/middle/region.rs
Original file line number Diff line number Diff line change
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
11 changes: 7 additions & 4 deletions src/librustc/middle/stability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -598,10 +598,13 @@ pub fn check_pat(tcx: &TyCtxt, pat: &hir::Pat,
};
match pat.node {
// Foo(a, b, c)
// A Variant(..) pattern `PatKind::TupleStruct(_, None)` doesn't have to be recursed into.
PatKind::TupleStruct(_, Some(ref pat_fields)) => {
for (field, struct_field) in pat_fields.iter().zip(&v.fields) {
maybe_do_stability_check(tcx, struct_field.did, field.span, cb)
PatKind::TupleStruct(_, ref pat_fields, ddpos) => {
let adjust = |i| {
let gap = v.fields.len() - pat_fields.len();
if ddpos.is_none() || ddpos.unwrap() > i { i } else { i + gap }
};
for (i, field) in pat_fields.iter().enumerate() {
maybe_do_stability_check(tcx, v.fields[adjust(i)].did, field.span, cb)
}
}
// Foo { a, b, c }
Expand Down
8 changes: 5 additions & 3 deletions src/librustc_front/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -970,9 +970,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 @@ -995,7 +995,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
13 changes: 8 additions & 5 deletions src/librustc_front/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -522,9 +522,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 @@ -536,8 +537,10 @@ pub enum PatKind {
/// PatKind::Path, and the resolver will have to sort that out.
QPath(QSelf, Path),

/// A tuple pattern `(a, b)`
Tup(HirVec<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
8 changes: 3 additions & 5 deletions src/librustc_front/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,11 +468,9 @@ pub fn walk_assoc_type_binding<'v, V: Visitor<'v>>(visitor: &mut V,

pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
match pattern.node {
PatKind::TupleStruct(ref path, ref opt_children) => {
PatKind::TupleStruct(ref path, ref children, _) => {
visitor.visit_path(path, pattern.id);
if let Some(ref children) = *opt_children {
walk_list!(visitor, visit_pat, children);
}
walk_list!(visitor, visit_pat, children);
}
PatKind::Path(ref path) => {
visitor.visit_path(path, pattern.id);
Expand All @@ -488,7 +486,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
visitor.visit_pat(&field.node.pat)
}
}
PatKind::Tup(ref tuple_elements) => {
PatKind::Tuple(ref tuple_elements, _) => {
walk_list!(visitor, visit_pat, tuple_elements);
}
PatKind::Box(ref subpattern) |
Expand Down
11 changes: 5 additions & 6 deletions src/librustc_front/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -925,10 +925,9 @@ pub fn lower_pat(lctx: &LoweringContext, p: &Pat) -> P<hir::Pat> {
sub.as_ref().map(|x| lower_pat(lctx, x)))
}
PatKind::Lit(ref e) => hir::PatKind::Lit(lower_expr(lctx, e)),
PatKind::TupleStruct(ref pth, ref pats) => {
PatKind::TupleStruct(ref pth, ref pats, ddpos) => {
hir::PatKind::TupleStruct(lower_path(lctx, pth),
pats.as_ref()
.map(|pats| pats.iter().map(|x| lower_pat(lctx, x)).collect()))
pats.iter().map(|x| lower_pat(lctx, x)).collect(), ddpos)
}
PatKind::Path(ref pth) => {
hir::PatKind::Path(lower_path(lctx, pth))
Expand Down Expand Up @@ -956,8 +955,8 @@ pub fn lower_pat(lctx: &LoweringContext, p: &Pat) -> P<hir::Pat> {
.collect();
hir::PatKind::Struct(pth, fs, etc)
}
PatKind::Tup(ref elts) => {
hir::PatKind::Tup(elts.iter().map(|x| lower_pat(lctx, x)).collect())
PatKind::Tuple(ref elts, ddpos) => {
hir::PatKind::Tuple(elts.iter().map(|x| lower_pat(lctx, x)).collect(), ddpos)
}
PatKind::Box(ref inner) => hir::PatKind::Box(lower_pat(lctx, inner)),
PatKind::Ref(ref inner, mutbl) => {
Expand Down Expand Up @@ -1839,7 +1838,7 @@ fn pat_enum(lctx: &LoweringContext,
let pt = if subpats.is_empty() {
hir::PatKind::Path(path)
} else {
hir::PatKind::TupleStruct(path, Some(subpats))
hir::PatKind::TupleStruct(path, subpats, None)
};
pat(lctx, span, pt)
}
Expand Down
Loading