about summary refs log tree commit diff
path: root/src/libsyntax/parse
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-03-22 02:00:16 +0000
committerbors <bors@rust-lang.org>2017-03-22 02:00:16 +0000
commit8c4f2c64c6759a82f143e23964a46a65c67509c9 (patch)
treef4d5f98790eb5e1501fe0a7429d0d6de02e1fa6c /src/libsyntax/parse
parent50c4e3e8d01020c83255c48d2fdbc58ea5ab34c9 (diff)
parentb5e889791a5ec8cb06224cf07273be8c84192698 (diff)
downloadrust-8c4f2c64c6759a82f143e23964a46a65c67509c9.tar.gz
rust-8c4f2c64c6759a82f143e23964a46a65c67509c9.zip
Auto merge of #40043 - petrochenkov:objpars, r=nikomatsakis
Refactor parsing of trait object types

Bugs are fixed and code is cleaned up.

User visible changes:
- `ty` matcher in macros accepts trait object types like `Write + Send` (https://github.com/rust-lang/rust/issues/39080)
- Buggy priority of `+` in trait object types starting with `for` is fixed (https://github.com/rust-lang/rust/issues/39317). `&for<'a> Trait<'a> + Send` is now parsed as `(&for<'a> Trait<'a>) + Send` and requires parens `&(for<'a> Trait<'a> + Send)`. For comparison, `&Send + for<'a> Trait<'a>` was parsed like this since [Nov 27, 2014](https://github.com/rust-lang/rust/pull/19298).
- Trailing `+`s are supported in trait objects, like in other bounds.
- Better error reporting for trait objects starting with `?Sized`.

Fixes https://github.com/rust-lang/rust/issues/39080
Fixes https://github.com/rust-lang/rust/issues/39317 [breaking-change]
Closes https://github.com/rust-lang/rust/issues/39298
cc https://github.com/rust-lang/rust/issues/39085 (fixed, then reverted https://github.com/rust-lang/rust/pull/40043#issuecomment-286570653)
cc https://github.com/rust-lang/rust/issues/39318 (fixed, then reverted https://github.com/rust-lang/rust/pull/40043#issuecomment-284493890)

r? @nikomatsakis
Diffstat (limited to 'src/libsyntax/parse')
-rw-r--r--src/libsyntax/parse/parser.rs404
-rw-r--r--src/libsyntax/parse/token.rs44
2 files changed, 206 insertions, 242 deletions
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 0a97accead6..df4ccc94c04 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -41,7 +41,7 @@ use ast::{BinOpKind, UnOp};
 use ast::RangeEnd;
 use {ast, attr};
 use codemap::{self, CodeMap, Spanned, spanned, respan};
-use syntax_pos::{self, Span, Pos, BytePos, mk_sp};
+use syntax_pos::{self, Span, BytePos, mk_sp};
 use errors::{self, DiagnosticBuilder};
 use parse::{self, classify, token};
 use parse::common::SeqSep;
@@ -1116,57 +1116,13 @@ impl<'a> Parser<'a> {
             self.check_keyword(keywords::Extern)
     }
 
-    pub fn get_lifetime(&mut self) -> ast::Ident {
+    fn get_label(&mut self) -> ast::Ident {
         match self.token {
             token::Lifetime(ref ident) => *ident,
             _ => self.bug("not a lifetime"),
         }
     }
 
-    pub fn parse_for_in_type(&mut self) -> PResult<'a, TyKind> {
-        /*
-        Parses whatever can come after a `for` keyword in a type.
-        The `for` hasn't been consumed.
-
-        - for <'lt> [unsafe] [extern "ABI"] fn (S) -> T
-        - for <'lt> path::foo(a, b) + Trait + 'a
-        */
-
-        let lo = self.span.lo;
-        let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
-
-        // examine next token to decide to do
-        if self.token_is_bare_fn_keyword() {
-            self.parse_ty_bare_fn(lifetime_defs)
-        } else {
-            let hi = self.span.hi;
-            let trait_ref = self.parse_trait_ref()?;
-            let poly_trait_ref = PolyTraitRef { bound_lifetimes: lifetime_defs,
-                                                trait_ref: trait_ref,
-                                                span: mk_sp(lo, hi)};
-            let other_bounds = if self.eat(&token::BinOp(token::Plus)) {
-                self.parse_ty_param_bounds()?
-            } else {
-                Vec::new()
-            };
-            let all_bounds =
-                Some(TraitTyParamBound(poly_trait_ref, TraitBoundModifier::None)).into_iter()
-                .chain(other_bounds)
-                .collect();
-            Ok(ast::TyKind::TraitObject(all_bounds))
-        }
-    }
-
-    pub fn parse_impl_trait_type(&mut self) -> PResult<'a, TyKind> {
-        // Parses whatever can come after a `impl` keyword in a type.
-        // The `impl` has already been consumed.
-        Ok(ast::TyKind::ImplTrait(self.parse_ty_param_bounds()?))
-    }
-
-    pub fn parse_ty_path(&mut self) -> PResult<'a, TyKind> {
-        Ok(TyKind::Path(None, self.parse_path(PathStyle::Type)?))
-    }
-
     /// parse a TyKind::BareFn type:
     pub fn parse_ty_bare_fn(&mut self, lifetime_defs: Vec<LifetimeDef>)
                             -> PResult<'a, TyKind> {
