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
