[clang] 211b88b - [Clang] prevent incorrect rejection of auto with reordered declaration specifiers in C23 (#177865)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 7 04:16:41 PDT 2026
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 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;
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}}
More information about the cfe-commits
mailing list