https://github.com/aalmkainzi updated https://github.com/llvm/llvm-project/pull/210353
>From d86b41d1bba99f109c20af17956cd1d8acca6f8f Mon Sep 17 00:00:00 2001 From: Abdulmalek Almkainzi <[email protected]> Date: Fri, 17 Jul 2026 00:33:31 +0300 Subject: [PATCH 1/5] 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 ae91153e34e3a..ced7607c71a69 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; @@ -4497,6 +4498,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. @@ -8684,6 +8690,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 11ba2b81baacc..5908c0459d4d6 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -5174,7 +5174,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 d701cbaa43bdd..261b87571f55b 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -1561,7 +1561,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 8f212fab4cdbf..b370ccde25d8e 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -3106,7 +3106,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 07d45925e892e..7aeb0c39fbf37 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -616,6 +616,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 >From 0170afdb664994f9d125c25de937bb2a77d8face Mon Sep 17 00:00:00 2001 From: Abdulmalek Almkainzi <[email protected]> Date: Sat, 25 Jul 2026 01:17:14 +0300 Subject: [PATCH 2/5] fixed `enum A : T {B} : 0` in C++ mode, where it used to parse T {B} as an expression, rather than the underlying type + enum body --- clang/lib/Parse/ParseExpr.cpp | 2 ++ clang/lib/Parse/ParseTentative.cpp | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index b370ccde25d8e..2277a0011ee68 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -2665,6 +2665,8 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool StopIfCastExpr, ParsedType &CastTy, SourceLocation &RParenLoc) { assert(Tok.is(tok::l_paren) && "Not a paren expr!"); ColonProtectionRAIIObject ColonProtection(*this, false); + GenericAssociationTypeRAIIObject NotParsingGenericAssociationType(*this, false); + BalancedDelimiterTracker T(*this, tok::l_paren); if (T.consumeOpen()) return ExprError(); diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 7aeb0c39fbf37..2c49581ff0fdb 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -1538,7 +1538,23 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename, // enum E : int { 4 }; // bit-field // }; if (getLangOpts().CPlusPlus11 && NextToken().is(tok::l_brace)) + { + if (ParsingGenericAssociationType) + { + RevertingTentativeParsingAction PA(*this); + ConsumeAnyToken(); // skip keyword + ConsumeBrace(); // skip l_brace + if (SkipUntil(tok::r_brace, StopBeforeMatch)) + { + ConsumeBrace(); // skip r_brace + if (Tok.is(tok::colon)) + { + return TPResult::True; + } + } + } return BracedCastResult; + } if (isStartOfObjCClassMessageMissingOpenBracket()) return TPResult::False; >From 1414fc3475380e9de3484830d35f67250e506672 Mon Sep 17 00:00:00 2001 From: Abdulmalek Almkainzi <[email protected]> Date: Tue, 28 Jul 2026 00:39:59 +0300 Subject: [PATCH 3/5] formatting --- clang/lib/Parse/ParseExpr.cpp | 3 ++- clang/lib/Parse/ParseTentative.cpp | 14 +++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 2277a0011ee68..2c2898aa99b24 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -2665,7 +2665,8 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool StopIfCastExpr, ParsedType &CastTy, SourceLocation &RParenLoc) { assert(Tok.is(tok::l_paren) && "Not a paren expr!"); ColonProtectionRAIIObject ColonProtection(*this, false); - GenericAssociationTypeRAIIObject NotParsingGenericAssociationType(*this, false); + GenericAssociationTypeRAIIObject NotParsingGenericAssociationType(*this, + false); BalancedDelimiterTracker T(*this, tok::l_paren); if (T.consumeOpen()) diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 2c49581ff0fdb..e9bbea576725e 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -1537,18 +1537,14 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename, // enum E : int { a = 4 }; // enum // enum E : int { 4 }; // bit-field // }; - if (getLangOpts().CPlusPlus11 && NextToken().is(tok::l_brace)) - { - if (ParsingGenericAssociationType) - { + if (getLangOpts().CPlusPlus11 && NextToken().is(tok::l_brace)) { + if (ParsingGenericAssociationType) { RevertingTentativeParsingAction PA(*this); ConsumeAnyToken(); // skip keyword - ConsumeBrace(); // skip l_brace - if (SkipUntil(tok::r_brace, StopBeforeMatch)) - { + ConsumeBrace(); // skip l_brace + if (SkipUntil(tok::r_brace, StopBeforeMatch)) { ConsumeBrace(); // skip r_brace - if (Tok.is(tok::colon)) - { + if (Tok.is(tok::colon)) { return TPResult::True; } } >From 088ecfa109fe72a7ea4869c73f144e49294563c9 Mon Sep 17 00:00:00 2001 From: Abdulmalek Almkainzi <[email protected]> Date: Tue, 28 Jul 2026 01:10:00 +0300 Subject: [PATCH 4/5] applied suggested changes --- clang/include/clang/Parse/Parser.h | 2 +- clang/include/clang/Parse/RAIIObjectsForParser.h | 4 ++-- clang/lib/Parse/ParseTentative.cpp | 5 ++--- clang/test/Parser/c23-enum-generic-assoc.c | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index ced7607c71a69..4dda0f9d5e5ff 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -8690,7 +8690,7 @@ class Parser : public CodeCompletionHandler { return isCXXTypeId(Context, isAmbiguous); } - bool isNextCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous); + bool isNextCXXTypeId(TentativeCXXTypeIdContext Context, bool &IsAmbiguous); bool isNextCXXTypeId(TentativeCXXTypeIdContext Context) { bool isAmbiguous; diff --git a/clang/include/clang/Parse/RAIIObjectsForParser.h b/clang/include/clang/Parse/RAIIObjectsForParser.h index 747dbe90b1bf6..7293897981b27 100644 --- a/clang/include/clang/Parse/RAIIObjectsForParser.h +++ b/clang/include/clang/Parse/RAIIObjectsForParser.h @@ -295,8 +295,8 @@ namespace clang { bool OldVal; public: - GenericAssociationTypeRAIIObject(Parser &p, bool Value = true) - : P(p), OldVal(P.ParsingGenericAssociationType) { + GenericAssociationTypeRAIIObject(Parser &P, bool Value = true) + : P(P), OldVal(P.ParsingGenericAssociationType) { P.ParsingGenericAssociationType = Value; } diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index e9bbea576725e..8204f2033e171 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -617,11 +617,10 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) { } bool Parser::isNextCXXTypeId(TentativeCXXTypeIdContext Context, - bool &isAmbiguous) { + bool &IsAmbiguous) { RevertingTentativeParsingAction PA(*this); ConsumeToken(); - bool ret = isCXXTypeId(Context, isAmbiguous); - return ret; + return isCXXTypeId(Context, IsAmbiguous); } CXX11AttributeKind diff --git a/clang/test/Parser/c23-enum-generic-assoc.c b/clang/test/Parser/c23-enum-generic-assoc.c index b0ea715bacc36..e522fcbe0ee3f 100644 --- a/clang/test/Parser/c23-enum-generic-assoc.c +++ b/clang/test/Parser/c23-enum-generic-assoc.c @@ -7,4 +7,4 @@ _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 +_Static_assert(_Generic(0L, enum A : l { B } : 0, int: 1) == 0, ""); >From ff738c92b773c185194742eaf96ba2d2b4f23a3b Mon Sep 17 00:00:00 2001 From: Abdulmalek Almkainzi <[email protected]> Date: Tue, 28 Jul 2026 01:18:23 +0300 Subject: [PATCH 5/5] applied suggested changes --- clang/include/clang/Parse/Parser.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 4dda0f9d5e5ff..d4ed848ed75ae 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -8693,8 +8693,8 @@ class Parser : public CodeCompletionHandler { bool isNextCXXTypeId(TentativeCXXTypeIdContext Context, bool &IsAmbiguous); bool isNextCXXTypeId(TentativeCXXTypeIdContext Context) { - bool isAmbiguous; - return isNextCXXTypeId(Context, isAmbiguous); + bool IsAmbiguous; + return isNextCXXTypeId(Context, IsAmbiguous); } /// TPResult - Used as the result value for functions whose purpose is to _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
