https://github.com/a-tarasyuk updated 
https://github.com/llvm/llvm-project/pull/177865

>From 886c342fcf9a2a84929f6fb3eb51ea74de5c6b83 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <[email protected]>
Date: Sun, 25 Jan 2026 19:05:45 +0200
Subject: [PATCH 1/4] [Clang] prevent incorrect rejection of auto with
 reordered declaration specifiers in C23

---
 clang/docs/ReleaseNotes.rst   |  1 +
 clang/lib/Parse/ParseDecl.cpp | 14 +++++++++++++-
 clang/test/Parser/c2x-auto.c  | 24 +++++++++++++++++++++++-
 3 files changed, 37 insertions(+), 2 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index a734804865c57..47e578448e1e8 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -169,6 +169,7 @@ Bug Fixes in This Version
 -------------------------
 
 - Fix lifetime extension of temporaries in for-range-initializers in 
templates. (#GH165182)
+- Fixed incorrect rejection of ``auto`` with reordered declaration specifiers 
in C23. (#GH164121)
 
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index f8c49646fcf3f..404f0a312b8bf 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4093,7 +4093,19 @@ void Parser::ParseDeclarationSpecifiers(
       break;
     case tok::kw_auto:
       if (getLangOpts().CPlusPlus11 || getLangOpts().C23) {
-        if (isKnownToBeTypeSpecifier(GetLookAheadToken(1))) {
+        auto IsTypeSpecifier = [&]() {
+          if (DS.getTypeSpecWidth() != TypeSpecifierWidth::Unspecified)
+            return true;
+
+          unsigned I = 1;
+          while (GetLookAheadToken(I).isOneOf(tok::kw_const, tok::kw_volatile,
+                                              tok::kw_restrict))
+            ++I;
+
+          return isKnownToBeTypeSpecifier(GetLookAheadToken(I));
+        };
+
+        if (IsTypeSpecifier()) {
           isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc,
                                              PrevSpec, DiagID, Policy);
           if (!isInvalid && !getLangOpts().C23)
diff --git a/clang/test/Parser/c2x-auto.c b/clang/test/Parser/c2x-auto.c
index 7f80b0717ab25..cee670bf7b952 100644
--- a/clang/test/Parser/c2x-auto.c
+++ b/clang/test/Parser/c2x-auto.c
@@ -147,7 +147,7 @@ auto int b1 = 0; // c23-error {{illegal storage class on 
file-scoped variable}}
 int auto b2 = 0; // c23-error {{cannot combine with previous 'int' declaration 
specifier}} \
                     c17-error {{illegal storage class on file-scoped variable}}
 
-void f() {
+void t1() {
   constexpr auto int c1 = 0; // c23-error {{cannot combine with previous 
'auto' declaration specifier}} \
                                 c17-error {{use of undeclared identifier 
'constexpr'}}
 
@@ -157,3 +157,25 @@ void f() {
   auto int d1 = 0;
   int auto d2 = 0; // c23-error {{cannot combine with previous 'int' 
declaration specifier}}
 }
+
+void t2() {
+  auto long long a1 = 0;
+  long auto long a2 = 0;
+  long long auto a3 = 0;
+
+  auto const long long b1 = 0;
+  long long const auto b2 = 0;
+  long long auto const b3 = 0;
+}
+
+void t3() {
+  const auto int a1 = 0;
+  auto const int a2 = 0;
+
+  volatile auto int a3 = 0;
+  auto volatile int a4 = 0;
+  auto volatile const int a5 = 0;
+  auto const volatile int a6 = 0;
+
+  auto restrict int a7 = 0; // expected-error {{restrict requires a pointer or 
reference ('int' is invalid}}
+}

>From 16e85becd7905f2896892541e76f6f3e7bb1b1b1 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <[email protected]>
Date: Tue, 27 Jan 2026 13:27:10 +0200
Subject: [PATCH 2/4] handle reordered c23 auto with type-specifier qualifiers

---
 clang/include/clang/Parse/Parser.h            |  8 ++--
 clang/lib/Parse/ParseDecl.cpp                 | 31 ++++++++++-----
 clang/lib/Parse/ParseObjc.cpp                 |  2 +-
 clang/test/AST/ByteCode/constexpr.c           |  4 +-
 .../dcl.spec.auto/p3-generic-lambda-1y.cpp    |  6 +--
 clang/test/Parser/c2x-auto.c                  | 39 ++++++++++++++++---
 clang/test/Sema/c2x-auto.c                    |  4 +-
 clang/test/Sema/constexpr.c                   |  4 +-
 clang/test/SemaCXX/auto-cxx0x.cpp             |  2 +-
 9 files changed, 71 insertions(+), 29 deletions(-)

diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index cd7dc14701914..1af9a91a76c41 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -2017,7 +2017,7 @@ class Parser : public CodeCompletionHandler {
 
   /// isTypeSpecifierQualifier - Return true if the current token could be the
   /// start of a specifier-qualifier-list.
-  bool isTypeSpecifierQualifier();
+  bool isTypeSpecifierQualifier(const Token &Tok);
 
   /// isKnownToBeTypeSpecifier - Return true if we know that the specified 
token
   /// is definitely a type-specifier.  Return false if it isn't part of a type
@@ -4335,7 +4335,7 @@ class Parser : public CodeCompletionHandler {
       return isCXXTypeId(TentativeCXXTypeIdContext::AsGenericSelectionArgument,
                          isAmbiguous);
     }
-    return isTypeSpecifierQualifier();
+    return isTypeSpecifierQualifier(Tok);
   }
 
   /// Checks if the current tokens form type-id or expression.
@@ -4346,7 +4346,7 @@ class Parser : public CodeCompletionHandler {
       bool isAmbiguous;
       return isCXXTypeId(TentativeCXXTypeIdContext::Unambiguous, isAmbiguous);
     }
-    return isTypeSpecifierQualifier();
+    return isTypeSpecifierQualifier(Tok);
   }
 
   /// ParseBlockId - Parse a block-id, which roughly looks like int (int x).
@@ -5015,7 +5015,7 @@ class Parser : public CodeCompletionHandler {
     if (getLangOpts().CPlusPlus)
       return isCXXTypeId(TentativeCXXTypeIdContext::InParens, isAmbiguous);
     isAmbiguous = false;
-    return isTypeSpecifierQualifier();
+    return isTypeSpecifierQualifier(Tok);
   }
   bool isTypeIdInParens() {
     bool isAmbiguous;
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 404f0a312b8bf..55fe7769765e5 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4094,15 +4094,26 @@ void Parser::ParseDeclarationSpecifiers(
     case tok::kw_auto:
       if (getLangOpts().CPlusPlus11 || getLangOpts().C23) {
         auto IsTypeSpecifier = [&]() {
-          if (DS.getTypeSpecWidth() != TypeSpecifierWidth::Unspecified)
+          if (DS.hasTypeSpecifier() &&
+              DS.getTypeSpecType() != DeclSpec::TST_auto)
             return true;
 
           unsigned I = 1;
-          while (GetLookAheadToken(I).isOneOf(tok::kw_const, tok::kw_volatile,
-                                              tok::kw_restrict))
-            ++I;
-
-          return isKnownToBeTypeSpecifier(GetLookAheadToken(I));
+          while (true) {
+            const Token &T = GetLookAheadToken(I);
+            if (isKnownToBeTypeSpecifier(T))
+              return true;
+
+            if (T.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual,
+                          tok::kw__Atomic) &&
+                GetLookAheadToken(I + 1).is(tok::l_paren))
+              return true;
+
+            if (isTypeSpecifierQualifier(T))
+              ++I;
+            else
+              return false;
+          }
         };
 
         if (IsTypeSpecifier()) {
@@ -5569,7 +5580,7 @@ bool Parser::isKnownToBeTypeSpecifier(const Token &Tok) 
const {
   }
 }
 
-bool Parser::isTypeSpecifierQualifier() {
+bool Parser::isTypeSpecifierQualifier(const Token &Tok) {
   switch (Tok.getKind()) {
   default: return false;
 
@@ -5582,9 +5593,9 @@ bool Parser::isTypeSpecifierQualifier() {
     // recurse to handle whatever we get.
     if (TryAnnotateTypeOrScopeToken())
       return true;
-    if (Tok.is(tok::identifier))
+    if (getCurToken().is(tok::identifier))
       return false;
-    return isTypeSpecifierQualifier();
+    return isTypeSpecifierQualifier(getCurToken());
 
   case tok::coloncolon:   // ::foo::bar
     if (NextToken().is(tok::kw_new) ||    // ::new
@@ -5593,7 +5604,7 @@ bool Parser::isTypeSpecifierQualifier() {
 
     if (TryAnnotateTypeOrScopeToken())
       return true;
-    return isTypeSpecifierQualifier();
+    return isTypeSpecifierQualifier(getCurToken());
 
     // GNU attributes support.
   case tok::kw___attribute:
diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp
index 0b9f113d9edc7..eb50b0e8546c6 100644
--- a/clang/lib/Parse/ParseObjc.cpp
+++ b/clang/lib/Parse/ParseObjc.cpp
@@ -1114,7 +1114,7 @@ ParsedType Parser::ParseObjCTypeName(ObjCDeclSpec &DS,
   SourceLocation TypeStartLoc = Tok.getLocation();
 
   ParsedType Ty;
-  if (isTypeSpecifierQualifier() || isObjCInstancetype()) {
+  if (isTypeSpecifierQualifier(Tok) || isObjCInstancetype()) {
     // Parse an abstract declarator.
     DeclSpec declSpec(AttrFactory);
     declSpec.setObjCQualifiers(&DS);
diff --git a/clang/test/AST/ByteCode/constexpr.c 
b/clang/test/AST/ByteCode/constexpr.c
index af96bf3a06f37..2869f55442631 100644
--- a/clang/test/AST/ByteCode/constexpr.c
+++ b/clang/test/AST/ByteCode/constexpr.c
@@ -39,7 +39,9 @@ constexpr auto Ulong = 1L;
 constexpr auto CompoundLiteral = (int){13};
 constexpr auto DoubleCast = (double)(1 / 3);
 constexpr auto String = "this is a string"; // both-error {{constexpr pointer 
initializer is not null}}
-constexpr signed auto Long = 1L; // both-error {{'auto' cannot be signed or 
unsigned}}
+// both-error@+2 {{cannot combine with previous 'auto' declaration specifier}}
+// both-error@+1 {{illegal storage class on file-scoped variable}}
+constexpr signed auto Long = 1L;
 _Static_assert(_Generic(Ulong, long : 1));
 _Static_assert(_Generic(CompoundLiteral, int : 1));
 _Static_assert(_Generic(DoubleCast, double : 1));
diff --git 
a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-generic-lambda-1y.cpp
 
b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-generic-lambda-1y.cpp
index 07bc884e7d5ee..10272eaf9b68b 100644
--- 
a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-generic-lambda-1y.cpp
+++ 
b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-generic-lambda-1y.cpp
@@ -63,12 +63,10 @@ int main()
     auto l = [](auto 
                       (*)(auto)) { }; //expected-error{{'auto' not allowed}}
     //FIXME: These diagnostics might need some work.
-    auto l2 = [](char auto::*pm) { };  //expected-error{{cannot combine with 
previous}}\
-                                         expected-error{{'pm' does not point 
into a class}}
+    auto l2 = [](char auto::*pm) { };  // expected-error {{'pm' does not point 
into a class}} \
+                                          expected-warning {{'auto' storage 
class specifier is not permitted in C++11, and will not be supported in future 
releases}}
     auto l3 = [](char (auto::*pmf)()) { };  //expected-error{{'auto' not 
allowed}}\
                                               expected-error{{'pmf' does not 
point into a class}}\
                                               expected-error{{function cannot 
return function type 'char ()'}}
   }
 }
