Skip to content

Fix the grammar of generic arguments#2247

Open
fmease wants to merge 2 commits intorust-lang:masterfrom
fmease:fix-generic-args-grammar
Open

Fix the grammar of generic arguments#2247
fmease wants to merge 2 commits intorust-lang:masterfrom
fmease:fix-generic-args-grammar

Conversation

@fmease
Copy link
Copy Markdown
Member

@fmease fmease commented May 2, 2026

Since PR rust-lang/rust#43540 (2017) the grammar of paths in types, expressions & paths has been somewhat unified.

Type paths are "now" basically identical to expression and pattern paths except that for the former, :: before generic arg lists is optional whereas for the latter it is mandatory. Notably, the grammar of generic arg lists is the same across these three kinds, so parenthesized generic argument lists are intentionally legal in expression and pattern contexts, too, as long as they're preceded by ::.

For example, the following is legal:

#[cfg(false)]
fn scope() {
    let F::() -> () = F::() -> ()::Y;
    // ^^^ to be understood as vvv
    let F::<(), Output = ()> = F::<(), Output = ()>::Y;
}

However, the Reference didn't reflect this fact and incorrectly claimed that parenthesized generic argument lists were exclusive to type paths. This PR rectifies this incongruity.

Context: Jonathan Brouwer and I stumbled upon this when discussing their most recent RFC.

Moreover, GenericArgsBinding and GenericArgsBounds use GenericArgs, meaning the Reference didn't correctly document that parenthesized generic arguments are legal there, too, like the following program:

#[cfg(false)]
fn scope() {
    let _: P<S<T> = T>;
    let _: P<S::<T>: B>; // wrongly deemed illegal by reference@master
    let _: P<S(T) = T>;  // similarly
    let _: P<S::(T): B>; // similarly
    let _: P<S<>: >; // similarly
    let _: P<crate<> = T>; // similarly
}

The new definition of GenericArgs provided in this PR automatically fixes this issue. Note that I still had to adjust GenericArgsBinding and GenericArgsBounds since in the past state it didn't admit turbofish or path segment keywords like crate. I just needed to replace IDENTIFIER GenericArgs? with TypePathSegment because that what it is, exactly.

The Reference currently also incorrectly claims that type path P<S<>: > is invalid which it is not, bounds are intentionally Kleene star, not plus. I might fix that in a separate PR since this issue might occur in other places, too.

r? ehuss (lemme know if I should stop assigning you)

@rustbot rustbot added the S-waiting-on-review Status: The marked PR is awaiting review from a maintainer label May 2, 2026
Comment thread src/paths.md Outdated
Comment on lines +55 to +56
`<` ( ( GenericArg `,` )* GenericArg `,`? )? `>`
| `(` ( ( Type `,` )* Type `,`? )? `)` (`->` TypeNoBounds)?
Copy link
Copy Markdown
Member Author

@fmease fmease May 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do admit that this looks very noisy even in its rendered form. I'm fine with introducing helper rules to make this more legible (note: we do already have some occurrences of ? )? in the Reference).

(I really don't like having `<` `>` | … which doesn't scale to the parenthesized version)

I was really struggling with names though. Introducing AngleGenericArgs vs. ParenGenericArgs wouldn't help, I need to name the rules inside of the brackets.

GenericArgs ->
      `<` AngleGenericArgsInnerBikeshed? `>`
    | `(` ParenGenericArgsInnerBikeshed? `)` (`->` TypeNoBounds)?

AngleGenericArgsInnerBikeshed ->
    ( GenericArg `,` )* GenericArg `,`?

ParenGenericArgsInnerBikeshed ->
    ( Type `,` )* Type `,`?

View changes since the review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about GenericArgList for inside the angle brackets, and FnTypeList for inside the parens? Assuming that the paren alternative continues to only be semantically valid for Fn-like traits?

Copy link
Copy Markdown
Member Author

@fmease fmease May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parenthesized generic argument lists are syntactically valid for all paths, therefore I'm not too keen on using the term Fn for this feature.

Semantically speaking, the path must refer to trait {,Async}Fn{,Mut,Once}. That doesn't mean that the path has to "literally" be Fn, std::ops::Fn etc. E.g., the following is perfectly valid, too:

fn f<T: X(i32) -> i32>() {}
use Fn as X;

I did think about GenericArgList before opening this PR and I might even reconsider it, thanks for the suggestion. I originally rejected it because only 2 or so (?) preexisting grammar rules bear a name ending with List and other "lists" like TypeParamBounds (now: Bounds) aren't suffixed by List, so I was hesitant.

I'll probably go with TypeList for the list of types, kinda self-explanatory.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied.

@fmease fmease changed the title Fix the grammar of generic arguments in expressions and patterns Fix the grammar of generic arguments May 5, 2026
@fmease fmease force-pushed the fix-generic-args-grammar branch from 5f50100 to 33e64bd Compare May 5, 2026 07:18
@rustbot

This comment has been minimized.

@fmease fmease force-pushed the fix-generic-args-grammar branch from 33e64bd to 3496d7e Compare May 6, 2026 13:36
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented May 6, 2026

This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: The marked PR is awaiting review from a maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants