https://github.com/Tsche updated 
https://github.com/llvm/llvm-project/pull/218290

>From 549fb95d33cfd3b85ea8889a165a0129d7a27ede Mon Sep 17 00:00:00 2001
From: Matthias Wippich <[email protected]>
Date: Sun, 23 Aug 2026 23:19:48 +0200
Subject: [PATCH 1/5] [clang] Diagnose invalid/unavailable operator overloads
 for scoped flag_enums

---
 clang/docs/ReleaseNotes.md                    |  3 +
 .../clang/Basic/DiagnosticSemaKinds.td        |  3 +
 clang/lib/Sema/Sema.cpp                       | 74 +++++++++++++++++++
 clang/test/Sema/flag-enum.cpp                 | 62 ++++++++++++++++
 4 files changed, 142 insertions(+)
 create mode 100644 clang/test/Sema/flag-enum.cpp

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index ca0dbfa2af229..c63c235bd17a2 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -416,6 +416,9 @@ features cannot lower the translation-unit ABI level;
 - `-Wc++98-compat` now diagnoses explicit conversion functions in C++20 and
   later, matching the behavior in C++11 through C++17. (#GH161689)
 
+- `-Wflag-enum` now warns if any of the bitwise operators for scoped 
enumeration types with
+  the `[[clang::flag_enum]]` attribute are not available, ambiguous or deleted.
+
 ### Improvements to Clang's time-trace
 
 ### Improvements to Coverage Mapping
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 3a910c9c3f2b9..1871ca8fa56ec 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6721,6 +6721,9 @@ def ext_enumerator_increment_too_large : ExtWarn<
 def warn_flag_enum_constant_out_of_range : Warning<
   "enumeration value %0 is out of range of flags in enumeration type %1">,
   InGroup<FlagEnum>;
+def warn_flag_enum_operator : Warning<
+  "%1 is %select{|not available|ambiguous|deleted}2 for flag-like enumeration 
type %0%select{|: %4}3">,
+  InGroup<FlagEnum>;
 
 def err_vm_decl_in_file_scope : Error<
   "variably modified type declaration not allowed at file scope">;
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index f1e328ccba426..b28cfbd5403dc 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -21,6 +21,7 @@
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/OperationKinds.h"
 #include "clang/AST/PrettyDeclStackTrace.h"
 #include "clang/AST/StmtCXX.h"
 #include "clang/AST/TypeOrdering.h"
@@ -38,6 +39,7 @@
 #include "clang/Sema/Initialization.h"
 #include "clang/Sema/MultiplexExternalSemaSource.h"
 #include "clang/Sema/ObjCMethodList.h"
+#include "clang/Sema/Overload.h"
 #include "clang/Sema/RISCVIntrinsicManager.h"
 #include "clang/Sema/Scope.h"
 #include "clang/Sema/ScopeInfo.h"
@@ -1200,6 +1202,74 @@ static bool IsRecordFullyDefined(const CXXRecordDecl *RD,
   return Complete;
 }
 
+static void DiagnoseInvalidFlagEnumOperators(Sema &S, const EnumDecl *ED) {
+  assert(ED->hasAttr<FlagEnumAttr>() && "not a flag-like enum");
+  if (!ED->isScoped())
+    return;
+
+  QualType T = S.Context.getCanonicalTagType(ED);
+
+  Expr *LHS = new (S.Context) OpaqueValueExpr(SourceLocation(), T, VK_PRValue);
+  Expr *RHS = new (S.Context) OpaqueValueExpr(SourceLocation(), T, VK_PRValue);
+
+  OverloadedOperatorKind OPs[] = {OO_Pipe, OO_Amp, OO_Caret, OO_Tilde};
+  for (const auto OP : OPs) {
+    auto Name = S.Context.DeclarationNames.getCXXOperatorName(OP);
+    LookupResult R(S, Name, SourceLocation(), Sema::LookupOperatorName);
+
+    S.LookupName(R, S.TUScope);
+
+    OverloadCandidateSet CandidateSet{SourceLocation(),
+                                      OverloadCandidateSet::CSK_Operator};
+
+    SmallVector<Expr *, 2> Args;
+    if (OP == OO_Tilde) {
+      Args = {LHS};
+      S.LookupOverloadedUnaryOp(CandidateSet, OP, R.asUnresolvedSet(), Args);
+    } else {
+      Args = {LHS, RHS};
+      S.LookupOverloadedBinOp(CandidateSet, OP, R.asUnresolvedSet(), Args);
+    }
+
+    OverloadCandidateSet::iterator Best;
+    OverloadingResult Result =
+        CandidateSet.BestViableFunction(S, SourceLocation(), Best);
+
+    switch (Result) {
+    case OR_Success:
+      break;
+    case OR_No_Viable_Function: {
+      S.Diag(ED->getLocation(), diag::warn_flag_enum_operator)
+          << ED->getName() << Name.getAsString() << OR_No_Viable_Function
+          << false << "";
+      auto Cands = CandidateSet.CompleteCandidates(S, OCD_AllCandidates, Args);
+      CandidateSet.NoteCandidates(S, Args, Cands, Name.getAsString());
+      break;
+    }
+    case OR_Ambiguous: {
+      S.Diag(ED->getLocation(), diag::warn_flag_enum_operator)
+          << ED->getName() << Name.getAsString() << OR_Ambiguous << false << 
"";
+      auto Cands =
+          CandidateSet.CompleteCandidates(S, OCD_AmbiguousCandidates, Args);
+      CandidateSet.NoteCandidates(S, Args, Cands, Name.getAsString());
+      break;
+    }
+    case OR_Deleted: {
+      StringLiteral *Msg = Best->Function->getDeletedMessage();
+
+      CandidateSet.NoteCandidates(
+          PartialDiagnosticAt(ED->getLocation(),
+                              S.PDiag(diag::warn_flag_enum_operator)
+                                  << ED->getName() << Name.getAsString()
+                                  << OR_Deleted << (Msg != nullptr)
+                                  << (Msg ? Msg->getString() : "")),
+          S, OCD_AllCandidates, Args, Name.getAsString());
+      break;
+    }
+    }
+  }
+}
+
 void Sema::getSortedUnusedLocalTypedefNameCandidates(
     SmallVectorImpl<const TypedefNameDecl *> &Sorted) const {
   // The candidates are collected while iterating a Scope's SmallPtrSet, so 
sort
@@ -1712,6 +1782,10 @@ void Sema::ActOnEndOfTranslationUnit() {
     }
   }
 
+  for (const auto &[ED, _] : FlagBitsCache)
+    if (!Diags.isIgnored(diag::warn_flag_enum_operator, ED->getLocation()))
+      DiagnoseInvalidFlagEnumOperators(*this, ED);
+
   AnalysisWarnings.IssueWarnings(Context.getTranslationUnitDecl());
 
   if (Context.hasAnyFunctionEffects())
diff --git a/clang/test/Sema/flag-enum.cpp b/clang/test/Sema/flag-enum.cpp
new file mode 100644
index 0000000000000..6e95a7c8f6691
--- /dev/null
+++ b/clang/test/Sema/flag-enum.cpp
@@ -0,0 +1,62 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+
+enum Unscoped { U0 = 1, U1 = 8 };
+enum class Scoped { S0 = 1, S1 = 8 };
+enum [[clang::flag_enum]] UnscopedFlag { D0 = 1, D1 = 8 };
+enum class [[clang::flag_enum]] WithOps { D0 = 1, D1 = 8 };
+enum class [[clang::flag_enum]] WithoutOps { D0 = 1, D1 = 8 };
+// expected-warning@-1 {{operator| is not available for flag-like enumeration 
type WithoutOps}} \
+// expected-warning@-1 {{operator& is not available for flag-like enumeration 
type WithoutOps}} \
+// expected-warning@-1 {{operator^ is not available for flag-like enumeration 
type WithoutOps}} \
+// expected-warning@-1 {{operator~ is not available for flag-like enumeration 
type WithoutOps}}
+
+WithOps operator|(WithOps L, WithOps R) {
+  return static_cast<WithOps>(static_cast<unsigned>(L) | 
static_cast<unsigned>(R));
+}
+
+WithOps operator&(WithOps L, WithOps R) {
+  return static_cast<WithOps>(static_cast<unsigned>(L) & 
static_cast<unsigned>(R));
+}
+
+WithOps operator^(WithOps L, WithOps R) {
+  return static_cast<WithOps>(static_cast<unsigned>(L) ^ 
static_cast<unsigned>(R));
+}
+
+WithOps operator~(WithOps L) {
+  return static_cast<WithOps>(~static_cast<unsigned>(L));
+}
+
+namespace test {
+enum class [[clang::flag_enum]] Foo { A=1, B=2 };
+// expected-warning@-1 {{operator| is ambiguous for flag-like enumeration type 
Foo}} \
+//   expected-note@#candidate1 {{candidate function}} \
+//   expected-note@#candidate2 {{candidate function}} \
+// expected-warning@-1 {{operator& is deleted for flag-like enumeration type 
Foo}} \
+//   expected-note@#deleted1 {{candidate function has been explicitly 
deleted}} \
+// expected-warning@-1 {{operator^ is deleted for flag-like enumeration type 
Foo: reason}} \
+//   expected-note@#deleted2 {{candidate function has been explicitly 
deleted}} \
+// expected-warning@-1 {{operator~ is not available for flag-like enumeration 
type Foo}}
+
+constexpr Foo operator|(Foo lhs, Foo rhs) { // #candidate1
+  return static_cast<Foo>(static_cast<unsigned>(lhs) | 
static_cast<unsigned>(rhs));
+}
+
+Foo operator&(Foo L, Foo R) = delete; // #deleted1
+Foo operator^(Foo L, Foo R) = delete("reason"); // #deleted2
+}
+
+constexpr test::Foo operator|(test::Foo lhs, test::Foo rhs) { // #candidate2
+  return static_cast<test::Foo>(static_cast<unsigned>(lhs) | 
static_cast<unsigned>(rhs));
+}
+
+
+template <class T>
+struct Foo {
+  enum class [[clang::flag_enum]] Bar : T { A=1, B=2 }; // #dependent-enum
+};
+
+template struct Foo<int>;
+// expected-warning@#dependent-enum {{operator| is not available for flag-like 
enumeration type Bar}} \
+// expected-warning@#dependent-enum {{operator& is not available for flag-like 
enumeration type Bar}} \
+// expected-warning@#dependent-enum {{operator^ is not available for flag-like 
enumeration type Bar}} \
+// expected-warning@#dependent-enum {{operator~ is not available for flag-like 
enumeration type Bar}}

