Rollup merge of #41077 - petrochenkov:boundparen, r=nikomatsakis

syntax: Support parentheses around trait bounds

An implementation for https://github.com/rust-lang/rust/issues/39318#issuecomment-290956826

r? @nikomatsakis
This commit is contained in:
Corey Farwell 2017-04-22 09:02:56 -04:00 committed by GitHub
commit 5e1351b17b
6 changed files with 139 additions and 39 deletions

View File

@ -781,10 +781,11 @@ never_type : "!" ;
### Type parameter bounds ### Type parameter bounds
```antlr ```antlr
bound-list := bound | bound '+' bound-list '+' ?
bound := ty_bound | lt_bound bound := ty_bound | lt_bound
lt_bound := lifetime lt_bound := lifetime
ty_bound := [?] [ for<lt_param_defs> ] simple_path ty_bound := ty_bound_noparen | (ty_bound_noparen)
bound-list := bound | bound '+' bound-list '+' ? ty_bound_noparen := [?] [ for<lt_param_defs> ] simple_path
``` ```
### Self types ### Self types

View File

@ -152,6 +152,7 @@ fn maybe_append(mut lhs: Vec<Attribute>, rhs: Option<Vec<Attribute>>)
enum PrevTokenKind { enum PrevTokenKind {
DocComment, DocComment,
Comma, Comma,
Plus,
Interpolated, Interpolated,
Eof, Eof,
Other, Other,
@ -1061,6 +1062,7 @@ impl<'a> Parser<'a> {
self.prev_token_kind = match self.token { self.prev_token_kind = match self.token {
token::DocComment(..) => PrevTokenKind::DocComment, token::DocComment(..) => PrevTokenKind::DocComment,
token::Comma => PrevTokenKind::Comma, token::Comma => PrevTokenKind::Comma,
token::BinOp(token::Plus) => PrevTokenKind::Plus,
token::Interpolated(..) => PrevTokenKind::Interpolated, token::Interpolated(..) => PrevTokenKind::Interpolated,
token::Eof => PrevTokenKind::Eof, token::Eof => PrevTokenKind::Eof,
_ => PrevTokenKind::Other, _ => PrevTokenKind::Other,
@ -1354,20 +1356,26 @@ impl<'a> Parser<'a> {
break; break;
} }
} }
let trailing_plus = self.prev_token_kind == PrevTokenKind::Plus;
self.expect(&token::CloseDelim(token::Paren))?; self.expect(&token::CloseDelim(token::Paren))?;
if ts.len() == 1 && !last_comma { if ts.len() == 1 && !last_comma {
let ty = ts.into_iter().nth(0).unwrap().unwrap(); let ty = ts.into_iter().nth(0).unwrap().unwrap();
let maybe_bounds = allow_plus && self.token == token::BinOp(token::Plus);
match ty.node { match ty.node {
// Accept `(Trait1) + Trait2 + 'a` for backward compatibility (#39318). // `(TY_BOUND_NOPAREN) + BOUND + ...`.
TyKind::Path(None, ref path) TyKind::Path(None, ref path) if maybe_bounds => {
if allow_plus && self.token == token::BinOp(token::Plus) => { self.parse_remaining_bounds(Vec::new(), path.clone(), lo, true)?
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 => {
let path = match bounds[0] {
TraitTyParamBound(ref pt, ..) => pt.trait_ref.path.clone(),
_ => self.bug("unexpected lifetime bound"),
};
self.parse_remaining_bounds(Vec::new(), path, lo, true)?
}
// `(TYPE)`
_ => TyKind::Paren(P(ty)) _ => TyKind::Paren(P(ty))
} }
} else { } else {
@ -1418,11 +1426,8 @@ impl<'a> Parser<'a> {
// Just a type path or bound list (trait object type) starting with a trait. // Just a type path or bound list (trait object type) starting with a trait.
// `Type` // `Type`
// `Trait1 + Trait2 + 'a` // `Trait1 + Trait2 + 'a`
if allow_plus && self.eat(&token::BinOp(token::Plus)) { if allow_plus && self.check(&token::BinOp(token::Plus)) {
let poly_trait = PolyTraitRef::new(Vec::new(), path, lo.to(self.prev_span)); self.parse_remaining_bounds(Vec::new(), path, lo, true)?
let mut bounds = vec![TraitTyParamBound(poly_trait, TraitBoundModifier::None)];
bounds.append(&mut self.parse_ty_param_bounds()?);
TyKind::TraitObject(bounds)
} else { } else {
TyKind::Path(None, path) TyKind::Path(None, path)
} }
@ -1440,12 +1445,8 @@ impl<'a> Parser<'a> {
self.parse_ty_bare_fn(lifetime_defs)? self.parse_ty_bare_fn(lifetime_defs)?
} else { } else {
let path = self.parse_path(PathStyle::Type)?; let path = self.parse_path(PathStyle::Type)?;
let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_span)); let parse_plus = allow_plus && self.check(&token::BinOp(token::Plus));
let mut bounds = vec![TraitTyParamBound(poly_trait, TraitBoundModifier::None)]; self.parse_remaining_bounds(lifetime_defs, path, lo, parse_plus)?
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) { } else if self.eat_keyword(keywords::Impl) {
// FIXME: figure out priority of `+` in `impl Trait1 + Trait2` (#34511). // FIXME: figure out priority of `+` in `impl Trait1 + Trait2` (#34511).
@ -1468,6 +1469,17 @@ impl<'a> Parser<'a> {
Ok(P(ty)) Ok(P(ty))
} }
fn parse_remaining_bounds(&mut self, lifetime_defs: Vec<LifetimeDef>, path: ast::Path,
lo: Span, parse_plus: bool) -> PResult<'a, TyKind> {
let poly_trait_ref = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_span));
let mut bounds = vec![TraitTyParamBound(poly_trait_ref, TraitBoundModifier::None)];
if parse_plus {
self.bump(); // `+`
bounds.append(&mut self.parse_ty_param_bounds()?);
}
Ok(TyKind::TraitObject(bounds))
}
fn maybe_recover_from_bad_type_plus(&mut self, allow_plus: bool, ty: &Ty) -> PResult<'a, ()> { fn maybe_recover_from_bad_type_plus(&mut self, allow_plus: bool, ty: &Ty) -> PResult<'a, ()> {
// Do not add `+` to expected tokens. // Do not add `+` to expected tokens.
if !allow_plus || self.token != token::BinOp(token::Plus) { if !allow_plus || self.token != token::BinOp(token::Plus) {
@ -4070,28 +4082,43 @@ impl<'a> Parser<'a> {
// Parse bounds of a type parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`. // Parse bounds of a type parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`.
// BOUND = TY_BOUND | LT_BOUND // BOUND = TY_BOUND | LT_BOUND
// LT_BOUND = LIFETIME (e.g. `'a`) // 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> { fn parse_ty_param_bounds_common(&mut self, allow_plus: bool) -> PResult<'a, TyParamBounds> {
let mut bounds = Vec::new(); let mut bounds = Vec::new();
loop { loop {
let question = if self.eat(&token::Question) { Some(self.prev_span) } else { None }; let is_bound_start = self.check_path() || self.check_lifetime() ||
if self.check_lifetime() { self.check(&token::Question) ||
if let Some(question_span) = question { self.check_keyword(keywords::For) ||
self.span_err(question_span, self.check(&token::OpenDelim(token::Paren));
"`?` may only modify trait bounds, not lifetime bounds"); if is_bound_start {
} let has_parens = self.eat(&token::OpenDelim(token::Paren));
bounds.push(RegionTyParamBound(self.expect_lifetime())); let question = if self.eat(&token::Question) { Some(self.prev_span) } else { None };
} else if self.check_keyword(keywords::For) || self.check_path() { if self.token.is_lifetime() {
let lo = self.span; if let Some(question_span) = question {
let lifetime_defs = self.parse_late_bound_lifetime_defs()?; self.span_err(question_span,
let path = self.parse_path(PathStyle::Type)?; "`?` may only modify trait bounds, not lifetime bounds");
let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_span)); }
let modifier = if question.is_some() { bounds.push(RegionTyParamBound(self.expect_lifetime()));
TraitBoundModifier::Maybe
} else { } else {
TraitBoundModifier::None let lo = self.span;
}; let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
bounds.push(TraitTyParamBound(poly_trait, modifier)); let path = self.parse_path(PathStyle::Type)?;
let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_span));
let modifier = if question.is_some() {
TraitBoundModifier::Maybe
} else {
TraitBoundModifier::None
};
bounds.push(TraitTyParamBound(poly_trait, modifier));
}
if has_parens {
self.expect(&token::CloseDelim(token::Paren))?;
if let Some(&RegionTyParamBound(..)) = bounds.last() {
self.span_err(self.prev_span,
"parenthesized lifetime bounds are not supported");
}
}
} else { } else {
break break
} }

View File

@ -0,0 +1,13 @@
// 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: ?>() {} //~ ERROR expected identifier, found `>`

View File

@ -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)`
}

View File

@ -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`
}

View File

@ -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