-
-
diff --git a/clang/test/Parser/c2x-auto.c b/clang/test/Parser/c2x-auto.c
index cee670bf7b952..2ae0dcf4f0aac 100644
--- a/clang/test/Parser/c2x-auto.c
+++ b/clang/test/Parser/c2x-auto.c
@@ -137,25 +137,29 @@ constexpr auto int a1 = 0; // c23-error {{illegal storage 
class on file-scoped v
                               c17-error {{illegal storage class on file-scoped 
variable}} \
                               c17-error {{unknown type name 'constexpr'}}
 
-constexpr int auto a2 = 0; // c23-error {{cannot combine with previous 'int' 
declaration specifier}} \
+constexpr int auto a2 = 0; // c23-error {{cannot combine with previous 'auto' 
declaration specifier}} \
+                              c23-error {{illegal storage class on file-scoped 
variable}} \
                               c17-error {{illegal storage class on file-scoped 
variable}} \
                               c17-error {{unknown type name 'constexpr'}}
 
 auto int b1 = 0; // c23-error {{illegal storage class on file-scoped 
variable}} \
                     c17-error {{illegal storage class on file-scoped variable}}
 
-int auto b2 = 0; // c23-error {{cannot combine with previous 'int' declaration 
specifier}} \
+int auto b2 = 0; // c23-error {{illegal storage class on file-scoped 
variable}} \
                     c17-error {{illegal storage class on file-scoped variable}}
 
+long auto long i = 0; // c23-error {{illegal storage class on file-scoped 
variable}} \
+                         c17-error {{illegal storage class on file-scoped 
variable}}
+
 void t1() {
   constexpr auto int c1 = 0; // c23-error {{cannot combine with previous 
'auto' declaration specifier}} \
                                 c17-error {{use of undeclared identifier 
'constexpr'}}
 
-  constexpr int auto c2 = 0; // c23-error {{cannot combine with previous 'int' 
declaration specifier}} \
+  constexpr int auto c2 = 0; // c23-error {{cannot combine with previous 
'auto' declaration specifier}} \
                                 c17-error {{use of undeclared identifier 
'constexpr'}}
 
   auto int d1 = 0;
-  int auto d2 = 0; // c23-error {{cannot combine with previous 'int' 
declaration specifier}}
+  int auto d2 = 0;
 }
 
 void t2() {
@@ -177,5 +181,30 @@ void t3() {
   auto volatile const int a5 = 0;
   auto const volatile int a6 = 0;
 
-  auto restrict int a7 = 0; // expected-error {{restrict requires a pointer or 
reference ('int' is invalid}}
+   auto restrict int a7 = 0; // expected-error {{restrict requires a pointer 
or reference ('int' is invalid)}}
+}
+
+void t4() {
+  static long auto long s1 = 0; // c23-error {{cannot combine with previous 
'static' declaration specifier}} \
+                                   c17-error {{cannot combine with previous 
'static' declaration specifier}}
+  extern long auto long e2;     // c23-error {{cannot combine with previous 
'extern' declaration specifier}} \
+                                   c17-error {{cannot combine with previous 
'extern' declaration specifier}}
+}
+
+void t5(void) {
+  const long auto long unsigned volatile _Atomic int x = 0;
+}
+
+void t6(void) {
+  auto typeof(0) t1 = 0;  // c17-error {{expected parameter declarator}} \
+                             c17-error {{expected ')'}} \
+                             c17-error {{type specifier missing, defaults to 
'int'; ISO C99 and later do not support implicit int}} \
+                             c17-error {{expected ';' at end of declaration}} \
+                             c17-error {{illegal storage class on function}} \
+                             c17-note {{to match this '('}}
+  typeof(0) auto t2 = 0;  // c17-error {{expected ';' after expression}} \
+                             c17-error {{type specifier missing, defaults to 
'int'; ISO C99 and later do not support implicit int}}
+
+  auto _Atomic(int) t3 = 0;
+  _Atomic(int) auto t4 = 0;
 }