>From 1514753a41c4ece387886e2f66afe79e95a9ddab Mon Sep 17 00:00:00 2001
From: Matthias Wippich <[email protected]>
Date: Mon, 24 Aug 2026 17:01:00 +0200
Subject: [PATCH 2/5] move RHS/LHS onto stack

---
 clang/lib/Sema/Sema.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index b28cfbd5403dc..4010813cf7d9d 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1209,8 +1209,8 @@ static void DiagnoseInvalidFlagEnumOperators(Sema &S, 
const EnumDecl *ED) {
 
   QualType T = S.Context.getCanonicalTagType(ED);
 
-  Expr *LHS = new (S.Context) OpaqueValueExpr(SourceLocation(), T, VK_PRValue);
-  Expr *RHS = new (S.Context) OpaqueValueExpr(SourceLocation(), T, VK_PRValue);
+  auto LHS = OpaqueValueExpr(SourceLocation(), T, VK_PRValue);
+  auto RHS = OpaqueValueExpr(SourceLocation(), T, VK_PRValue);
 
   OverloadedOperatorKind OPs[] = {OO_Pipe, OO_Amp, OO_Caret, OO_Tilde};
   for (const auto OP : OPs) {
@@ -1224,10 +1224,10 @@ static void DiagnoseInvalidFlagEnumOperators(Sema &S, 
const EnumDecl *ED) {
 
     SmallVector<Expr *, 2> Args;
     if (OP == OO_Tilde) {
-      Args = {LHS};
+      Args = {&LHS};
       S.LookupOverloadedUnaryOp(CandidateSet, OP, R.asUnresolvedSet(), Args);
     } else {
-      Args = {LHS, RHS};
+      Args = {&LHS, &RHS};
       S.LookupOverloadedBinOp(CandidateSet, OP, R.asUnresolvedSet(), Args);
     }
 

>From a04ff8ab2464b65aeb2191deed228fef6f4e5309 Mon Sep 17 00:00:00 2001
From: Matthias Wippich <[email protected]>
Date: Thu, 27 Aug 2026 23:41:40 +0200
Subject: [PATCH 3/5] remove definitions in test, clean up diagnostic arguments

---
 clang/lib/Sema/Sema.cpp       | 10 +++----
 clang/test/Sema/flag-enum.cpp | 53 ++++++++++++-----------------------
 2 files changed, 22 insertions(+), 41 deletions(-)

diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 4010813cf7d9d..1b366cf09da6a 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1240,15 +1240,14 @@ static void DiagnoseInvalidFlagEnumOperators(Sema &S, 
const EnumDecl *ED) {
       break;
     case OR_No_Viable_Function: {
       S.Diag(ED->getLocation(), diag::warn_flag_enum_operator)
-          << ED->getName() << Name.getAsString() << OR_No_Viable_Function
-          << false << "";
+          << ED << Name << OR_No_Viable_Function << false << "";
       auto Cands = CandidateSet.CompleteCandidates(S, OCD_AllCandidates, Args);
       CandidateSet.NoteCandidates(S, Args, Cands, Name.getAsString());
       break;
     }
     case OR_Ambiguous: {
       S.Diag(ED->getLocation(), diag::warn_flag_enum_operator)
-          << ED->getName() << Name.getAsString() << OR_Ambiguous << false << 
"";
+          << ED << Name << OR_Ambiguous << false << "";
       auto Cands =
           CandidateSet.CompleteCandidates(S, OCD_AmbiguousCandidates, Args);
       CandidateSet.NoteCandidates(S, Args, Cands, Name.getAsString());
@@ -1256,12 +1255,11 @@ static void DiagnoseInvalidFlagEnumOperators(Sema &S, 
const EnumDecl *ED) {
     }
     case OR_Deleted: {
       StringLiteral *Msg = Best->Function->getDeletedMessage();
-
       CandidateSet.NoteCandidates(
           PartialDiagnosticAt(ED->getLocation(),
                               S.PDiag(diag::warn_flag_enum_operator)
-                                  << ED->getName() << Name.getAsString()
-                                  << OR_Deleted << (Msg != nullptr)
+                                  << ED << Name << OR_Deleted
+                                  << (Msg != nullptr)
                                   << (Msg ? Msg->getString() : "")),
           S, OCD_AllCandidates, Args, Name.getAsString());
       break;
diff --git a/clang/test/Sema/flag-enum.cpp b/clang/test/Sema/flag-enum.cpp
index 6e95a7c8f6691..1f1da08d172fa 100644
--- a/clang/test/Sema/flag-enum.cpp
+++ b/clang/test/Sema/flag-enum.cpp
@@ -5,50 +5,33 @@ enum class Scoped { S0 = 1, S1 = 8 };
 enum [[clang::flag_enum]] UnscopedFlag { D0 = 1, D1 = 8 };
 enum class [[clang::flag_enum]] WithOps { D0 = 1, D1 = 8 };
 enum class [[clang::flag_enum]] WithoutOps { D0 = 1, D1 = 8 };
-// expected-warning@-1 {{operator| is not available for flag-like enumeration 
type WithoutOps}} \
-// expected-warning@-1 {{operator& is not available for flag-like enumeration 
type WithoutOps}} \
-// expected-warning@-1 {{operator^ is not available for flag-like enumeration 
type WithoutOps}} \
-// expected-warning@-1 {{operator~ is not available for flag-like enumeration 
type WithoutOps}}
+// expected-warning@-1 {{'operator|' is not available for flag-like 
enumeration type 'WithoutOps'}} \
+// expected-warning@-1 {{'operator&' is not available for flag-like 
enumeration type 'WithoutOps'}} \
+// expected-warning@-1 {{'operator^' is not available for flag-like 
enumeration type 'WithoutOps'}} \
+// expected-warning@-1 {{'operator~' is not available for flag-like 
enumeration type 'WithoutOps'}}
 
