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

Implement multiple patterns with | in if let and while let (RFC 2175) #48490

Merged
merged 1 commit into from
Feb 25, 2018
Merged
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
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 @@ -449,6 +449,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 @@ -1686,6 +1689,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