about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>2017-04-05 01:12:53 +0300
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>2017-04-21 21:32:44 +0300
commite038f58105cc1769a9c7991981822d01ebffe277 (patch)
tree1aa398de845539946c8c16f7fd3f622aa7e49111 /src
parent5695c3e9435a5872e23a6bb61c1ea0b3ada83eef (diff)
downloadrust-e038f58105cc1769a9c7991981822d01ebffe277.tar.gz
rust-e038f58105cc1769a9c7991981822d01ebffe277.zip
syntax: Support parentheses around trait bounds
Diffstat (limited to 'src')
-rw-r--r--src/libsyntax/parse/parser.rs29
-rw-r--r--src/test/parse-fail/trait-object-bad-parens.rs20
-rw-r--r--src/test/parse-fail/trait-object-lifetime-parens.rs18
-rw-r--r--src/test/parse-fail/trait-object-trait-parens.rs21
4 files changed, 84 insertions, 4 deletions
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 0cdb09a842f..1232411d1ec 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -152,6 +152,7 @@ fn maybe_append(mut lhs: Vec<Attribute>, rhs: Option<Vec<Attribute>>)
 enum PrevTokenKind {
     DocComment,
     Comma,
+    Plus,
     Interpolated,
     Eof,
     Other,
@@ -1061,6 +1062,7 @@ impl<'a> Parser<'a> {
         self.prev_token_kind = match self.token {
             token::DocComment(..) => PrevTokenKind::DocComment,
             token::Comma => PrevTokenKind::Comma,
+            token::BinOp(token::Plus) => PrevTokenKind::Plus,
             token::Interpolated(..) => PrevTokenKind::Interpolated,
             token::Eof => PrevTokenKind::Eof,
             _ => PrevTokenKind::Other,
@@ -1354,20 +1356,29 @@ impl<'a> Parser<'a> {
                     break;
                 }
             }
+            let trailing_plus = self.prev_token_kind == PrevTokenKind::Plus;
             self.expect(&token::CloseDelim(token::Paren))?;
 
             if ts.len() == 1 && !last_comma {
                 let ty = ts.into_iter().nth(0).unwrap().unwrap();
+                let maybe_bounds = allow_plus && self.token == token::BinOp(token::Plus);
                 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) => {
+                    // `(TY_BOUND_NOPAREN) + BOUND + ...`.
+                    TyKind::Path(None, ref path) if maybe_bounds => {
                         self.bump(); // `+`
                         let pt = PolyTraitRef::new(Vec::new(), path.clone(), lo.to(self.prev_span));
                         let mut bounds = vec![TraitTyParamBound(pt, TraitBoundModifier::None)];
                         bounds.append(&mut self.parse_ty_param_bounds()?);
                         TyKind::TraitObject(bounds)
                     }
+                    TyKind::TraitObject(ref bounds)
+                            if maybe_bounds && bounds.len() == 1 && !trailing_plus => {
+                        self.bump(); // `+`
+                        let mut bounds = bounds.clone();
+                        bounds.append(&mut self.parse_ty_param_bounds()?);
+                        TyKind::TraitObject(bounds)
+                    }
+                    // `(TYPE)`
                     _ => TyKind::Paren(P(ty))
                 }
             } else {
@@ -4070,10 +4081,12 @@ impl<'a> Parser<'a> {
     // Parse bounds of a type parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`.
     // 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>`)
+    // TY_BOUND = TY_BOUND_NOPAREN | (TY_BOUND_NOPAREN)
+    // TY_BOUND_NOPAREN = [?] [for<LT_PARAM_DEFS>] SIMPLE_PATH (e.g. `?for<'a: 'b> m::Trait<'a>`)
     fn parse_ty_param_bounds_common(&mut self, allow_plus: bool) -> PResult<'a, TyParamBounds> {
         let mut bounds = Vec::new();
         loop {
+            let has_parens = self.eat(&token::OpenDelim(token::Paren));
             let question = if self.eat(&token::Question) { Some(self.prev_span) } else { None };
             if self.check_lifetime() {
                 if let Some(question_span) = question {
@@ -4081,6 +4094,11 @@ impl<'a> Parser<'a> {
                                   "`?` may only modify trait bounds, not lifetime bounds");
                 }
                 bounds.push(RegionTyParamBound(self.expect_lifetime()));
+                if has_parens {
+                    self.expect(&token::CloseDelim(token::Paren))?;
+                    self.span_err(self.prev_span,
+                                  "parenthesized lifetime bounds are not supported");
+                }
             } else if self.check_keyword(keywords::For) || self.check_path() {
                 let lo = self.span;
                 let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
@@ -4092,6 +4110,9 @@ impl<'a> Parser<'a> {
                     TraitBoundModifier::None
                 };
                 bounds.push(TraitTyParamBound(poly_trait, modifier));
+                if has_parens {
+                    self.expect(&token::CloseDelim(token::Paren))?;
+                }
             } else {
                 break
             }
diff --git a/src/test/parse-fail/trait-object-bad-parens.rs b/src/test/parse-fail/trait-object-bad-parens.rs
new file mode 100644
index 00000000000..a44c0c3f32f
--- /dev/null
+++ b/src/test/parse-fail/trait-object-bad-parens.rs
@@ -0,0 +1,20 @@
+// Copyright 2017 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -Z parse-only -Z continue-parse-after-error
+
+fn main() {
+    let _: Box<((Copy)) + Copy>;
+    //~^ ERROR expected a path on the left-hand side of `+`, not `((Copy))`
+    let _: Box<(Copy + Copy) + Copy>;
+    //~^ ERROR expected a path on the left-hand side of `+`, not `( Copy + Copy)`
+    let _: Box<(Copy +) + Copy>;
+    //~^ ERROR expected a path on the left-hand side of `+`, not `( Copy)`
+}
diff --git a/src/test/parse-fail/trait-object-lifetime-parens.rs b/src/test/parse-fail/trait-object-lifetime-parens.rs
new file mode 100644
index 00000000000..6be62d966eb
--- /dev/null
+++ b/src/test/parse-fail/trait-object-lifetime-parens.rs
@@ -0,0 +1,18 @@
+// Copyright 2017 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -Z parse-only -Z continue-parse-after-error
+
+fn f<T: Copy + ('a)>() {} //~ ERROR parenthesized lifetime bounds are not supported
+
+fn main() {
+    let _: Box<Copy + ('a)>; //~ ERROR parenthesized lifetime bounds are not supported
+    let _: Box<('a) + Copy>; //~ ERROR expected type, found `'a`
+}
diff --git a/src/test/parse-fail/trait-object-trait-parens.rs b/src/test/parse-fail/trait-object-trait-parens.rs
new file mode 100644
index 00000000000..dc44f4f3fb1
--- /dev/null
+++ b/src/test/parse-fail/trait-object-trait-parens.rs
@@ -0,0 +1,21 @@
+// Copyright 2017 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -Z parse-only
+
+fn f<T: (Copy) + (?Sized) + (for<'a> Trait<'a>)>() {}
+
+fn main() {
+    let _: Box<(Copy) + (?Sized) + (for<'a> Trait<'a>)>;
+    let _: Box<(?Sized) + (for<'a> Trait<'a>) + (Copy)>;
+    let _: Box<(for<'a> Trait<'a>) + (Copy) + (?Sized)>;
+}
+
+FAIL //~ ERROR