From 8640a51ff8d580bbb87aa3dc0ff8bacbad111010 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 24 Feb 2018 03:12:35 +0300 Subject: [PATCH] Implement multiple patterns with `|` in `if let` and `while let` --- src/librustc/hir/lowering.rs | 12 +- src/librustc_resolve/lib.rs | 36 ++-- src/librustc_save_analysis/dump_visitor.rs | 163 +++++++++--------- src/libsyntax/ast.rs | 4 +- src/libsyntax/feature_gate.rs | 9 + src/libsyntax/fold.rs | 8 +- src/libsyntax/parse/parser.rs | 8 +- src/libsyntax/print/pprust.rs | 41 +++-- src/libsyntax/visit.rs | 8 +- .../rfc-2175-or-if-while-let/basic.rs | 30 ++++ .../ui/feature-gate-if_while_or_patterns.rs | 18 ++ .../feature-gate-if_while_or_patterns.stderr | 22 +++ 12 files changed, 231 insertions(+), 128 deletions(-) create mode 100644 src/test/run-pass/rfc-2175-or-if-while-let/basic.rs create mode 100644 src/test/ui/feature-gate-if_while_or_patterns.rs create mode 100644 src/test/ui/feature-gate-if_while_or_patterns.stderr diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index e3af285053805..89ed47ea194fb 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -2956,7 +2956,7 @@ impl<'a> LoweringContext<'a> { // Desugar ExprIfLet // From: `if let = []` - 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 { @@ -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)); } // _ => [|()] @@ -3000,7 +3000,7 @@ impl<'a> LoweringContext<'a> { // Desugar ExprWhileLet // From: `[opt_ident]: while let = ` - 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 { @@ -3021,8 +3021,8 @@ impl<'a> LoweringContext<'a> { // ` => ` 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` diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index e6b9150fa3aec..e4e9ee58330cc 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -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}; @@ -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]) { + 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; } @@ -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); @@ -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]; @@ -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(); @@ -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(); }); diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index bf82b0774238b..6e98604101345 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -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]) { + 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 { + "".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 @@ -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)); @@ -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 { - "".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); } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index c7ce7fffaa21b..6609b77b132c6 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1085,7 +1085,7 @@ pub enum ExprKind { /// `if let pat = expr { block } else { expr }` /// /// This is desugared to a `match` expression. - IfLet(P, P, P, Option>), + IfLet(Vec>, P, P, Option>), /// A while loop, with an optional label /// /// `'label: while expr { block }` @@ -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, P, P, Option