Bug 114816 - Non-standard behavior with void arguments
Summary: Non-standard behavior with void arguments
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: c (show other bugs)
Version: 14.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2024-04-23 03:58 UTC by Halalaluyafail3
Modified: 2024-04-23 15:01 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Halalaluyafail3 2024-04-23 03:58:53 UTC
After thinking about qualified void return types I decided to think about void function parameters:

> The special case of an unnamed parameter of type void as the only item in
> the list specifies that the function has no parameters.
Section 6.7.6.3 "Function declarators (including prototypes)" Paragraph 10 ISO/IEC 9899:2018

No other restrictions are present that would forbid void parameter types. GCC seems to accept functions that use such arguments as long as they have names, for example:

void a(void x);
void b(const void x,register void y,register volatile void z);

Are accepted by GCC, but the following declarations are not accepted by GCC when they should be valid:

void c(void,void);
void d(const void);

Other incomplete types are accepted by GCC. Note that the type of the parameter is const void and not void so d is a function taking an argument of type const void and returning void, which means that the types of a and d are compatible.

The storage class register can be used on arguments and the same is true for void arguments, which doesn't affect the type so the following program should be valid:

int main(register void){}

Clang accepts this while GCC rejects. The storage class register doesn't affect the type and it is still one unnamed parameter of type void so this should be a declaration for zero arguments.

Calling any function with a void parameter is a constraint violation because of the following contraint:

> If the expression that denotes the called function has a type that includes
> a prototype, the number of arguments shall agree with the number of
> parameters. Each argument shall have a type such that its value may be
> assigned to an object with the unqualified version of the type of its
> corresponding parameter.
Section 6.5.2.2 "Function calls" Paragraph 2 ISO/IEC 9899:2018

GCC does not diagnose the error and instead just interprets the first void argument as being a sentinel value indicating when the arguments end, for example with the function a from before GCC accepts calling it with zero arguments. Qualified versions of void for example with the function b from before are properly diagnosed.

When referencing a function type with a single void parameter GCC will omit the name of the parameter as usual which leads to some confusing error messages, for example:

void f(void x);
void f(void);

The error message indicates that these types are incompatible, but it describes the first declaration as having type void(void). Here is the error output omitting source locations:
> error: conflicting types for 'f'; have 'void(void)'
> void f(void);
>      ^
> note: previous declaration of 'f' with type 'void(void)'
> void f(void x);
>      ^
Comment 1 Joseph S. Myers 2024-04-23 12:06:54 UTC
Note that it's not possible to define a function with such parameters ("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.").

"register void" and "const void" (single unnamed parameter) are in the list of undefined behavior in Annex J ("A storage-class specifier or type qualifier modifies the keyword void as a function parameter type list") - presumably implicitly undefined for lack of definition. As for named void parameters (or more than one void parameter, etc.), the response to C99 DR#295 says "The Committee agrees that there do not appear to be any constraints forbidding constructions like void func(void parm); nor are any semantics provided for this construction." (which would also be implicitly undefined behavior).

Given the desire to reduce undefined behavior in C2y it would be reasonable to submit a paper tightening this up by making all the problem cases (any case of a void parameter except for the special case of a single unnamed parameter of type void, no storage class specifier, no ellipsis following) into constraint violations for function declarators - or indeed to raise this through the issue tracking process once we have the new issue tracker set up, with a view to inviting WG14 to consider this a defect applicable to older standard versions.