Name n3798, alx-0077r4 - disallow function parameters of function type Principles - Uphold the character of the language. - Avoid quiet changes. And from previous charters: C23: - APIs should be self-documenting when possible. Category Language; function parameters. Authors Alejandro Colomar Alex Celeste Cc: Martin Uecker Acked-by: Bruno Haible Acked-by: Doug McIlroy Acked-by: Andrew Clayton Acked-by: onf Cc: Joseph Myers History r0 (2026-01-24): - Initial draft. r1 (2026-01-25): - Re-title. r2 (2026-01-25): - Acked-by Bruno. - Add Comments section. r3 (2026-01-26): - Acked-by. - Tweak the constraint in p3 instead of adding a new one. - Co-authored-by Alex Celeste. - ffix - Add Celeste's comment about the C89/C90 rationale and K&R. r4 (2026-02-01; n3798): - Acked-by. - Update 6.7.9p6(EX3) - Mention cases. Abstract A function parameter of function type is adjusted to a pointer: void f(void fp(void)); is adjusted to void f(void (*fp)(void)); This is unnecessary and confusing; let's disallow it. Discussion I've never seen any code written declaring a function parameter of function type. Unlike array parameters, this adjustment seems to be seldom used, if at all. We can turn this into a constraint violation, which will require that the few users of this quirk of C to tweak their declarations to declare a pointer type (which is what these parameters have always been). I've tried finding such uses with a search engine, but didn't find anything; it could be that there are no uses, or it could be that it's hard to write a regex that would find this. FWIW, the standard has one example, just to document this weird allowance, and uses the usual pointer to function syntax elsewhere. Apart from resulting in more explicit code, this simplifies the wording, which would get more complex with other proposals that will modify compatibility rules regarding adjustment (it would require specifying some exceptions). Chris did find one case, although it seemed very clearly to be a typo, since the function declaration and the function definition had different syntax (one had a pointer, and the other not). Most likely, a typo that got uncaught, as there are no diagnostics for this. I've also heard that Jens's book uses function types to denote nonnull-ness of a function pointer. This is something that should be handled by a language feature that is consistent with arbitrary pointer types, such as _Optional, _Nullable, [static], or [[gnu::nonnull]]. Comments On 2026-01-25T19:48:04+0100, Bruno Haible wrote: > I saw such a function parameter of function type once in 25 years of > C programming, and I found it confusing. > > This is an incompatible change to the language. But the impact on > existing code is so small that it's worth it. --- On 2026-01-25T18:19:02-0500, Douglas McIlroy wrote: > All six proposals look eminently reasonable. They simplify > the language and remove surprises. I suspect these proposals > will invalidate very few existing programs. In any event, the > required corrections will improve the legibility and > maintainability of such programs. > > Doug McIlroy --- On 2026-01-26T02:27:02+0000, Alex Celeste wrote: > I was surprised not to find any comment in the C90 Rationale > about this. > > KnR1 says that 'since a reference to an array in any context > (in particular as an actual parameter) is taken to mean a > pointer to the first element of the array, declarations of > formal parameters declared ‘‘array of...’’ are adjusted to > read ‘‘pointer to ...’’.' There's no mention of functions in > this part, even though the KnR1 language does explicitly > describe using "bare" function identifiers to decay to > function pointers. > > This honestly feels like something that was added for > stylistic consistency. Function pointers were already > well-developed before this became a language feature. > Combined with the fact that you _can't_ use a function type in > a definition of a thing with that type, and using it in an > external declaration creates something incompatible with an > object of function pointer type, and I think this was an > attempt at consistency that ended up creating an inconsistency > instead. > The [...] example does look unintentional to me for that > reason. Proposed wording Based on N3685. 6.7.7.4 Function declarators @@ Constraints, p3 After adjustment, the parameters in a parameter type list in a function declarator that is part of a definition of that function -shall not have incomplete type. +shall have complete object type. @@ Semantics, p8 -A declaration of a parameter -as "function returning type" -shall be adjusted -to "pointer to function returning type", -as in 6.3.3.1. @@ p15 In the determination of type compatibility and of a composite type, each parameter declared with -function or array type +array type is taken as having the adjusted type and each parameter declared with qualified type is taken as having the unqualified version of its declared type. 6.7.9 Type definitions @@ Syntax, p6 (EXAMPLE 3) - t f(t (t)); + t f(t (*)(t)); 6.9.2 Function definitions @@ Semantics, p15 EXAMPLE 2 To pass one function to another, one can say int f(void); /* ... */ - g(f); + g(f); /* or g(&f); */ Then the definition of g can read void g(int (*funcp)(void)) { /* ... */ (*funcp)(); /* or funcp(); */ } -or, equivalently, - void g(int func(void)) - { - /* ... */ - func(); /* or (*func)(); */ - } ## I added 'g(&f)' for consistency with comments that show ## alternate valid syntax; it seems an accidental omission that ## could be fixed editorially.