Skip to content

Fix the grammar of function pointer types#2240

Draft
fmease wants to merge 4 commits intorust-lang:masterfrom
fmease:fix-grammar-fn-ptr-tys-etc
Draft

Fix the grammar of function pointer types#2240
fmease wants to merge 4 commits intorust-lang:masterfrom
fmease:fix-grammar-fn-ptr-tys-etc

Conversation

@fmease
Copy link
Copy Markdown
Member

@fmease fmease commented Apr 24, 2026

The grammar of function pointer types on master drastically diverges from reality.
Please read the individual commit messages for details (changes, motivation).

For example, according to the grammar on master, the following snippets are syntactically illegal but that is in conflict with rustc which deems them syntactically well-formed:

  1. safe fn()
  2. fn(...), fn(...,), fn(_: ...), fn(v: ...), fn(..., ())
  3. fn(&self), fn(&'static mut self, ())
  4. fn(mut x: ()), fn(false: bool), fn(&_: ()), fn(&&true: ())

Context: #t-lang > Naming arguments in Fn traits and rust-lang/rfcs#3955 where this was quite relevant.

@rustbot rustbot added the S-waiting-on-review Status: The marked PR is awaiting review from a maintainer label Apr 24, 2026
Comment thread src/types/function-pointer.md Outdated
MaybeNamedFunctionParametersVariadic ->
( MaybeNamedParam `,` )* MaybeNamedParam `,` OuterAttribute* `...`
MaybeNamedPattern ->
`mut`? IDENTIFIER | ( `&` | `&&` )? ( `_` | `false` | `true` )
Copy link
Copy Markdown
Member Author

@fmease fmease Apr 24, 2026

Choose a reason for hiding this comment

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

Yes, indeed, that's the grammar! It's a tiny subset of patterns.

Note that this subgrammar is also used for associated functions in traits in Rust 2015. I didn't know how I am meant to branch on editions, so I didn't update the corresponding function item grammar that currently only has FunctionParam -> … Type[^fn-param-2015] … which is not sufficient strictly speaking.

I guess I could do sth. akin to "ModernGrammar[^1] | LegacyGrammar[^2] // [^1]: Rust 2018–2024 // [^2]: Rust 2015 only" (pseudo). Let me know what you think.

View changes since the review

@fmease
Copy link
Copy Markdown
Member Author

fmease commented Apr 24, 2026

r? ehuss or reassign

Comment thread src/items/functions.md Outdated
Comment thread src/types/function-pointer.md Outdated
MaybeNamedFunctionParametersVariadic ->
( MaybeNamedParam `,` )* MaybeNamedParam `,` OuterAttribute* `...`
MaybeNamedPattern ->
`mut`? IDENTIFIER | ( `&` | `&&` )? ( `_` | `false` | `true` )
Copy link
Copy Markdown
Member Author

@fmease fmease Apr 24, 2026

Choose a reason for hiding this comment

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

TODO: Note somewhere (maybe in a footnote that semantically only identifiers and underscores are valid)

View changes since the review

Copy link
Copy Markdown
Contributor

@ehuss ehuss left a comment

Choose a reason for hiding this comment

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

Thanks!

This is just an initial review. I think we'll want to add rules that express each of the semantic restrictions. Whether it's conditional (like items.fn.safety-qualifiers) or unconditional (like items.traits.associated-visibility).

View changes since this review

Comment thread src/types/function-pointer.md Outdated
MaybeNamedFunctionParametersVariadic ->
( MaybeNamedParam `,` )* MaybeNamedParam `,` OuterAttribute* `...`
MaybeNamedPattern ->
`mut`? IDENTIFIER | ( `&` | `&&` )? ( `_` | `false` | `true` )
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.

This doesn't look quite right. My understanding is that this logic comes from is_named_param.

I think it would look something closer to:

Suggested change
`mut`? IDENTIFIER | ( `&` | `&&` )? ( `_` | `false` | `true` )
((`mut`? | `&` | `&&`)? IDENTIFIER) | ((`&` | `&&`)? (`_` | `false` | `true`))

because ident can be preceded by & or &&.

However, this seems to be getting close to the territory of unintentional behavior. I'm wondering if this really is a bug in the compiler, and the grammar should be something else.

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.

Good catch, I messed that up when copying over & adjusting #t-lang > Naming arguments in Fn traits @ 💬 (which had its own problems).

Yeah, so the gnarly grammar stems from INTERSECT( PatternNoTopAlt , ( `mut` | `&` | `&&` ) IDENTIFIER_OR_KEYWORD ).

Right, the & and && are ancient remnants of argument modes (~2012/2013) and should be banished. The rest should be removed, too. I'm going to spin up a crater run soon and add more context with regards to "argument modes" etc. Slightly more context: rust-lang/rfcs#3955 (comment).

@ehuss
Copy link
Copy Markdown
Contributor

ehuss commented Apr 26, 2026

I did some research to see how these got missed. Some of them just weren't communicated during implementation/stabilization. Some were just missed when reverse-engineering the compiler. Some were missed when trying to handle too many things at once.

safe syntactically allowed on a function pointer -- Missed in #1536 (not communicated, missed in verification)

... allowed as a standalone argument -- Missed in #1494 (not communicated, missed in verification)

... as last argument switch to a semantic restriction -- Missed in #927 (oversight, overwhelmed in large number of changes)

trailing ... can be followed by a comma -- Missed in #927 (oversight, overwhelmed in large number of changes)

self allowed in function pointer -- Missed in #927 (from rust-lang/rust#68764) (oversight, overwhelmed in large number of changes)

... allows named argument -- Added in rust-lang/rust#57760 which should have been unstable. No notification of this change.

function pointer parameters have limited patterns -- Missed in initial introduction in #433. I don't remember what happened there. The current behavior does not look intentional to me, though.

@fmease fmease marked this pull request as draft May 2, 2026 09:51
@rustbot rustbot removed the S-waiting-on-review Status: The marked PR is awaiting review from a maintainer label May 2, 2026
fmease added 4 commits May 2, 2026 13:33
* `safe` is a syntactically valid function pointer type qualifier next to
  `unsafe` but it wasn't listed prior.
* renamed `ItemSafety` to `Safety` since it's now also used in the
  grammar of function pointer types
* footnote on non-terminal `Safety` in rule `StaticItem` called `safe` &
  `unsafe` *function* modifiers when they're evidentally more general and
  can be placed on *static* items, too
* made the footnotes related to `Safety` more precise
The grammar was incorrect since

* C variadics `...` no longer need to be preceded by a normal parameter
  * that's the case since <rust-lang/rust#124048>
* C variadics don't need to be the final element, `fn(..., i32)` is
  perfectly fine
* C variadics can indeed be named (e.g., `fn f(_: ...)` or `fn(v: ...)`)
* C variadics can be followed by a comma (e.g., `fn(...,)`)
`fn(&self)`, `fn(&'static mut self)` etc. are syntactically valid
function pointer types
It's not just (common) identifiers or underscores, it's more complex.
@fmease fmease force-pushed the fix-grammar-fn-ptr-tys-etc branch from 400cb4b to 01e9ac6 Compare May 2, 2026 11:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants