// 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> {
{
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>|()]
// 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 {
// `<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`
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};
// 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;
}
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);
&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];
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();
});
}
- 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();
});
}
}
+ 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
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));
}
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);
}
/// `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 }`
/// `'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 }`
// 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! (
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);
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)))
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)))
-> PResult<'a, P<Expr>> {
let lo = self.prev_span;
self.expect_keyword(keywords::Let)?;
- let pat = self.parse_pat()?;
+ let pats = self.parse_pats()?;
self.expect(&token::Eq)?;
let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
let thn = self.parse_block()?;
} else {
(thn.span, None)
};
- Ok(self.mk_expr(lo.to(hi), ExprKind::IfLet(pat, expr, thn, els), attrs))
+ Ok(self.mk_expr(lo.to(hi), ExprKind::IfLet(pats, expr, thn, els), attrs))
}
// `move |args| expr`
span_lo: Span,
mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> {
self.expect_keyword(keywords::Let)?;
- let pat = self.parse_pat()?;
+ let pats = self.parse_pats()?;
self.expect(&token::Eq)?;
let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
let (iattrs, body) = self.parse_inner_attrs_and_block()?;
attrs.extend(iattrs);
let span = span_lo.to(body.span);
- return Ok(self.mk_expr(span, ExprKind::WhileLet(pat, expr, body, opt_label), attrs));
+ return Ok(self.mk_expr(span, ExprKind::WhileLet(pats, expr, body, opt_label), attrs));
}
// parse `loop {...}`, `loop` token already eaten
self.print_else(e.as_ref().map(|e| &**e))
}
// "another else-if-let"
- ast::ExprKind::IfLet(ref pat, ref expr, ref then, ref e) => {
+ ast::ExprKind::IfLet(ref pats, ref expr, ref then, ref e) => {
self.cbox(INDENT_UNIT - 1)?;
self.ibox(0)?;
self.s.word(" else if let ")?;
- self.print_pat(pat)?;
+ self.print_pats(pats)?;
self.s.space()?;
self.word_space("=")?;
self.print_expr_as_cond(expr)?;
self.print_else(elseopt)
}
- pub fn print_if_let(&mut self, pat: &ast::Pat, expr: &ast::Expr, blk: &ast::Block,
+ pub fn print_if_let(&mut self, pats: &[P<ast::Pat>], expr: &ast::Expr, blk: &ast::Block,
elseopt: Option<&ast::Expr>) -> io::Result<()> {
self.head("if let")?;
- self.print_pat(pat)?;
+ self.print_pats(pats)?;
self.s.space()?;
self.word_space("=")?;
self.print_expr_as_cond(expr)?;
ast::ExprKind::If(ref test, ref blk, ref elseopt) => {
self.print_if(test, blk, elseopt.as_ref().map(|e| &**e))?;
}
- ast::ExprKind::IfLet(ref pat, ref expr, ref blk, ref elseopt) => {
- self.print_if_let(pat, expr, blk, elseopt.as_ref().map(|e| &**e))?;
+ ast::ExprKind::IfLet(ref pats, ref expr, ref blk, ref elseopt) => {
+ self.print_if_let(pats, expr, blk, elseopt.as_ref().map(|e| &**e))?;
}
ast::ExprKind::While(ref test, ref blk, opt_label) => {
if let Some(label) = opt_label {
self.s.space()?;
self.print_block_with_attrs(blk, attrs)?;
}
- ast::ExprKind::WhileLet(ref pat, ref expr, ref blk, opt_label) => {
+ ast::ExprKind::WhileLet(ref pats, ref expr, ref blk, opt_label) => {
if let Some(label) = opt_label {
self.print_ident(label.ident)?;
self.word_space(":")?;
}
self.head("while let")?;
- self.print_pat(pat)?;
+ self.print_pats(pats)?;
self.s.space()?;
self.word_space("=")?;
self.print_expr_as_cond(expr)?;
self.ann.post(self, NodePat(pat))
}
+ fn print_pats(&mut self, pats: &[P<ast::Pat>]) -> io::Result<()> {
+ let mut first = true;
+ for p in pats {
+ if first {
+ first = false;
+ } else {
+ self.s.space()?;
+ self.word_space("|")?;
+ }
+ self.print_pat(p)?;
+ }
+ Ok(())
+ }
+
fn print_arm(&mut self, arm: &ast::Arm) -> io::Result<()> {
// I have no idea why this check is necessary, but here it
// is :(
self.ibox(0)?;
self.maybe_print_comment(arm.pats[0].span.lo())?;
self.print_outer_attributes(&arm.attrs)?;
- let mut first = true;
- for p in &arm.pats {
- if first {
- first = false;
- } else {
- self.s.space()?;
- self.word_space("|")?;
- }
- self.print_pat(p)?;
- }
+ self.print_pats(&arm.pats)?;
self.s.space()?;
if let Some(ref e) = arm.guard {
self.word_space("if")?;
visitor.visit_expr(subexpression);
visitor.visit_block(block);
}
- ExprKind::IfLet(ref pattern, ref subexpression, ref if_block, ref optional_else) => {
- visitor.visit_pat(pattern);
+ ExprKind::IfLet(ref pats, ref subexpression, ref if_block, ref optional_else) => {
+ walk_list!(visitor, visit_pat, pats);
visitor.visit_expr(subexpression);
visitor.visit_block(if_block);
walk_list!(visitor, visit_expr, optional_else);
}
- ExprKind::WhileLet(ref pattern, ref subexpression, ref block, ref opt_label) => {
+ ExprKind::WhileLet(ref pats, ref subexpression, ref block, ref opt_label) => {
walk_list!(visitor, visit_label, opt_label);
- visitor.visit_pat(pattern);
+ walk_list!(visitor, visit_pat, pats);
visitor.visit_expr(subexpression);
visitor.visit_block(block);
}
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(if_while_or_patterns)]
+
+enum E {
+ V(u8),
+ U(u8),
+ W,
+}
+use E::*;
+
+fn main() {
+ let mut e = V(10);
+
+ if let V(x) | U(x) = e {
+ assert_eq!(x, 10);
+ }
+ while let V(x) | U(x) = e {
+ assert_eq!(x, 10);
+ e = W;
+ }
+}
--- /dev/null
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+ if let 0 | 1 = 0 { //~ ERROR multiple patterns in `if let` and `while let` are unstable
+ ;
+ }
+ while let 0 | 1 = 1 { //~ ERROR multiple patterns in `if let` and `while let` are unstable
+ break;
+ }
+}
--- /dev/null
+error[E0658]: multiple patterns in `if let` and `while let` are unstable (see issue #48215)
+ --> $DIR/feature-gate-if_while_or_patterns.rs:12:5
+ |
+12 | / if let 0 | 1 = 0 { //~ ERROR multiple patterns in `if let` and `while let` are unstable
+13 | | ;
+14 | | }
+ | |_____^
+ |
+ = help: add #![feature(if_while_or_patterns)] to the crate attributes to enable
+
+error[E0658]: multiple patterns in `if let` and `while let` are unstable (see issue #48215)
+ --> $DIR/feature-gate-if_while_or_patterns.rs:15:5
+ |
+15 | / while let 0 | 1 = 1 { //~ ERROR multiple patterns in `if let` and `while let` are unstable
+16 | | break;
+17 | | }
+ | |_____^
+ |
+ = help: add #![feature(if_while_or_patterns)] to the crate attributes to enable
+
+error: aborting due to 2 previous errors
+