diff --git a/clang/test/Sema/c2x-auto.c b/clang/test/Sema/c2x-auto.c
index 7d62db9ea6c28..c6128cba88689 100644
--- a/clang/test/Sema/c2x-auto.c
+++ b/clang/test/Sema/c2x-auto.c
@@ -6,14 +6,14 @@ void test_basic_types(void) {
   auto auto_int = 4;
   auto auto_long = 4UL;
   auto int auto_int_ts = 12;
-  signed auto a = 1L; // expected-error {{'auto' cannot be signed or unsigned}}
+  signed auto a = 1L;
 
   _Static_assert(_Generic(auto_int, int : 1));
   _Static_assert(_Generic(auto_long, unsigned long : 1));
 }
 
 void test_complex_types(void) {
-  _Complex auto i = 12.0; // expected-error {{'_Complex auto' is invalid}}
+  _Complex auto i = 12.0; // expected-warning {{plain '_Complex' requires a 
type specifier; assuming '_Complex double'}}
 }
 
 void test_gnu_extensions(void) {
diff --git a/clang/test/Sema/constexpr.c b/clang/test/Sema/constexpr.c
index ae01c71e09b06..4b72289d4d3d3 100644
--- a/clang/test/Sema/constexpr.c
+++ b/clang/test/Sema/constexpr.c
@@ -39,7 +39,9 @@ constexpr auto Ulong = 1L;
 constexpr auto CompoundLiteral = (int){13};
 constexpr auto DoubleCast = (double)(1 / 3);
 constexpr auto String = "this is a string"; // expected-error {{constexpr 
pointer initializer is not null}}
-constexpr signed auto Long = 1L; // expected-error {{'auto' cannot be signed 
or unsigned}}
+// expected-error@+2 {{cannot combine with previous 'auto' declaration 
specifier}}
+// expected-error@+1 {{illegal storage class on file-scoped variable}}
+constexpr signed auto Long = 1L;
 _Static_assert(_Generic(Ulong, long : 1));
 _Static_assert(_Generic(CompoundLiteral, int : 1));
 _Static_assert(_Generic(DoubleCast, double : 1));
diff --git a/clang/test/SemaCXX/auto-cxx0x.cpp 
b/clang/test/SemaCXX/auto-cxx0x.cpp
index 07687b6066790..1ce4932c00ff0 100644
--- a/clang/test/SemaCXX/auto-cxx0x.cpp
+++ b/clang/test/SemaCXX/auto-cxx0x.cpp
@@ -2,7 +2,7 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++1y
 void f() {
   auto int a; // expected-warning {{'auto' storage class specifier is not 
permitted in C++11, and will not be supported in future releases}}
-  int auto b; // expected-error{{cannot combine with previous 'int' 
declaration specifier}}
+  int auto b; // expected-warning {{'auto' storage class specifier is not 
permitted in C++11, and will not be supported in future releases}}
 }
 
 typedef auto PR25449(); // expected-error {{'auto' not allowed in typedef}}

>From 820697922f70c725cce42397ae86fd81421fecb4 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <[email protected]>
Date: Thu, 29 Jan 2026 01:11:48 +0200
Subject: [PATCH 3/4] refine diagnostics and add tests

---
 clang/lib/Parse/ParseDecl.cpp                 | 20 +++++++------
 clang/test/AST/ByteCode/constexpr.c           |  4 +--
 .../dcl.spec.auto/p3-generic-lambda-1y.cpp    | 10 +++----
 clang/test/Parser/c2x-auto.c                  | 29 ++++++++++++-------
 clang/test/Sema/constexpr.c                   |  4 +--
 clang/test/SemaCXX/auto-cxx0x.cpp             |  2 +-
 6 files changed, 38 insertions(+), 31 deletions(-)

diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 55fe7769765e5..6cb1a415658df 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4093,9 +4093,10 @@ void Parser::ParseDeclarationSpecifiers(
       break;
     case tok::kw_auto:
       if (getLangOpts().CPlusPlus11 || getLangOpts().C23) {
-        auto IsTypeSpecifier = [&]() {
-          if (DS.hasTypeSpecifier() &&
-              DS.getTypeSpecType() != DeclSpec::TST_auto)
+        auto MayBeTypeSpecifier = [&]() {
+          if (getLangOpts().C23 && DS.hasTypeSpecifier() &&
+              DS.getTypeSpecType() != DeclSpec::TST_auto &&
+              DS.getConstexprSpecifier() == ConstexprSpecKind::Unspecified)
             return true;
 
           unsigned I = 1;
@@ -4104,11 +4105,6 @@ void Parser::ParseDeclarationSpecifiers(
             if (isKnownToBeTypeSpecifier(T))
               return true;
 
-            if (T.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual,
-                          tok::kw__Atomic) &&
-                GetLookAheadToken(I + 1).is(tok::l_paren))
-              return true;
-
             if (isTypeSpecifierQualifier(T))
               ++I;
             else
@@ -4116,7 +4112,7 @@ void Parser::ParseDeclarationSpecifiers(
           }
         };
 
-        if (IsTypeSpecifier()) {
+        if (MayBeTypeSpecifier()) {
           isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc,
                                              PrevSpec, DiagID, Policy);
           if (!isInvalid && !getLangOpts().C23)
@@ -5574,6 +5570,12 @@ bool Parser::isKnownToBeTypeSpecifier(const Token &Tok) 
const {
     // enum-specifier
   case tok::kw_enum:
 
+  case tok::kw_typeof:
+  case tok::kw_typeof_unqual:
+
+  // C11 _Atomic
+  case tok::kw__Atomic:
+
     // typedef-name
   case tok::annot_typename:
     return true;
diff --git a/clang/test/AST/ByteCode/constexpr.c 
b/clang/test/AST/ByteCode/constexpr.c
index 2869f55442631..af96bf3a06f37 100644
--- a/clang/test/AST/ByteCode/constexpr.c
+++ b/clang/test/AST/ByteCode/constexpr.c
@@ -39,9 +39,7 @@ constexpr auto Ulong = 1L;
 constexpr auto CompoundLiteral = (int){13};
 constexpr auto DoubleCast = (double)(1 / 3);
 constexpr auto String = "this is a string"; // both-error {{constexpr pointer 
initializer is not null}}
-// both-error@+2 {{cannot combine with previous 'auto' declaration specifier}}
-// both-error@+1 {{illegal storage class on file-scoped variable}}
-constexpr signed auto Long = 1L;
+constexpr signed auto Long = 1L; // both-error {{'auto' cannot be signed or 
unsigned}}
 _Static_assert(_Generic(Ulong, long : 1));
 _Static_assert(_Generic(CompoundLiteral, int : 1));
 _Static_assert(_Generic(DoubleCast, double : 1));
diff --git 
a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-generic-lambda-1y.cpp
 
b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-generic-lambda-1y.cpp
index 10272eaf9b68b..ca8d50fe22728 100644
--- 
a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-generic-lambda-1y.cpp
+++ 
b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-generic-lambda-1y.cpp
@@ -63,10 +63,10 @@ int main()
     auto l = [](auto 
                       (*)(auto)) { }; //expected-error{{'auto' not allowed}}
     //FIXME: These diagnostics might need some work.
-    auto l2 = [](char auto::*pm) { };  // expected-error {{'pm' does not point 
into a class}} \
-                                          expected-warning {{'auto' storage 
class specifier is not permitted in C++11, and will not be supported in future 
releases}}
-    auto l3 = [](char (auto::*pmf)()) { };  //expected-error{{'auto' not 
allowed}}\
-                                              expected-error{{'pmf' does not 
point into a class}}\
-                                              expected-error{{function cannot 
return function type 'char ()'}}
+    auto l2 = [](char auto::*pm) { };       // expected-error {{cannot combine 
with previous 'char' declaration specifier}} \
+                                               expected-error{{'pm' does not 
point into a class}}
+    auto l3 = [](char (auto::*pmf)()) { };  // expected-error{{'auto' not 
allowed}}\
+                                               expected-error{{'pmf' does not 
point into a class}}\
+                                               expected-error{{function cannot 
return function type 'char ()'}}
   }
 }
diff --git a/clang/test/Parser/c2x-auto.c b/clang/test/Parser/c2x-auto.c
index 2ae0dcf4f0aac..96eda18e44336 100644
--- a/clang/test/Parser/c2x-auto.c
+++ b/clang/test/Parser/c2x-auto.c
@@ -137,8 +137,7 @@ constexpr auto int a1 = 0; // c23-error {{illegal storage 
class on file-scoped v
                               c17-error {{illegal storage class on file-scoped 
variable}} \
                               c17-error {{unknown type name 'constexpr'}}
 
-constexpr int auto a2 = 0; // c23-error {{cannot combine with previous 'auto' 
declaration specifier}} \
-                              c23-error {{illegal storage class on file-scoped 
variable}} \
+constexpr int auto a2 = 0; // c23-error {{cannot combine with previous 'int' 
declaration specifier}} \
                               c17-error {{illegal storage class on file-scoped 
variable}} \
                               c17-error {{unknown type name 'constexpr'}}
 
