Author: Oleksandr Tarasiuk Date: 2026-04-07T14:16:35+03:00 New Revision: 211b88b64b0e409a2e294a1fe444b305aa94a2a6
URL: https://github.com/llvm/llvm-project/commit/211b88b64b0e409a2e294a1fe444b305aa94a2a6 DIFF: https://github.com/llvm/llvm-project/commit/211b88b64b0e409a2e294a1fe444b305aa94a2a6.diff LOG: [Clang] prevent incorrect rejection of auto with reordered declaration specifiers in C23 (#177865) Fixes #164121 --- This patch addresses the issue where `auto` was incorrectly rejected with reordered declaration specifiers in C23. Added: Modified: clang/docs/ReleaseNotes.rst clang/include/clang/Parse/Parser.h clang/lib/Parse/ParseDecl.cpp clang/lib/Parse/ParseObjc.cpp clang/lib/Sema/DeclSpec.cpp clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-generic-lambda-1y.cpp clang/test/Parser/c2x-auto.c clang/test/Sema/c2x-auto.c clang/test/Sema/constexpr.c clang/test/SemaCXX/auto-cxx0x.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 08d7ffd826b41..1a70fdea4bb04 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -399,6 +399,7 @@ Bug Fixes in This Version to "lambda" instead of "block". (#GH188661) - Fixed a crash on _BitInt(N) arrays where 129 ≤ N ≤ 192 due to incorrect array filler lowering. (#GH189643) - Fixed the behavior in C23 of ``auto``, by emitting an error when an array type is specified for a ``char *``. (#GH162694) +- Fixed incorrect rejection of ``auto`` with reordered declaration specifiers in C23. (#GH164121) Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 08a3d88ee6a36..0919525fbf117 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2029,7 +2029,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 @@ -4350,7 +4350,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. @@ -4361,7 +4361,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). @@ -5054,7 +5054,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 d448cb8a552bc..e2ac86bc5e064 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -4093,7 +4093,28 @@ void Parser::ParseDeclarationSpecifiers( break; case tok::kw_auto: if (getLangOpts().CPlusPlus11 || getLangOpts().C23) { - if (isKnownToBeTypeSpecifier(GetLookAheadToken(1))) { + auto MayBeTypeSpecifier = [&]() { + // In pre-C23 C, auto can be used as a storage-class specifier. + // C23 removes auto from the storage-class specifiers and repurposes + // it for type inference (6.7.10). + if (getLangOpts().C23 && DS.hasTypeSpecifier() && + DS.getTypeSpecType() != DeclSpec::TST_auto) + return true; + + unsigned I = 1; + while (true) { + const Token &T = GetLookAheadToken(I); + if (isKnownToBeTypeSpecifier(T)) + return true; + + if (getLangOpts().C23 && isTypeSpecifierQualifier(T)) + ++I; + else + return false; + } + }; + + if (MayBeTypeSpecifier()) { isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc, PrevSpec, DiagID, Policy); if (!isInvalid && !getLangOpts().C23) @@ -5575,13 +5596,19 @@ 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; } } -bool Parser::isTypeSpecifierQualifier() { +bool Parser::isTypeSpecifierQualifier(const Token &Tok) { switch (Tok.getKind()) { default: return false; @@ -5594,9 +5621,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 @@ -5605,7 +5632,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/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index 479a959e0aadc..660b1805c450e 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -1398,13 +1398,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/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..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,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 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 7f80b0717ab25..d60c96e2b168f 100644 --- a/clang/test/Parser/c2x-auto.c +++ b/clang/test/Parser/c2x-auto.c @@ -133,27 +133,88 @@ 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'}} 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}} -void f() { - constexpr auto int c1 = 0; // c23-error {{cannot combine with previous 'auto' declaration specifier}} \ +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 '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; - int auto d2 = 0; // c23-error {{cannot combine with previous 'int' declaration specifier}} + int auto d2 = 0; +} + +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)}} +} + +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) 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 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) a3 = 0; + _Atomic(int) auto a4 = 0; +} + +void t7(void) { + signed int _Atomic auto a1 = 0; } diff --git a/clang/test/Sema/c2x-auto.c b/clang/test/Sema/c2x-auto.c index a11c42aa6fb49..158414f8f35ac 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 3a6b49fd90748..0b8de906e1838 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; diff --git a/clang/test/SemaCXX/auto-cxx0x.cpp b/clang/test/SemaCXX/auto-cxx0x.cpp index 07687b6066790..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-error{{cannot combine with previous 'int' declaration specifier}} + int auto b; // expected-error {{cannot combine with previous 'int' declaration specifier}} } typedef auto PR25449(); // expected-error {{'auto' not allowed in typedef}} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
