Pascal Cuoq
Pascal Cuoq

Reputation: 80325

Different prototypes accepted by Clang, GCC for the same function

I would like to report a bug against Clang and GCC for accepting multiple incompatible prototypes for the same function.

Consider the examples below:

$ clang -v
Ubuntu clang version 3.4-1ubuntu3 (tags/RELEASE_34/final) (based on LLVM 3.4)
Target: x86_64-pc-linux-gnu
…
$ gcc -v
…
gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1) 
$ cat t1.c
int f(void);

float f(void);
$ gcc -c t1.c
t1.c:3:7: error: conflicting types for ‘f’
 float f(void);
       ^
t1.c:1:5: note: previous declaration of ‘f’ was here
 int f(void);
     ^
$ clang -c t1.c
t1.c:3:7: error: conflicting types for 'f'
float f(void);
      ^
t1.c:1:5: note: previous declaration is here
int f(void);
    ^
1 error generated.

Both GCC and Clang conform to what I am going to call the “expected behavior”. However, if f is set to return an enum or an unsigned int:

$ cat t2.c
typedef enum { m1 } t ;

t f();

unsigned int f();
$ gcc -c t2.c
$ clang -c t.c

When the returned types in the two separate declarations of f are a simple enum and unsigned int, neither GCC nor Clang emit a diagnostic. I would like to report this behavior as a bug. In the C11 standard, clause 6.2.7:2 make the two programs t1.c and t2.c above undefined behavior:

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

However, 6.2.7:2 is not inside a Constraints section, so the two compilers are allowed to do what they want with these undefined behaviors, including accepting them silently. Is there any other clause that would make a diagnostic mandatory in a program like t2.c, and would make it right to report the absence of diagnostic as a compiler bug? Or am I perhaps wrong in expecting that an enumerated type be incompatible with unsigned int?

Upvotes: 3

Views: 236

Answers (2)

mafso
mafso

Reputation: 5543

The "expected behavior" for the first example is required by the constraints in C11 (n1570) 6.7 p4:

All declarations in the same scope that refer to the same object or function shall specify compatible types.

As your answer states, enumeration types may be compatible with unsigned int, what they usually are in case of Gcc:

Normally, the type is unsigned int if there are no negative values in the enumeration, otherwise int. If -fshort-enums is specified, then if there are negative values it is the first of signed char, short and int that can represent all the values, otherwise it is the first of unsigned char, unsigned short and unsigned int that can represent all the values.

(I couldn't find the corresponging part in the Clang documentation, but I'd expect it to be the same.)

For the second example, the diagnostic is required if and only if the enumeration type is incompatible with unsigned int. If it isn't, the behavior (beyond the diagnostic) is undefined as per the standard quote in the question.

OT: In C++, the second code is invalid, as enumeration types are types on its own, incompatible with other integer types.

Upvotes: 2

Pascal Cuoq
Pascal Cuoq

Reputation: 80325

I found the answer as I was writing the last sentence in the above question:

There is no undefined behavior in t2.c. Each enumerated type is compatible with one plain integer type, chosen by the compiler. In the example t2.c, GCC and Clang have both chosen unsigned int to be compatible with the enum typedef'd as t.

6.7.2.2:4 Each enumerated type shall be compatible with char, a signed integer type, or an unsigned integer type. The choice of type is implementation-defined,128 but shall be capable of representing the values of all the members of the enumeration […]

128) An implementation may delay the choice of which integer type until all enumeration constants have been seen.

Upvotes: 5

Related Questions