From 88511de985161d6be7dfa6d80f27a3df1e8b45f4 Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Sun, 2 Nov 2025 17:03:02 +0200 Subject: [PATCH] [clang] Reject 'auto' storage class with type specifier in C++ Previously, clang allowed 'auto int x = 1;' in C++ as an extension (for C compatibility), emitting only a warning. This was confusing since 'auto' in C++11+ is a type specifier, not a storage class. This patch: - Adds a new error diagnostic 'err_auto_type_specifier' - Updates the parser to emit an error (instead of warning) when 'auto' is used as a storage class with a type specifier in C++ mode - Preserves C23 behavior where 'auto int' is valid - Adds comprehensive tests Fixes #164273 Signed-off-by: Osama Abdelkader --- .../clang/Basic/DiagnosticParseKinds.td | 5 ++- clang/lib/Parse/ParseDecl.cpp | 35 +++++++++++++----- .../test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp | 36 ++++++++++++------- .../dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp | 2 +- .../dcl.spec/dcl.type/dcl.spec.auto/p3.cpp | 7 +++- clang/test/CXX/drs/cwg3xx.cpp | 8 +++-- clang/test/Parser/cxx-auto-type-specifier.cpp | 22 ++++++++++++ clang/test/Parser/opencl-storage-class.cl | 4 +-- clang/test/SemaCXX/auto-cxx0x.cpp | 2 +- clang/test/SemaCXX/class.cpp | 7 ++-- clang/test/SemaCXX/static-data-member.cpp | 7 +++- clang/test/SemaOpenCL/storageclass.cl | 6 +--- 12 files changed, 99 insertions(+), 42 deletions(-) create mode 100644 clang/test/Parser/cxx-auto-type-specifier.cpp diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index e5e071f43fa75..170616afff9b9 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -399,9 +399,8 @@ def err_requires_clause_on_declarator_not_declaring_a_function : Error< "trailing requires clause can only be used when declaring a function">; def err_requires_clause_inside_parens : Error< "trailing requires clause should be placed outside parentheses">; -def ext_auto_storage_class : ExtWarn< - "'auto' storage class specifier is not permitted in C++11, and will not " - "be supported in future releases">, InGroup>; +def err_auto_type_specifier : Error< + "'auto' cannot be combined with a type specifier">; def ext_decltype_auto_type_specifier : ExtWarn< "'decltype(auto)' type specifier is a C++14 extension">, InGroup; def warn_cxx11_compat_decltype_auto_type_specifier : Warning< diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 7e4a164e34eda..4f5a56bad1612 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -4093,19 +4093,36 @@ void Parser::ParseDeclarationSpecifiers( isStorageClass = true; break; case tok::kw_auto: - if (getLangOpts().CPlusPlus11 || getLangOpts().C23) { - if (isKnownToBeTypeSpecifier(GetLookAheadToken(1))) { + if (isKnownToBeTypeSpecifier(GetLookAheadToken(1))) { + // 'auto' cannot be combined with a type specifier, except in C23 and + // C++98. + if (getLangOpts().C23) { + // C23 allows 'auto' as storage class with type specifier. isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc, PrevSpec, DiagID, Policy); - if (!isInvalid && !getLangOpts().C23) - Diag(Tok, diag::ext_auto_storage_class) - << FixItHint::CreateRemoval(DS.getStorageClassSpecLoc()); - } else + } else if (getLangOpts().CPlusPlus11 || getLangOpts().OpenCL) { + // In C++11+ or OpenCL, 'auto' cannot be combined with a type + // specifier. + isInvalid = true; + PrevSpec = Tok.getIdentifierInfo()->getNameStart(); + DiagID = diag::err_auto_type_specifier; + } else { + // In C++98 or C, 'auto' can be a storage class specifier with a type. + isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc, + PrevSpec, DiagID, Policy); + } + } else { + // 'auto' is not followed by a type specifier. + if (getLangOpts().CPlusPlus11 || getLangOpts().C23) { + // In C++11+ or C23, 'auto' is a type specifier (type deduction). isInvalid = DS.SetTypeSpecType(DeclSpec::TST_auto, Loc, PrevSpec, DiagID, Policy); - } else - isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc, - PrevSpec, DiagID, Policy); + } else { + // In C (not C++11+ and not C23), 'auto' is a storage class specifier. + isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc, + PrevSpec, DiagID, Policy); + } + } isStorageClass = true; break; case tok::kw___auto_type: diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp index 723a79628116c..da252d11175ab 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp @@ -5,14 +5,18 @@ // The auto or register specifiers can be applied only to names of objects // declared in a block (6.3) or to function parameters (8.4). -auto int ao; // expected-error {{illegal storage class on file-scoped variable}} +auto int ao; #if __cplusplus >= 201103L // C++11 or later -// expected-warning@-2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} +// expected-error@-2 {{'auto' cannot be combined with a type specifier}} +#else +// expected-error@-4 {{illegal storage class on file-scoped variable}} #endif -auto void af(); // expected-error {{illegal storage class on function}} +auto void af(); #if __cplusplus >= 201103L // C++11 or later -// expected-warning@-2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} +// expected-error@-2 {{'auto' cannot be combined with a type specifier}} +#else +// expected-error@-4 {{illegal storage class on function}} #endif register int ro; // expected-error {{illegal storage class on file-scoped variable}} @@ -25,13 +29,17 @@ register int ro; // expected-error {{illegal storage class on file-scoped variab register void rf(); // expected-error {{illegal storage class on function}} struct S { - auto int ao; // expected-error {{storage class specified for a member declaration}} + auto int ao; #if __cplusplus >= 201103L // C++11 or later -// expected-warning@-2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} +// expected-error@-2 {{'auto' cannot be combined with a type specifier}} +#else +// expected-error@-4 {{storage class specified for a member declaration}} #endif - auto void af(); // expected-error {{storage class specified for a member declaration}} + auto void af(); #if __cplusplus >= 201103L // C++11 or later -// expected-warning@-2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} +// expected-error@-2 {{'auto' cannot be combined with a type specifier}} +#else +// expected-error@-4 {{storage class specified for a member declaration}} #endif register int ro; // expected-error {{storage class specified for a member declaration}} @@ -40,19 +48,21 @@ struct S { void foo(auto int ap, register int rp) { #if __cplusplus >= 201703L -// expected-warning@-2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} +// expected-error@-2 {{'auto' cannot be combined with a type specifier}} // expected-error@-3 {{ISO C++17 does not allow 'register' storage class specifier}} #elif __cplusplus >= 201103L -// expected-warning@-5 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} +// expected-error@-5 {{'auto' cannot be combined with a type specifier}} // expected-warning@-6 {{'register' storage class specifier is deprecated}} #endif auto int abo; #if __cplusplus >= 201103L // C++11 or later -// expected-warning@-2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} +// expected-error@-2 {{'auto' cannot be combined with a type specifier}} #endif - auto void abf(); // expected-error {{illegal storage class on function}} + auto void abf(); #if __cplusplus >= 201103L // C++11 or later -// expected-warning@-2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} +// expected-error@-2 {{'auto' cannot be combined with a type specifier}} +#else +// expected-error@-4 {{illegal storage class on function}} #endif register int rbo; diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp index e8f12156a4242..907765780d069 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp @@ -56,7 +56,7 @@ namespace p3_example { auto x = 5; const auto *v = &x, u = 6; static auto y = 0.0; - auto int r; // expected-warning {{storage class}} expected-error {{file-scope}} + auto int r; // expected-error {{'auto' cannot be combined with a type specifier}} static_assert(is_same(), ""); static_assert(is_same(), ""); diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp index 440c78201293b..8d7765523461c 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp @@ -42,7 +42,12 @@ void p3example() { static auto y = 0.0; // In C++98: 'auto' storage class specifier is redundant and incompatible with C++0x // In C++0x: 'auto' storage class specifier is not permitted in C++0x, and will not be supported in future releases - auto int r; // expected-warning {{'auto' storage class specifier}} + auto int r; +#if __cplusplus >= 201103L + // expected-error@-2 {{'auto' cannot be combined with a type specifier}} +#else + // expected-warning@-4 {{'auto' storage class specifier}} +#endif same<__typeof(x), int> xHasTypeInt; same<__typeof(v), const int*> vHasTypeConstIntPtr; diff --git a/clang/test/CXX/drs/cwg3xx.cpp b/clang/test/CXX/drs/cwg3xx.cpp index bbd87c060801a..2e735df3d8d78 100644 --- a/clang/test/CXX/drs/cwg3xx.cpp +++ b/clang/test/CXX/drs/cwg3xx.cpp @@ -1732,11 +1732,13 @@ namespace cwg395 { // cwg395: 3.0 namespace cwg396 { // cwg396: 3.0 void f() { auto int a(); - // since-cxx11-error@-1 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} - // expected-error@-2 {{illegal storage class on function}} + // cxx98-error@-1 {{illegal storage class on function}} + // since-cxx11-error@-2 {{'auto' cannot be combined with a type specifier}} + // since-cxx11-warning@-3 {{empty parentheses interpreted as a function declaration}} + // since-cxx11-note@-4 {{replace parentheses with an initializer to declare a variable}} int (i); // #cwg396-i auto int (i); - // since-cxx11-error@-1 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} + // since-cxx11-error@-1 {{'auto' cannot be combined with a type specifier}} // expected-error@-2 {{redefinition of 'i'}} // expected-note@#cwg396-i {{previous definition is here}} } diff --git a/clang/test/Parser/cxx-auto-type-specifier.cpp b/clang/test/Parser/cxx-auto-type-specifier.cpp new file mode 100644 index 0000000000000..689e25abdea05 --- /dev/null +++ b/clang/test/Parser/cxx-auto-type-specifier.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++23 %s + +// Test that 'auto' cannot be combined with a type specifier in C++. +void f() { + auto int x = 1; // expected-error {{'auto' cannot be combined with a type specifier}} + auto char c = 'a'; // expected-error {{'auto' cannot be combined with a type specifier}} + auto float f = 1.0f; // expected-error {{'auto' cannot be combined with a type specifier}} + auto double d = 1.0; // expected-error {{'auto' cannot be combined with a type specifier}} + auto long l = 1L; // expected-error {{'auto' cannot be combined with a type specifier}} +} + +// Test that regular 'auto' (type deduction) still works in C++. +void h() { + auto x = 1; + auto y = 2.0; + auto z = 'c'; +} + diff --git a/clang/test/Parser/opencl-storage-class.cl b/clang/test/Parser/opencl-storage-class.cl index 7d3e202e8127a..af4e0340ec795 100644 --- a/clang/test/Parser/opencl-storage-class.cl +++ b/clang/test/Parser/opencl-storage-class.cl @@ -5,11 +5,11 @@ void test_storage_class_specs() static int a; // expected-error {{OpenCL C version 1.0 does not support the 'static' storage class specifier}} register int b; // expected-error {{OpenCL C version 1.0 does not support the 'register' storage class specifier}} extern int c; // expected-error {{OpenCL C version 1.0 does not support the 'extern' storage class specifier}} - auto int d; // expected-error {{OpenCL C version 1.0 does not support the 'auto' storage class specifier}} + auto int d; // expected-error {{'auto' cannot be combined with a type specifier}} #pragma OPENCL EXTENSION cl_clang_storage_class_specifiers : enable static int e; // expected-error {{static local variable must reside in constant address space}} register int f; extern int g; // expected-error {{extern variable must reside in constant address space}} - auto int h; + auto int h; // expected-error {{'auto' cannot be combined with a type specifier}} } diff --git a/clang/test/SemaCXX/auto-cxx0x.cpp b/clang/test/SemaCXX/auto-cxx0x.cpp index 07687b6066790..8282444b1e96e 100644 --- a/clang/test/SemaCXX/auto-cxx0x.cpp +++ b/clang/test/SemaCXX/auto-cxx0x.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11 // 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}} + auto int a; // expected-error {{'auto' cannot be combined with a type specifier}} int auto b; // expected-error{{cannot combine with previous 'int' declaration specifier}} } diff --git a/clang/test/SemaCXX/class.cpp b/clang/test/SemaCXX/class.cpp index f1e02d5158aac..fae85db68ddc4 100644 --- a/clang/test/SemaCXX/class.cpp +++ b/clang/test/SemaCXX/class.cpp @@ -2,11 +2,12 @@ // RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx98 -Wc++11-compat %s -std=c++98 class C { public: - auto int errx; // expected-error {{storage class specified for a member declaration}} + auto int errx; #if __cplusplus <= 199711L - // expected-warning@-2 {{'auto' storage class specifier is redundant}} + // expected-error@-2 {{storage class specified for a member declaration}} + // expected-warning@-3 {{'auto' storage class specifier is redundant and incompatible with C++11}} #else - // expected-warning@-4 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} + // expected-error@-5 {{'auto' cannot be combined with a type specifier}} #endif register int erry; // expected-error {{storage class specified for a member declaration}} extern int errz; // expected-error {{storage class specified for a member declaration}} diff --git a/clang/test/SemaCXX/static-data-member.cpp b/clang/test/SemaCXX/static-data-member.cpp index fb63da9b40099..2dbcae1745e5b 100644 --- a/clang/test/SemaCXX/static-data-member.cpp +++ b/clang/test/SemaCXX/static-data-member.cpp @@ -13,7 +13,12 @@ double ABC::a = 1.0; extern double ABC::b = 1.0; // expected-error {{static data member definition cannot specify a storage class}} static double ABC::c = 1.0; // expected-error {{'static' can only be specified inside the class definition}} __private_extern__ double ABC::d = 1.0; // expected-error {{static data member definition cannot specify a storage class}} -auto double ABC::e = 1.0; // expected-error {{static data member definition cannot specify a storage class}} +auto double ABC::e = 1.0; +#if __cplusplus >= 201103L +// expected-error@-2 {{'auto' cannot be combined with a type specifier}} +#else +// expected-error@-4 {{static data member definition cannot specify a storage class}} +#endif #if __cplusplus < 201703L register double ABC::f = 1.0; // expected-error {{static data member definition cannot specify a storage class}} #endif diff --git a/clang/test/SemaOpenCL/storageclass.cl b/clang/test/SemaOpenCL/storageclass.cl index 4b9d6e9dd4f2d..53d0012d67fda 100644 --- a/clang/test/SemaOpenCL/storageclass.cl +++ b/clang/test/SemaOpenCL/storageclass.cl @@ -135,11 +135,7 @@ void kernel foo(int x) { } auto int L3 = 7; -#if (__OPENCL_CPP_VERSION__ == 202100) -// expected-error@-2{{C++ for OpenCL version 2021 does not support the 'auto' storage class specifier}} -#else -// expected-error-re@-4{{OpenCL C version {{1.2|3.0}} does not support the 'auto' storage class specifier}} -#endif +// expected-error@-1{{'auto' cannot be combined with a type specifier}} global int L4; // expected-error{{function scope variable cannot be declared in global address space}} __attribute__((address_space(100))) int L5; // expected-error{{automatic variable qualified with an invalid address space}}