@@ -1347,84 +1303,9 @@ impl<'a> Parser<'a> {
         }
     }
 
-    /// Parse a type.
+    // Parse a type
     pub fn parse_ty(&mut self) -> PResult<'a, P<Ty>> {
-        let lo = self.span.lo;
-        let lhs = self.parse_ty_no_plus()?;
-
-        if !self.eat(&token::BinOp(token::Plus)) {
-            return Ok(lhs);
-        }
-
-        let mut bounds = self.parse_ty_param_bounds()?;
-
-        // In type grammar, `+` is treated like a binary operator,
-        // and hence both L and R side are required.
-        if bounds.is_empty() {
-            let prev_span = self.prev_span;
-            self.span_err(prev_span,
-                          "at least one type parameter bound \
-                          must be specified");
-        }
-
-        let mut lhs = lhs.unwrap();
-        if let TyKind::Paren(ty) = lhs.node {
-            // We have to accept the first bound in parens for backward compatibility.
-            // Example: `(Bound) + Bound + Bound`
-            lhs = ty.unwrap();
-        }
-        if let TyKind::Path(None, path) = lhs.node {
-            let poly_trait_ref = PolyTraitRef {
-                bound_lifetimes: Vec::new(),
-                trait_ref: TraitRef { path: path, ref_id: lhs.id },
-                span: lhs.span,
-            };
-            let poly_trait_ref = TraitTyParamBound(poly_trait_ref, TraitBoundModifier::None);
-            bounds.insert(0, poly_trait_ref);
-        } else {
-            let mut err = struct_span_err!(self.sess.span_diagnostic, lhs.span, E0178,
-                                            "expected a path on the left-hand side \
-                                            of `+`, not `{}`",
-                                            pprust::ty_to_string(&lhs));
-            err.span_label(lhs.span, &format!("expected a path"));
-            let hi = bounds.iter().map(|x| match *x {
-                TraitTyParamBound(ref tr, _) => tr.span.hi,
-                RegionTyParamBound(ref r) => r.span.hi,
-            }).max_by_key(|x| x.to_usize());
-            let full_span = hi.map(|hi| Span {
-                lo: lhs.span.lo,
-                hi: hi,
-                expn_id: lhs.span.expn_id,
-            });
-            match (&lhs.node, full_span) {
-                (&TyKind::Rptr(ref lifetime, ref mut_ty), Some(full_span)) => {
-                    let ty_str = pprust::to_string(|s| {
-                        use print::pp::word;
-                        use print::pprust::PrintState;
-
-                        word(&mut s.s, "&")?;
-                        s.print_opt_lifetime(lifetime)?;
-                        s.print_mutability(mut_ty.mutbl)?;
-                        s.popen()?;
-                        s.print_type(&mut_ty.ty)?;
-                        s.print_bounds(" +", &bounds)?;
-                        s.pclose()
-                    });
-                    err.span_suggestion(full_span, "try adding parentheses (per RFC 438):",
-                                        ty_str);
-                }
-
-                _ => {
-                    help!(&mut err,
-                                "perhaps you forgot parentheses? (per RFC 438)");
-                }
-            }
-            err.emit();
-        }
-
-        let sp = mk_sp(lo, self.prev_span.hi);
-        let sum = TyKind::TraitObject(bounds);
-        Ok(P(Ty {id: ast::DUMMY_NODE_ID, node: sum, span: sp}))
+        self.parse_ty_common(true)
     }
 
     /// Parse a type in restricted contexts where `+` is not permitted.
