]> git.lizzy.rs Git - rust.git/commitdiff
syntax: Disambiguate generics and qualified paths
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Sun, 14 Jan 2018 15:10:19 +0000 (18:10 +0300)
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Sun, 14 Jan 2018 15:10:19 +0000 (18:10 +0300)
src/libsyntax/parse/parser.rs
src/test/compile-fail/private-in-public-ill-formed.rs
src/test/parse-fail/impl-qpath.rs [new file with mode: 0644]
src/test/parse-fail/where_with_bound.rs

index a3ea659940aa0d8fe26d05609c660c9c51f006e0..e7565d357397cbce0f1a91eed9bb00d6db582e9a 100644 (file)
@@ -4772,21 +4772,13 @@ pub fn parse_where_clause(&mut self) -> PResult<'a, WhereClause> {
         }
         let lo = self.prev_span;
 
-        // This is a temporary future proofing.
-        //
         // We are considering adding generics to the `where` keyword as an alternative higher-rank
         // parameter syntax (as in `where<'a>` or `where<T>`. To avoid that being a breaking
-        // change, for now we refuse to parse `where < (ident | lifetime) (> | , | :)`.
-        if token::Lt == self.token {
-            let ident_or_lifetime = self.look_ahead(1, |t| t.is_ident() || t.is_lifetime());
-            if ident_or_lifetime {
-                let gt_comma_or_colon = self.look_ahead(2, |t| {
-                    *t == token::Gt || *t == token::Comma || *t == token::Colon
-                });
-                if gt_comma_or_colon {
-                    self.span_err(self.span, "syntax `where<T>` is reserved for future use");
-                }
-            }
+        // change we parse those generics now, but report an error.
+        if self.choose_generics_over_qpath() {
+            let generics = self.parse_generics()?;
+            self.span_err(generics.span,
+                          "generic parameters on `where` clauses are reserved for future use");
         }
 
         loop {
@@ -5348,6 +5340,29 @@ fn parse_item_trait(&mut self, is_auto: IsAuto, unsafety: Unsafety) -> PResult<'
         }
     }
 
+    fn choose_generics_over_qpath(&self) -> bool {
+        // There's an ambiguity between generic parameters and qualified paths in impls.
+        // If we see `<` it may start both, so we have to inspect some following tokens.
+        // The following combinations can only start generics,
+        // but not qualified paths (with one exception):
+        //     `<` `>` - empty generic parameters
+        //     `<` `#` - generic parameters with attributes
+        //     `<` (LIFETIME|IDENT) `>` - single generic parameter
+        //     `<` (LIFETIME|IDENT) `,` - first generic parameter in a list
+        //     `<` (LIFETIME|IDENT) `:` - generic parameter with bounds
+        //     `<` (LIFETIME|IDENT) `=` - generic parameter with a default
+        // The only truly ambiguous case is
+        //     `<` IDENT `>` `::` IDENT ...
+        // we disambiguate it in favor of generics (`impl<T> ::absolute::Path<T> { ... }`)
+        // because this is what almost always expected in practice, qualified paths in impls
+        // (`impl <Type>::AssocTy { ... }`) aren't even allowed by type checker at the moment.
+        self.token == token::Lt &&
+            (self.look_ahead(1, |t| t == &token::Pound || t == &token::Gt) ||
+             self.look_ahead(1, |t| t.is_lifetime() || t.is_ident()) &&
+                self.look_ahead(2, |t| t == &token::Gt || t == &token::Comma ||
+                                       t == &token::Colon || t == &token::Eq))
+    }
+
     fn parse_impl_body(&mut self) -> PResult<'a, (Vec<ImplItem>, Vec<Attribute>)> {
         self.expect(&token::OpenDelim(token::Brace))?;
         let attrs = self.parse_inner_attributes()?;
@@ -5378,8 +5393,11 @@ fn parse_impl_body(&mut self) -> PResult<'a, (Vec<ImplItem>, Vec<Attribute>)> {
     fn parse_item_impl(&mut self, unsafety: Unsafety, defaultness: Defaultness)
                        -> PResult<'a, ItemInfo> {
         // First, parse generic parameters if necessary.
-        // FIXME: Disambiguate generic parameters and qualified paths (`impl <A as B>::C {}`).
-        let mut generics = self.parse_generics()?;
+        let mut generics = if self.choose_generics_over_qpath() {
+            self.parse_generics()?
+        } else {
+            ast::Generics::default()
+        };
 
         // Disambiguate `impl !Trait for Type { ... }` and `impl ! { ... }` for the never type.
         let polarity = if self.check(&token::Not) && self.look_ahead(1, |t| t.can_begin_type()) {
index 4e10614bf62c6b274fce6a581e4928f7d1ad5b7a..480406054adb52d11b2ec3cf75472604b48f384c 100644 (file)
@@ -21,7 +21,7 @@ impl PrivTr for Priv {
         type AssocAlias = m::Pub3;
     }
 
-    impl (<Priv as PrivTr>::AssocAlias) { //~ ERROR no base type found for inherent implementation
+    impl <Priv as PrivTr>::AssocAlias { //~ ERROR no base type found for inherent implementation
         pub fn f(arg: Priv) {} // private type `aliases_pub::Priv` in public interface
     }
 }
@@ -37,7 +37,7 @@ impl PrivTr for Priv {
         type AssocAlias = Priv3;
     }
 
-    impl (<Priv as PrivTr>::AssocAlias) { //~ ERROR no base type found for inherent implementation
+    impl <Priv as PrivTr>::AssocAlias { //~ ERROR no base type found for inherent implementation
         pub fn f(arg: Priv) {} // OK
     }
 }
diff --git a/src/test/parse-fail/impl-qpath.rs b/src/test/parse-fail/impl-qpath.rs
new file mode 100644 (file)
index 0000000..48dd888
--- /dev/null
@@ -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
+
+impl <*const u8>::AssocTy {} // OK
+impl <Type as Trait>::AssocTy {} // OK
+impl <'a + Trait>::AssocTy {} // OK
+impl <<Type>::AssocTy>::AssocTy {} // OK
+
+FAIL //~ ERROR
index cb57500df797eae06475148616c6e092409471cc..2948619ccd07dce770b2837074a565c22b74d557 100644 (file)
@@ -11,6 +11,6 @@
 // compile-flags: -Z parse-only
 
 fn foo<T>() where <T>::Item: ToString, T: Iterator { }
-               //~^ syntax `where<T>` is reserved for future use
+//~^ ERROR generic parameters on `where` clauses are reserved for future use
 
 fn main() {}