From 3da6cf535a7621826e71d478b63f50d1d1f65a8c Mon Sep 17 00:00:00 2001 From: Philip Munksgaard Date: Wed, 4 Mar 2015 17:35:05 +0100 Subject: [PATCH 1/7] if-let: Add support for multiple patterns This commit adds support for multiple patterns in if-let statements: ```rust let x = Some(0); if let Some(0) | Some(1) = x { println!("Got 0 or 1"); } ``` --- src/libsyntax/ast.rs | 2 +- src/libsyntax/ext/expand.rs | 6 +++--- src/libsyntax/fold.rs | 4 ++-- src/libsyntax/parse/parser.rs | 4 ++-- src/libsyntax/print/pprust.rs | 25 +++++++++++++++++++------ src/libsyntax/visit.rs | 6 ++++-- 6 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 6d6fdffa95095..d3f548bb8a102 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -732,7 +732,7 @@ pub enum Expr_ { ExprLit(P), ExprCast(P, P), ExprIf(P, P, Option>), - ExprIfLet(P, P, P, Option>), + ExprIfLet(Vec>, P, P, Option>), // FIXME #6993: change to Option ... or not, if these are hygienic. ExprWhile(P, P, Option), // FIXME #6993: change to Option ... or not, if these are hygienic. diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index bea57ae14e4af..43d9844dc31b4 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -127,8 +127,8 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { } // Desugar ExprIfLet - // From: `if let = []` - ast::ExprIfLet(pat, expr, body, mut elseopt) => { + // From: `if let = []` + ast::ExprIfLet(pats, expr, body, mut elseopt) => { // to: // // match { @@ -140,7 +140,7 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { // ` => ` let pat_arm = { let body_expr = fld.cx.expr_block(body); - fld.cx.arm(pat.span, vec![pat], body_expr) + fld.cx.arm(span, pats, body_expr) }; // `[_ if => ,]` diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index a556b2dfd2a99..b780f126621ca 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1288,8 +1288,8 @@ pub fn noop_fold_expr(Expr {id, node, span}: Expr, folder: &mut T) -> folder.fold_block(tr), fl.map(|x| folder.fold_expr(x))) } - ExprIfLet(pat, expr, tr, fl) => { - ExprIfLet(folder.fold_pat(pat), + ExprIfLet(pats, expr, tr, fl) => { + ExprIfLet(pats.move_map(|x| folder.fold_pat(x)), folder.fold_expr(expr), folder.fold_block(tr), fl.map(|x| folder.fold_expr(x))) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index c1acee57cf806..5e2d530ab4614 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3065,7 +3065,7 @@ impl<'a> Parser<'a> { pub fn parse_if_let_expr(&mut self) -> P { let lo = self.last_span.lo; 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(RESTRICTION_NO_STRUCT_LITERAL); let thn = self.parse_block(); @@ -3075,7 +3075,7 @@ impl<'a> Parser<'a> { } else { (thn.span.hi, None) }; - self.mk_expr(lo, hi, ExprIfLet(pat, expr, thn, els)) + self.mk_expr(lo, hi, ExprIfLet(pats, expr, thn, els)) } // `|args| expr` diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index af16e19c9f034..b6a4c0e3bea42 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1459,11 +1459,11 @@ impl<'a> State<'a> { self.print_else(e.as_ref().map(|e| &**e)) } // "another else-if-let" - ast::ExprIfLet(ref pat, ref expr, ref then, ref e) => { + ast::ExprIfLet(ref pats, ref expr, ref then, ref e) => { try!(self.cbox(indent_unit - 1)); try!(self.ibox(0)); try!(word(&mut self.s, " else if let ")); - try!(self.print_pat(&**pat)); + try!(self.print_pats(pats)); try!(space(&mut self.s)); try!(self.word_space("=")); try!(self.print_expr(&**expr)); @@ -1497,10 +1497,10 @@ impl<'a> State<'a> { 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], expr: &ast::Expr, blk: &ast::Block, elseopt: Option<&ast::Expr>) -> IoResult<()> { try!(self.head("if let")); - try!(self.print_pat(pat)); + try!(self.print_pats(pats)); try!(space(&mut self.s)); try!(self.word_space("=")); try!(self.print_expr(expr)); @@ -1721,8 +1721,8 @@ impl<'a> State<'a> { ast::ExprIf(ref test, ref blk, ref elseopt) => { try!(self.print_if(&**test, &**blk, elseopt.as_ref().map(|e| &**e))); } - ast::ExprIfLet(ref pat, ref expr, ref blk, ref elseopt) => { - try!(self.print_if_let(&**pat, &**expr, &** blk, elseopt.as_ref().map(|e| &**e))); + ast::ExprIfLet(ref pats, ref expr, ref blk, ref elseopt) => { + try!(self.print_if_let(&**pats, &**expr, &** blk, elseopt.as_ref().map(|e| &**e))); } ast::ExprWhile(ref test, ref blk, opt_ident) => { if let Some(ident) = opt_ident { @@ -2253,6 +2253,19 @@ impl<'a> State<'a> { self.ann.post(self, NodePat(pat)) } + pub fn print_pats(&mut self, pats: &[ast::Pat]) -> IoResult<()> { + let mut first = true; + for ref p in pats { + if first { + first = false; + } else { + try!(space(&mut self.s)); + try!(self.word_space("|")); + } + try!(self.print_pat(&**p)); + } + } + fn print_arm(&mut self, arm: &ast::Arm) -> IoResult<()> { // I have no idea why this check is necessary, but here it // is :( diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 33d8d56b4b114..a70d77158f929 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -805,8 +805,10 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { visitor.visit_expr(&**subexpression); visitor.visit_block(&**block) } - ExprIfLet(ref pattern, ref subexpression, ref if_block, ref optional_else) => { - visitor.visit_pat(&**pattern); + ExprIfLet(ref patterns, ref subexpression, ref if_block, ref optional_else) => { + for pattern in patterns { + visitor.visit_pat(&**pattern) + } visitor.visit_expr(&**subexpression); visitor.visit_block(&**if_block); walk_expr_opt(visitor, optional_else); From b32189e1d550236096ba6a56523d256551795d33 Mon Sep 17 00:00:00 2001 From: Philip Munksgaard Date: Wed, 4 Mar 2015 17:38:40 +0100 Subject: [PATCH 2/7] Test for if-let with multiple patterns This just tests very basic usage. --- src/test/run-pass/if-let-multiple-patterns.rs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/test/run-pass/if-let-multiple-patterns.rs diff --git a/src/test/run-pass/if-let-multiple-patterns.rs b/src/test/run-pass/if-let-multiple-patterns.rs new file mode 100644 index 0000000000000..b68e06bc4571d --- /dev/null +++ b/src/test/run-pass/if-let-multiple-patterns.rs @@ -0,0 +1,24 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +enum Foo { One(u8), Two(u8), Three } +use Foo::*; + +fn main () { + let x = Two(42); + let mut result = 0; + + if let One(n) | Two(n) = x { + result = n; + } + + + assert_eq!(result, 42) +} From 3d086122a0b54fef32468295101136bcb5e9f79e Mon Sep 17 00:00:00 2001 From: Philip Munksgaard Date: Wed, 4 Mar 2015 17:39:32 +0100 Subject: [PATCH 3/7] while-let: Add support for multiple patterns This mirrors 3da6cf5 --- src/libsyntax/ast.rs | 2 +- src/libsyntax/ext/expand.rs | 4 ++-- src/libsyntax/fold.rs | 4 ++-- src/libsyntax/parse/parser.rs | 4 ++-- src/libsyntax/print/pprust.rs | 4 ++-- src/libsyntax/visit.rs | 6 ++++-- 6 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index d3f548bb8a102..e68c8d28fc5ab 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -736,7 +736,7 @@ pub enum Expr_ { // FIXME #6993: change to Option ... or not, if these are hygienic. ExprWhile(P, P, Option), // FIXME #6993: change to Option ... or not, if these are hygienic. - ExprWhileLet(P, P, P, Option), + ExprWhileLet(Vec>, P, P, Option), // FIXME #6993: change to Option ... or not, if these are hygienic. ExprForLoop(P, P, P, Option), // Conditionless loop (can be exited with break, cont, or ret) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 43d9844dc31b4..ad793bf731b24 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -92,7 +92,7 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { // Desugar ExprWhileLet // From: `[opt_ident]: while let = ` - ast::ExprWhileLet(pat, expr, body, opt_ident) => { + ast::ExprWhileLet(pats, expr, body, opt_ident) => { // to: // // [opt_ident]: loop { @@ -105,7 +105,7 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { // ` => ` let pat_arm = { let body_expr = fld.cx.expr_block(body); - fld.cx.arm(pat.span, vec![pat], body_expr) + fld.cx.arm(span, pats, body_expr) }; // `_ => break` diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index b780f126621ca..7f5516daf399f 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1299,8 +1299,8 @@ pub fn noop_fold_expr(Expr {id, node, span}: Expr, folder: &mut T) -> folder.fold_block(body), opt_ident.map(|i| folder.fold_ident(i))) } - ExprWhileLet(pat, expr, body, opt_ident) => { - ExprWhileLet(folder.fold_pat(pat), + ExprWhileLet(pats, expr, body, opt_ident) => { + ExprWhileLet(pats.move_map(|x| folder.fold_pat(x)), folder.fold_expr(expr), folder.fold_block(body), opt_ident.map(|i| folder.fold_ident(i))) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 5e2d530ab4614..c78bded1e5420 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3138,12 +3138,12 @@ impl<'a> Parser<'a> { pub fn parse_while_let_expr(&mut self, opt_ident: Option) -> P { let lo = self.last_span.lo; 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(RESTRICTION_NO_STRUCT_LITERAL); let body = self.parse_block(); let hi = body.span.hi; - return self.mk_expr(lo, hi, ExprWhileLet(pat, expr, body, opt_ident)); + return self.mk_expr(lo, hi, ExprWhileLet(pats, expr, body, opt_ident)); } pub fn parse_loop_expr(&mut self, opt_ident: Option) -> P { diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index b6a4c0e3bea42..763b5d148fd08 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1734,13 +1734,13 @@ impl<'a> State<'a> { try!(space(&mut self.s)); try!(self.print_block(&**blk)); } - ast::ExprWhileLet(ref pat, ref expr, ref blk, opt_ident) => { + ast::ExprWhileLet(ref pats, ref expr, ref blk, opt_ident) => { if let Some(ident) = opt_ident { try!(self.print_ident(ident)); try!(self.word_space(":")); } try!(self.head("while let")); - try!(self.print_pat(&**pat)); + try!(self.print_pats(pats)); try!(space(&mut self.s)); try!(self.word_space("=")); try!(self.print_expr(&**expr)); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index a70d77158f929..2459e6ac2925b 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -813,8 +813,10 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { visitor.visit_block(&**if_block); walk_expr_opt(visitor, optional_else); } - ExprWhileLet(ref pattern, ref subexpression, ref block, _) => { - visitor.visit_pat(&**pattern); + ExprWhileLet(ref patterns, ref subexpression, ref block, _) => { + for pattern in patterns { + visitor.visit_pat(&**pattern); + } visitor.visit_expr(&**subexpression); visitor.visit_block(&**block); } From 6c7410b581e2266b49d61dfa380eae63288461c7 Mon Sep 17 00:00:00 2001 From: Philip Munksgaard Date: Wed, 4 Mar 2015 17:45:25 +0100 Subject: [PATCH 4/7] Test for while-let with multiple patterns --- .../run-pass/while-let-multiple-patterns.rs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/test/run-pass/while-let-multiple-patterns.rs diff --git a/src/test/run-pass/while-let-multiple-patterns.rs b/src/test/run-pass/while-let-multiple-patterns.rs new file mode 100644 index 0000000000000..23370ef274267 --- /dev/null +++ b/src/test/run-pass/while-let-multiple-patterns.rs @@ -0,0 +1,39 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[derive(Debug)] +enum Foo { One, Two, Three } +use Foo::*; + +fn inc(x: Foo) -> Foo { + match x { + One => Two, + Two => Three, + Three => Three, + } +} + +pub fn main() { + let mut x = Some(2); + while let Some(1) | Some(2) = x { + x = None; + } + + assert_eq!(x , None); + + let mut x = One; + let mut n = 0; + while let One | Two = x { + n += 1; + x = inc(x); + } + + assert_eq!(n, 2); +} From e85dd8884c27bf078828670d1d13c7b945b57263 Mon Sep 17 00:00:00 2001 From: Philip Munksgaard Date: Wed, 4 Mar 2015 18:23:16 +0100 Subject: [PATCH 5/7] Update grammar.md Update grammar.md to reflect the changes to `if let` and `while let`. --- src/doc/grammar.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/grammar.md b/src/doc/grammar.md index d7a29ea530952..83aad7267e746 100644 --- a/src/doc/grammar.md +++ b/src/doc/grammar.md @@ -661,7 +661,7 @@ match_pat : pat [ '|' pat ] * [ "if" expr ] ? ; ### If let expressions ```antlr -if_let_expr : "if" "let" pat '=' expr '{' block '}' +if_let_expr : "if" "let" pat [ '|' pat ] * '=' expr '{' block '}' else_tail ? ; else_tail : "else" [ if_expr | if_let_expr | '{' block '}' ] ; ``` @@ -669,7 +669,7 @@ else_tail : "else" [ if_expr | if_let_expr | '{' block '}' ] ; ### While let loops ```antlr -while_let_expr : "while" "let" pat '=' expr '{' block '}' ; +while_let_expr : "while" "let" pat [ '|' pat ] * '=' expr '{' block '}' ; ``` ### Return expressions From 310a22627ac2f7ba7170b005ac49c4e38334bcb2 Mon Sep 17 00:00:00 2001 From: Philip Munksgaard Date: Wed, 4 Mar 2015 22:08:59 +0100 Subject: [PATCH 6/7] Fix pprust::State::print_pats --- src/libsyntax/print/pprust.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 763b5d148fd08..bb48cbfe69cd7 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -2253,7 +2253,7 @@ impl<'a> State<'a> { self.ann.post(self, NodePat(pat)) } - pub fn print_pats(&mut self, pats: &[ast::Pat]) -> IoResult<()> { + pub fn print_pats(&mut self, pats: &[P]) -> IoResult<()> { let mut first = true; for ref p in pats { if first { @@ -2264,6 +2264,7 @@ impl<'a> State<'a> { } try!(self.print_pat(&**p)); } + Ok(()) } fn print_arm(&mut self, arm: &ast::Arm) -> IoResult<()> { From d570bacd25f4bff6bad5b9008022beb0a77595f7 Mon Sep 17 00:00:00 2001 From: Philip Munksgaard Date: Mon, 16 Mar 2015 15:51:20 +0100 Subject: [PATCH 7/7] Nicer print_pats As per @tamird's suggestion. --- src/libsyntax/print/pprust.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index e8e1e1dbb8c12..9a3e10e5aa104 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -2254,15 +2254,17 @@ impl<'a> State<'a> { } pub fn print_pats(&mut self, pats: &[P]) -> io::Result<()> { - let mut first = true; - for ref p in pats { - if first { - first = false; - } else { - try!(space(&mut self.s)); - try!(self.word_space("|")); + match pats { + [] => {}, + [ref p, ref ps..] => { + try!(self.print_pat(&*p)); + + for p in *ps { + try!(space(&mut self.s)); + try!(self.word_space("|")); + try!(self.print_pat(&*p)); + } } - try!(self.print_pat(&**p)); } Ok(()) }