@@ -1432,15 +1313,17 @@ impl<'a> Parser<'a> {
     ///     `+` is prohibited to maintain operator priority (P(+) < P(&)).
     /// Example 2: `value1 as TYPE + value2`
     ///     `+` is prohibited to avoid interactions with expression grammar.
-    pub fn parse_ty_no_plus(&mut self) -> PResult<'a, P<Ty>> {
+    fn parse_ty_no_plus(&mut self) -> PResult<'a, P<Ty>> {
+        self.parse_ty_common(false)
+    }
+
+    fn parse_ty_common(&mut self, allow_plus: bool) -> PResult<'a, P<Ty>> {
         maybe_whole!(self, NtTy, |x| x);
 
         let lo = self.span.lo;
-
-        let t = if self.eat(&token::OpenDelim(token::Paren)) {
-            // (t) is a parenthesized ty
-            // (t,) is the type of a tuple with only one field,
-            // of type t
+        let node = if self.eat(&token::OpenDelim(token::Paren)) {
+            // `(TYPE)` is a parenthesized type.
+            // `(TYPE,)` is a tuple with a single field of type TYPE.
             let mut ts = vec![];
             let mut last_comma = false;
             while self.token != token::CloseDelim(token::Paren) {
@@ -1452,81 +1335,162 @@ impl<'a> Parser<'a> {
                     break;
                 }
             }
-
             self.expect(&token::CloseDelim(token::Paren))?;
+
             if ts.len() == 1 && !last_comma {
-                TyKind::Paren(ts.into_iter().nth(0).unwrap())
+                let ty = ts.into_iter().nth(0).unwrap().unwrap();
+                match ty.node {
+                    // Accept `(Trait1) + Trait2 + 'a` for backward compatibility (#39318).
+                    TyKind::Path(None, ref path)
+                            if allow_plus && self.token == token::BinOp(token::Plus) => {
+                        self.bump(); // `+`
+                        let pt = PolyTraitRef::new(Vec::new(), path.clone(), lo, self.prev_span.hi);
+                        let mut bounds = vec![TraitTyParamBound(pt, TraitBoundModifier::None)];
+                        bounds.append(&mut self.parse_ty_param_bounds()?);
+                        TyKind::TraitObject(bounds)
+                    }
+                    _ => TyKind::Paren(P(ty))
+                }
             } else {
                 TyKind::Tup(ts)
             }
         } else if self.eat(&token::Not) {
+            // Never type `!`
             TyKind::Never
         } else if self.eat(&token::BinOp(token::Star)) {
-            // STAR POINTER (bare pointer?)
+            // Raw pointer
             TyKind::Ptr(self.parse_ptr()?)
         } else if self.eat(&token::OpenDelim(token::Bracket)) {
-            // VECTOR
+            // Array or slice
             let t = self.parse_ty()?;
-
-            // Parse the `; e` in `[ i32; e ]`
-            // where `e` is a const expression
+            // Parse optional `; EXPR` in `[TYPE; EXPR]`
             let t = match self.maybe_parse_fixed_length_of_vec()? {
                 None => TyKind::Slice(t),
-                Some(suffix) => TyKind::Array(t, suffix)
+                Some(suffix) => TyKind::Array(t, suffix),
             };
             self.expect(&token::CloseDelim(token::Bracket))?;
             t
-        } else if self.check(&token::BinOp(token::And)) ||
-                  self.check(&token::AndAnd) {
-            // BORROWED POINTER
+        } else if self.check(&token::BinOp(token::And)) || self.check(&token::AndAnd) {
+            // Reference
             self.expect_and()?;
             self.parse_borrowed_pointee()?
-        } else if self.check_keyword(keywords::For) {
-            // FIXME `+` has incorrect priority in trait object types starting with `for` (#39317).
-            self.parse_for_in_type()?
-        } else if self.eat_keyword(keywords::Impl) {
-            // FIXME figure out priority of `+` in `impl Trait1 + Trait2` (#34511).
-            self.parse_impl_trait_type()?
-        } else if self.token_is_bare_fn_keyword() {
-            // BARE FUNCTION
-            self.parse_ty_bare_fn(Vec::new())?
         } else if self.eat_keyword_noexpect(keywords::Typeof) {
-            // TYPEOF
+            // `typeof(EXPR)`
             // In order to not be ambiguous, the type must be surrounded by parens.
             self.expect(&token::OpenDelim(token::Paren))?;
             let e = self.parse_expr()?;
             self.expect(&token::CloseDelim(token::Paren))?;
             TyKind::Typeof(e)
+        } else if self.eat(&token::Underscore) {
+            // A type to be inferred `_`
+            TyKind::Infer
         } else if self.eat_lt() {
+            // Qualified path
             let (qself, path) = self.parse_qualified_path(PathStyle::Type)?;
             TyKind::Path(Some(qself), path)
         } else if self.token.is_path_start() {
+            // Simple path
             let path = self.parse_path(PathStyle::Type)?;
             if self.eat(&token::Not) {
-                // MACRO INVOCATION
+                // Macro invocation in type position
                 let (_, tts) = self.expect_delimited_token_tree()?;
-                let hi = self.span.hi;
-                TyKind::Mac(spanned(lo, hi, Mac_ { path: path, tts: tts }))
+                TyKind::Mac(spanned(lo, self.span.hi, Mac_ { path: path, tts: tts }))
             } else {
-                // NAMED TYPE
-                TyKind::Path(None, path)
+                // Just a type path or bound list (trait object type) starting with a trait.
+                //   `Type`
+                //   `Trait1 + Trait2 + 'a`
+                if allow_plus && self.eat(&token::BinOp(token::Plus)) {
+                    let poly_trait = PolyTraitRef::new(Vec::new(), path, lo, self.prev_span.hi);
+                    let mut bounds = vec![TraitTyParamBound(poly_trait, TraitBoundModifier::None)];
+                    bounds.append(&mut self.parse_ty_param_bounds()?);
+                    TyKind::TraitObject(bounds)
+                } else {
+                    TyKind::Path(None, path)
+                }
             }
-        } else if self.eat(&token::Underscore) {
-            // TYPE TO BE INFERRED
-            TyKind::Infer
+        } else if self.token_is_bare_fn_keyword() {
+            // Function pointer type
+            self.parse_ty_bare_fn(Vec::new())?
+        } else if self.check_keyword(keywords::For) {
+            // Function pointer type or bound list (trait object type) starting with a poly-trait.
+            //   `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T`
+            //   `for<'lt> Trait1<'lt> + Trait2 + 'a`
+            let lo = self.span.lo;
+            let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
+            if self.token_is_bare_fn_keyword() {
+                self.parse_ty_bare_fn(lifetime_defs)?
+            } else {
+                let path = self.parse_path(PathStyle::Type)?;
+                let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo, self.prev_span.hi);
+                let mut bounds = vec![TraitTyParamBound(poly_trait, TraitBoundModifier::None)];
+                if allow_plus && self.eat(&token::BinOp(token::Plus)) {
+                    bounds.append(&mut self.parse_ty_param_bounds()?)
+                }
+                TyKind::TraitObject(bounds)
+            }
+        } else if self.eat_keyword(keywords::Impl) {
+            // FIXME: figure out priority of `+` in `impl Trait1 + Trait2` (#34511).
+            TyKind::ImplTrait(self.parse_ty_param_bounds()?)
+        } else if self.check(&token::Question) {
+            // Bound list (trait object type)
+            // Bound lists starting with `'lt` are not currently supported (#40043)
+            TyKind::TraitObject(self.parse_ty_param_bounds_common(allow_plus)?)
         } else {
             let msg = format!("expected type, found {}", self.this_token_descr());
             return Err(self.fatal(&msg));
         };
 