-WithOps operator|(WithOps L, WithOps R) {
-  return static_cast<WithOps>(static_cast<unsigned>(L) | 
static_cast<unsigned>(R));
-}
-
-WithOps operator&(WithOps L, WithOps R) {
-  return static_cast<WithOps>(static_cast<unsigned>(L) & 
static_cast<unsigned>(R));
-}
-
-WithOps operator^(WithOps L, WithOps R) {
-  return static_cast<WithOps>(static_cast<unsigned>(L) ^ 
static_cast<unsigned>(R));
-}
-
-WithOps operator~(WithOps L) {
-  return static_cast<WithOps>(~static_cast<unsigned>(L));
-}
+WithOps operator|(WithOps L, WithOps R);
+WithOps operator&(WithOps L, WithOps R);
+WithOps operator^(WithOps L, WithOps R);
+WithOps operator~(WithOps L);
 
 namespace test {
 enum class [[clang::flag_enum]] Foo { A=1, B=2 };
-// expected-warning@-1 {{operator| is ambiguous for flag-like enumeration type 
Foo}} \
+// expected-warning@-1 {{'operator|' is ambiguous for flag-like enumeration 
type 'Foo'}} \
 //   expected-note@#candidate1 {{candidate function}} \
 //   expected-note@#candidate2 {{candidate function}} \
