https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121212

            Bug ID: 121212
           Summary: Two enumerated types without enumerator lists
                    shouldn't be compatible
           Product: gcc
           Version: 16.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: luigighiron at gmail dot com
  Target Milestone: ---

The following program demonstrates why two enumerated types without enumerator
lists shouldn't be compatible (in the same translation unit), as GCC allows
them to be if they have the same tag:

enum E:int;
typedef enum E A;
int main(){
    enum E:int;
    typedef enum E B;
    extern A x;
    extern B x;
    enum E:int{h};
    A*p=&x;
    B*q=&x;
}

First, an enumerated type called E with no enumerator list is declared at file
scope and aliased with the name A. Second, in the scope of main another
enumerated type called E with no enumerator list is declared and aliased with
the name B. Both enumerated types have an underlying type of int. Third, x is
declared twice with external linkage using the types A and B. Fourth, the
enumerated type E in main is redeclared to give it an enumerator list. Lastly,
two pointers to x of types A* and B* are declared.

After the two declarations of x, it has the composite type of A and B which is
some enumerated type which is compatible with both types. But after the
redeclaration which gives the enumerated type E in main an enumerator list, A
and B are no longer compatible and there is no type which is compatible with
both A and B. Hence, the composite type can't be compatible with both types
which violates the definition of a composite type. GCC currently just chooses
the first enumerated type as the composite type, so the declaration of p is
accepted while q is rejected.

Though not explicitly spelled out in the standard, I think that the intent here
is that this code should be invalid, just like how this program is invalid:

struct S;
typedef struct S A;
int main(){
    struct S;
    typedef struct S B;
    extern A x;
    extern B x;
}

The lack of an enumerator list should prevent that type from being compatible
with another enumerated type in the same translation unit, just like how a
structure type lacking a definition prevents it from being compatible with
another structure type in the same translation unit. Because GCC allows two
enumerated types without enumerator lists to be compatible, it creates strange
scenarios like the first program which later add enumerator lists to make the
types incompatible. Another example is:

enum E:int;
typedef enum E A;
A x=0;
int main(){
    enum E:int;
    typedef enum E B;
    B*p=&x;
    B b=*p;
    enum E:int{h};
}

A and B are compatible at the point of *p, so maybe it is defined for that
reason. Alternatively, maybe it is undefined because A and B later become
incompatible and the access is done at runtime so it should consider that.

Clang currently accepts the first program because it allows an enumerated type
without an enumerator list to be compatible with any enumerated type with the
same tag and underlying type, regardless of the whether it has an enumerator
list. I don't think this is any more correct than what GCC currently does, and
the same issue can be recreated by giving the file scope enumerated type an
incompatible enumerator list.

Reply via email to