Skip to content

Commit 48a9e10

Browse files
committed
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.
2 parents d12b857 + b65265b commit 48a9e10

File tree

4 files changed

+56
-13
lines changed

4 files changed

+56
-13
lines changed

Diff for: compiler/rustc_ast/src/ast.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2483,7 +2483,7 @@ pub struct TraitRef {
24832483

24842484
#[derive(Clone, Encodable, Decodable, Debug)]
24852485
pub struct PolyTraitRef {
2486-
/// The `'a` in `<'a> Foo<&'a T>`.
2486+
/// The `'a` in `for<'a> Foo<&'a T>`.
24872487
pub bound_generic_params: Vec<GenericParam>,
24882488

24892489
/// The `Foo<&'a T>` in `<'a> Foo<&'a T>`.

Diff for: compiler/rustc_parse/src/parser/path.rs

+36-12
Original file line numberDiff line numberDiff line change
@@ -518,10 +518,20 @@ impl<'a> Parser<'a> {
518518
match arg {
519519
Some(arg) => {
520520
if self.check(&token::Colon) | self.check(&token::Eq) {
521-
let (ident, gen_args) = match self.get_ident_from_generic_arg(arg) {
521+
let arg_span = arg.span();
522+
let (binder, ident, gen_args) = match self.get_ident_from_generic_arg(&arg) {
522523
Ok(ident_gen_args) => ident_gen_args,
523-
Err(arg) => return Ok(Some(AngleBracketedArg::Arg(arg))),
524+
Err(()) => return Ok(Some(AngleBracketedArg::Arg(arg))),
524525
};
526+
if binder.is_some() {
527+
// FIXME(compiler-errors): this could be improved by suggesting lifting
528+
// this up to the trait, at least before this becomes real syntax.
529+
// e.g. `Trait<for<'a> Assoc = Ty>` -> `for<'a> Trait<Assoc = Ty>`
530+
return Err(self.struct_span_err(
531+
arg_span,
532+
"`for<...>` is not allowed on associated type bounds",
533+
));
534+
}
525535
let kind = if self.eat(&token::Colon) {
526536
// Parse associated type constraint bound.
527537

@@ -700,18 +710,32 @@ impl<'a> Parser<'a> {
700710
Ok(Some(arg))
701711
}
702712

713+
/// Given a arg inside of generics, we try to destructure it as if it were the LHS in
714+
/// `LHS = ...`, i.e. an associated type binding.
715+
/// This returns (optionally, if they are present) any `for<'a, 'b>` binder args, the
716+
/// identifier, and any GAT arguments.
703717
fn get_ident_from_generic_arg(
704718
&self,
705-
gen_arg: GenericArg,
706-
) -> Result<(Ident, Option<GenericArgs>), GenericArg> {
707-
if let GenericArg::Type(ty) = &gen_arg
708-
&& let ast::TyKind::Path(qself, path) = &ty.kind
709-
&& qself.is_none()
710-
&& path.segments.len() == 1
711-
{
712-
let seg = &path.segments[0];
713-
return Ok((seg.ident, seg.args.as_deref().cloned()));
719+
gen_arg: &GenericArg,
720+
) -> Result<(Option<Vec<ast::GenericParam>>, Ident, Option<GenericArgs>), ()> {
721+
if let GenericArg::Type(ty) = gen_arg {
722+
if let ast::TyKind::Path(qself, path) = &ty.kind
723+
&& qself.is_none()
724+
&& let [seg] = path.segments.as_slice()
725+
{
726+
return Ok((None, seg.ident, seg.args.as_deref().cloned()));
727+
} else if let ast::TyKind::TraitObject(bounds, ast::TraitObjectSyntax::None) = &ty.kind
728+
&& let [ast::GenericBound::Trait(trait_ref, ast::TraitBoundModifier::None)] =
729+
bounds.as_slice()
730+
&& let [seg] = trait_ref.trait_ref.path.segments.as_slice()
731+
{
732+
return Ok((
733+
Some(trait_ref.bound_generic_params.clone()),
734+
seg.ident,
735+
seg.args.as_deref().cloned(),
736+
));
737+
}
714738
}
715-
Err(gen_arg)
739+
Err(())
716740
}
717741
}
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#![feature(generic_associated_types)]
2+
3+
trait Trait {
4+
type Bound<'a>;
5+
}
6+
7+
fn foo() where Trait<for<'a> Bound<'a> = &'a ()> {
8+
//~^ ERROR `for<...>` is not allowed on associated type bounds
9+
}
10+
11+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: `for<...>` is not allowed on associated type bounds
2+
--> $DIR/binder-on-bound.rs:7:22
3+
|
4+
LL | fn foo() where Trait<for<'a> Bound<'a> = &'a ()> {
5+
| ^^^^^^^^^^^^^^^^^
6+
7+
error: aborting due to previous error
8+

0 commit comments

Comments
 (0)