@@ -148,14 +147,20 @@ auto int b1 = 0; // c23-error {{illegal storage class on 
file-scoped variable}}
 int auto b2 = 0; // c23-error {{illegal storage class on file-scoped 
variable}} \
                     c17-error {{illegal storage class on file-scoped variable}}
 
-long auto long i = 0; // c23-error {{illegal storage class on file-scoped 
variable}} \
-                         c17-error {{illegal storage class on file-scoped 
variable}}
+long auto long b3 = 0; // c23-error {{illegal storage class on file-scoped 
variable}} \
+                          c17-error {{illegal storage class on file-scoped 
variable}}
+
+const long auto long unsigned volatile _Atomic int b4 = 0; // c23-error 
{{illegal storage class on file-scoped variable}} \
+                                                              c17-error 
{{illegal storage class on file-scoped variable}}
+
+signed int _Atomic auto b5 = 0; // c23-error {{illegal storage class on 
file-scoped variable}} \
+                                   c17-error {{illegal storage class on 
file-scoped variable}}
 
 void t1() {
   constexpr auto int c1 = 0; // c23-error {{cannot combine with previous 
'auto' declaration specifier}} \
                                 c17-error {{use of undeclared identifier 
'constexpr'}}
 
-  constexpr int auto c2 = 0; // c23-error {{cannot combine with previous 
'auto' declaration specifier}} \
+  constexpr int auto c2 = 0; // c23-error {{cannot combine with previous 'int' 
declaration specifier}} \
                                 c17-error {{use of undeclared identifier 
'constexpr'}}
 
   auto int d1 = 0;
@@ -181,7 +186,7 @@ void t3() {
   auto volatile const int a5 = 0;
   auto const volatile int a6 = 0;
 
-   auto restrict int a7 = 0; // expected-error {{restrict requires a pointer 
or reference ('int' is invalid)}}
+  auto restrict int a7 = 0; // expected-error {{restrict requires a pointer or 
reference ('int' is invalid)}}
 }
 
 void t4() {
@@ -196,15 +201,19 @@ void t5(void) {
 }
 
 void t6(void) {
-  auto typeof(0) t1 = 0;  // c17-error {{expected parameter declarator}} \
+  auto typeof(0) a1 = 0;  // c17-error {{expected parameter declarator}} \
                              c17-error {{expected ')'}} \
                              c17-error {{type specifier missing, defaults to 
'int'; ISO C99 and later do not support implicit int}} \
                              c17-error {{expected ';' at end of declaration}} \
                              c17-error {{illegal storage class on function}} \
                              c17-note {{to match this '('}}
-  typeof(0) auto t2 = 0;  // c17-error {{expected ';' after expression}} \
+  typeof(0) auto a2 = 0;  // c17-error {{expected ';' after expression}} \
                              c17-error {{type specifier missing, defaults to 
'int'; ISO C99 and later do not support implicit int}}
 
-  auto _Atomic(int) t3 = 0;
-  _Atomic(int) auto t4 = 0;
+  auto _Atomic(int) a3 = 0;
+  _Atomic(int) auto a4 = 0;
+}
+
+void t7(void) {
+  signed int _Atomic auto a1 = 0;
 }
diff --git a/clang/test/Sema/constexpr.c b/clang/test/Sema/constexpr.c
index 4b72289d4d3d3..ae01c71e09b06 100644
--- a/clang/test/Sema/constexpr.c
+++ b/clang/test/Sema/constexpr.c
@@ -39,9 +39,7 @@ constexpr auto Ulong = 1L;
 constexpr auto CompoundLiteral = (int){13};
 constexpr auto DoubleCast = (double)(1 / 3);
 constexpr auto String = "this is a string"; // expected-error {{constexpr 
pointer initializer is not null}}
-// expected-error@+2 {{cannot combine with previous 'auto' declaration 
specifier}}
-// expected-error@+1 {{illegal storage class on file-scoped variable}}
-constexpr signed auto Long = 1L;
+constexpr signed auto Long = 1L; // expected-error {{'auto' cannot be signed 
or unsigned}}
 _Static_assert(_Generic(Ulong, long : 1));
 _Static_assert(_Generic(CompoundLiteral, int : 1));
 _Static_assert(_Generic(DoubleCast, double : 1));
diff --git a/clang/test/SemaCXX/auto-cxx0x.cpp 
b/clang/test/SemaCXX/auto-cxx0x.cpp
index 1ce4932c00ff0..f429bebb9941a 100644
--- a/clang/test/SemaCXX/auto-cxx0x.cpp
+++ b/clang/test/SemaCXX/auto-cxx0x.cpp
@@ -2,7 +2,7 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++1y
 void f() {
   auto int a; // expected-warning {{'auto' storage class specifier is not 
permitted in C++11, and will not be supported in future releases}}
-  int auto b; // expected-warning {{'auto' storage class specifier is not 
permitted in C++11, and will not be supported in future releases}}
+  int auto b; // expected-error {{cannot combine with previous 'int' 
declaration specifier}}
 }
 
 typedef auto PR25449(); // expected-error {{'auto' not allowed in typedef}}

