[clang] [Clang] prevent incorrect rejection of auto with reordered declaration specifiers in C23 (PR #177865)
Oleksandr Tarasiuk via cfe-commits
cfe-commits at lists.llvm.org
Thu Feb 19 05:20:07 PST 2026
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 <oleksandr.tarasiuk at outlook.com>
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 <oleksandr.tarasiuk at outlook.com>
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 at +2 {{cannot combine with previous 'auto' declaration specifier}}
+// both-error at +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 at +2 {{cannot combine with previous 'auto' declaration specifier}}
+// expected-error at +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 <oleksandr.tarasiuk at outlook.com>
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 at +2 {{cannot combine with previous 'auto' declaration specifier}}
-// both-error at +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 at +2 {{cannot combine with previous 'auto' declaration specifier}}
-// expected-error at +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 <oleksandr.tarasiuk at outlook.com>
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 at -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 at -1 {{cannot combine with previous 'extern' declaration specifier}}
+// both-error at -1 {{cannot combine with previous 'constexpr' declaration specifier}}
// both-error at -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 at -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 at -1 {{cannot combine with previous 'extern' declaration specifier}}
+// expected-error at -1 {{cannot combine with previous 'constexpr' declaration specifier}}
// expected-error at -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;
More information about the cfe-commits
mailing list