https://github.com/avikivity updated 
https://github.com/llvm/llvm-project/pull/223244

>From 73720001d4568831bc16c90e4c8356f7f7b76c4a Mon Sep 17 00:00:00 2001
From: Avi Kivity <[email protected]>
Date: Sun, 13 Sep 2026 16:48:31 +0300
Subject: [PATCH] Revert "[clang][NFC] Refactor flag enum caching (#210632)"

This reverts commit f5437c4d4f2e9920614d856158d9683d650acffa.

That change moved population of Sema::FlagBitsCache out of
IsValueInFlagEnum and into ActOnEnumBody, and turned the lookup into
DenseMap::at(). ActOnEnumBody only runs for enum definitions that are
parsed in the current translation unit, so an EnumDecl deserialized from
a PCH never gets a cache entry and DenseMap::at() dereferences end(),
crashing clang in builds without assertions.

A switch over any enum with the flag_enum attribute that is defined in a
PCH is enough to reproduce. The crash cannot be avoided by turning the
switch diagnostics off: unlike DiagnoseAssignmentEnum, the switch path in
ActOnFinishSwitchStmt has no Diags.isIgnored() guard, because it runs the
analysis regardless in order to record the result in the AST.

In practice this breaks any translation unit that instantiates
std::basic_regex<char> while <regex> is in a PCH, since libstdc++ marks
std::regex_constants::syntax_option_type as a flag enum and
_Compiler<_TraitsT>::_S_validate switches over it. That became reachable
with 44b33260a38c, which made clang honor the gnu:: spelling of the
attribute, but the underlying defect is the one reverted here.

Add a regression test for the PCH path. It checks the computed flag bits
rather than just the absence of a crash, so that a silently empty cache
entry fails the test instead of passing it.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>

Fixes #223243
---
 clang/include/clang/Sema/Sema.h |  2 +-
 clang/lib/Sema/SemaDecl.cpp     | 26 +++++++++++---------------
 clang/test/PCH/flag-enum.c      | 31 +++++++++++++++++++++++++++++++
 3 files changed, 43 insertions(+), 16 deletions(-)
 create mode 100644 clang/test/PCH/flag-enum.c

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 4ff4c669a6b703..d32eb3600ce548 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3580,7 +3580,7 @@ class Sema final : public SemaBase {
 
   /// A cache of the flags available in enumerations with the flag_enum
   /// attribute.
-  llvm::DenseMap<const EnumDecl *, llvm::APInt> FlagBitsCache;
+  mutable llvm::DenseMap<const EnumDecl *, llvm::APInt> FlagBitsCache;
 
   /// A cache of enumerator values for enums checked by -Wassign-enum.
   llvm::DenseMap<const EnumDecl *, llvm::SmallVector<llvm::APSInt>>
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 5de5821fe263e5..8f5a8e5133cc91 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -21116,7 +21116,17 @@ bool Sema::IsValueInFlagEnum(const EnumDecl *ED, const 
llvm::APInt &Val,
   assert(ED->isClosedFlag() && "looking for value in non-flag or open enum");
   assert(ED->isCompleteDefinition() && "expected enum definition");
 
-  llvm::APInt FlagBits = FlagBitsCache.at(ED);
+  auto R = FlagBitsCache.try_emplace(ED);
+  llvm::APInt &FlagBits = R.first->second;
+
+  if (R.second) {
+    for (auto *E : ED->enumerators()) {
+      const auto &EVal = E->getInitVal();
+      // Only single-bit enumerators introduce new flag values.
+      if (EVal.isPowerOf2())
+        FlagBits = FlagBits.zext(EVal.getBitWidth()) | EVal;
+    }
+  }
 
   // A value is in a flag enum if either its bits are a subset of the enum's
   // flag bits (the first condition) or we are allowing masks and the same is
@@ -21327,20 +21337,6 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, 
SourceRange BraceRange,
   CheckForDuplicateEnumValues(*this, Elements, Enum, EnumType);
   CheckForComparisonInEnumInitializer(*this, Enum);
 
-  if (Enum->hasAttr<FlagEnumAttr>()) {
-    auto R = FlagBitsCache.try_emplace(Enum);
-    llvm::APInt &FlagBits = R.first->second;
-
-    if (R.second) {
-      for (auto *E : Enum->enumerators()) {
-        const auto &EVal = E->getInitVal();
-        // Only single-bit enumerators introduce new flag values.
-        if (EVal.isPowerOf2())
-          FlagBits = FlagBits.zext(EVal.getBitWidth()) | EVal;
-      }
-    }
-  }
-
   if (Enum->isClosedFlag()) {
     for (Decl *D : Elements) {
       EnumConstantDecl *ECD = cast_or_null<EnumConstantDecl>(D);
diff --git a/clang/test/PCH/flag-enum.c b/clang/test/PCH/flag-enum.c
new file mode 100644
index 00000000000000..2d9bada686184f
--- /dev/null
+++ b/clang/test/PCH/flag-enum.c
@@ -0,0 +1,31 @@
+// Check that an enum with the flag_enum attribute that is deserialized from a
+// PCH does not crash, and that its flag bits are still computed correctly.
+
+// RUN: %clang_cc1 -emit-pch -o %t %s
+// RUN: %clang_cc1 -include-pch %t -fsyntax-only -verify %s
+
+#ifndef HEADER
+#define HEADER
+
+enum __attribute__((flag_enum)) FlagEnum {
+  A = 0x1,
+  B = 0x2,
+  C = 0x4,
+};
+
+#else
+
+int f(enum FlagEnum e) {
+  switch (e) {
+  case A:
+    return 1;
+  case B | C: // no-warning
+    return 2;
+  case 0x8: // expected-warning {{case value not in enumerated type 'enum 
FlagEnum'}}
+    return 3;
+  default:
+    return 0;
+  }
+}
+
+#endif

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to