https://github.com/zahiraam updated 
https://github.com/llvm/llvm-project/pull/224019

>From 22306d4baabb27701a4ba3bdec21f1b83d738a6e Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <[email protected]>
Date: Wed, 16 Sep 2026 06:49:13 -0700
Subject: [PATCH 1/4] [Clang][C23] Fix typedef-name after 'auto' as
 storage-class use

---
 clang/lib/Parse/ParseDecl.cpp | 31 +++++++++++++++++++++++++++++++
 clang/test/C/C23/n3007.c      | 18 ++++++++++++++++++
 2 files changed, 49 insertions(+)

diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index a4bdec00ca80a..e123a07204ebc 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4119,6 +4119,27 @@ void Parser::ParseDeclarationSpecifiers(
       break;
     case tok::kw_auto:
       if (getLangOpts().CPlusPlus11 || getLangOpts().C23) {
+        // FIXME: In C++, `auto` as a storage-class specifier is a
+        // deprecated extension. This lookahead runs for C only; teaching it
+        // to also recognize typedef-names in C++ would require broader
+        // design discussion around `ext_auto_storage_class`. See
+        // https://github.com/llvm/llvm-project/issues/164930.
+        auto IsTypedefName = [&](const Token &T) {
+          if (!T.is(tok::identifier))
+            return false;
+          IdentifierInfo *II = T.getIdentifierInfo();
+          if (!II)
+            return false;
+          // Use a raw suppressed lookup (rather than Sema::getTypeName) to
+          // avoid emitting deprecation/availability diagnostics on the
+          // typedef during this speculative peek — the real parse will look
+          // the name up again and emit them at the right time.
+          LookupResult R(Actions, II, T.getLocation(), 
Sema::LookupOrdinaryName);
+          Actions.LookupName(R, getCurScope(),
+                             /*AllowBuiltinCreation=*/false);
+          R.suppressDiagnostics();
+          return R.isSingleResult() && isa<TypeDecl>(R.getFoundDecl());
+        };
         auto MayBeTypeSpecifier = [&]() {
           // In pre-C23 C, auto can be used as a storage-class specifier.
           // C23 removes auto from the storage-class specifiers and repurposes
@@ -4133,6 +4154,16 @@ void Parser::ParseDeclarationSpecifiers(
             if (isKnownToBeTypeSpecifier(T))
               return true;
 
+            // C23: a bare identifier that names a typedef is a type
+            // specifier here, so `auto typedefName varName;` should be
+            // parsed with `auto` as the storage-class specifier — not as
+            // type inference. Without this check the parser would consume
+            // `auto` as type-inference and then error on the missing
+            // initializer for what it thinks is `typedefName` (issue
+            // #164930).
+            if (getLangOpts().C23 && IsTypedefName(T))
+              return true;
+
             if (getLangOpts().C23 && isTypeSpecifierQualifier(T))
               ++I;
             else
diff --git a/clang/test/C/C23/n3007.c b/clang/test/C/C23/n3007.c
index a881b89443462..143a2fcc6796e 100644
--- a/clang/test/C/C23/n3007.c
+++ b/clang/test/C/C23/n3007.c
@@ -209,3 +209,21 @@ void test_macros(int in_int) {
   _Static_assert(_Generic(c, int : 1));
   _Static_assert(_Generic(result, int : 1));
 }
+
+// Regression test for #164930: `auto <typedef-name> <var>;` should parse as a
+// declaration of <var> with type <typedef-name> (auto used as storage-class in
+// C23 with an explicit type-name), not as inferred type deduction on the
+// typedef.
+void test_auto_typedef(void) {
+  typedef int T;
+  {
+    auto T at_local;
+    at_local = 42;
+    _Static_assert(_Generic(at_local, int : 1));
+  }
+  {
+    // Also works with qualifiers.
+    const auto T at_const = 1;
+    _Static_assert(_Generic(&at_const, const int * : 1));
+  }
+}

>From 8b0a6ac5b03cb684bbc3db984ab6cebd7b1ca7b1 Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <[email protected]>
Date: Wed, 16 Sep 2026 07:21:40 -0700
Subject: [PATCH 2/4] Fix format

---
 clang/lib/Parse/ParseDecl.cpp | 11 ++++-------
 clang/test/C/C23/n3007.c      |  8 ++++----
 2 files changed, 8 insertions(+), 11 deletions(-)

diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index e123a07204ebc..ad0836a94a17b 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4120,10 +4120,7 @@ void Parser::ParseDeclarationSpecifiers(
     case tok::kw_auto:
       if (getLangOpts().CPlusPlus11 || getLangOpts().C23) {
         // FIXME: In C++, `auto` as a storage-class specifier is a
-        // deprecated extension. This lookahead runs for C only; teaching it
-        // to also recognize typedef-names in C++ would require broader
-        // design discussion around `ext_auto_storage_class`. See
-        // https://github.com/llvm/llvm-project/issues/164930.
+        // deprecated extension. This lookahead runs for C only.
         auto IsTypedefName = [&](const Token &T) {
           if (!T.is(tok::identifier))
             return false;
@@ -4134,7 +4131,8 @@ void Parser::ParseDeclarationSpecifiers(
           // avoid emitting deprecation/availability diagnostics on the
           // typedef during this speculative peek — the real parse will look
           // the name up again and emit them at the right time.
-          LookupResult R(Actions, II, T.getLocation(), 
Sema::LookupOrdinaryName);
+          LookupResult R(Actions, II, T.getLocation(),
+                         Sema::LookupOrdinaryName);
           Actions.LookupName(R, getCurScope(),
                              /*AllowBuiltinCreation=*/false);
           R.suppressDiagnostics();
@@ -4159,8 +4157,7 @@ void Parser::ParseDeclarationSpecifiers(
             // parsed with `auto` as the storage-class specifier — not as
             // type inference. Without this check the parser would consume
             // `auto` as type-inference and then error on the missing
-            // initializer for what it thinks is `typedefName` (issue
-            // #164930).
+            // initializer for what it thinks is `typedefName`.
             if (getLangOpts().C23 && IsTypedefName(T))
               return true;
 
diff --git a/clang/test/C/C23/n3007.c b/clang/test/C/C23/n3007.c
index 143a2fcc6796e..e2aaee1cd08b9 100644
--- a/clang/test/C/C23/n3007.c
+++ b/clang/test/C/C23/n3007.c
@@ -3,6 +3,7 @@
 /* WG14 N3007: Yes
  * Type Inference for object definitions
  */
+
 void test_auto_int(void) {
   auto int auto_int = 12;
 }
@@ -210,10 +211,9 @@ void test_macros(int in_int) {
   _Static_assert(_Generic(result, int : 1));
 }
 
-// Regression test for #164930: `auto <typedef-name> <var>;` should parse as a
-// declaration of <var> with type <typedef-name> (auto used as storage-class in
-// C23 with an explicit type-name), not as inferred type deduction on the
-// typedef.
+// `auto <typedef-name> <var>;` should parse as a declaration of <var> with
+// type <typedef-name> (auto used as storage-class in C23 with an explicit
+// type-name), not as inferred type deduction on the typedef.
 void test_auto_typedef(void) {
   typedef int T;
   {

>From 390561b7e538e8a362207817d6f36d297c7d55a5 Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <[email protected]>
Date: Thu, 17 Sep 2026 08:36:51 -0700
Subject: [PATCH 3/4] Addressed review comments

---
 clang/include/clang/Parse/Parser.h |  4 ++++
 clang/lib/Parse/ParseDecl.cpp      | 16 +++++++++-------
 clang/lib/Parse/Parser.cpp         | 10 ++++++++++
 clang/test/C/C23/n3007.c           | 24 +++++++++++++++++++-----
 4 files changed, 42 insertions(+), 12 deletions(-)

diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index 6913c42884a36..3b68aa5a5e583 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -647,6 +647,10 @@ class Parser : public CodeCompletionHandler {
   unsigned NumCachedScopes;
   Scope *ScopeCache[ScopeCacheSize];
 
+  /// Cache mapping an identifier to whether it resolves to a typedef in the
+  /// current scope.
+  llvm::DenseMap<IdentifierInfo *, bool> IsTypedefNameCache;
+
   /// Identifiers used for SEH handling in Borland. These are only
   /// allowed in particular circumstances
   // __except block
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index ad0836a94a17b..37daee3e89425 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4119,24 +4119,26 @@ void Parser::ParseDeclarationSpecifiers(
       break;
     case tok::kw_auto:
       if (getLangOpts().CPlusPlus11 || getLangOpts().C23) {
-        // FIXME: In C++, `auto` as a storage-class specifier is a
-        // deprecated extension. This lookahead runs for C only.
         auto IsTypedefName = [&](const Token &T) {
           if (!T.is(tok::identifier))
             return false;
           IdentifierInfo *II = T.getIdentifierInfo();
           if (!II)
             return false;
-          // Use a raw suppressed lookup (rather than Sema::getTypeName) to
-          // avoid emitting deprecation/availability diagnostics on the
-          // typedef during this speculative peek — the real parse will look
-          // the name up again and emit them at the right time.
+          // Memoize per scope so we do at most one LookupName per
+          // identifier per scope.
+          auto It = IsTypedefNameCache.find(II);
+          if (It != IsTypedefNameCache.end())
+            return It->second;
+          // Suppress diagnostics; the real parse will emit them later.
           LookupResult R(Actions, II, T.getLocation(),
                          Sema::LookupOrdinaryName);
           Actions.LookupName(R, getCurScope(),
                              /*AllowBuiltinCreation=*/false);
           R.suppressDiagnostics();
-          return R.isSingleResult() && isa<TypeDecl>(R.getFoundDecl());
+          bool Result = R.isSingleResult() && isa<TypeDecl>(R.getFoundDecl());
+          IsTypedefNameCache[II] = Result;
+          return Result;
         };
         auto MayBeTypeSpecifier = [&]() {
           // In pre-C23 C, auto can be used as a storage-class specifier.
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index da5f23c4ca30e..3d6ca45cdc13e 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -425,6 +425,11 @@ bool Parser::SkipUntil(ArrayRef<tok::TokenKind> Toks, 
SkipUntilFlags Flags) {
 
//===----------------------------------------------------------------------===//
 
 void Parser::EnterScope(unsigned ScopeFlags) {
+  // Invalidate the typedef-name cache: an identifier may resolve differently
+  // in the new scope.
+  if (!IsTypedefNameCache.empty())
+    IsTypedefNameCache.clear();
+
   if (NumCachedScopes) {
     Scope *N = ScopeCache[--NumCachedScopes];
     N->Init(getCurScope(), ScopeFlags);
@@ -437,6 +442,11 @@ void Parser::EnterScope(unsigned ScopeFlags) {
 void Parser::ExitScope() {
   assert(getCurScope() && "Scope imbalance!");
 
+  // Invalidate the typedef-name cache: an identifier may resolve differently
+  // in the enclosing scope.
+  if (!IsTypedefNameCache.empty())
+    IsTypedefNameCache.clear();
+
   // Inform the actions module that this scope is going away if there are any
   // decls in it.
   Actions.ActOnPopScope(Tok.getLocation(), getCurScope());
diff --git a/clang/test/C/C23/n3007.c b/clang/test/C/C23/n3007.c
index e2aaee1cd08b9..4457dd535b505 100644
--- a/clang/test/C/C23/n3007.c
+++ b/clang/test/C/C23/n3007.c
@@ -211,19 +211,33 @@ void test_macros(int in_int) {
   _Static_assert(_Generic(result, int : 1));
 }
 
-// `auto <typedef-name> <var>;` should parse as a declaration of <var> with
-// type <typedef-name> (auto used as storage-class in C23 with an explicit
-// type-name), not as inferred type deduction on the typedef.
 void test_auto_typedef(void) {
   typedef int T;
   {
     auto T at_local;
-    at_local = 42;
+    at_local = 10;
     _Static_assert(_Generic(at_local, int : 1));
   }
   {
-    // Also works with qualifiers.
     const auto T at_const = 1;
     _Static_assert(_Generic(&at_const, const int * : 1));
   }
+  {
+    auto T a;
+    auto T b;
+    auto T c;
+    a = 1; b = 2; c = 3;
+    _Static_assert(_Generic(a, int : 1));
+    _Static_assert(_Generic(b, int : 1));
+    _Static_assert(_Generic(c, int : 1));
+  }
+  {
+    int T = 7;
+    (void)T;
+  }
+  {
+    auto T at_after_shadow;
+    at_after_shadow = 5;
+    _Static_assert(_Generic(at_after_shadow, int : 1));
+  }
 }

>From a4e664d59034fa2a8168ebdb81f866fe77c112e1 Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <[email protected]>
Date: Tue, 22 Sep 2026 07:36:56 -0700
Subject: [PATCH 4/4] Dropped the cache

---
 clang/include/clang/Parse/Parser.h |  4 ----
 clang/lib/Parse/ParseDecl.cpp      |  9 +--------
 clang/lib/Parse/Parser.cpp         | 10 ----------
 3 files changed, 1 insertion(+), 22 deletions(-)

diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index 3b68aa5a5e583..6913c42884a36 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -647,10 +647,6 @@ class Parser : public CodeCompletionHandler {
   unsigned NumCachedScopes;
   Scope *ScopeCache[ScopeCacheSize];
 
-  /// Cache mapping an identifier to whether it resolves to a typedef in the
-  /// current scope.
-  llvm::DenseMap<IdentifierInfo *, bool> IsTypedefNameCache;
-
   /// Identifiers used for SEH handling in Borland. These are only
   /// allowed in particular circumstances
   // __except block
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 37daee3e89425..f14f2b32da99e 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4125,20 +4125,13 @@ void Parser::ParseDeclarationSpecifiers(
           IdentifierInfo *II = T.getIdentifierInfo();
           if (!II)
             return false;
-          // Memoize per scope so we do at most one LookupName per
-          // identifier per scope.
-          auto It = IsTypedefNameCache.find(II);
-          if (It != IsTypedefNameCache.end())
-            return It->second;
           // Suppress diagnostics; the real parse will emit them later.
           LookupResult R(Actions, II, T.getLocation(),
                          Sema::LookupOrdinaryName);
           Actions.LookupName(R, getCurScope(),
                              /*AllowBuiltinCreation=*/false);
           R.suppressDiagnostics();
-          bool Result = R.isSingleResult() && isa<TypeDecl>(R.getFoundDecl());
-          IsTypedefNameCache[II] = Result;
-          return Result;
+          return R.isSingleResult() && isa<TypeDecl>(R.getFoundDecl());
         };
         auto MayBeTypeSpecifier = [&]() {
           // In pre-C23 C, auto can be used as a storage-class specifier.
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 3d6ca45cdc13e..da5f23c4ca30e 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -425,11 +425,6 @@ bool Parser::SkipUntil(ArrayRef<tok::TokenKind> Toks, 
SkipUntilFlags Flags) {
 
//===----------------------------------------------------------------------===//
 
 void Parser::EnterScope(unsigned ScopeFlags) {
-  // Invalidate the typedef-name cache: an identifier may resolve differently
-  // in the new scope.
-  if (!IsTypedefNameCache.empty())
-    IsTypedefNameCache.clear();
-
   if (NumCachedScopes) {
     Scope *N = ScopeCache[--NumCachedScopes];
     N->Init(getCurScope(), ScopeFlags);
@@ -442,11 +437,6 @@ void Parser::EnterScope(unsigned ScopeFlags) {
 void Parser::ExitScope() {
   assert(getCurScope() && "Scope imbalance!");
 
-  // Invalidate the typedef-name cache: an identifier may resolve differently
-  // in the enclosing scope.
-  if (!IsTypedefNameCache.empty())
-    IsTypedefNameCache.clear();
-
   // Inform the actions module that this scope is going away if there are any
   // decls in it.
   Actions.ActOnPopScope(Tok.getLocation(), getCurScope());

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

Reply via email to