Author: Conscat
Date: 2026-06-01T16:51:33+02:00
New Revision: 87f09d325da79b379723b29755861da9baf2dc7a

URL: 
https://github.com/llvm/llvm-project/commit/87f09d325da79b379723b29755861da9baf2dc7a
DIFF: 
https://github.com/llvm/llvm-project/commit/87f09d325da79b379723b29755861da9baf2dc7a.diff

LOG: [clang] Parse `__typeof_unqual__` consistently with `__typeof__` (#198948)

C23 `__typeof_unqual` and `__typeof_unqual__` are supported in all
language modes as an extension.
However, existing tests missed this form:
```cpp
int main() {
   __typeof_unqual(int) x = 0;
}
```
That doesn't compile today.
```
<source>:2:4: error: expected expression
    2 |    __typeof_unqual(int) x = 0;
      |    ^
1 error generated.
Compiler returned: 1
```
I think the fix is to parse `tok::kw_typeof_unqual` everywhere that we
currently parse `tok::kw_typeof`. It simply falls through `case`s
beneath `tok::kw_typeof`, so this should handle them equivalently. No
keyword semantics are changed by this PR.

While in here I noticed Clang Format ignores the token too, so I gave
Clang Format the same parsing parity between these tokens. It now holds
true under `isTypeName()`, just like `typeof`. `__typeof_unqual` kind of
accidentally formatted correctly in C++ anyways, but not in C, so this
is a meaningful fix. Previously it had no tests at all.

In the `clang-format` release notes, I'm not sure whether this should be
considered a fix or a new feature. I've left the clang-format notes
unchanged for now.

Assisted-by: Cursor/Claude Opus

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/Parse/ParseExpr.cpp
    clang/lib/Parse/ParseExprCXX.cpp
    clang/lib/Parse/ParseTentative.cpp
    clang/test/SemaCXX/typeof.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index d1c58435ab399..f1023f0cb42f8 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -651,6 +651,7 @@ Bug Fixes in This Version
   an array via an element-at-a-time copy loop (#GH192026)
 - Fixed an issue where certain designated initializers would be rejected for 
constexpr variables. (#GH193373)
 - Fixed a crash when ``#embed`` is used with C++ modules (#GH195350)
+- Fixed an issue where ``__typeof_unqual`` and ``__typeof_unqual__`` were 
rejected as a declaration specifier in block scope in C++.
 - Fixed crash when checking for overflow for unary operator that can't 
overflow (#GH170072)
 
 Bug Fixes to Compiler Builtins

diff  --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index e38481f05da63..2987d32d6e0d2 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -1343,6 +1343,7 @@ Parser::ParseCastExpression(CastParseKind ParseKind, bool 
isAddressOfOperand,
   case tok::kw_auto:
   case tok::kw_typename:
   case tok::kw_typeof:
+  case tok::kw_typeof_unqual:
   case tok::kw___vector:
   case tok::kw__Accum:
   case tok::kw__Fract:

diff  --git a/clang/lib/Parse/ParseExprCXX.cpp 
b/clang/lib/Parse/ParseExprCXX.cpp
index 5646597622832..ae49fc20e36f2 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -2173,6 +2173,7 @@ void Parser::ParseCXXSimpleTypeSpecifier(DeclSpec &DS) {
 
   // GNU typeof support.
   case tok::kw_typeof:
+  case tok::kw_typeof_unqual:
     ParseTypeofSpecifier(DS);
     DS.Finish(Actions, Policy);
     return;

diff  --git a/clang/lib/Parse/ParseTentative.cpp 
b/clang/lib/Parse/ParseTentative.cpp
index f77b1001332fe..1477fc38bcc6d 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -171,6 +171,7 @@ Parser::TPResult Parser::TryConsumeDeclarationSpecifier() {
     }
     [[fallthrough]];
   case tok::kw_typeof:
+  case tok::kw_typeof_unqual:
   case tok::kw___attribute:
 #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait:
 #include "clang/Basic/TransformTypeTraits.def"
@@ -1525,7 +1526,8 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext 
AllowImplicitTypename,
     return TPResult::True;
 
   // GNU typeof support.
-  case tok::kw_typeof: {
+  case tok::kw_typeof:
+  case tok::kw_typeof_unqual: {
     if (NextToken().isNot(tok::l_paren))
       return TPResult::True;
 
@@ -1590,6 +1592,7 @@ bool Parser::isCXXDeclarationSpecifierAType() {
   case tok::annot_template_id:
   case tok::annot_typename:
   case tok::kw_typeof:
+  case tok::kw_typeof_unqual:
 #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait:
 #include "clang/Basic/TransformTypeTraits.def"
     return true;
@@ -1650,7 +1653,8 @@ bool Parser::isCXXDeclarationSpecifierAType() {
 }
 
 Parser::TPResult Parser::TryParseTypeofSpecifier() {
-  assert(Tok.is(tok::kw_typeof) && "Expected 'typeof'!");
+  assert(Tok.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual) &&
+         "Expected 'typeof' or 'typeof_unqual'!");
   ConsumeToken();
 
   assert(Tok.is(tok::l_paren) && "Expected '('");

diff  --git a/clang/test/SemaCXX/typeof.cpp b/clang/test/SemaCXX/typeof.cpp
index 421cfc59f5311..4db803564309b 100644
--- a/clang/test/SemaCXX/typeof.cpp
+++ b/clang/test/SemaCXX/typeof.cpp
@@ -11,3 +11,11 @@ namespace GH97646 {
     !x;
   }
 }
+
+// Ensure that __typeof_unqual / __typeof_unqual__ parse as a declaration
+// specifier in block scope, for symmetry with __typeof__.
+void block_scope_typeof_unqual() {
+  __typeof_unqual(int) a = 0;
+  __typeof_unqual__(int) b = 0;
+  (void)a; (void)b;
+}


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

Reply via email to