Skip to content

Commit

Permalink
Auto merge of #95754 - compiler-errors:binder-assoc-ty, r=nagisa
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bors committed Apr 11, 2022
2 parents d12b857 + b65265b commit 48a9e10
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 13 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>`.
Expand Down
48 changes: 36 additions & 12 deletions compiler/rustc_parse/src/parser/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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(())
}
}
11 changes: 11 additions & 0 deletions src/test/ui/associated-type-bounds/binder-on-bound.rs
Original file line number Diff line number Diff line change
@@ -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() {}
8 changes: 8 additions & 0 deletions src/test/ui/associated-type-bounds/binder-on-bound.stderr
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 48a9e10

Please sign in to comment.