-// expected-warning@-1 {{operator& is deleted for flag-like enumeration type 
Foo}} \
+// expected-warning@-1 {{'operator&' is deleted for flag-like enumeration type 
'Foo'}} \
 //   expected-note@#deleted1 {{candidate function has been explicitly 
deleted}} \
-// expected-warning@-1 {{operator^ is deleted for flag-like enumeration type 
Foo: reason}} \
+// expected-warning@-1 {{'operator^' is deleted for flag-like enumeration type 
'Foo': reason}} \
 //   expected-note@#deleted2 {{candidate function has been explicitly 
deleted}} \
-// expected-warning@-1 {{operator~ is not available for flag-like enumeration 
type Foo}}
-
-constexpr Foo operator|(Foo lhs, Foo rhs) { // #candidate1
-  return static_cast<Foo>(static_cast<unsigned>(lhs) | 
static_cast<unsigned>(rhs));
-}
+// expected-warning@-1 {{'operator~' is not available for flag-like 
enumeration type 'Foo'}}
 
+Foo operator|(Foo lhs, Foo rhs); // #candidate1
 Foo operator&(Foo L, Foo R) = delete; // #deleted1
 Foo operator^(Foo L, Foo R) = delete("reason"); // #deleted2
 }
 
-constexpr test::Foo operator|(test::Foo lhs, test::Foo rhs) { // #candidate2
-  return static_cast<test::Foo>(static_cast<unsigned>(lhs) | 
static_cast<unsigned>(rhs));
-}
-
+constexpr test::Foo operator|(test::Foo lhs, test::Foo rhs); // #candidate2
 
 template <class T>
 struct Foo {
@@ -56,7 +39,7 @@ struct Foo {
 };
 
 template struct Foo<int>;
-// expected-warning@#dependent-enum {{operator| is not available for flag-like 
enumeration type Bar}} \
-// expected-warning@#dependent-enum {{operator& is not available for flag-like 
enumeration type Bar}} \
-// expected-warning@#dependent-enum {{operator^ is not available for flag-like 
enumeration type Bar}} \
-// expected-warning@#dependent-enum {{operator~ is not available for flag-like 
enumeration type Bar}}
+// expected-warning@#dependent-enum {{'operator|' is not available for 
flag-like enumeration type 'Bar'}} \
+// expected-warning@#dependent-enum {{'operator&' is not available for 
flag-like enumeration type 'Bar'}} \
+// expected-warning@#dependent-enum {{'operator^' is not available for 
flag-like enumeration type 'Bar'}} \
+// expected-warning@#dependent-enum {{'operator~' is not available for 
flag-like enumeration type 'Bar'}}

