Skip to content

Commit 8640a51

Browse files
committed
Implement multiple patterns with | in if let and while let
1 parent 063deba commit 8640a51

File tree

12 files changed

+231
-128
lines changed

12 files changed

+231
-128
lines changed

src/librustc/hir/lowering.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -2956,7 +2956,7 @@ impl<'a> LoweringContext<'a> {
29562956

29572957
// Desugar ExprIfLet
29582958
// From: `if let <pat> = <sub_expr> <body> [<else_opt>]`
2959-
ExprKind::IfLet(ref pat, ref sub_expr, ref body, ref else_opt) => {
2959+
ExprKind::IfLet(ref pats, ref sub_expr, ref body, ref else_opt) => {
29602960
// to:
29612961
//
29622962
// match <sub_expr> {
@@ -2970,8 +2970,8 @@ impl<'a> LoweringContext<'a> {
29702970
{
29712971
let body = self.lower_block(body, false);
29722972
let body_expr = P(self.expr_block(body, ThinVec::new()));
2973-
let pat = self.lower_pat(pat);
2974-
arms.push(self.arm(hir_vec![pat], body_expr));
2973+
let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
2974+
arms.push(self.arm(pats, body_expr));
29752975
}
29762976

29772977
// _ => [<else_opt>|()]
@@ -3000,7 +3000,7 @@ impl<'a> LoweringContext<'a> {
30003000

30013001
// Desugar ExprWhileLet
30023002
// From: `[opt_ident]: while let <pat> = <sub_expr> <body>`
3003-
ExprKind::WhileLet(ref pat, ref sub_expr, ref body, opt_label) => {
3003+
ExprKind::WhileLet(ref pats, ref sub_expr, ref body, opt_label) => {
30043004
// to:
30053005
//
30063006
// [opt_ident]: loop {
@@ -3021,8 +3021,8 @@ impl<'a> LoweringContext<'a> {
30213021
// `<pat> => <body>`
30223022
let pat_arm = {
30233023
let body_expr = P(self.expr_block(body, ThinVec::new()));
3024-
let pat = self.lower_pat(pat);
3025-
self.arm(hir_vec![pat], body_expr)
3024+
let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
3025+
self.arm(pats, body_expr)
30263026
};
30273027

30283028
// `_ => break`

src/librustc_resolve/lib.rs

+24-12
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ use syntax::ast::{Label, Local, Mutability, Pat, PatKind, Path};
5959
use syntax::ast::{QSelf, TraitItemKind, TraitRef, Ty, TyKind};
6060
use syntax::feature_gate::{feature_err, emit_feature_err, GateIssue};
6161
use syntax::parse::token;
62+
use syntax::ptr::P;
6263

6364
use syntax_pos::{Span, DUMMY_SP, MultiSpan};
6465
use errors::{DiagnosticBuilder, DiagnosticId};
@@ -2329,17 +2330,17 @@ impl<'a> Resolver<'a> {
23292330

23302331
// check that all of the arms in an or-pattern have exactly the
23312332
// same set of bindings, with the same binding modes for each.
2332-
fn check_consistent_bindings(&mut self, arm: &Arm) {
2333-
if arm.pats.is_empty() {
2333+
fn check_consistent_bindings(&mut self, pats: &[P<Pat>]) {
2334+
if pats.is_empty() {
23342335
return;
23352336
}
23362337

23372338
let mut missing_vars = FxHashMap();
23382339
let mut inconsistent_vars = FxHashMap();
2339-
for (i, p) in arm.pats.iter().enumerate() {
2340+
for (i, p) in pats.iter().enumerate() {
23402341
let map_i = self.binding_mode_map(&p);
23412342

2342-
for (j, q) in arm.pats.iter().enumerate() {
2343+
for (j, q) in pats.iter().enumerate() {
23432344
if i == j {
23442345
continue;
23452346
}
@@ -2404,9 +2405,8 @@ impl<'a> Resolver<'a> {
24042405
self.resolve_pattern(&pattern, PatternSource::Match, &mut bindings_list);
24052406
}
24062407

2407-
// This has to happen *after* we determine which
2408-
// pat_idents are variants
2409-
self.check_consistent_bindings(arm);
2408+
// This has to happen *after* we determine which pat_idents are variants
2409+
self.check_consistent_bindings(&arm.pats);
24102410

24112411
walk_list!(self, visit_expr, &arm.guard);
24122412
self.visit_expr(&arm.body);
@@ -2490,7 +2490,9 @@ impl<'a> Resolver<'a> {
24902490
&ident.node.name.as_str())
24912491
);
24922492
}
2493-
Some(..) if pat_src == PatternSource::Match => {
2493+
Some(..) if pat_src == PatternSource::Match ||
2494+
pat_src == PatternSource::IfLet ||
2495+
pat_src == PatternSource::WhileLet => {
24942496
// `Variant1(a) | Variant2(a)`, ok
24952497
// Reuse definition from the first `a`.
24962498
def = self.ribs[ValueNS].last_mut().unwrap().bindings[&ident.node];
@@ -3480,11 +3482,16 @@ impl<'a> Resolver<'a> {
34803482
visit::walk_expr(self, expr);
34813483
}
34823484

3483-
ExprKind::IfLet(ref pattern, ref subexpression, ref if_block, ref optional_else) => {
3485+
ExprKind::IfLet(ref pats, ref subexpression, ref if_block, ref optional_else) => {
34843486
self.visit_expr(subexpression);
34853487

34863488
self.ribs[ValueNS].push(Rib::new(NormalRibKind));
3487-
self.resolve_pattern(pattern, PatternSource::IfLet, &mut FxHashMap());
3489+
let mut bindings_list = FxHashMap();
3490+
for pat in pats {
3491+
self.resolve_pattern(pat, PatternSource::IfLet, &mut bindings_list);
3492+
}
3493+
// This has to happen *after* we determine which pat_idents are variants
3494+
self.check_consistent_bindings(pats);
34883495
self.visit_block(if_block);
34893496
self.ribs[ValueNS].pop();
34903497

@@ -3500,11 +3507,16 @@ impl<'a> Resolver<'a> {
35003507
});
35013508
}
35023509

3503-
ExprKind::WhileLet(ref pattern, ref subexpression, ref block, label) => {
3510+
ExprKind::WhileLet(ref pats, ref subexpression, ref block, label) => {
35043511
self.with_resolved_label(label, expr.id, |this| {
35053512
this.visit_expr(subexpression);
35063513
this.ribs[ValueNS].push(Rib::new(NormalRibKind));
3507-
this.resolve_pattern(pattern, PatternSource::WhileLet, &mut FxHashMap());
3514+
let mut bindings_list = FxHashMap();
3515+
for pat in pats {
3516+
this.resolve_pattern(pat, PatternSource::WhileLet, &mut bindings_list);
3517+
}
3518+
// This has to happen *after* we determine which pat_idents are variants
3519+
this.check_consistent_bindings(pats);
35083520
this.visit_block(block);
35093521
this.ribs[ValueNS].pop();
35103522
});

src/librustc_save_analysis/dump_visitor.rs

+85-78
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,81 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
10311031
}
10321032
}
10331033

1034+
fn process_var_decl_multi(&mut self, pats: &'l [P<ast::Pat>]) {
1035+
let mut collector = PathCollector::new();
1036+
for pattern in pats {
1037+
// collect paths from the arm's patterns
1038+
collector.visit_pat(&pattern);
1039+
self.visit_pat(&pattern);
1040+
}
1041+
1042+
// process collected paths
1043+
for (id, i, sp, immut) in collector.collected_idents {
1044+
match self.save_ctxt.get_path_def(id) {
1045+
HirDef::Local(id) => {
1046+
let mut value = if immut == ast::Mutability::Immutable {
1047+
self.span.snippet(sp).to_string()
1048+
} else {
1049+
"<mutable>".to_string()
1050+
};
1051+
let hir_id = self.tcx.hir.node_to_hir_id(id);
1052+
let typ = self.save_ctxt
1053+
.tables
1054+
.node_id_to_type_opt(hir_id)
1055+
.map(|t| t.to_string())
1056+
.unwrap_or(String::new());
1057+
value.push_str(": ");
1058+
value.push_str(&typ);
1059+
1060+
if !self.span.filter_generated(Some(sp), sp) {
1061+
let qualname = format!("{}${}", i.to_string(), id);
1062+
let id = ::id_from_node_id(id, &self.save_ctxt);
1063+
let span = self.span_from_span(sp);
1064+
1065+
self.dumper.dump_def(
1066+
&Access {
1067+
public: false,
1068+
reachable: false,
1069+
},
1070+
Def {
1071+
kind: DefKind::Local,
1072+
id,
1073+
span,
1074+
name: i.to_string(),
1075+
qualname,
1076+
value: typ,
1077+
parent: None,
1078+
children: vec![],
1079+
decl_id: None,
1080+
docs: String::new(),
1081+
sig: None,
1082+
attributes: vec![],
1083+
},
1084+
);
1085+
}
1086+
}
1087+
HirDef::StructCtor(..) |
1088+
HirDef::VariantCtor(..) |
1089+
HirDef::Const(..) |
1090+
HirDef::AssociatedConst(..) |
1091+
HirDef::Struct(..) |
1092+
HirDef::Variant(..) |
1093+
HirDef::TyAlias(..) |
1094+
HirDef::AssociatedTy(..) |
1095+
HirDef::SelfTy(..) => {
1096+
self.dump_path_ref(id, &ast::Path::from_ident(sp, i));
1097+
}
1098+
def => error!(
1099+
"unexpected definition kind when processing collected idents: {:?}",
1100+
def
1101+
),
1102+
}
1103+
}
1104+
1105+
for (id, ref path) in collector.collected_paths {
1106+
self.process_path(id, path);
1107+
}
1108+
}
10341109

10351110
fn process_var_decl(&mut self, p: &'l ast::Pat, value: String) {
10361111
// The local could declare multiple new vars, we must walk the
@@ -1622,17 +1697,21 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc
16221697
v.nest_scope(ex.id, |v| v.visit_expr(body))
16231698
});
16241699
}
1625-
ast::ExprKind::ForLoop(ref pattern, ref subexpression, ref block, _) |
1626-
ast::ExprKind::WhileLet(ref pattern, ref subexpression, ref block, _) => {
1700+
ast::ExprKind::ForLoop(ref pattern, ref subexpression, ref block, _) => {
16271701
let value = self.span.snippet(subexpression.span);
16281702
self.process_var_decl(pattern, value);
16291703
debug!("for loop, walk sub-expr: {:?}", subexpression.node);
16301704
self.visit_expr(subexpression);
16311705
visit::walk_block(self, block);
16321706
}
1633-
ast::ExprKind::IfLet(ref pattern, ref subexpression, ref block, ref opt_else) => {
1634-
let value = self.span.snippet(subexpression.span);
1635-
self.process_var_decl(pattern, value);
1707+
ast::ExprKind::WhileLet(ref pats, ref subexpression, ref block, _) => {
1708+
self.process_var_decl_multi(pats);
1709+
debug!("for loop, walk sub-expr: {:?}", subexpression.node);
1710+
self.visit_expr(subexpression);
1711+
visit::walk_block(self, block);
1712+
}
1713+
ast::ExprKind::IfLet(ref pats, ref subexpression, ref block, ref opt_else) => {
1714+
self.process_var_decl_multi(pats);
16361715
self.visit_expr(subexpression);
16371716
visit::walk_block(self, block);
16381717
opt_else.as_ref().map(|el| self.visit_expr(el));
@@ -1661,79 +1740,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc
16611740
}
16621741

16631742
fn visit_arm(&mut self, arm: &'l ast::Arm) {
1664-
let mut collector = PathCollector::new();
1665-
for pattern in &arm.pats {
1666-
// collect paths from the arm's patterns
1667-
collector.visit_pat(&pattern);
1668-
self.visit_pat(&pattern);
1669-
}
1670-
1671-
// process collected paths
1672-
for (id, i, sp, immut) in collector.collected_idents {
1673-
match self.save_ctxt.get_path_def(id) {
1674-
HirDef::Local(id) => {
1675-
let mut value = if immut == ast::Mutability::Immutable {
1676-
self.span.snippet(sp).to_string()
1677-
} else {
1678-
"<mutable>".to_string()
1679-
};
1680-
let hir_id = self.tcx.hir.node_to_hir_id(id);
1681-
let typ = self.save_ctxt
1682-
.tables
1683-
.node_id_to_type_opt(hir_id)
1684-
.map(|t| t.to_string())
1685-
.unwrap_or(String::new());
1686-
value.push_str(": ");
1687-
value.push_str(&typ);
1688-
1689-
if !self.span.filter_generated(Some(sp), sp) {
1690-
let qualname = format!("{}${}", i.to_string(), id);
1691-
let id = ::id_from_node_id(id, &self.save_ctxt);
1692-
let span = self.span_from_span(sp);
1693-
1694-
self.dumper.dump_def(
1695-
&Access {
1696-
public: false,
1697-
reachable: false,
1698-
},
1699-
Def {
1700-
kind: DefKind::Local,
1701-
id,
1702-
span,
1703-
name: i.to_string(),
1704-
qualname,
1705-
value: typ,
1706-
parent: None,
1707-
children: vec![],
1708-
decl_id: None,
1709-
docs: String::new(),
1710-
sig: None,
1711-
attributes: vec![],
1712-
},
1713-
);
1714-
}
1715-
}
1716-
HirDef::StructCtor(..) |
1717-
HirDef::VariantCtor(..) |
1718-
HirDef::Const(..) |
1719-
HirDef::AssociatedConst(..) |
1720-
HirDef::Struct(..) |
1721-
HirDef::Variant(..) |
1722-
HirDef::TyAlias(..) |
1723-
HirDef::AssociatedTy(..) |
1724-
HirDef::SelfTy(..) => {
1725-
self.dump_path_ref(id, &ast::Path::from_ident(sp, i));
1726-
}
1727-
def => error!(
1728-
"unexpected definition kind when processing collected idents: {:?}",
1729-
def
1730-
),
1731-
}
1732-
}
1733-
1734-
for (id, ref path) in collector.collected_paths {
1735-
self.process_path(id, path);
1736-
}
1743+
self.process_var_decl_multi(&arm.pats);
17371744
walk_list!(self, visit_expr, &arm.guard);
17381745
self.visit_expr(&arm.body);
17391746
}

src/libsyntax/ast.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1085,7 +1085,7 @@ pub enum ExprKind {
10851085
/// `if let pat = expr { block } else { expr }`
10861086
///
10871087
/// This is desugared to a `match` expression.
1088-
IfLet(P<Pat>, P<Expr>, P<Block>, Option<P<Expr>>),
1088+
IfLet(Vec<P<Pat>>, P<Expr>, P<Block>, Option<P<Expr>>),
10891089
/// A while loop, with an optional label
10901090
///
10911091
/// `'label: while expr { block }`
@@ -1095,7 +1095,7 @@ pub enum ExprKind {
10951095
/// `'label: while let pat = expr { block }`
10961096
///
10971097
/// This is desugared to a combination of `loop` and `match` expressions.
1098-
WhileLet(P<Pat>, P<Expr>, P<Block>, Option<Label>),
1098+
WhileLet(Vec<P<Pat>>, P<Expr>, P<Block>, Option<Label>),
10991099
/// A for loop, with an optional label
11001100
///
11011101
/// `'label: for pat in expr { block }`

src/libsyntax/feature_gate.rs

+9
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,9 @@ declare_features! (
449449

450450
// Use `?` as the Kleene "at most one" operator
451451
(active, macro_at_most_once_rep, "1.25.0", Some(48075)),
452+
453+
// Multiple patterns with `|` in `if let` and `while let`
454+
(active, if_while_or_patterns, "1.26.0", Some(48215)),
452455
);
453456

454457
declare_features! (
@@ -1686,6 +1689,12 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
16861689
ast::ExprKind::Catch(_) => {
16871690
gate_feature_post!(&self, catch_expr, e.span, "`catch` expression is experimental");
16881691
}
1692+
ast::ExprKind::IfLet(ref pats, ..) | ast::ExprKind::WhileLet(ref pats, ..) => {
1693+
if pats.len() > 1 {
1694+
gate_feature_post!(&self, if_while_or_patterns, e.span,
1695+
"multiple patterns in `if let` and `while let` are unstable");
1696+
}
1697+
}
16891698
_ => {}
16901699
}
16911700
visit::walk_expr(self, e);

src/libsyntax/fold.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1210,8 +1210,8 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
12101210
folder.fold_block(tr),
12111211
fl.map(|x| folder.fold_expr(x)))
12121212
}
1213-
ExprKind::IfLet(pat, expr, tr, fl) => {
1214-
ExprKind::IfLet(folder.fold_pat(pat),
1213+
ExprKind::IfLet(pats, expr, tr, fl) => {
1214+
ExprKind::IfLet(pats.move_map(|pat| folder.fold_pat(pat)),
12151215
folder.fold_expr(expr),
12161216
folder.fold_block(tr),
12171217
fl.map(|x| folder.fold_expr(x)))
@@ -1221,8 +1221,8 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
12211221
folder.fold_block(body),
12221222
opt_label.map(|label| folder.fold_label(label)))
12231223
}
1224-
ExprKind::WhileLet(pat, expr, body, opt_label) => {
1225-
ExprKind::WhileLet(folder.fold_pat(pat),
1224+
ExprKind::WhileLet(pats, expr, body, opt_label) => {
1225+
ExprKind::WhileLet(pats.move_map(|pat| folder.fold_pat(pat)),
12261226
folder.fold_expr(expr),
12271227
folder.fold_block(body),
12281228
opt_label.map(|label| folder.fold_label(label)))

0 commit comments

Comments
 (0)