-        let sp = mk_sp(lo, self.prev_span.hi);
-        Ok(P(Ty {id: ast::DUMMY_NODE_ID, node: t, span: sp}))
+        let span = mk_sp(lo, self.prev_span.hi);
+        let ty = Ty { node: node, span: span, id: ast::DUMMY_NODE_ID };
+
+        // Try to recover from use of `+` with incorrect priority.
+        self.maybe_recover_from_bad_type_plus(allow_plus, &ty)?;
+
+        Ok(P(ty))
     }
 
-    pub fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> {
-        // look for `&'lt` or `&'foo ` and interpret `foo` as the region name:
-        let opt_lifetime = self.eat_lifetime();
-        let mutbl = self.parse_mutability()?;
+    fn maybe_recover_from_bad_type_plus(&mut self, allow_plus: bool, ty: &Ty) -> PResult<'a, ()> {
+        // Do not add `+` to expected tokens.
+        if !allow_plus || self.token != token::BinOp(token::Plus) {
+            return Ok(())
+        }
+
+        self.bump(); // `+`
+        let bounds = self.parse_ty_param_bounds()?;
+        let sum_span = mk_sp(ty.span.lo, self.prev_span.hi);
+
+        let mut err = struct_span_err!(self.sess.span_diagnostic, ty.span, E0178,
+            "expected a path on the left-hand side of `+`, not `{}`", pprust::ty_to_string(&ty));
+        err.span_label(ty.span, &format!("expected a path"));
+
+        match ty.node {
+            TyKind::Rptr(ref lifetime, ref mut_ty) => {
+                let sum_with_parens = pprust::to_string(|s| {
+                    use print::pp::word;
+                    use print::pprust::PrintState;
+
+                    word(&mut s.s, "&")?;
+                    s.print_opt_lifetime(lifetime)?;
+                    s.print_mutability(mut_ty.mutbl)?;
+                    s.popen()?;
+                    s.print_type(&mut_ty.ty)?;
+                    s.print_bounds(" +", &bounds)?;
+                    s.pclose()
+                });
+                err.span_suggestion(sum_span, "try adding parentheses:", sum_with_parens);
+            }
+            TyKind::Ptr(..) | TyKind::BareFn(..) => {
+                help!(&mut err, "perhaps you forgot parentheses?");
+            }
+            _ => {}
+        }
+        err.emit();
+        Ok(())
+    }
+
+    fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> {
+        let opt_lifetime = if self.check_lifetime() { Some(self.expect_lifetime()) } else { None };
+        let mutbl = self.parse_mutability();
         let ty = self.parse_ty_no_plus()?;
         return Ok(TyKind::Rptr(opt_lifetime, MutTy { ty: ty, mutbl: mutbl }));
     }