>From 7a90cf3853680aa1831f78c014b80bd6bbe2c7c8 Mon Sep 17 00:00:00 2001
From: Matthias Wippich <[email protected]>
Date: Thu, 10 Sep 2026 05:22:37 +0200
Subject: [PATCH 4/5] improve tests

---
 clang/test/Sema/flag-enum.cpp | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/clang/test/Sema/flag-enum.cpp b/clang/test/Sema/flag-enum.cpp
index 1f1da08d172fa..c962c3ab93963 100644
--- a/clang/test/Sema/flag-enum.cpp
+++ b/clang/test/Sema/flag-enum.cpp
@@ -11,9 +11,9 @@ enum class [[clang::flag_enum]] WithoutOps { D0 = 1, D1 = 8 };
 // expected-warning@-1 {{'operator~' is not available for flag-like 
enumeration type 'WithoutOps'}}
 
 WithOps operator|(WithOps L, WithOps R);
-WithOps operator&(WithOps L, WithOps R);
-WithOps operator^(WithOps L, WithOps R);
-WithOps operator~(WithOps L);
+WithOps operator&(WithOps const& L, WithOps const& R);
+WithOps operator^(WithOps L, WithOps&& R);
+WithOps operator~(WithOps const&& L);
 
 namespace test {
 enum class [[clang::flag_enum]] Foo { A=1, B=2 };
@@ -25,10 +25,12 @@ enum class [[clang::flag_enum]] Foo { A=1, B=2 };
 // expected-warning@-1 {{'operator^' is deleted for flag-like enumeration type 
'Foo': reason}} \
 //   expected-note@#deleted2 {{candidate function has been explicitly 
