Skip to content

Commit ac1a270

Browse files
committedMay 14, 2014
syntax: Fix parsing << with closure types
This uses the trick of replacing the << token with a < token to parse closure types correctly. Closes #13324
1 parent 042c8ae commit ac1a270

File tree

3 files changed

+71
-7
lines changed

3 files changed

+71
-7
lines changed
 

‎src/libsyntax/parse/parser.rs

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -587,16 +587,64 @@ impl<'a> Parser<'a> {
587587
self.replace_token(token::BINOP(token::OR), lo, self.span.hi)
588588
}
589589
_ => {
590-
let token_str = self.this_token_to_str();
591-
let found_token =
590+
let found_token = self.this_token_to_str();
591+
let token_str =
592592
Parser::token_to_str(&token::BINOP(token::OR));
593593
self.fatal(format!("expected `{}`, found `{}`",
594-
found_token,
595-
token_str))
594+
token_str, found_token))
596595
}
597596
}
598597
}
599598

599+
// Attempt to consume a `<`. If `<<` is seen, replace it with a single
600+
// `<` and continue. If a `<` is not seen, return false.
601+
//
602+
// This is meant to be used when parsing generics on a path to get the
603+
// starting token. The `force` parameter is used to forcefully break up a
604+
// `<<` token. If `force` is false, then `<<` is only broken when a lifetime
605+
// shows up next. For example, consider the expression:
606+
//
607+
// foo as bar << test
608+
//
609+
// The parser needs to know if `bar <<` is the start of a generic path or if
610+
// it's a left-shift token. If `test` were a lifetime, then it's impossible
611+
// for the token to be a left-shift, but if it's not a lifetime, then it's
612+
// considered a left-shift.
613+
//
614+
// The reason for this is that the only current ambiguity with `<<` is when
615+
// parsing closure types:
616+
//
617+
// foo::<<'a> ||>();
618+
// impl Foo<<'a> ||>() { ... }
619+
fn eat_lt(&mut self, force: bool) -> bool {
620+
match self.token {
621+
token::LT => { self.bump(); true }
622+
token::BINOP(token::SHL) => {
623+
let next_lifetime = self.look_ahead(1, |t| match *t {
624+
token::LIFETIME(..) => true,
625+
_ => false,
626+
});
627+
if force || next_lifetime {
628+
let lo = self.span.lo + BytePos(1);
629+
self.replace_token(token::LT, lo, self.span.hi);
630+
true
631+
} else {
632+
false
633+
}
634+
}
635+
_ => false,
636+
}
637+
}
638+
639+
fn expect_lt(&mut self) {
640+
if !self.eat_lt(true) {
641+
let found_token = self.this_token_to_str();
642+
let token_str = Parser::token_to_str(&token::LT);
643+
self.fatal(format!("expected `{}`, found `{}`",
644+
token_str, found_token))
645+
}
646+
}
647+
600648
// Parse a sequence bracketed by `|` and `|`, stopping before the `|`.
601649
fn parse_seq_to_before_or<T>(
602650
&mut self,
@@ -1500,7 +1548,7 @@ impl<'a> Parser<'a> {
15001548

15011549
// Parse the `<` before the lifetime and types, if applicable.
15021550
let (any_lifetime_or_types, lifetimes, types) = {
1503-
if mode != NoTypesAllowed && self.eat(&token::LT) {
1551+
if mode != NoTypesAllowed && self.eat_lt(false) {
15041552
let (lifetimes, types) =
15051553
self.parse_generic_values_after_lt();
15061554
(true, lifetimes, OwnedSlice::from_vec(types))
@@ -1948,7 +1996,7 @@ impl<'a> Parser<'a> {
19481996
hi = self.span.hi;
19491997
self.bump();
19501998
let (_, tys) = if self.eat(&token::MOD_SEP) {
1951-
self.expect(&token::LT);
1999+
self.expect_lt();
19522000
self.parse_generic_values_after_lt()
19532001
} else {
19542002
(Vec::new(), Vec::new())

‎src/test/run-pass/borrowck-pat-enum.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
// ignore-pretty
12+
1113
fn match_ref(v: Option<int>) -> int {
1214
match v {
1315
Some(ref i) => {

‎src/test/run-pass/closure-syntax.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ fn g<'a>(a: &'a int, f: proc<'b>(&'b int) -> &'b int) -> &'a int {
4343
f(a)
4444
}
4545

46+
struct A;
47+
48+
impl A {
49+
fn foo<T>(&self) {}
50+
}
51+
4652
fn bar<'b>() {
4753
foo::<||>();
4854
foo::<|| -> ()>();
@@ -58,17 +64,25 @@ fn bar<'b>() {
5864
foo::<proc():Share>();
5965
foo::<proc<'a>(int, f32, &'a int):'static + Share -> &'a int>();
6066

67+
foo::<<'a>||>();
68+
6169
// issue #11209
6270
let _: ||: 'b; // for comparison
6371
let _: <'a> ||;
6472

6573
let _: Option<||:'b>;
66-
// let _: Option<<'a>||>;
74+
let _: Option<<'a>||>;
6775
let _: Option< <'a>||>;
6876

6977
// issue #11210
7078
let _: ||: 'static;
79+
80+
let a = A;
81+
a.foo::<<'a>||>();
7182
}
7283

84+
struct B<T>;
85+
impl<'b> B<<'a>||: 'b> {}
86+
7387
pub fn main() {
7488
}

0 commit comments

Comments
 (0)