about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-04-11 05:16:48 +0000
committerbors <bors@rust-lang.org>2022-04-11 05:16:48 +0000
commit48a9e104dfafdec3a5d00d365229d6369939f433 (patch)
tree62532890ac763b98533a77695a317e5bdea2c710
parentd12b8578163ac67e5d088550920f7cafd435f52b (diff)
parentb65265b5e1c70d8c1685efc3a899ce3d89362ad7 (diff)
downloadrust-48a9e104dfafdec3a5d00d365229d6369939f433.tar.gz
rust-48a9e104dfafdec3a5d00d365229d6369939f433.zip
Auto merge of #95754 - compiler-errors:binder-assoc-ty, r=nagisa
Better error for `for<...>` on associated type bound

With GATs just around the corner, we'll probably see more people trying out `Trait<for<'a> Assoc<'a> = ..>`.

This PR improves the syntax error slightly, and also makes it slightly easier to make this into real syntax in the future.

Feel free to push back if the reviewer thinks this should have a suggestion on how to fix it (i.e. push the `for<'a>` outside of the angle brackets), but that can also be handled in a follow-up PR.
-rw-r--r--compiler/rustc_ast/src/ast.rs2
-rw-r--r--compiler/rustc_parse/src/parser/path.rs48
-rw-r--r--src/test/ui/associated-type-bounds/binder-on-bound.rs11
-rw-r--r--src/test/ui/associated-type-bounds/binder-on-bound.stderr8
4 files changed, 56 insertions, 13 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 75ccbc92be1..42ec206fc65 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2483,7 +2483,7 @@ pub struct TraitRef {
 
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct PolyTraitRef {
-    /// The `'a` in `<'a> Foo<&'a T>`.
+    /// The `'a` in `for<'a> Foo<&'a T>`.
     pub bound_generic_params: Vec<GenericParam>,
 
     /// The `Foo<&'a T>` in `<'a> Foo<&'a T>`.
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index 207ecd00e0c..b9e3adaac03 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -518,10 +518,20 @@ impl<'a> Parser<'a> {
         match arg {
             Some(arg) => {
                 if self.check(&token::Colon) | self.check(&token::Eq) {
-                    let (ident, gen_args) = match self.get_ident_from_generic_arg(arg) {
+                    let arg_span = arg.span();
+                    let (binder, ident, gen_args) = match self.get_ident_from_generic_arg(&arg) {
                         Ok(ident_gen_args) => ident_gen_args,
-                        Err(arg) => return Ok(Some(AngleBracketedArg::Arg(arg))),
+                        Err(()) => return Ok(Some(AngleBracketedArg::Arg(arg))),
                     };
+                    if binder.is_some() {
+                        // FIXME(compiler-errors): this could be improved by suggesting lifting
+                        // this up to the trait, at least before this becomes real syntax.
+                        // e.g. `Trait<for<'a> Assoc = Ty>` -> `for<'a> Trait<Assoc = Ty>`
+                        return Err(self.struct_span_err(
+                            arg_span,
+                            "`for<...>` is not allowed on associated type bounds",
+                        ));
+                    }
                     let kind = if self.eat(&token::Colon) {
                         // Parse associated type constraint bound.
 
@@ -700,18 +710,32 @@ impl<'a> Parser<'a> {
         Ok(Some(arg))
     }
 
+    /// Given a arg inside of generics, we try to destructure it as if it were the LHS in
+    /// `LHS = ...`, i.e. an associated type binding.
+    /// This returns (optionally, if they are present) any `for<'a, 'b>` binder args, the
+    /// identifier, and any GAT arguments.
     fn get_ident_from_generic_arg(
         &self,
-        gen_arg: GenericArg,
-    ) -> Result<(Ident, Option<GenericArgs>), GenericArg> {
-        if let GenericArg::Type(ty) = &gen_arg
-            && let ast::TyKind::Path(qself, path) = &ty.kind
-            && qself.is_none()
-            && path.segments.len() == 1
-        {
-            let seg = &path.segments[0];
-            return Ok((seg.ident, seg.args.as_deref().cloned()));
+        gen_arg: &GenericArg,
+    ) -> Result<(Option<Vec<ast::GenericParam>>, Ident, Option<GenericArgs>), ()> {
+        if let GenericArg::Type(ty) = gen_arg {
+            if let ast::TyKind::Path(qself, path) = &ty.kind
+                && qself.is_none()
+                && let [seg] = path.segments.as_slice()
+            {
+                return Ok((None, seg.ident, seg.args.as_deref().cloned()));
+            } else if let ast::TyKind::TraitObject(bounds, ast::TraitObjectSyntax::None) = &ty.kind
+                && let [ast::GenericBound::Trait(trait_ref, ast::TraitBoundModifier::None)] =
+                    bounds.as_slice()
+                && let [seg] = trait_ref.trait_ref.path.segments.as_slice()
+            {
+                return Ok((
+                    Some(trait_ref.bound_generic_params.clone()),
+                    seg.ident,
+                    seg.args.as_deref().cloned(),
+                ));
+            }
         }
-        Err(gen_arg)
+        Err(())
     }
 }
diff --git a/src/test/ui/associated-type-bounds/binder-on-bound.rs b/src/test/ui/associated-type-bounds/binder-on-bound.rs
new file mode 100644
index 00000000000..0b4b24b9820
--- /dev/null
+++ b/src/test/ui/associated-type-bounds/binder-on-bound.rs
@@ -0,0 +1,11 @@
+#![feature(generic_associated_types)]
+
+trait Trait {
+    type Bound<'a>;
+}
+
+fn foo() where Trait<for<'a> Bound<'a> = &'a ()> {
+    //~^ ERROR `for<...>` is not allowed on associated type bounds
+}
+
+fn main() {}
diff --git a/src/test/ui/associated-type-bounds/binder-on-bound.stderr b/src/test/ui/associated-type-bounds/binder-on-bound.stderr
new file mode 100644
index 00000000000..3432672e03c
--- /dev/null
+++ b/src/test/ui/associated-type-bounds/binder-on-bound.stderr
@@ -0,0 +1,8 @@
+error: `for<...>` is not allowed on associated type bounds
+  --> $DIR/binder-on-bound.rs:7:22
+   |
+LL | fn foo() where Trait<for<'a> Bound<'a> = &'a ()> {
+   |                      ^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+