From ac1a27043a7481676502e383716e20c017122bcb Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 10 May 2014 21:27:44 -0700 Subject: [PATCH] syntax: Fix parsing << with closure types This uses the trick of replacing the << token with a < token to parse closure types correctly. Closes #13324 --- src/libsyntax/parse/parser.rs | 60 +++++++++++++++++++++++--- src/test/run-pass/borrowck-pat-enum.rs | 2 + src/test/run-pass/closure-syntax.rs | 16 ++++++- 3 files changed, 71 insertions(+), 7 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index a83bb7d1bf5..92e5f8da6aa 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -587,16 +587,64 @@ impl<'a> Parser<'a> { self.replace_token(token::BINOP(token::OR), lo, self.span.hi) } _ => { - let token_str = self.this_token_to_str(); - let found_token = + let found_token = self.this_token_to_str(); + let token_str = Parser::token_to_str(&token::BINOP(token::OR)); self.fatal(format!("expected `{}`, found `{}`", - found_token, - token_str)) + token_str, found_token)) } } } + // Attempt to consume a `<`. If `<<` is seen, replace it with a single + // `<` and continue. If a `<` is not seen, return false. + // + // This is meant to be used when parsing generics on a path to get the + // starting token. The `force` parameter is used to forcefully break up a + // `<<` token. If `force` is false, then `<<` is only broken when a lifetime + // shows up next. For example, consider the expression: + // + // foo as bar << test + // + // The parser needs to know if `bar <<` is the start of a generic path or if + // it's a left-shift token. If `test` were a lifetime, then it's impossible + // for the token to be a left-shift, but if it's not a lifetime, then it's + // considered a left-shift. + // + // The reason for this is that the only current ambiguity with `<<` is when + // parsing closure types: + // + // foo::<<'a> ||>(); + // impl Foo<<'a> ||>() { ... } + fn eat_lt(&mut self, force: bool) -> bool { + match self.token { + token::LT => { self.bump(); true } + token::BINOP(token::SHL) => { + let next_lifetime = self.look_ahead(1, |t| match *t { + token::LIFETIME(..) => true, + _ => false, + }); + if force || next_lifetime { + let lo = self.span.lo + BytePos(1); + self.replace_token(token::LT, lo, self.span.hi); + true + } else { + false + } + } + _ => false, + } + } + + fn expect_lt(&mut self) { + if !self.eat_lt(true) { + let found_token = self.this_token_to_str(); + let token_str = Parser::token_to_str(&token::LT); + self.fatal(format!("expected `{}`, found `{}`", + token_str, found_token)) + } + } + // Parse a sequence bracketed by `|` and `|`, stopping before the `|`. fn parse_seq_to_before_or( &mut self, @@ -1500,7 +1548,7 @@ impl<'a> Parser<'a> { // Parse the `<` before the lifetime and types, if applicable. let (any_lifetime_or_types, lifetimes, types) = { - if mode != NoTypesAllowed && self.eat(&token::LT) { + if mode != NoTypesAllowed && self.eat_lt(false) { let (lifetimes, types) = self.parse_generic_values_after_lt(); (true, lifetimes, OwnedSlice::from_vec(types)) @@ -1948,7 +1996,7 @@ impl<'a> Parser<'a> { hi = self.span.hi; self.bump(); let (_, tys) = if self.eat(&token::MOD_SEP) { - self.expect(&token::LT); + self.expect_lt(); self.parse_generic_values_after_lt() } else { (Vec::new(), Vec::new()) diff --git a/src/test/run-pass/borrowck-pat-enum.rs b/src/test/run-pass/borrowck-pat-enum.rs index 7b668029499..74ce8ef2e45 100644 --- a/src/test/run-pass/borrowck-pat-enum.rs +++ b/src/test/run-pass/borrowck-pat-enum.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-pretty + fn match_ref(v: Option) -> int { match v { Some(ref i) => { diff --git a/src/test/run-pass/closure-syntax.rs b/src/test/run-pass/closure-syntax.rs index 30c01ba9d51..2bb0e6fa19c 100644 --- a/src/test/run-pass/closure-syntax.rs +++ b/src/test/run-pass/closure-syntax.rs @@ -43,6 +43,12 @@ fn g<'a>(a: &'a int, f: proc<'b>(&'b int) -> &'b int) -> &'a int { f(a) } +struct A; + +impl A { + fn foo(&self) {} +} + fn bar<'b>() { foo::<||>(); foo::<|| -> ()>(); @@ -58,17 +64,25 @@ fn bar<'b>() { foo::(); foo::(int, f32, &'a int):'static + Share -> &'a int>(); + foo::<<'a>||>(); + // issue #11209 let _: ||: 'b; // for comparison let _: <'a> ||; let _: Option<||:'b>; - // let _: Option<<'a>||>; + let _: Option<<'a>||>; let _: Option< <'a>||>; // issue #11210 let _: ||: 'static; + + let a = A; + a.foo::<<'a>||>(); } +struct B; +impl<'b> B<<'a>||: 'b> {} + pub fn main() { }