[clang] C23 auto (PR #224019)
Zahira Ammarguellat via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 16 07:05:20 PDT 2026
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 <Zahira.Ammarguellat at intel.com>
Date: Wed, 16 Sep 2026 06:49:13 -0700
Subject: [PATCH] [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));
+ }
+}
More information about the cfe-commits
mailing list