deleted}} \
 // expected-warning@-1 {{'operator~' is not available for flag-like 
enumeration type 'Foo'}}
+//   expected-note@#nonviable {{candidate function not viable: expects an 
lvalue for 1st argument}}
 
 Foo operator|(Foo lhs, Foo rhs); // #candidate1
 Foo operator&(Foo L, Foo R) = delete; // #deleted1
 Foo operator^(Foo L, Foo R) = delete("reason"); // #deleted2
+Foo operator~(Foo& L); // #nonviable
 }
 
 constexpr test::Foo operator|(test::Foo lhs, test::Foo rhs); // #candidate2

>From 7c259f5f6f622ac4b7441f0712a43e8ec74a34e9 Mon Sep 17 00:00:00 2001
From: Matthias Wippich <[email protected]>
Date: Mon, 5 Oct 2026 02:26:31 +0200
Subject: [PATCH 5/5] restore eager cache population, add pch/modules tests

---
 clang/lib/Sema/Sema.cpp           | 13 +++++++---
 clang/lib/Sema/SemaDecl.cpp       | 22 +++++++++++-----
 clang/test/Modules/flag-enum.cppm | 42 +++++++++++++++++++++++++++++++
 clang/test/PCH/flag-enum.c        | 31 -----------------------
 clang/test/PCH/flag-enum.cpp      | 35 ++++++++++++++++++++++++++
 5 files changed, 102 insertions(+), 41 deletions(-)
 create mode 100644 clang/test/Modules/flag-enum.cppm
 delete mode 100644 clang/test/PCH/flag-enum.c
 create mode 100644 clang/test/PCH/flag-enum.cpp

diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 2b143024a1ff5..dffe41a6b8e27 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1424,6 +1424,15 @@ void Sema::ActOnEndOfTranslationUnit() {
                      }),
       UnusedFileScopedDecls.end());
 
+  for (const auto &[ED, _] : FlagBitsCache) {
+    const EnumDecl *Definition = ED->getDefinition();
+    if (!Definition || Definition->isFromASTFile())
+      continue;
+    if (!Diags.isIgnored(diag::warn_flag_enum_operator,
+                         Definition->getLocation()))
+      DiagnoseInvalidFlagEnumOperators(*this, Definition);
+  }
+
   if (TUKind == TU_Prefix) {
     // Translation unit prefixes don't need any of the checking below.
     if (!PP.isIncrementalProcessingEnabled())
@@ -1790,10 +1799,6 @@ void Sema::ActOnEndOfTranslationUnit() {
     }
   }
 
-  for (const auto &[ED, _] : FlagBitsCache)
-    if (!Diags.isIgnored(diag::warn_flag_enum_operator, ED->getLocation()))
-      DiagnoseInvalidFlagEnumOperators(*this, ED);
-
   AnalysisWarnings.IssueWarnings(Context.getTranslationUnitDecl());
 
   if (Context.hasAnyFunctionEffects())
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 88f25903d97c3..45f02633d7018 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -21117,12 +21117,9 @@ static void CheckForDuplicateEnumValues(Sema &S, 
ArrayRef<Decl *> Elements,
   }
 }
 
