[llvm-branch-commits] [clang] release/21.x: [C23] Handle type compatibility for enumerations better (#150282) (PR #151137)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Jul 29 05:27:59 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: None (llvmbot)
<details>
<summary>Changes</summary>
Backport 315e2e2
Requested by: @<!-- -->AaronBallman
---
Full diff: https://github.com/llvm/llvm-project/pull/151137.diff
2 Files Affected:
- (modified) clang/lib/AST/ASTStructuralEquivalence.cpp (+23-1)
- (modified) clang/test/C/C23/n3037.c (+36)
``````````diff
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index 3aa6b37844103..f8996b264ed7b 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -873,7 +873,29 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
else if (T1->getTypeClass() == Type::FunctionNoProto &&
T2->getTypeClass() == Type::FunctionProto)
TC = Type::FunctionNoProto;
- else
+ else if (Context.LangOpts.C23 && !Context.StrictTypeSpelling &&
+ (T1->getTypeClass() == Type::Enum ||
+ T2->getTypeClass() == Type::Enum)) {
+ // In C23, if not being strict about token equivalence, we need to handle
+ // the case where one type is an enumeration and the other type is an
+ // integral type.
+ //
+ // C23 6.7.3.3p16: The enumerated type is compatible with the underlying
+ // type of the enumeration.
+ //
+ // Treat the enumeration as its underlying type and use the builtin type
+ // class comparison.
+ if (T1->getTypeClass() == Type::Enum) {
+ T1 = T1->getAs<EnumType>()->getDecl()->getIntegerType();
+ if (!T2->isBuiltinType() || T1.isNull()) // Sanity check
+ return false;
+ } else if (T2->getTypeClass() == Type::Enum) {
+ T2 = T2->getAs<EnumType>()->getDecl()->getIntegerType();
+ if (!T1->isBuiltinType() || T2.isNull()) // Sanity check
+ return false;
+ }
+ TC = Type::Builtin;
+ } else
return false;
}
diff --git a/clang/test/C/C23/n3037.c b/clang/test/C/C23/n3037.c
index ce6f4c4ea7acf..c5828133cee2e 100644
--- a/clang/test/C/C23/n3037.c
+++ b/clang/test/C/C23/n3037.c
@@ -401,3 +401,39 @@ _Static_assert(0 == _Generic(inner_anon_tagged.untagged, struct { int i; } : 1,
// unions and structures are both RecordDecl objects, whereas EnumDecl is not).
enum { E_Untagged1 } nontag_enum; // both-note {{previous definition is here}}
_Static_assert(0 == _Generic(nontag_enum, enum { E_Untagged1 } : 1, default : 0)); // both-error {{redefinition of enumerator 'E_Untagged1'}}
+
+// Test that enumerations are compatible with their underlying type, but still
+// diagnose when "same type" is required rather than merely "compatible type".
+enum E1 : int { e1 }; // Fixed underlying type
+enum E2 { e2 }; // Unfixed underlying type, defaults to int or unsigned int
+
+struct GH149965_1 { int h; };
+// This typeof trick is used to get the underlying type of the enumeration in a
+// platform agnostic way.
+struct GH149965_2 { __typeof__(+(enum E2){}) h; };
+void gh149965(void) {
+ extern struct GH149965_1 x1; // c17-note {{previous declaration is here}}
+ extern struct GH149965_2 x2; // c17-note {{previous declaration is here}}
+
+ // Both the structure and the variable declarations are fine because only a
+ // compatible type is required, not the same type, because the structures are
+ // declared in different scopes.
+ struct GH149965_1 { enum E1 h; };
+ struct GH149965_2 { enum E2 h; };
+
+ extern struct GH149965_1 x1; // c17-error {{redeclaration of 'x1'}}
+ extern struct GH149965_2 x2; // c17-error {{redeclaration of 'x2'}}
+
+ // However, in the same scope, the same type is required, not just compatible
+ // types.
+ // FIXME: this should be an error in both C17 and C23 mode.
+ struct GH149965_3 { int h; }; // c17-note {{previous definition is here}}
+ struct GH149965_3 { enum E1 h; }; // c17-error {{redefinition of 'GH149965_3'}}
+
+ // For Clang, the composite type after declaration merging is the enumeration
+ // type rather than an integer type.
+ enum E1 *eptr;
+ [[maybe_unused]] __typeof__(x1.h) *ptr = eptr;
+ enum E2 *eptr2;
+ [[maybe_unused]] __typeof__(x2.h) *ptr2 = eptr2;
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/151137
More information about the llvm-branch-commits
mailing list