>From 8ee2dbfef2cef14ef4bb42496e04873af51c32d6 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <[email protected]>
Date: Fri, 30 Jan 2026 13:30:39 +0200
Subject: [PATCH 4/4] remove constexpr check from auto specifier classification

---
 clang/lib/Parse/ParseDecl.cpp       | 5 ++---
 clang/lib/Sema/DeclSpec.cpp         | 9 ++++-----
 clang/test/AST/ByteCode/constexpr.c | 7 ++++---
 clang/test/Parser/c2x-auto.c        | 9 +++++----
 clang/test/Sema/constexpr.c         | 7 ++++---
 5 files changed, 19 insertions(+), 18 deletions(-)

diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 310131de864ed..3a1946edf3310 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4095,8 +4095,7 @@ void Parser::ParseDeclarationSpecifiers(
       if (getLangOpts().CPlusPlus11 || getLangOpts().C23) {
         auto MayBeTypeSpecifier = [&]() {
           if (getLangOpts().C23 && DS.hasTypeSpecifier() &&
-              DS.getTypeSpecType() != DeclSpec::TST_auto &&
-              DS.getConstexprSpecifier() == ConstexprSpecKind::Unspecified)
+              DS.getTypeSpecType() != DeclSpec::TST_auto)
             return true;
 
           unsigned I = 1;
@@ -4105,7 +4104,7 @@ void Parser::ParseDeclarationSpecifiers(
             if (isKnownToBeTypeSpecifier(T))
               return true;
 
-            if (isTypeSpecifierQualifier(T))
+            if (getLangOpts().C23 && isTypeSpecifierQualifier(T))
               ++I;
             else
               return false;
diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp
index 9da3d0d2ef599..765b94cba2f83 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -1367,13 +1367,12 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy 
&Policy) {
     }
   }
 
-  if (S.getLangOpts().C23 &&
+  if (S.getLangOpts().C23 && getTypeSpecType() != DeclSpec::TST_unspecified &&
       getConstexprSpecifier() == ConstexprSpecKind::Constexpr &&
-      getTypeSpecType() != TST_unspecified &&
       (StorageClassSpec == SCS_extern || StorageClassSpec == SCS_auto)) {
-    S.Diag(ConstexprLoc, diag::err_invalid_decl_spec_combination)
-        << DeclSpec::getSpecifierName(getStorageClassSpec())
-        << SourceRange(getStorageClassSpecLoc());
+    S.Diag(getStorageClassSpecLoc(), diag::err_invalid_decl_spec_combination)
+        << DeclSpec::getSpecifierName(getConstexprSpecifier())
+        << SourceRange(getConstexprSpecLoc());
   }
 
   // If no type specifier was provided and we're parsing a language where
diff --git a/clang/test/AST/ByteCode/constexpr.c 
b/clang/test/AST/ByteCode/constexpr.c
index af96bf3a06f37..c6a1e196c2968 100644
--- a/clang/test/AST/ByteCode/constexpr.c
+++ b/clang/test/AST/ByteCode/constexpr.c
@@ -39,7 +39,8 @@ constexpr auto Ulong = 1L;
 constexpr auto CompoundLiteral = (int){13};
 constexpr auto DoubleCast = (double)(1 / 3);
 constexpr auto String = "this is a string"; // both-error {{constexpr pointer 
initializer is not null}}
-constexpr signed auto Long = 1L; // both-error {{'auto' cannot be signed or 
unsigned}}
+constexpr signed auto Long = 1L; // both-error {{illegal storage class on 
file-scoped variable}}
+// both-error@-1 {{cannot combine with previous 'constexpr' declaration 
specifier}}
 _Static_assert(_Generic(Ulong, long : 1));
 _Static_assert(_Generic(CompoundLiteral, int : 1));
 _Static_assert(_Generic(DoubleCast, double : 1));
@@ -56,7 +57,7 @@ void f3(constexpr register int P1) { // both-error {{function 
parameter cannot b
 constexpr thread_local int V11 = 38; // both-error {{cannot combine with 
previous '_Thread_local' declaration specifier}}
 constexpr static thread_local double V12 = 38; // both-error {{cannot combine 
with previous '_Thread_local' declaration specifier}}
 constexpr extern thread_local char V13; // both-error {{cannot combine with 
previous '_Thread_local' declaration specifier}}
-// both-error@-1 {{cannot combine with previous 'extern' declaration 
specifier}}
+// both-error@-1 {{cannot combine with previous 'constexpr' declaration 
specifier}}
 // both-error@-2 {{constexpr variable declaration must be a definition}}
 constexpr thread_local short V14 = 38; // both-error {{cannot combine with 
previous '_Thread_local' declaration specifier}}
 
@@ -68,7 +69,7 @@ constexpr volatile int V17 = 0; // both-error {{constexpr 
variable cannot have t
 
 constexpr int * restrict V18 = 0; // both-error {{constexpr variable cannot 
have type 'int *const restrict'}}
 
