Skip to content

Commit 5e1351b

Browse files
authored
Rollup merge of rust-lang#41077 - petrochenkov:boundparen, r=nikomatsakis
syntax: Support parentheses around trait bounds An implementation for rust-lang#39318 (comment) r? @nikomatsakis
2 parents ff13b7c + 8838cd1 commit 5e1351b

File tree

6 files changed

+139
-39
lines changed

6 files changed

+139
-39
lines changed

src/doc/grammar.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -781,10 +781,11 @@ never_type : "!" ;
781781
### Type parameter bounds
782782

783783
```antlr
784+
bound-list := bound | bound '+' bound-list '+' ?
784785
bound := ty_bound | lt_bound
785786
lt_bound := lifetime
786-
ty_bound := [?] [ for<lt_param_defs> ] simple_path
787-
bound-list := bound | bound '+' bound-list '+' ?
787+
ty_bound := ty_bound_noparen | (ty_bound_noparen)
788+
ty_bound_noparen := [?] [ for<lt_param_defs> ] simple_path
788789
```
789790

790791
### Self types

src/libsyntax/parse/parser.rs

Lines changed: 64 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ fn maybe_append(mut lhs: Vec<Attribute>, rhs: Option<Vec<Attribute>>)
152152
enum PrevTokenKind {
153153
DocComment,
154154
Comma,
155+
Plus,
155156
Interpolated,
156157
Eof,
157158
Other,
@@ -1061,6 +1062,7 @@ impl<'a> Parser<'a> {
10611062
self.prev_token_kind = match self.token {
10621063
token::DocComment(..) => PrevTokenKind::DocComment,
10631064
token::Comma => PrevTokenKind::Comma,
1065+
token::BinOp(token::Plus) => PrevTokenKind::Plus,
10641066
token::Interpolated(..) => PrevTokenKind::Interpolated,
10651067
token::Eof => PrevTokenKind::Eof,
10661068
_ => PrevTokenKind::Other,
@@ -1354,20 +1356,26 @@ impl<'a> Parser<'a> {
13541356
break;
13551357
}
13561358
}
1359+
let trailing_plus = self.prev_token_kind == PrevTokenKind::Plus;
13571360
self.expect(&token::CloseDelim(token::Paren))?;
13581361

13591362
if ts.len() == 1 && !last_comma {
13601363
let ty = ts.into_iter().nth(0).unwrap().unwrap();
1364+
let maybe_bounds = allow_plus && self.token == token::BinOp(token::Plus);
13611365
match ty.node {
1362-
// Accept `(Trait1) + Trait2 + 'a` for backward compatibility (#39318).
1363-
TyKind::Path(None, ref path)
1364-
if allow_plus && self.token == token::BinOp(token::Plus) => {
1365-
self.bump(); // `+`
1366-
let pt = PolyTraitRef::new(Vec::new(), path.clone(), lo.to(self.prev_span));
1367-
let mut bounds = vec![TraitTyParamBound(pt, TraitBoundModifier::None)];
1368-
bounds.append(&mut self.parse_ty_param_bounds()?);
1369-
TyKind::TraitObject(bounds)
1366+
// `(TY_BOUND_NOPAREN) + BOUND + ...`.
1367+
TyKind::Path(None, ref path) if maybe_bounds => {
1368+
self.parse_remaining_bounds(Vec::new(), path.clone(), lo, true)?
13701369
}
1370+
TyKind::TraitObject(ref bounds)
1371+
if maybe_bounds && bounds.len() == 1 && !trailing_plus => {
1372+
let path = match bounds[0] {
1373+
TraitTyParamBound(ref pt, ..) => pt.trait_ref.path.clone(),
1374+
_ => self.bug("unexpected lifetime bound"),
1375+
};
1376+
self.parse_remaining_bounds(Vec::new(), path, lo, true)?
1377+
}
1378+
// `(TYPE)`
13711379
_ => TyKind::Paren(P(ty))
13721380
}
13731381
} else {
@@ -1418,11 +1426,8 @@ impl<'a> Parser<'a> {
14181426
// Just a type path or bound list (trait object type) starting with a trait.
14191427
// `Type`
14201428
// `Trait1 + Trait2 + 'a`
1421-
if allow_plus && self.eat(&token::BinOp(token::Plus)) {
1422-
let poly_trait = PolyTraitRef::new(Vec::new(), path, lo.to(self.prev_span));
1423-
let mut bounds = vec![TraitTyParamBound(poly_trait, TraitBoundModifier::None)];
1424-
bounds.append(&mut self.parse_ty_param_bounds()?);
1425-
TyKind::TraitObject(bounds)
1429+
if allow_plus && self.check(&token::BinOp(token::Plus)) {
1430+
self.parse_remaining_bounds(Vec::new(), path, lo, true)?
14261431
} else {
14271432
TyKind::Path(None, path)
14281433
}
@@ -1440,12 +1445,8 @@ impl<'a> Parser<'a> {
14401445
self.parse_ty_bare_fn(lifetime_defs)?
14411446
} else {
14421447
let path = self.parse_path(PathStyle::Type)?;
1443-
let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_span));
1444-
let mut bounds = vec![TraitTyParamBound(poly_trait, TraitBoundModifier::None)];
1445-
if allow_plus && self.eat(&token::BinOp(token::Plus)) {
1446-
bounds.append(&mut self.parse_ty_param_bounds()?)
1447-
}
1448-
TyKind::TraitObject(bounds)
1448+
let parse_plus = allow_plus && self.check(&token::BinOp(token::Plus));
1449+
self.parse_remaining_bounds(lifetime_defs, path, lo, parse_plus)?
14491450
}
14501451
} else if self.eat_keyword(keywords::Impl) {
14511452
// FIXME: figure out priority of `+` in `impl Trait1 + Trait2` (#34511).
@@ -1468,6 +1469,17 @@ impl<'a> Parser<'a> {
14681469
Ok(P(ty))
14691470
}
14701471

1472+
fn parse_remaining_bounds(&mut self, lifetime_defs: Vec<LifetimeDef>, path: ast::Path,
1473+
lo: Span, parse_plus: bool) -> PResult<'a, TyKind> {
1474+
let poly_trait_ref = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_span));
1475+
let mut bounds = vec![TraitTyParamBound(poly_trait_ref, TraitBoundModifier::None)];
1476+
if parse_plus {
1477+
self.bump(); // `+`
1478+
bounds.append(&mut self.parse_ty_param_bounds()?);
1479+
}
1480+
Ok(TyKind::TraitObject(bounds))
1481+
}
1482+
14711483
fn maybe_recover_from_bad_type_plus(&mut self, allow_plus: bool, ty: &Ty) -> PResult<'a, ()> {
14721484
// Do not add `+` to expected tokens.
14731485
if !allow_plus || self.token != token::BinOp(token::Plus) {
@@ -4070,28 +4082,43 @@ impl<'a> Parser<'a> {
40704082
// Parse bounds of a type parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`.
40714083
// BOUND = TY_BOUND | LT_BOUND
40724084
// LT_BOUND = LIFETIME (e.g. `'a`)
4073-
// TY_BOUND = [?] [for<LT_PARAM_DEFS>] SIMPLE_PATH (e.g. `?for<'a: 'b> m::Trait<'a>`)
4085+
// TY_BOUND = TY_BOUND_NOPAREN | (TY_BOUND_NOPAREN)
4086+
// TY_BOUND_NOPAREN = [?] [for<LT_PARAM_DEFS>] SIMPLE_PATH (e.g. `?for<'a: 'b> m::Trait<'a>`)
40744087
fn parse_ty_param_bounds_common(&mut self, allow_plus: bool) -> PResult<'a, TyParamBounds> {
40754088
let mut bounds = Vec::new();
40764089
loop {
4077-
let question = if self.eat(&token::Question) { Some(self.prev_span) } else { None };
4078-
if self.check_lifetime() {
4079-
if let Some(question_span) = question {
4080-
self.span_err(question_span,
4081-
"`?` may only modify trait bounds, not lifetime bounds");
4082-
}
4083-
bounds.push(RegionTyParamBound(self.expect_lifetime()));
4084-
} else if self.check_keyword(keywords::For) || self.check_path() {
4085-
let lo = self.span;
4086-
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
4087-
let path = self.parse_path(PathStyle::Type)?;
4088-
let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_span));
4089-
let modifier = if question.is_some() {
4090-
TraitBoundModifier::Maybe
4090+
let is_bound_start = self.check_path() || self.check_lifetime() ||
4091+
self.check(&token::Question) ||
4092+
self.check_keyword(keywords::For) ||
4093+
self.check(&token::OpenDelim(token::Paren));
4094+
if is_bound_start {
4095+
let has_parens = self.eat(&token::OpenDelim(token::Paren));
4096+
let question = if self.eat(&token::Question) { Some(self.prev_span) } else { None };
4097+
if self.token.is_lifetime() {
4098+
if let Some(question_span) = question {
4099+
self.span_err(question_span,
4100+
"`?` may only modify trait bounds, not lifetime bounds");
4101+
}
4102+
bounds.push(RegionTyParamBound(self.expect_lifetime()));
40914103
} else {
4092-
TraitBoundModifier::None
4093-
};
4094-
bounds.push(TraitTyParamBound(poly_trait, modifier));
4104+
let lo = self.span;
4105+
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
4106+
let path = self.parse_path(PathStyle::Type)?;
4107+
let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_span));
4108+
let modifier = if question.is_some() {
4109+
TraitBoundModifier::Maybe
4110+
} else {
4111+
TraitBoundModifier::None
4112+
};
4113+
bounds.push(TraitTyParamBound(poly_trait, modifier));
4114+
}
4115+
if has_parens {
4116+
self.expect(&token::CloseDelim(token::Paren))?;
4117+
if let Some(&RegionTyParamBound(..)) = bounds.last() {
4118+
self.span_err(self.prev_span,
4119+
"parenthesized lifetime bounds are not supported");
4120+
}
4121+
}
40954122
} else {
40964123
break
40974124
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags: -Z parse-only
12+
13+
fn f<T: ?>() {} //~ ERROR expected identifier, found `>`
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags: -Z parse-only -Z continue-parse-after-error
12+
13+
fn main() {
14+
let _: Box<((Copy)) + Copy>;
15+
//~^ ERROR expected a path on the left-hand side of `+`, not `((Copy))`
16+
let _: Box<(Copy + Copy) + Copy>;
17+
//~^ ERROR expected a path on the left-hand side of `+`, not `( Copy + Copy)`
18+
let _: Box<(Copy +) + Copy>;
19+
//~^ ERROR expected a path on the left-hand side of `+`, not `( Copy)`
20+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags: -Z parse-only -Z continue-parse-after-error
12+
13+
fn f<T: Copy + ('a)>() {} //~ ERROR parenthesized lifetime bounds are not supported
14+
15+
fn main() {
16+
let _: Box<Copy + ('a)>; //~ ERROR parenthesized lifetime bounds are not supported
17+
let _: Box<('a) + Copy>; //~ ERROR expected type, found `'a`
18+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags: -Z parse-only
12+
13+
fn f<T: (Copy) + (?Sized) + (for<'a> Trait<'a>)>() {}
14+
15+
fn main() {
16+
let _: Box<(Copy) + (?Sized) + (for<'a> Trait<'a>)>;
17+
let _: Box<(?Sized) + (for<'a> Trait<'a>) + (Copy)>;
18+
let _: Box<(for<'a> Trait<'a>) + (Copy) + (?Sized)>;
19+
}
20+
21+
FAIL //~ ERROR

0 commit comments

Comments
 (0)