Skip to content

Commit

Permalink
Rollup merge of rust-lang#48490 - petrochenkov:orpat, r=eddyb
Browse files Browse the repository at this point in the history
Implement multiple patterns with `|` in `if let` and `while let` (RFC 2175)

cc rust-lang#48215
  • Loading branch information
Manishearth committed Feb 24, 2018
2 parents 7e68299 + 8640a51 commit 9523c82
Show file tree
Hide file tree
Showing 12 changed files with 231 additions and 128 deletions.
12 changes: 6 additions & 6 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2956,7 +2956,7 @@ impl<'a> LoweringContext<'a> {

// Desugar ExprIfLet
// From: `if let <pat> = <sub_expr> <body> [<else_opt>]`
ExprKind::IfLet(ref pat, ref sub_expr, ref body, ref else_opt) => {
ExprKind::IfLet(ref pats, ref sub_expr, ref body, ref else_opt) => {
// to:
//
// match <sub_expr> {
Expand All @@ -2970,8 +2970,8 @@ impl<'a> LoweringContext<'a> {
{
let body = self.lower_block(body, false);
let body_expr = P(self.expr_block(body, ThinVec::new()));
let pat = self.lower_pat(pat);
arms.push(self.arm(hir_vec![pat], body_expr));
let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
arms.push(self.arm(pats, body_expr));
}

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

// Desugar ExprWhileLet
// From: `[opt_ident]: while let <pat> = <sub_expr> <body>`
ExprKind::WhileLet(ref pat, ref sub_expr, ref body, opt_label) => {
ExprKind::WhileLet(ref pats, ref sub_expr, ref body, opt_label) => {
// to:
//
// [opt_ident]: loop {
Expand All @@ -3021,8 +3021,8 @@ impl<'a> LoweringContext<'a> {
// `<pat> => <body>`
let pat_arm = {
let body_expr = P(self.expr_block(body, ThinVec::new()));
let pat = self.lower_pat(pat);
self.arm(hir_vec![pat], body_expr)
let pats = pats.iter().map(|pat| self.lower_pat(pat)).collect();
self.arm(pats, body_expr)
};

// `_ => break`
Expand Down
36 changes: 24 additions & 12 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ use syntax::ast::{Label, Local, Mutability, Pat, PatKind, Path};
use syntax::ast::{QSelf, TraitItemKind, TraitRef, Ty, TyKind};
use syntax::feature_gate::{feature_err, emit_feature_err, GateIssue};
use syntax::parse::token;
use syntax::ptr::P;

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

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

let mut missing_vars = FxHashMap();
let mut inconsistent_vars = FxHashMap();
for (i, p) in arm.pats.iter().enumerate() {
for (i, p) in pats.iter().enumerate() {
let map_i = self.binding_mode_map(&p);

for (j, q) in arm.pats.iter().enumerate() {
for (j, q) in pats.iter().enumerate() {
if i == j {
continue;
}
Expand Down Expand Up @@ -2404,9 +2405,8 @@ impl<'a> Resolver<'a> {
self.resolve_pattern(&pattern, PatternSource::Match, &mut bindings_list);
}

// This has to happen *after* we determine which
// pat_idents are variants
self.check_consistent_bindings(arm);
// This has to happen *after* we determine which pat_idents are variants
self.check_consistent_bindings(&arm.pats);

walk_list!(self, visit_expr, &arm.guard);
self.visit_expr(&arm.body);
Expand Down Expand Up @@ -2490,7 +2490,9 @@ impl<'a> Resolver<'a> {
&ident.node.name.as_str())
);
}
Some(..) if pat_src == PatternSource::Match => {
Some(..) if pat_src == PatternSource::Match ||
pat_src == PatternSource::IfLet ||
pat_src == PatternSource::WhileLet => {
// `Variant1(a) | Variant2(a)`, ok
// Reuse definition from the first `a`.
def = self.ribs[ValueNS].last_mut().unwrap().bindings[&ident.node];
Expand Down Expand Up @@ -3480,11 +3482,16 @@ impl<'a> Resolver<'a> {
visit::walk_expr(self, expr);
}

ExprKind::IfLet(ref pattern, ref subexpression, ref if_block, ref optional_else) => {
ExprKind::IfLet(ref pats, ref subexpression, ref if_block, ref optional_else) => {
self.visit_expr(subexpression);

self.ribs[ValueNS].push(Rib::new(NormalRibKind));
self.resolve_pattern(pattern, PatternSource::IfLet, &mut FxHashMap());
let mut bindings_list = FxHashMap();
for pat in pats {
self.resolve_pattern(pat, PatternSource::IfLet, &mut bindings_list);
}
// This has to happen *after* we determine which pat_idents are variants
self.check_consistent_bindings(pats);
self.visit_block(if_block);
self.ribs[ValueNS].pop();

Expand All @@ -3500,11 +3507,16 @@ impl<'a> Resolver<'a> {
});
}

ExprKind::WhileLet(ref pattern, ref subexpression, ref block, label) => {
ExprKind::WhileLet(ref pats, ref subexpression, ref block, label) => {
self.with_resolved_label(label, expr.id, |this| {
this.visit_expr(subexpression);
this.ribs[ValueNS].push(Rib::new(NormalRibKind));
this.resolve_pattern(pattern, PatternSource::WhileLet, &mut FxHashMap());
let mut bindings_list = FxHashMap();
for pat in pats {
this.resolve_pattern(pat, PatternSource::WhileLet, &mut bindings_list);
}
// This has to happen *after* we determine which pat_idents are variants
this.check_consistent_bindings(pats);
this.visit_block(block);
this.ribs[ValueNS].pop();
});
Expand Down
163 changes: 85 additions & 78 deletions src/librustc_save_analysis/dump_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,81 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
}
}

fn process_var_decl_multi(&mut self, pats: &'l [P<ast::Pat>]) {
let mut collector = PathCollector::new();
for pattern in pats {
// collect paths from the arm's patterns
collector.visit_pat(&pattern);
self.visit_pat(&pattern);
}

// process collected paths
for (id, i, sp, immut) in collector.collected_idents {
match self.save_ctxt.get_path_def(id) {
HirDef::Local(id) => {
let mut value = if immut == ast::Mutability::Immutable {
self.span.snippet(sp).to_string()
} else {
"<mutable>".to_string()
};
let hir_id = self.tcx.hir.node_to_hir_id(id);
let typ = self.save_ctxt
.tables
.node_id_to_type_opt(hir_id)
.map(|t| t.to_string())
.unwrap_or(String::new());
value.push_str(": ");
value.push_str(&typ);

if !self.span.filter_generated(Some(sp), sp) {
let qualname = format!("{}${}", i.to_string(), id);
let id = ::id_from_node_id(id, &self.save_ctxt);
let span = self.span_from_span(sp);

self.dumper.dump_def(
&Access {
public: false,
reachable: false,
},
Def {
kind: DefKind::Local,
id,
span,
name: i.to_string(),
qualname,
value: typ,
parent: None,
children: vec![],
decl_id: None,
docs: String::new(),
sig: None,
attributes: vec![],
},
);
}
}
HirDef::StructCtor(..) |
HirDef::VariantCtor(..) |
HirDef::Const(..) |
HirDef::AssociatedConst(..) |
HirDef::Struct(..) |
HirDef::Variant(..) |
HirDef::TyAlias(..) |
HirDef::AssociatedTy(..) |
HirDef::SelfTy(..) => {
self.dump_path_ref(id, &ast::Path::from_ident(sp, i));
}
def => error!(
"unexpected definition kind when processing collected idents: {:?}",
def
),
}
}

for (id, ref path) in collector.collected_paths {
self.process_path(id, path);
}
}

fn process_var_decl(&mut self, p: &'l ast::Pat, value: String) {
// The local could declare multiple new vars, we must walk the
Expand Down Expand Up @@ -1622,17 +1697,21 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc
v.nest_scope(ex.id, |v| v.visit_expr(body))
});
}
ast::ExprKind::ForLoop(ref pattern, ref subexpression, ref block, _) |
ast::ExprKind::WhileLet(ref pattern, ref subexpression, ref block, _) => {
ast::ExprKind::ForLoop(ref pattern, ref subexpression, ref block, _) => {
let value = self.span.snippet(subexpression.span);
self.process_var_decl(pattern, value);
debug!("for loop, walk sub-expr: {:?}", subexpression.node);
self.visit_expr(subexpression);
visit::walk_block(self, block);
}
ast::ExprKind::IfLet(ref pattern, ref subexpression, ref block, ref opt_else) => {
let value = self.span.snippet(subexpression.span);
self.process_var_decl(pattern, value);
ast::ExprKind::WhileLet(ref pats, ref subexpression, ref block, _) => {
self.process_var_decl_multi(pats);
debug!("for loop, walk sub-expr: {:?}", subexpression.node);
self.visit_expr(subexpression);
visit::walk_block(self, block);
}
ast::ExprKind::IfLet(ref pats, ref subexpression, ref block, ref opt_else) => {
self.process_var_decl_multi(pats);
self.visit_expr(subexpression);
visit::walk_block(self, block);
opt_else.as_ref().map(|el| self.visit_expr(el));
Expand Down Expand Up @@ -1661,79 +1740,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc
}

fn visit_arm(&mut self, arm: &'l ast::Arm) {
let mut collector = PathCollector::new();
for pattern in &arm.pats {
// collect paths from the arm's patterns
collector.visit_pat(&pattern);
self.visit_pat(&pattern);
}

// process collected paths
for (id, i, sp, immut) in collector.collected_idents {
match self.save_ctxt.get_path_def(id) {
HirDef::Local(id) => {
let mut value = if immut == ast::Mutability::Immutable {
self.span.snippet(sp).to_string()
} else {
"<mutable>".to_string()
};
let hir_id = self.tcx.hir.node_to_hir_id(id);
let typ = self.save_ctxt
.tables
.node_id_to_type_opt(hir_id)
.map(|t| t.to_string())
.unwrap_or(String::new());
value.push_str(": ");
value.push_str(&typ);

if !self.span.filter_generated(Some(sp), sp) {
let qualname = format!("{}${}", i.to_string(), id);
let id = ::id_from_node_id(id, &self.save_ctxt);
let span = self.span_from_span(sp);

self.dumper.dump_def(
&Access {
public: false,
reachable: false,
},
Def {
kind: DefKind::Local,
id,
span,
name: i.to_string(),
qualname,
value: typ,
parent: None,
children: vec![],
decl_id: None,
docs: String::new(),
sig: None,
attributes: vec![],
},
);
}
}
HirDef::StructCtor(..) |
HirDef::VariantCtor(..) |
HirDef::Const(..) |
HirDef::AssociatedConst(..) |
HirDef::Struct(..) |
HirDef::Variant(..) |
HirDef::TyAlias(..) |
HirDef::AssociatedTy(..) |
HirDef::SelfTy(..) => {
self.dump_path_ref(id, &ast::Path::from_ident(sp, i));
}
def => error!(
"unexpected definition kind when processing collected idents: {:?}",
def
),
}
}

for (id, ref path) in collector.collected_paths {
self.process_path(id, path);
}
self.process_var_decl_multi(&arm.pats);
walk_list!(self, visit_expr, &arm.guard);
self.visit_expr(&arm.body);
}
Expand Down
4 changes: 2 additions & 2 deletions src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1085,7 +1085,7 @@ pub enum ExprKind {
/// `if let pat = expr { block } else { expr }`
///
/// This is desugared to a `match` expression.
IfLet(P<Pat>, P<Expr>, P<Block>, Option<P<Expr>>),
IfLet(Vec<P<Pat>>, P<Expr>, P<Block>, Option<P<Expr>>),
/// A while loop, with an optional label
///
/// `'label: while expr { block }`
Expand All @@ -1095,7 +1095,7 @@ pub enum ExprKind {
/// `'label: while let pat = expr { block }`
///
/// This is desugared to a combination of `loop` and `match` expressions.
WhileLet(P<Pat>, P<Expr>, P<Block>, Option<Label>),
WhileLet(Vec<P<Pat>>, P<Expr>, P<Block>, Option<Label>),
/// A for loop, with an optional label
///
/// `'label: for pat in expr { block }`
Expand Down
9 changes: 9 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,9 @@ declare_features! (

// Use `?` as the Kleene "at most one" operator
(active, macro_at_most_once_rep, "1.25.0", Some(48075)),

// Multiple patterns with `|` in `if let` and `while let`
(active, if_while_or_patterns, "1.26.0", Some(48215)),
);

declare_features! (
Expand Down Expand Up @@ -1618,6 +1621,12 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
ast::ExprKind::Catch(_) => {
gate_feature_post!(&self, catch_expr, e.span, "`catch` expression is experimental");
}
ast::ExprKind::IfLet(ref pats, ..) | ast::ExprKind::WhileLet(ref pats, ..) => {
if pats.len() > 1 {
gate_feature_post!(&self, if_while_or_patterns, e.span,
"multiple patterns in `if let` and `while let` are unstable");
}
}
_ => {}
}
visit::walk_expr(self, e);
Expand Down
8 changes: 4 additions & 4 deletions src/libsyntax/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1210,8 +1210,8 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
folder.fold_block(tr),
fl.map(|x| folder.fold_expr(x)))
}
ExprKind::IfLet(pat, expr, tr, fl) => {
ExprKind::IfLet(folder.fold_pat(pat),
ExprKind::IfLet(pats, expr, tr, fl) => {
ExprKind::IfLet(pats.move_map(|pat| folder.fold_pat(pat)),
folder.fold_expr(expr),
folder.fold_block(tr),
fl.map(|x| folder.fold_expr(x)))
Expand All @@ -1221,8 +1221,8 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
folder.fold_block(body),
opt_label.map(|label| folder.fold_label(label)))
}
ExprKind::WhileLet(pat, expr, body, opt_label) => {
ExprKind::WhileLet(folder.fold_pat(pat),
ExprKind::WhileLet(pats, expr, body, opt_label) => {
ExprKind::WhileLet(pats.move_map(|pat| folder.fold_pat(pat)),
folder.fold_expr(expr),
folder.fold_block(body),
opt_label.map(|label| folder.fold_label(label)))
Expand Down
Loading

0 comments on commit 9523c82

Please sign in to comment.