-bool Sema::IsValueInFlagEnum(const EnumDecl *ED, const llvm::APInt &Val,
-                             bool AllowMask) const {
-  assert(ED->isClosedFlag() && "looking for value in non-flag or open enum");
-  assert(ED->isCompleteDefinition() && "expected enum definition");
-
-  auto R = FlagBitsCache.try_emplace(ED);
+static llvm::APInt CacheFlagEnum(const Sema &S, const EnumDecl *ED) {
+  assert(ED->hasAttr<FlagEnumAttr>() && "not a flag-like enum");
+  auto R = S.FlagBitsCache.try_emplace(ED);
   llvm::APInt &FlagBits = R.first->second;
 
   if (R.second) {
@@ -21133,6 +21130,15 @@ bool Sema::IsValueInFlagEnum(const EnumDecl *ED, const 
llvm::APInt &Val,
         FlagBits = FlagBits.zext(EVal.getBitWidth()) | EVal;
     }
   }
+  return FlagBits;
+}
+
+bool Sema::IsValueInFlagEnum(const EnumDecl *ED, const llvm::APInt &Val,
+                             bool AllowMask) const {
+  assert(ED->isClosedFlag() && "looking for value in non-flag or open enum");
+  assert(ED->isCompleteDefinition() && "expected enum definition");
+
+  llvm::APInt FlagBits = CacheFlagEnum(*this, ED);
 
   // 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
@@ -21343,6 +21349,10 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, 
SourceRange BraceRange,
   CheckForDuplicateEnumValues(*this, Elements, Enum, EnumType);
   CheckForComparisonInEnumInitializer(*this, Enum);
 
+  if (Enum->hasAttr<FlagEnumAttr>()) {
+    (void)CacheFlagEnum(*this, Enum);
+  }
+
   if (Enum->isClosedFlag()) {
     for (Decl *D : Elements) {
       EnumConstantDecl *ECD = cast_or_null<EnumConstantDecl>(D);
diff --git a/clang/test/Modules/flag-enum.cppm 
b/clang/test/Modules/flag-enum.cppm
new file mode 100644
index 0000000000000..fb488fb7eb1e3
--- /dev/null
+++ b/clang/test/Modules/flag-enum.cppm
@@ -0,0 +1,42 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: %clang_cc1 -std=c++11 -x c++ -fmodules -emit-module \
+// RUN:   -fmodule-name=ClangModule %t/module.modulemap -o %t/ClangModule.pcm \
+// RUN:   -verify=module
+// RUN: %clang_cc1 -std=c++11 -fmodules -fno-implicit-modules \
+// RUN:   -fmodule-map-file=%t/module.modulemap \
+// RUN:   -fmodule-file=ClangModule=%t/ClangModule.pcm -fsyntax-only \
+// RUN:   -verify=use %t/use-clang-module.cpp
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/NamedModule.cppm \
+// RUN:   -o %t/NamedModule.pcm -verify=module
+// RUN: %clang_cc1 -std=c++20 -fmodule-file=NamedModule=%t/NamedModule.pcm \
+// RUN:   -fsyntax-only -verify=use %t/use-named-module.cpp
+
+//--- module.modulemap
+module ClangModule {
+  header "clang-module.h"
+}
+
+//--- clang-module.h
+enum class [[clang::flag_enum]] ClangModuleFlags { A = 1, B = 2 };
+// module-warning@-1 {{'operator|' is not available for flag-like enumeration 
type 'ClangModuleFlags'}}
+// module-warning@-2 {{'operator&' is not available for flag-like enumeration 
type 'ClangModuleFlags'}}
+// module-warning@-3 {{'operator^' is not available for flag-like enumeration 
type 'ClangModuleFlags'}}
+// module-warning@-4 {{'operator~' is not available for flag-like enumeration 
type 'ClangModuleFlags'}}
+
+//--- use-clang-module.cpp
+// use-no-diagnostics
+#include "clang-module.h"
+
+//--- NamedModule.cppm
+export module NamedModule;
+export enum class [[clang::flag_enum]] NamedModuleFlags { A = 1, B = 2 };
+// module-warning@-1 {{'operator|' is not available for flag-like enumeration 
type 'NamedModuleFlags'}}
+// module-warning@-2 {{'operator&' is not available for flag-like enumeration 
type 'NamedModuleFlags'}}
+// module-warning@-3 {{'operator^' is not available for flag-like enumeration 
type 'NamedModuleFlags'}}
+// module-warning@-4 {{'operator~' is not available for flag-like enumeration 
type 'NamedModuleFlags'}}
+
+//--- use-named-module.cpp
+// use-no-diagnostics
+import NamedModule;
+NamedModuleFlags flags;
diff --git a/clang/test/PCH/flag-enum.c b/clang/test/PCH/flag-enum.c
deleted file mode 100644
index 2d9bada686184..0000000000000
--- a/clang/test/PCH/flag-enum.c
+++ /dev/null
@@ -1,31 +0,0 @@
-// 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
diff --git a/clang/test/PCH/flag-enum.cpp b/clang/test/PCH/flag-enum.cpp
new file mode 100644
index 0000000000000..82a7be53243d7
--- /dev/null
+++ b/clang/test/PCH/flag-enum.cpp
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -std=c++11 -emit-pch -o %t -verify %s
+// RUN: %clang_cc1 -std=c++11 -include-pch %t -fsyntax-only -verify %s
+
+#ifndef HEADER
+#define HEADER
+
+enum class [[clang::flag_enum]] FlagEnum {
+  A = 0x1,
+  B = 0x2,
+  C = 0x4
+};
+// expected-warning@-5 {{'operator&' is not available for flag-like 
enumeration type 'FlagEnum'}} \
+// expected-warning@-5 {{'operator^' is not available for flag-like 
enumeration type 'FlagEnum'}} \
+// expected-warning@-5 {{'operator~' is not available for flag-like 
enumeration type 'FlagEnum'}}
+
+constexpr FlagEnum operator|(FlagEnum lhs, FlagEnum rhs) {
+  return static_cast<FlagEnum>(static_cast<int>(lhs) | static_cast<int>(rhs));
+}
+
+#else
+
+int f(FlagEnum flags) {
+  switch (flags) {
+  case FlagEnum::A:
+    return 1;
+  case FlagEnum::B | FlagEnum::C: // no-warning
+    return 2;
+  case FlagEnum(8): // expected-warning {{case value not in enumerated type 
'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