Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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<DiagGroup<"auto-storage-class">>;
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<CXX14>;
def warn_cxx11_compat_decltype_auto_type_specifier : Warning<
Expand Down
35 changes: 26 additions & 9 deletions clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
36 changes: 23 additions & 13 deletions clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
Comment on lines +10 to +12
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be slightly easier to review this file if we used -verify= and different prefixes instead of preprocessor conditionals, but I don't insist on a change for this PR either unless others have strong feelings.

#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}}
Expand All @@ -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}}
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<decltype(x), int>(), "");
static_assert(is_same<decltype(v), const int*>(), "");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ void p3example() {
static auto y = 0.0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line should also be diagnosed in C++98 mode... this seems to be a preexisting bug: https://godbolt.org/z/3cza618cx (we have a similar bug with register: https://godbolt.org/z/zs5918YY4)

GitHub is too useless to let me put this comment on the correct line, but line 40 has a similar C++98 bug: https://godbolt.org/z/xEozMhe6z because there's no type specifier there, and line 41 does as well for the same reason.

Hmmm, it seems we're treating type deduction as an extension in C++98 mode, but we don't list it in the language extensions page as such and we don't issue pedantic diagnostics for it. @zygoloid do you recall if this was an intentional extension or not? Regardless, because this is existing behavior, nothing needs to change as part of this PR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, it seems we're treating type deduction as an extension in C++98 mode, but we don't list it in the language extensions page as such and we don't issue pedantic diagnostics for it. @zygoloid do you recall if this was an intentional extension or not?

I see a diagnostic for it appearing in all of your test cases:

<source>:2:10: warning: 'auto' type specifier is a C++11 extension [-Wc++11-extensions]
    2 |   static auto y = 0.0;
      |     

... and -pedantic-errors does promote that to an error. So yeah, this is an intentional extension, from the general category of feature from a later language mode accepted in earlier language modes, but I guess the table in our documentation is incomplete -- it only seems to include the C++ features with feature test macros (plus one that I guess postdates the table).

If we want that table to be complete, it's probably worth going through the diagnostics in the -Wc++XY-extensions groups and making sure they're all listed. All the relevant extensions should have diagnostics already.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see a diagnostic for it appearing in all of your test cases:

Ugh, yeah, I am seeing that now, I think I was looking at the wrong output before. :-D

Yeah, I think the table just needs to be updated, that doesn't need to be a part of this PR though. Thanks!

// 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;
Expand Down
8 changes: 5 additions & 3 deletions clang/test/CXX/drs/cwg3xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
}
Expand Down
22 changes: 22 additions & 0 deletions clang/test/Parser/cxx-auto-type-specifier.cpp
Original file line number Diff line number Diff line change
@@ -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}}
Comment on lines +8 to +9
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more test to add:

template <typename Ty>
void g() {
  auto Ty x;
}

void test() {
  g<float>();
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Template-dependent types can't be detected at parse time AFAIK.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's why there's an instantiation of the template. The point is mostly: we need to be sure we're rejecting that code even though it's a semantic error rather than a parsing error. It's possible that the better way to approach this is from Sema instead of from Parser.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can definitely detect that there's both an auto here and another kind of type-specifier during parsing. (We don't even have a way to represent that combination in an AST, so we can't detect this during instantiation.)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think:

if (isKnownToBeTypeSpecifier(GetLookAheadToken(1)))

will need to be factored away, then. When I looked into this, the issue is that the token we get is not actually annotated because nothing calls a TryAnnotate*() function to turn it into an annotation token. IIRC, those functions all work off Tok, not a given Token, and they work by modifying the token stream itself (so may not work well as a lookahead function).

Copy link
Contributor Author

@osamakader osamakader Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I tried that:

     TentativeParsingAction TPA(*this);
     ConsumeToken(); 
     TryAnnotateTypeOrScopeToken()
     NextIsTypeSpecifier = isKnownToBeTypeSpecifier(Tok);
     TPA.Revert();

TryAnnotate* incorrectly annotates some syntax e.g. in llvm-project/clang/test/Parser/cxx-concepts-requires-clause.cpp:

template <typename T> T Foo;
template <typename T> auto C(Foo<T>);

As true which breaks this test and many other tests.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What kind of test failures are you seeing, exactly?

Btw, I think it should be TryAnnotateTypeOrScopeToken(AllowImplicitTypename) instead of (); that's an optional parameter, but one that is relevant to ParseDeclarationSpecifiers().

Do we maybe also need to do TryAnnotateTypeConstraint() as well?

CC @erichkeane @mizvekov

Copy link
Contributor Author

@osamakader osamakader Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests e.g. llvm-project/clang/test/Parser/cxx-concepts-requires-clause.cpp, lines

template <typename T> T Foo;
template <typename T> auto C(Foo<T>);

fire the diagnostic message, false positives. Even with TryAnnotateTypeOrScopeToken(AllowImplicitTypename).

Going to try TryAnnotateTypeConstraint().

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can someone clarify why we would need new annotation or tentative parsing here? It seems to me that we can handle auto incrementally while building the list of decl specifiers without any novel lookahead, and I thought that's what we already did. I would expect that we're just converting an extension warning to an error here, and then doing some simplification if we can.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I don't see what annotation we would need. For this example:

template <typename Ty>
void g() {
  auto Ty x;
}

We know Ty is a type, and we know there is no type which could be substituted here making this definition valid, so we can diagnose this right away.

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';
}

4 changes: 2 additions & 2 deletions clang/test/Parser/opencl-storage-class.cl
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
}
2 changes: 1 addition & 1 deletion clang/test/SemaCXX/auto-cxx0x.cpp
Original file line number Diff line number Diff line change
@@ -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}}
}

Expand Down
7 changes: 4 additions & 3 deletions clang/test/SemaCXX/class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
Expand Down
7 changes: 6 additions & 1 deletion clang/test/SemaCXX/static-data-member.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
6 changes: 1 addition & 5 deletions clang/test/SemaOpenCL/storageclass.cl
Original file line number Diff line number Diff line change
Expand Up @@ -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}}

Expand Down