Why does mismatched prototype and definition with empty argument list give different results in GCC and Clang? Why does mismatched prototype and definition with empty argument list give different results in GCC and Clang? c c

Why does mismatched prototype and definition with empty argument list give different results in GCC and Clang?


(C11, 6.7p4 Constraints) "All declarations in the same scope that refer to the same object or function shall specify compatible types"

and

(C11, 6.7.6.3p14) "An identifier list declares only the identifiers of the parameters of the function. An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. [...]"

My opinion is constraint of 6.7p4 is violated and diagnostic has to be issued.

EDIT:

as pointed out by @hvd it is actually not correct. 6.7.6.3p14 does not mean void foo() {} provides a prototype for foo as per DR#317. In that sense, the 6.7p4 constraint is not violated and so clang is right not to complain.


Disclaimer: I am not a , but I play one on .

If the compiler does not issue a diagnostic, it would be non-conforming, and could be considered a bug if the compiler claims to be conforming.

C.2011§6.7.6.3¶14 (emphasis mine):

An identifier list declares only the identifiers of the parameters of the function. An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied.

Thus, the definition of foo specifies no parameters, while the declaration of foo earlier specified two parameters.

C.2011§6.7.6.3¶15:

For two function types to be compatible, both shall specify compatible return types.146) Moreover, the parameter type lists, if both are present, shall agree in the number of parameters and in use of the ellipsis terminator; corresponding parameters shall have compatible types.
146) If both function types are ‘‘old style’’, parameter types are not compared.

Thus, the two declarators of foo are not compatible.
Dang! From @hvd's comment:

It's well-established that void foo() does not provide a prototype, even in a definition. There was a DR that answered this explicitly. The type of foo in that definition is void foo(), not void foo(void), and void foo() and void foo(int, int) are compatible types. This answer is incorrect.

The emphasized part of the text above from the standard is the loophole that allows for the disagreement in number of arguments, but compatible types. Although the function definition specifies a function that takes no arguments, since the parameter type list is actually missing, there is actually no incompatibility between the type of foo in its function prototype and the type of foo in the function definition.

Thus, clang 4.0 seems to have it right, since there is no constraint violation.

My original argument becomes invalid, so editing out that part of my original answer.


In comments you actually presented the following example:

void foo () {}int main () { foo(1, 2); return 0; }

And asked why the compiler does not complain for this case. This is actually addressed here, but in a nutshell: C.2011 still accepts K&R C function definition syntax. So, while void foo() {} is a definition that takes no arguments, the prototype that is used for argument validation is the same as void foo();, because the empty argument list is parsed as K&R. The modern C syntax to force proper argument checking would be to use void foo(void) {} instead.


From the C Standard (6.7.6.3 Function declarators (including prototypes))

15 For two function types to be compatible, both shall specify compatible return types.146) Moreover, ... If one type has a parameter type list and the other type is specified by a function definition that contains a (possibly empty) identifier list, both shall agree in the number of parameters, and the type of each prototype parameter shall be compatible with the type that results from the application of the default argument promotions to the type of the corresponding identifier. (In the determination of type compatibility and of a composite type, each parameter declared with function or 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.)

And (6.2.7 Compatible type and composite type)

2 All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined

Thus the shown program in the question has undefined behavior. The compiler may issue a diagnostic message as GCC did.