-constexpr extern char Oops = 1; // both-error {{cannot combine with previous 
'extern' declaration specifier}} \
+constexpr extern char Oops = 1; // both-error {{cannot combine with previous 
'constexpr' declaration specifier}} \
                                 // both-warning {{'extern' variable has an 
initializer}}
 
 constexpr int * restrict * Oops1 = 0;
diff --git a/clang/test/Parser/c2x-auto.c b/clang/test/Parser/c2x-auto.c
index 96eda18e44336..d60c96e2b168f 100644
--- a/clang/test/Parser/c2x-auto.c
+++ b/clang/test/Parser/c2x-auto.c
@@ -133,11 +133,12 @@ void attributes(void) {
 
 /** GH163090 */
 constexpr auto int a1 = 0; // c23-error {{illegal storage class on file-scoped 
variable}} \
-                              c23-error {{cannot combine with previous 'auto' 
declaration specifier}} \
+                              c23-error {{cannot combine with previous 
'constexpr' declaration specifier}} \
                               c17-error {{illegal storage class on file-scoped 
variable}} \
                               c17-error {{unknown type name 'constexpr'}}
 
-constexpr int auto a2 = 0; // c23-error {{cannot combine with previous 'int' 
declaration specifier}} \
+constexpr int auto a2 = 0; // c23-error {{illegal storage class on file-scoped 
variable}} \
+                              c23-error {{cannot combine with previous 
'constexpr' declaration specifier}} \
                               c17-error {{illegal storage class on file-scoped 
variable}} \
                               c17-error {{unknown type name 'constexpr'}}
 
@@ -157,10 +158,10 @@ signed int _Atomic auto b5 = 0; // c23-error {{illegal 
storage class on file-sco
                                    c17-error {{illegal storage class on 
file-scoped variable}}
 
 void t1() {
-  constexpr auto int c1 = 0; // c23-error {{cannot combine with previous 
'auto' declaration specifier}} \
+  constexpr auto int c1 = 0; // c23-error {{cannot combine with previous 
'constexpr' declaration specifier}} \
                                 c17-error {{use of undeclared identifier 
'constexpr'}}
 
-  constexpr int auto c2 = 0; // c23-error {{cannot combine with previous 'int' 
declaration specifier}} \
+  constexpr int auto c2 = 0; // c23-error {{cannot combine with previous 
'constexpr' declaration specifier}} \
                                 c17-error {{use of undeclared identifier 
'constexpr'}}
 
   auto int d1 = 0;
diff --git a/clang/test/Sema/constexpr.c b/clang/test/Sema/constexpr.c
index ae01c71e09b06..44cd1e1ebdc61 100644
--- a/clang/test/Sema/constexpr.c
+++ b/clang/test/Sema/constexpr.c
@@ -39,7 +39,8 @@ constexpr auto Ulong = 1L;
 constexpr auto CompoundLiteral = (int){13};
 constexpr auto DoubleCast = (double)(1 / 3);
 constexpr auto String = "this is a string"; // expected-error {{constexpr 
pointer initializer is not null}}
-constexpr signed auto Long = 1L; // expected-error {{'auto' cannot be signed 
or unsigned}}
+constexpr signed auto Long = 1L; // expected-error {{illegal storage class on 
file-scoped variable}}
+// expected-error@-1 {{cannot combine with previous 'constexpr' declaration 
specifier}}
 _Static_assert(_Generic(Ulong, long : 1));
 _Static_assert(_Generic(CompoundLiteral, int : 1));
 _Static_assert(_Generic(DoubleCast, double : 1));
@@ -56,7 +57,7 @@ void f3(constexpr register int P1) { // expected-error 
{{function parameter cann
 constexpr thread_local int V11 = 38; // expected-error {{cannot combine with 
previous '_Thread_local' declaration specifier}}
 constexpr static thread_local double V12 = 38; // expected-error {{cannot 
combine with previous '_Thread_local' declaration specifier}}
 constexpr extern thread_local char V13; // expected-error {{cannot combine 
with previous '_Thread_local' declaration specifier}}
-// expected-error@-1 {{cannot combine with previous 'extern' declaration 
specifier}}
+// expected-error@-1 {{cannot combine with previous 'constexpr' declaration 
specifier}}
 // expected-error@-2 {{constexpr variable declaration must be a definition}}
 constexpr thread_local short V14 = 38; // expected-error {{cannot combine with 
previous '_Thread_local' declaration specifier}}
 
@@ -68,7 +69,7 @@ constexpr volatile int V17 = 0; // expected-error {{constexpr 
variable cannot ha
 
 constexpr int * restrict V18 = 0; // expected-error {{constexpr variable 
cannot have type 'int *const restrict'}}
 
-constexpr extern char Oops = 1; // expected-error {{cannot combine with 
previous 'extern' declaration specifier}} \
+constexpr extern char Oops = 1; // expected-error {{cannot combine with 
previous 'constexpr' declaration specifier}} \
                                 // expected-warning {{'extern' variable has an 
initializer}}
 
 constexpr int * restrict * Oops1 = 0;

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

Reply via email to