Skip to content

Commit c68e36f

Browse files
author
git apple-llvm automerger
committed
Merge commit '3b53c84e338b' from llvm.org/release/21.x into stable/21.x
2 parents 69c7d2f + 3b53c84 commit c68e36f

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)