Skip to content

Commit 3b53c84

Browse files
AaronBallmantru
authored andcommitted
[C23] Handle type compatibility for enumerations better (#150282)
An enumeration is compatible with its underlying type, which means that code like the following should be accepted: struct A { int h; }; void func() { extern struct A x; enum E : int { e }; struct A { enum E h; }; extern struct A x; } because the structures are declared in different scopes, the two declarations of 'x' are both compatible. Note, the structural equivalence checker does not take scope into account, but that is something the C standard requires. This means we are accepting code we should be rejecting per the standard, like: void func() { struct A { int h; }; extern struct A x; enum E : int { e }; struct A { enum E h; }; extern struct A x; } Because the structures are declared in the same scope, the type compatibility rule require the structures to use the same types, not merely compatible ones. Fixes #149965 (cherry picked from commit 315e2e2)
1 parent 6df1a28 commit 3b53c84

File tree

2 files changed

+59
-1
lines changed

2 files changed

+59
-1
lines changed

clang/lib/AST/ASTStructuralEquivalence.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -873,7 +873,29 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
873873
else if (T1->getTypeClass() == Type::FunctionNoProto &&
874874
T2->getTypeClass() == Type::FunctionProto)
875875
TC = Type::FunctionNoProto;
876-
else
876+
else if (Context.LangOpts.C23 && !Context.StrictTypeSpelling &&
877+
(T1->getTypeClass() == Type::Enum ||
878+
T2->getTypeClass() == Type::Enum)) {
879+
// In C23, if not being strict about token equivalence, we need to handle
880+
// the case where one type is an enumeration and the other type is an
881+
// integral type.
882+
//
883+
// C23 6.7.3.3p16: The enumerated type is compatible with the underlying
884+
// type of the enumeration.
885+
//
886+
// Treat the enumeration as its underlying type and use the builtin type
887+
// class comparison.
888+
if (T1->getTypeClass() == Type::Enum) {
889+
T1 = T1->getAs<EnumType>()->getDecl()->getIntegerType();
890+
if (!T2->isBuiltinType() || T1.isNull()) // Sanity check
891+
return false;
892+
} else if (T2->getTypeClass() == Type::Enum) {
893+
T2 = T2->getAs<EnumType>()->getDecl()->getIntegerType();
894+
if (!T1->isBuiltinType() || T2.isNull()) // Sanity check
895+
return false;
896+
}
897+
TC = Type::Builtin;
898+
} else
877899
return false;
878900
}
879901

clang/test/C/C23/n3037.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,3 +401,39 @@ _Static_assert(0 == _Generic(inner_anon_tagged.untagged, struct { int i; } : 1,
401401
// unions and structures are both RecordDecl objects, whereas EnumDecl is not).
402402
enum { E_Untagged1 } nontag_enum; // both-note {{previous definition is here}}
403403
_Static_assert(0 == _Generic(nontag_enum, enum { E_Untagged1 } : 1, default : 0)); // both-error {{redefinition of enumerator 'E_Untagged1'}}
404+
405+
// Test that enumerations are compatible with their underlying type, but still
406+
// diagnose when "same type" is required rather than merely "compatible type".
407+
enum E1 : int { e1 }; // Fixed underlying type
408+
enum E2 { e2 }; // Unfixed underlying type, defaults to int or unsigned int
409+
410+
struct GH149965_1 { int h; };
411+
// This typeof trick is used to get the underlying type of the enumeration in a
412+
// platform agnostic way.
413+
struct GH149965_2 { __typeof__(+(enum E2){}) h; };
414+
void gh149965(void) {
415+
extern struct GH149965_1 x1; // c17-note {{previous declaration is here}}
416+
extern struct GH149965_2 x2; // c17-note {{previous declaration is here}}
417+
418+
// Both the structure and the variable declarations are fine because only a
419+
// compatible type is required, not the same type, because the structures are
420+
// declared in different scopes.
421+
struct GH149965_1 { enum E1 h; };
422+
struct GH149965_2 { enum E2 h; };
423+
424+
extern struct GH149965_1 x1; // c17-error {{redeclaration of 'x1'}}
425+
extern struct GH149965_2 x2; // c17-error {{redeclaration of 'x2'}}
426+
427+
// However, in the same scope, the same type is required, not just compatible
428+
// types.
429+
// FIXME: this should be an error in both C17 and C23 mode.
430+
struct GH149965_3 { int h; }; // c17-note {{previous definition is here}}
431+
struct GH149965_3 { enum E1 h; }; // c17-error {{redefinition of 'GH149965_3'}}
432+
433+
// For Clang, the composite type after declaration merging is the enumeration
434+
// type rather than an integer type.
435+
enum E1 *eptr;
436+
[[maybe_unused]] __typeof__(x1.h) *ptr = eptr;
437+
enum E2 *eptr2;
438+
[[maybe_unused]] __typeof__(x2.h) *ptr2 = eptr2;
439+
}

0 commit comments

Comments
 (0)