4

The declaration qboolean SNDDMA_InitDirect (void); appears in 'WinQuake/snd_win.c' on line 69. However, the function's definition (appearing on line 183 of the same file is written as:

sndinitstat SNDDMA_InitDirect (void) { /* Actual implementation is unimportant in this discussion. */ } 

Both qboolean, and sndinitstat are typedefs of enumerations: sndinitstat on line 33 of 'WinQuake/snd_win.c',

typedef enum {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat; 

and qboolean in 'WinQuake/common.h' (line 30)

typedef enum {false, true} qboolean; 

These are different enumerations.

I have been compiling this source with both Visual Studio 2015's built in compiler -- cl.exe -- and Clang v3.7.1 via the LLVM plugin for Visual Studio. Clang states that this difference in declaration/definition is an error. Visual Studio compiles this okay. Who is correct?

Now, to quote C: A Reference Manual (Fourth Edition):

  • Section 5.5 Enumerated Types (page 127)

    The enumeration constants are specified when the type is defined and have the type int.

  • Section 5.10 Typedef Names (page 149)

    Declarations with the typedef storage specifier do not introduce new types; the names are considered synonymous for types that could be specified in other ways.

These two paragraphs read to me as if Clang's error is, while helpful, incorrect according to the standard. But I know Microsoft don't have the greatest reputation with correctly compiling C.

10
  • 4
    Are they typedefs for the same list of enumerations? Or to put it another way, you should add the definitions of qboolean and sndinitstat to the question. Commented Feb 5, 2016 at 23:42
  • Inconsistencies of C standard interpretations are not uncommon between compilers. In my compiler, even when set to its most strict settings does not recognize different alias's for the same type as an error. In my opinion, at the most any compiler should do is flag it as a warning, but not an error. Commented Feb 5, 2016 at 23:45
  • I found typedef enum {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat; in the link. Do you have access to the typedef for qboolean? It is referenced in the link, but its definition is probably in one of the header files. Commented Feb 5, 2016 at 23:51
  • @user3386109 - If the enums for the two alias name were the same, then a redeclaration compiler error would be required. The enums have to be completely different. Commented Feb 5, 2016 at 23:54
  • 3
    Because Visual Studio treats anonymous enums as type int. So the typedefs are equivalent to typedef int qboolean and typedef int sndinitstat. Hence, Visual Studio thinks qboolean and sndinitstat are the same type. Common sense (and clang) says that's spectacularly wrong. How a language lawyer would would interpret the spec is debatable, but somewhat pointless. If the correct interpretation of the spec says that Visual Studio is correct, then that just means that the spec is spectacularly wrong. Commented Feb 6, 2016 at 0:38

2 Answers 2

3

They refer to different enumerations:

typedef enum {false, true} qboolean; typedef enum {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat; 

According to 6.2.5:16,

Each distinct enumeration constitutes a different enumerated type.

So Clang is certainly correct.

Sign up to request clarification or add additional context in comments.

Comments

1

If enum types hidden behind typedef names qboolean and sndinitstat are compatible, then the code is OK. If they are not compatible, then the code is erroneous. (See 6.2.7 Compatible type and composite type).

If both function declarations are present in the same translation unit then the return type compatibility requirement is becomes more strict to the point where the types have to be identical.

In your case, two tagless enum types are used to define these typedef names. The enum declarations are sufficiently different to make them incompatible. That means that the code in question does indeed contain an error and Clang is right to complain. Visual Studio misses this error.

Note though that Clang (as well as GCC) take it to another extremity - they report an error even when there's none. For example, these declarations

enum { A1, A2, A3 } foo(); enum { B1, B2, B3 } foo(); 

result in the same error in Clang and GCC, even though the code is valid. In this example the tagless enum types are compatible, which is sufficient to justify the change in the return type of foo between declarations.

4 Comments

While the other answer is technically correct, your answer provides a more indepth response with reading materials so I'm marking this as the accepted answer. Thank-you!
I'm not convinced that those types are compatible. The standard says (emphasis added) "Moreover, two structure, union, or enumerated types declared in separate translation units are compatible if their tags and members satisfy the following requirements:". Clang and GCC seem to be taking the standard at its word.
@rici: You are right. The "...declared in separate translation units..." part is indeed very important. I missed it.
@Alex Melbourne: My answer is not entirely accurate. In your case the conflict happens when two declarations meet in the same translation unit. In such cases in order to be compatible the return types have to be identical.