[clang] Fixed #207357 (PR #210353)

Abdulmalek Almkainzi via cfe-commits cfe-commits at lists.llvm.org
Fri Jul 17 07:57:18 PDT 2026


https://github.com/aalmkainzi created https://github.com/llvm/llvm-project/pull/210353

added a new `ParsingGenericAssociationType` flag that is managed by `GenericAssociationTypeRAIIObject`. If that flag is true, when encountering a `:` after enum name, try to parse next token as a type, if successful, then that's the underlying type, if failed, then that is the end of the enum declaration, and the colon will be parsed as the generic association.

to check if the next tokens are a type, I added a new function `isNextCXXTypeId`, which works the same as `isCXXTypeId`, but starts the check one token ahead.

>From 3377a5323cca1654f8a8505ff063bc60dea697b8 Mon Sep 17 00:00:00 2001
From: Abdulmalek Almkainzi <aalmkainzi at gmail.com>
Date: Fri, 17 Jul 2026 00:33:31 +0300
Subject: [PATCH] Fixed #207357

added a new ParsingGenericAssociationType flag that is managed by GenericAssociationTypeRAIIObject. Using that, when encountering a : after enum name, try to parse next token as a type, if successful, then that's the underlying type, if failed, then that is the end of the enum declaration, and the colon will be parsed as the generic association.
---
 clang/include/clang/Parse/Parser.h               | 13 +++++++++++++
 clang/include/clang/Parse/RAIIObjectsForParser.h | 15 +++++++++++++++
 clang/lib/Parse/ParseDecl.cpp                    |  4 +++-
 clang/lib/Parse/ParseDeclCXX.cpp                 |  3 ++-
 clang/lib/Parse/ParseExpr.cpp                    |  3 ++-
 clang/lib/Parse/ParseTentative.cpp               |  8 ++++++++
 clang/test/Parser/c23-enum-generic-assoc.c       | 10 ++++++++++
 7 files changed, 53 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/Parser/c23-enum-generic-assoc.c

diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 163aa483a84e3..f6b259ddaff8d 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -279,6 +279,7 @@ class Parser : public CodeCompletionHandler {
 
 public:
   friend class ColonProtectionRAIIObject;
+  friend class GenericAssociationTypeRAIIObject;
   friend class PoisonSEHIdentifiersRAIIObject;
   friend class ParenBraceBracketBalancer;
   friend class BalancedDelimiterTracker;
@@ -4490,6 +4491,11 @@ class Parser : public CodeCompletionHandler {
   /// ColonProtectionRAIIObject RAII object.
   bool ColonIsSacred;
 
+  // ParsingGenericAssociationType - Currently parsing the typename in
+  // _Generic association. This is to consume the colon if what comes after it
+  // is a type.
+  bool ParsingGenericAssociationType;
+
   /// ParseCXXAmbiguousParenExpression - We have parsed the left paren of a
   /// parenthesized ambiguous type-id. This uses tentative parsing to
   /// disambiguate based on the context past the parens.
@@ -8677,6 +8683,13 @@ class Parser : public CodeCompletionHandler {
     return isCXXTypeId(Context, isAmbiguous);
   }
 
+  bool isNextCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous);
+
+  bool isNextCXXTypeId(TentativeCXXTypeIdContext Context) {
+    bool isAmbiguous;
+    return isNextCXXTypeId(Context, isAmbiguous);
+  }
+
   /// TPResult - Used as the result value for functions whose purpose is to
   /// disambiguate C++ constructs by "tentatively parsing" them.
   enum class TPResult { True, False, Ambiguous, Error };
diff --git a/clang/include/clang/Parse/RAIIObjectsForParser.h b/clang/include/clang/Parse/RAIIObjectsForParser.h
index 3adcbfe9d7016..747dbe90b1bf6 100644
--- a/clang/include/clang/Parse/RAIIObjectsForParser.h
+++ b/clang/include/clang/Parse/RAIIObjectsForParser.h
@@ -290,6 +290,21 @@ namespace clang {
     }
   };
 
