Skip to content

Commit 60c48dd

Browse files
committed
syntax: Disambiguate generics and qualified paths
1 parent d19e4c4 commit 60c48dd

File tree

4 files changed

+54
-18
lines changed

4 files changed

+54
-18
lines changed

src/libsyntax/parse/parser.rs

+33-15
Original file line numberDiff line numberDiff line change
@@ -4772,21 +4772,13 @@ impl<'a> Parser<'a> {
47724772
}
47734773
let lo = self.prev_span;
47744774

4775-
// This is a temporary future proofing.
4776-
//
47774775
// We are considering adding generics to the `where` keyword as an alternative higher-rank
47784776
// parameter syntax (as in `where<'a>` or `where<T>`. To avoid that being a breaking
4779-
// change, for now we refuse to parse `where < (ident | lifetime) (> | , | :)`.
4780-
if token::Lt == self.token {
4781-
let ident_or_lifetime = self.look_ahead(1, |t| t.is_ident() || t.is_lifetime());
4782-
if ident_or_lifetime {
4783-
let gt_comma_or_colon = self.look_ahead(2, |t| {
4784-
*t == token::Gt || *t == token::Comma || *t == token::Colon
4785-
});
4786-
if gt_comma_or_colon {
4787-
self.span_err(self.span, "syntax `where<T>` is reserved for future use");
4788-
}
4789-
}
4777+
// change we parse those generics now, but report an error.
4778+
if self.choose_generics_over_qpath() {
4779+
let generics = self.parse_generics()?;
4780+
self.span_err(generics.span,
4781+
"generic parameters on `where` clauses are reserved for future use");
47904782
}
47914783

47924784
loop {
@@ -5348,6 +5340,29 @@ impl<'a> Parser<'a> {
53485340
}
53495341
}
53505342

5343+
fn choose_generics_over_qpath(&self) -> bool {
5344+
// There's an ambiguity between generic parameters and qualified paths in impls.
5345+
// If we see `<` it may start both, so we have to inspect some following tokens.
5346+
// The following combinations can only start generics,
5347+
// but not qualified paths (with one exception):
5348+
// `<` `>` - empty generic parameters
5349+
// `<` `#` - generic parameters with attributes
5350+
// `<` (LIFETIME|IDENT) `>` - single generic parameter
5351+
// `<` (LIFETIME|IDENT) `,` - first generic parameter in a list
5352+
// `<` (LIFETIME|IDENT) `:` - generic parameter with bounds
5353+
// `<` (LIFETIME|IDENT) `=` - generic parameter with a default
5354+
// The only truly ambiguous case is
5355+
// `<` IDENT `>` `::` IDENT ...
5356+
// we disambiguate it in favor of generics (`impl<T> ::absolute::Path<T> { ... }`)
5357+
// because this is what almost always expected in practice, qualified paths in impls
5358+
// (`impl <Type>::AssocTy { ... }`) aren't even allowed by type checker at the moment.
5359+
self.token == token::Lt &&
5360+
(self.look_ahead(1, |t| t == &token::Pound || t == &token::Gt) ||
5361+
self.look_ahead(1, |t| t.is_lifetime() || t.is_ident()) &&
5362+
self.look_ahead(2, |t| t == &token::Gt || t == &token::Comma ||
5363+
t == &token::Colon || t == &token::Eq))
5364+
}
5365+
53515366
fn parse_impl_body(&mut self) -> PResult<'a, (Vec<ImplItem>, Vec<Attribute>)> {
53525367
self.expect(&token::OpenDelim(token::Brace))?;
53535368
let attrs = self.parse_inner_attributes()?;
@@ -5378,8 +5393,11 @@ impl<'a> Parser<'a> {
53785393
fn parse_item_impl(&mut self, unsafety: Unsafety, defaultness: Defaultness)
53795394
-> PResult<'a, ItemInfo> {
53805395
// First, parse generic parameters if necessary.
5381-
// FIXME: Disambiguate generic parameters and qualified paths (`impl <A as B>::C {}`).
5382-
let mut generics = self.parse_generics()?;
5396+
let mut generics = if self.choose_generics_over_qpath() {
5397+
self.parse_generics()?
5398+
} else {
5399+
ast::Generics::default()
5400+
};
53835401

53845402
// Disambiguate `impl !Trait for Type { ... }` and `impl ! { ... }` for the never type.
53855403
let polarity = if self.check(&token::Not) && self.look_ahead(1, |t| t.can_begin_type()) {

src/test/compile-fail/private-in-public-ill-formed.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ mod aliases_pub {
2121
type AssocAlias = m::Pub3;
2222
}
2323

24-
impl (<Priv as PrivTr>::AssocAlias) { //~ ERROR no base type found for inherent implementation
24+
impl <Priv as PrivTr>::AssocAlias { //~ ERROR no base type found for inherent implementation
2525
pub fn f(arg: Priv) {} // private type `aliases_pub::Priv` in public interface
2626
}
2727
}
@@ -37,7 +37,7 @@ mod aliases_priv {
3737
type AssocAlias = Priv3;
3838
}
3939

40-
impl (<Priv as PrivTr>::AssocAlias) { //~ ERROR no base type found for inherent implementation
40+
impl <Priv as PrivTr>::AssocAlias { //~ ERROR no base type found for inherent implementation
4141
pub fn f(arg: Priv) {} // OK
4242
}
4343
}

src/test/parse-fail/impl-qpath.rs

+18
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
12+
13+
impl <*const u8>::AssocTy {} // OK
14+
impl <Type as Trait>::AssocTy {} // OK
15+
impl <'a + Trait>::AssocTy {} // OK
16+
impl <<Type>::AssocTy>::AssocTy {} // OK
17+
18+
FAIL //~ ERROR

src/test/parse-fail/where_with_bound.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@
1111
// compile-flags: -Z parse-only
1212

1313
fn foo<T>() where <T>::Item: ToString, T: Iterator { }
14-
//~^ syntax `where<T>` is reserved for future use
14+
//~^ ERROR generic parameters on `where` clauses are reserved for future use
1515

1616
fn main() {}

0 commit comments

Comments
 (0)