@@ -1927,30 +1891,28 @@ impl<'a> Parser<'a> {
         }
     }
 
-    /// Parse single lifetime 'a or nothing.
-    pub fn eat_lifetime(&mut self) -> Option<Lifetime> {
+    fn check_lifetime(&mut self) -> bool {
+        self.expected_tokens.push(TokenType::Lifetime);
+        self.token.is_lifetime()
+    }
+
+    /// Parse single lifetime 'a or panic.
+    fn expect_lifetime(&mut self) -> Lifetime {
         match self.token {
             token::Lifetime(ident) => {
                 self.bump();
-                Some(Lifetime {
-                    id: ast::DUMMY_NODE_ID,
-                    span: self.prev_span,
-                    name: ident.name
-                })
-            }
-            _ => {
-                self.expected_tokens.push(TokenType::Lifetime);
-                None
+                Lifetime { name: ident.name, span: self.prev_span, id: ast::DUMMY_NODE_ID }
             }
+            _ => self.span_bug(self.span, "not a lifetime")
         }
     }
 
     /// Parse mutability (`mut` or nothing).
-    pub fn parse_mutability(&mut self) -> PResult<'a, Mutability> {
+    fn parse_mutability(&mut self) -> Mutability {
         if self.eat_keyword(keywords::Mut) {
-            Ok(Mutability::Mutable)
+            Mutability::Mutable
         } else {
-            Ok(Mutability::Immutable)
+            Mutability::Immutable
         }
     }
 
@@ -2207,7 +2169,7 @@ impl<'a> Parser<'a> {
                     return self.parse_while_expr(None, lo, attrs);
                 }
                 if self.token.is_lifetime() {
-                    let label = Spanned { node: self.get_lifetime(),
+                    let label = Spanned { node: self.get_label(),
                                           span: self.span };
                     let lo = self.span.lo;
                     self.bump();
@@ -2230,7 +2192,7 @@ impl<'a> Parser<'a> {
                 if self.eat_keyword(keywords::Continue) {
                     let ex = if self.token.is_lifetime() {
                         let ex = ExprKind::Continue(Some(Spanned{
-                            node: self.get_lifetime(),
+                            node: self.get_label(),
                             span: self.span
                         }));
                         self.bump();
@@ -2267,7 +2229,7 @@ impl<'a> Parser<'a> {
                 } else if self.eat_keyword(keywords::Break) {
                     let lt = if self.token.is_lifetime() {
                         let spanned_lt = Spanned {
-                            node: self.get_lifetime(),
+                            node: self.get_label(),
                             span: self.span
                         };
                         self.bump();
@@ -2700,7 +2662,7 @@ impl<'a> Parser<'a> {
             }
             token::BinOp(token::And) | token::AndAnd => {
                 self.expect_and()?;
-                let m = self.parse_mutability()?;
+                let m = self.parse_mutability();
                 let e = self.parse_prefix_expr(None);
                 let (span, e) = self.interpolated_or_expr_span(e)?;
                 hi = span.hi;
@@ -3422,7 +3384,7 @@ impl<'a> Parser<'a> {
             token::BinOp(token::And) | token::AndAnd => {
                 // Parse &pat / &mut pat
                 self.expect_and()?;
-                let mutbl = self.parse_mutability()?;
+                let mutbl = self.parse_mutability();
                 if let token::Lifetime(ident) = self.token {
                     return Err(self.fatal(&format!("unexpected lifetime `{}` in pattern", ident)));
                 }
@@ -3449,7 +3411,7 @@ impl<'a> Parser<'a> {
                 pat = self.parse_pat_ident(BindingMode::ByValue(Mutability::Mutable))?;
             } else if self.eat_keyword(keywords::Ref) {
                 // Parse ref ident @ pat / ref mut ident @ pat
-                let mutbl = self.parse_mutability()?;
+                let mutbl = self.parse_mutability();
                 pat = self.parse_pat_ident(BindingMode::ByRef(mutbl))?;
             } else if self.eat_keyword(keywords::Box) {
                 // Parse box pat
@@ -4069,30 +4031,32 @@ impl<'a> Parser<'a> {
     // BOUND = TY_BOUND | LT_BOUND
     // LT_BOUND = LIFETIME (e.g. `'a`)
     // TY_BOUND = [?] [for<LT_PARAM_DEFS>] SIMPLE_PATH (e.g. `?for<'a: 'b> m::Trait<'a>`)
-    fn parse_ty_param_bounds(&mut self) -> PResult<'a, TyParamBounds>
-    {
+    fn parse_ty_param_bounds_common(&mut self, allow_plus: bool) -> PResult<'a, TyParamBounds> {
         let mut bounds = Vec::new();
         loop {
             let question = if self.eat(&token::Question) { Some(self.prev_span) } else { None };
-            if let Some(lifetime) = self.eat_lifetime() {
+            if self.check_lifetime() {
                 if let Some(question_span) = question {
                     self.span_err(question_span,
                                   "`?` may only modify trait bounds, not lifetime bounds");
                 }
-                bounds.push(RegionTyParamBound(lifetime));
-            } else {if self.check_keyword(keywords::For) || self.check_path() {
-                let poly_trait_ref = self.parse_poly_trait_ref()?;
+                bounds.push(RegionTyParamBound(self.expect_lifetime()));
+            } else if self.check_keyword(keywords::For) || self.check_path() {
+                let lo = self.span.lo;
+                let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
+                let path = self.parse_path(PathStyle::Type)?;
+                let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo, self.prev_span.hi);
                 let modifier = if question.is_some() {
                     TraitBoundModifier::Maybe
                 } else {
                     TraitBoundModifier::None
                 };
-                bounds.push(TraitTyParamBound(poly_trait_ref, modifier));
+                bounds.push(TraitTyParamBound(poly_trait, modifier));
             } else {
                 break
-            }}
+            }
 
-            if !self.eat(&token::BinOp(token::Plus)) {
+            if !allow_plus || !self.eat(&token::BinOp(token::Plus)) {
                 break
             }
         }
@@ -4100,12 +4064,16 @@ impl<'a> Parser<'a> {
         return Ok(bounds);
     }
 
+    fn parse_ty_param_bounds(&mut self) -> PResult<'a, TyParamBounds> {
+        self.parse_ty_param_bounds_common(true)
+    }
+
     // Parse bounds of a type parameter `BOUND + BOUND + BOUND` without trailing `+`.
     // BOUND = LT_BOUND (e.g. `'a`)
     fn parse_lt_param_bounds(&mut self) -> Vec<Lifetime> {
         let mut lifetimes = Vec::new();
-        while let Some(lifetime) = self.eat_lifetime() {
-            lifetimes.push(lifetime);
+        while self.check_lifetime() {
+            lifetimes.push(self.expect_lifetime());
 
             if !self.eat(&token::BinOp(token::Plus)) {
                 break
@@ -4150,7 +4118,8 @@ impl<'a> Parser<'a> {
         let mut seen_ty_param = false;
         loop {
             let attrs = self.parse_outer_attributes()?;
-            if let Some(lifetime) = self.eat_lifetime() {
+            if self.check_lifetime() {
+                let lifetime = self.expect_lifetime();
                 // Parse lifetime parameter.
                 let bounds = if self.eat(&token::Colon) {
                     self.parse_lt_param_bounds()
@@ -4166,7 +4135,7 @@ impl<'a> Parser<'a> {
                     self.span_err(self.prev_span,
                         "lifetime parameters must be declared prior to type parameters");
                 }
-            } else {if self.check_ident() {
+            } else if self.check_ident() {
                 // Parse type parameter.
                 ty_params.push(self.parse_ty_param(attrs)?);
                 seen_ty_param = true;
@@ -4178,7 +4147,7 @@ impl<'a> Parser<'a> {
                         &format!("trailing attribute after {} parameters", param_kind));
                 }
                 break
-            }}
+            }
 
             if !self.eat(&token::Comma) {
                 break
@@ -4224,14 +4193,14 @@ impl<'a> Parser<'a> {
         let mut seen_type = false;
         let mut seen_binding = false;
         loop {
-            if let Some(lifetime) = self.eat_lifetime() {
+            if self.check_lifetime() && self.look_ahead(1, |t| t != &token::BinOp(token::Plus)) {
                 // Parse lifetime argument.
-                lifetimes.push(lifetime);
+                lifetimes.push(self.expect_lifetime());
                 if seen_type || seen_binding {
                     self.span_err(self.prev_span,
                         "lifetime parameters must be declared prior to type parameters");
                 }
-            } else {if self.check_ident() && self.look_ahead(1, |t| t == &token::Eq) {
+            } else if self.check_ident() && self.look_ahead(1, |t| t == &token::Eq) {
                 // Parse associated type binding.
                 let lo = self.span.lo;
                 let ident = self.parse_ident()?;
@@ -4254,7 +4223,7 @@ impl<'a> Parser<'a> {
                 seen_type = true;
             } else {
                 break
-            }}
+            }
 
             if !self.eat(&token::Comma) {
                 break
@@ -4299,7 +4268,8 @@ impl<'a> Parser<'a> {
 
         loop {
             let lo = self.span.lo;
-            if let Some(lifetime) = self.eat_lifetime() {
+            if self.check_lifetime() && self.look_ahead(1, |t| t != &token::BinOp(token::Plus)) {
+                let lifetime = self.expect_lifetime();
                 // Bounds starting with a colon are mandatory, but possibly empty.
                 self.expect(&token::Colon)?;
                 let bounds = self.parse_lt_param_bounds();
@@ -4310,7 +4280,7 @@ impl<'a> Parser<'a> {
                         bounds: bounds,
                     }
                 ));
-            } else {if self.check_type() {
+            } else if self.check_type() {
                 // Parse optional `for<'a, 'b>`.
                 // This `for` is parsed greedily and applies to the whole predicate,
                 // the bounded type can have its own `for` applying only to it.
@@ -4348,7 +4318,7 @@ impl<'a> Parser<'a> {
                 }
             } else {
                 break
-            }}
+            }
 
             if !self.eat(&token::Comma) {
                 break
@@ -4453,13 +4423,13 @@ impl<'a> Parser<'a> {
                 } else if self.look_ahead(1, |t| t.is_lifetime()) &&
                           isolated_self(self, 2) {
                     self.bump();
-                    let lt = self.eat_lifetime().expect("not a lifetime");
+                    let lt = self.expect_lifetime();
                     (SelfKind::Region(Some(lt), Mutability::Immutable), expect_ident(self))
                 } else if self.look_ahead(1, |t| t.is_lifetime()) &&
                           self.look_ahead(2, |t| t.is_keyword(keywords::Mut)) &&
                           isolated_self(self, 3) {
                     self.bump();
-                    let lt = self.eat_lifetime().expect("not a lifetime");
+                    let lt = self.expect_lifetime();
                     self.bump();
                     (SelfKind::Region(Some(lt), Mutability::Mutable), expect_ident(self))
                 } else {
@@ -4852,14 +4822,6 @@ impl<'a> Parser<'a> {
         }
     }
 
-    /// Parse a::B<String,i32>
-    fn parse_trait_ref(&mut self) -> PResult<'a, TraitRef> {
-        Ok(TraitRef {
-            path: self.parse_path(PathStyle::Type)?,
-            ref_id: ast::DUMMY_NODE_ID,
-        })
-    }
-
     fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, Vec<LifetimeDef>> {
         if self.eat_keyword(keywords::For) {
             self.expect_lt()?;
@@ -4875,18 +4837,6 @@ impl<'a> Parser<'a> {
         }
     }
 
-    /// Parse for<'l> a::B<String,i32>
-    fn parse_poly_trait_ref(&mut self) -> PResult<'a, PolyTraitRef> {
-        let lo = self.span.lo;
-        let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
-
-        Ok(PolyTraitRef {
-            bound_lifetimes: lifetime_defs,
-            trait_ref: self.parse_trait_ref()?,
-            span: mk_sp(lo, self.prev_span.hi),
-        })
-    }
-
     /// Parse struct Foo { ... }
     fn parse_item_struct(&mut self) -> PResult<'a, ItemInfo> {
         let class_name = self.parse_ident()?;
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index 75852629ce1..519d5bd98e4 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -103,6 +103,21 @@ fn ident_can_begin_expr(ident: ast::Ident) -> bool {
     ].contains(&ident.name)
 }
 
+fn ident_can_begin_type(ident: ast::Ident) -> bool {
+    let ident_token: Token = Ident(ident);
+
+    !ident_token.is_any_keyword() ||
+    ident_token.is_path_segment_keyword() ||
+    [
+        keywords::For.name(),
+        keywords::Impl.name(),
+        keywords::Fn.name(),
+        keywords::Unsafe.name(),
+        keywords::Extern.name(),
+        keywords::Typeof.name(),
+    ].contains(&ident.name)
+}
+
 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug)]
 pub enum Token {
     /* Expression-operator symbols. */
@@ -182,23 +197,21 @@ impl Token {
     /// Returns `true` if the token can appear at the start of an expression.
     pub fn can_begin_expr(&self) -> bool {
         match *self {
-            OpenDelim(..)               => true,
-            Ident(ident)                => ident_can_begin_expr(ident),
-            Literal(..)                 => true,
-            Not                         => true,
-            BinOp(Minus)                => true,
-            BinOp(Star)                 => true,
-            BinOp(And)                  => true,
-            BinOp(Or)                   => true, // in lambda syntax
-            OrOr                        => true, // in lambda syntax
-            AndAnd                      => true, // double borrow
+            Ident(ident)                => ident_can_begin_expr(ident), // value name or keyword
+            OpenDelim(..)               => true, // tuple, array or block
+            Literal(..)                 => true, // literal
+            Not                         => true, // operator not
+            BinOp(Minus)                => true, // unary minus
+            BinOp(Star)                 => true, // dereference
+            BinOp(Or) | OrOr            => true, // closure
+            BinOp(And)                  => true, // reference
+            AndAnd                      => true, // double reference
             DotDot | DotDotDot          => true, // range notation
             Lt | BinOp(Shl)             => true, // associated path
-            ModSep                      => true,
-            Pound                       => true, // for expression attributes
+            ModSep                      => true, // global path
+            Pound                       => true, // expression attributes
             Interpolated(ref nt) => match **nt {
                 NtExpr(..) => true,
-                NtIdent(..) => true,
                 NtBlock(..) => true,
                 NtPath(..) => true,
                 _ => false,
@@ -210,19 +223,20 @@ impl Token {
     /// Returns `true` if the token can appear at the start of a type.
     pub fn can_begin_type(&self) -> bool {
         match *self {
+            Ident(ident)                => ident_can_begin_type(ident), // type name or keyword
             OpenDelim(Paren)            => true, // tuple
             OpenDelim(Bracket)          => true, // array
-            Ident(..)                   => true, // type name or keyword
             Underscore                  => true, // placeholder
             Not                         => true, // never
             BinOp(Star)                 => true, // raw pointer
             BinOp(And)                  => true, // reference
             AndAnd                      => true, // double reference
+            Question                    => true, // maybe bound in trait object
+            Lifetime(..)                => true, // lifetime bound in trait object
             Lt | BinOp(Shl)             => true, // associated path
             ModSep                      => true, // global path
             Interpolated(ref nt) => match **nt {
                 NtTy(..) => true,
-                NtIdent(..) => true,
                 NtPath(..) => true,
                 _ => false,
             },