+  class GenericAssociationTypeRAIIObject {
+    Parser &P;
+    bool OldVal;
+
+  public:
+    GenericAssociationTypeRAIIObject(Parser &p, bool Value = true)
+        : P(p), OldVal(P.ParsingGenericAssociationType) {
+      P.ParsingGenericAssociationType = Value;
+    }
+
+    void restore() { P.ParsingGenericAssociationType = OldVal; }
+
+    ~GenericAssociationTypeRAIIObject() { restore(); }
+  };
+
   /// Activates OpenMP parsing mode to preseve OpenMP specific annotation
   /// tokens.
   class ParsingOpenMPDirectiveRAII {
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 88f07bb104fcb..6f551a00df0e9 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -5200,7 +5200,9 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
       getCurScope()->isClassScope() && ScopedEnumKWLoc.isInvalid() && Name;
 
   // Parse the fixed underlying type.
-  if (Tok.is(tok::colon)) {
+  if (Tok.is(tok::colon) &&
+      (!ParsingGenericAssociationType ||
+       isNextCXXTypeId(TentativeCXXTypeIdContext::Unambiguous))) {
     // This might be an enum-base or part of some unrelated enclosing context.
     //
     // 'enum E : base' is permitted in two circumstances:
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index d7a9c72eb2da8..40b08b1bab09b 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -1479,7 +1479,8 @@ bool Parser::isValidAfterTypeSpecifier(bool CouldBeBitfield) {
     return true;
   case tok::colon:
     return CouldBeBitfield || // enum E { ... }   :         2;
-           ColonIsSacred;     // _Generic(..., enum E :     2);
+           ColonIsSacred ||
+           ParsingGenericAssociationType; // _Generic(..., enum E :     2);
   // Microsoft compatibility
   case tok::kw___cdecl:      // struct foo {...} __cdecl      x;
   case tok::kw___fastcall:   // struct foo {...} __fastcall   x;
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 74209f579776d..fac8a6897c071 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -3104,7 +3104,8 @@ ExprResult Parser::ParseGenericSelectionExpression() {
       DefaultLoc = ConsumeToken();
       Ty = nullptr;
     } else {
-      ColonProtectionRAIIObject X(*this);
+      GenericAssociationTypeRAIIObject X(*this);
+
       TypeResult TR = ParseTypeName(nullptr, DeclaratorContext::Association);
       if (TR.isInvalid()) {
         SkipUntil(tok::r_paren, StopAtSemi);
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index 1477fc38bcc6d..1969af9110816 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -602,6 +602,14 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) {
   return TPR == TPResult::True;
 }
 
+bool Parser::isNextCXXTypeId(TentativeCXXTypeIdContext Context,
+                             bool &isAmbiguous) {
+  RevertingTentativeParsingAction PA(*this);
+  ConsumeToken();
+  bool ret = isCXXTypeId(Context, isAmbiguous);
+  return ret;
+}
+
 CXX11AttributeKind
 Parser::isCXX11AttributeSpecifier(bool Disambiguate,
                                   bool OuterMightBeMessageSend) {
diff --git a/clang/test/Parser/c23-enum-generic-assoc.c b/clang/test/Parser/c23-enum-generic-assoc.c
new file mode 100644
index 0000000000000..b0ea715bacc36
--- /dev/null
+++ b/clang/test/Parser/c23-enum-generic-assoc.c
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c23 %s
+// expected-no-diagnostics
+
+typedef long l;
+
+_Static_assert(_Generic(0L, enum E : long { A } : 0, int: 1) == 0, "");
+
+_Static_assert(_Generic(0L, enum E : 0, int: 1) == 0, "");
+
+_Static_assert(_Generic(0L, enum A : l { B } : 0, int: 1) == 0, "");
\ No newline at end of file



More information about the cfe-commits mailing list