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
