[clang] Reject 'auto' storage class with type specifier in C++ (#166004)
Fixes #164273
---------
Signed-off-by: Osama Abdelkader <osama.abdelkader@gmail.com>
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index a573599..61caf2d 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -116,6 +116,8 @@
as being of type `std::size_t` instead of `int`,
matching the deduction of array sizes from `int(&)[N]`.
This is a breaking change for code that depended on the previously deduced type. (#GH195033)
+- Clang now rejects C++ declarations that combine the `auto` type specifier
+ with another type specifier, such as `auto int`.
- Clang now rejects nested local classes defined in a different
block scope than their parent class. (#GH193472)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 15867f6..45f909f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2759,6 +2759,8 @@
"'decltype(auto)' not allowed here">;
def err_decltype_auto_cannot_be_combined : Error<
"'decltype(auto)' cannot be combined with other type specifiers">;
+def err_auto_type_specifier : Error<
+ "'auto' cannot be combined with a type specifier">;
def err_decltype_auto_function_declarator_not_declaration : Error<
"'decltype(auto)' can only be used as a return type "
"in a function declaration">;
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 6e7f9cd..d1abc0e 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -363,6 +363,10 @@
unsigned TypeSpecSat : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned ConstrainedAuto : 1;
+ // Track conflicting type specifier when 'auto' is set (for Finish()
+ // detection)
+ LLVM_PREFERRED_TYPE(TST)
+ unsigned ConflictingTypeSpecifier : 7;
// type-qualifiers
LLVM_PREFERRED_TYPE(TQ)
@@ -427,10 +431,50 @@
SourceLocation FS_explicitCloseParenLoc;
SourceLocation FS_forceinlineLoc;
SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc;
- SourceLocation TQ_pipeLoc;
+ SourceLocation TQ_pipeLoc, ConflictingTypeSpecifierLoc;
WrittenBuiltinSpecs writtenBS;
void SaveWrittenBuiltinSpecs();
+ void setConflictingTypeSpecifier(TST T, SourceLocation Loc) {
+ // Store conflicting type specifier for Finish() to detect:
+ // - If 'auto' is already set, store the conflicting type (e.g., "auto int")
+ // - If 'auto' is being set after another type, store TST_auto
+ // (e.g., "int auto").
+ if (TypeSpecType == TST_auto) {
+ ConflictingTypeSpecifier = T;
+ ConflictingTypeSpecifierLoc = Loc;
+ } else if (T == TST_auto) {
+ ConflictingTypeSpecifier = TST_auto;
+ ConflictingTypeSpecifierLoc = Loc;
+ }
+ }
+ void setConflictingTypeSpecifier(TST T, SourceLocation Loc,
+ SourceLocation NameLoc, ParsedType Rep) {
+ setConflictingTypeSpecifier(T, Loc);
+ if (TypeSpecType == TST_auto) {
+ TypeRep = Rep;
+ TSTNameLoc = NameLoc;
+ TypeSpecOwned = false;
+ }
+ }
+ void setConflictingTypeSpecifier(TST T, SourceLocation Loc, Expr *Rep) {
+ setConflictingTypeSpecifier(T, Loc);
+ if (TypeSpecType == TST_auto) {
+ ExprRep = Rep;
+ TSTNameLoc = Loc;
+ TypeSpecOwned = false;
+ }
+ }
+ void setConflictingTypeSpecifier(TST T, SourceLocation Loc,
+ SourceLocation NameLoc, Decl *Rep,
+ bool Owned) {
+ setConflictingTypeSpecifier(T, Loc);
+ if (TypeSpecType == TST_auto) {
+ DeclRep = Rep;
+ TSTNameLoc = NameLoc;
+ TypeSpecOwned = Owned && Rep != nullptr;
+ }
+ }
ObjCDeclSpec *ObjCQualifiers;
@@ -474,13 +518,15 @@
TypeSpecType(TST_unspecified), TypeAltiVecVector(false),
TypeAltiVecPixel(false), TypeAltiVecBool(false), TypeSpecOwned(false),
TypeSpecPipe(false), TypeSpecSat(false), ConstrainedAuto(false),
+ ConflictingTypeSpecifier(TST_unspecified),
TypeQualifiers(TQ_unspecified),
OB_state(static_cast<unsigned>(OverflowBehaviorState::Unspecified)),
FS_inline_specified(false), FS_forceinline_specified(false),
FS_virtual_specified(false), FS_noreturn_specified(false),
FriendSpecifiedFirst(false), ConstexprSpecifier(static_cast<unsigned>(
ConstexprSpecKind::Unspecified)),
- Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {}
+ Attrs(attrFactory), ConflictingTypeSpecifierLoc(), writtenBS(),
+ ObjCQualifiers(nullptr) {}
// storage-class-specifier
SCS getStorageClassSpec() const { return (SCS)StorageClassSpec; }
@@ -520,6 +566,9 @@
return static_cast<TypeSpecifierSign>(TypeSpecSign);
}
TST getTypeSpecType() const { return (TST)TypeSpecType; }
+ bool hasConflictingTypeSpecifier() const {
+ return ConflictingTypeSpecifier != TST_unspecified;
+ }
bool isTypeAltiVecVector() const { return TypeAltiVecVector; }
bool isTypeAltiVecPixel() const { return TypeAltiVecPixel; }
bool isTypeAltiVecBool() const { return TypeAltiVecBool; }
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 3f41e7c..1b86519 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -3775,7 +3775,9 @@
// This identifier can only be a typedef name if we haven't already seen
// a type-specifier. Without this check we misparse:
// typedef int X; struct Y { short X; }; as 'short int'.
- if (DS.hasTypeSpecifier())
+ // However, if 'auto' is set, we need to check if this identifier is a
+ // type name to detect conflicts (e.g., "auto MyInt").
+ if (DS.hasTypeSpecifier() && DS.getTypeSpecType() != DeclSpec::TST_auto)
goto DoneWithDeclSpec;
// If the token is an identifier named "__declspec" and Microsoft
@@ -3860,6 +3862,14 @@
DS.isFriendSpecified()))
goto DoneWithDeclSpec;
+ // If 'auto' is set and we're in a template parameter context, the
+ // identifier is always the parameter name, not a type specifier, so skip
+ // type name lookup to avoid false ambiguity errors.
+ if (DS.getTypeSpecType() == DeclSpec::TST_auto &&
+ DSContext == DeclSpecContext::DSC_template_param) {
+ goto DoneWithDeclSpec;
+ }
+
ParsedType TypeRep = Actions.getTypeName(
*Tok.getIdentifierInfo(), Tok.getLocation(), getCurScope(), nullptr,
false, false, nullptr, false, false,
@@ -3867,11 +3877,16 @@
// If this is not a typedef name, don't parse it as part of the declspec,
// it must be an implicit int or an error.
+ // However, if 'auto' is already set, we can't have an implicit int.
if (!TypeRep) {
if (TryAnnotateTypeConstraint())
goto DoneWithDeclSpec;
if (Tok.isNot(tok::identifier))
continue;
+ // If 'auto' is set, the identifier must be a type name or it's an
+ // error. Don't try to parse it as implicit int.
+ if (DS.getTypeSpecType() == DeclSpec::TST_auto)
+ goto DoneWithDeclSpec;
ParsedAttributes Attrs(AttrFactory);
if (ParseImplicitInt(DS, nullptr, TemplateInfo, AS, DSContext, Attrs)) {
if (!Attrs.empty()) {
@@ -3883,6 +3898,25 @@
goto DoneWithDeclSpec;
}
+ // If 'auto' is set and this identifier is a type name, stop parsing
+ // declaration specifiers when the next token indicates this is the
+ // declarator-id rather than another type specifier.
+ if (DS.getTypeSpecType() == DeclSpec::TST_auto && TypeRep) {
+ Token Next = NextToken();
+ if (Next.isOneOf(tok::equal, tok::l_paren, tok::l_square, tok::amp,
+ tok::ampamp, tok::star, tok::coloncolon, tok::comma,
+ tok::semi, tok::colon, tok::greater, tok::r_paren,
+ tok::arrow)) {
+ // This identifier is likely the variable/parameter name, stop parsing
+ // decl specifiers. Note: ':' is for range-based for loops:
+ // for (auto Arg: x).
+ // Note: '>' is for template parameters: template<auto V>
+ // Note: ')' is for function/lambda parameters: [](auto c)
+ // Note: '->' is for lambda return types: [](auto c) -> int
+ goto DoneWithDeclSpec;
+ }
+ }
+
// Likewise, if this is a context where the identifier could be a template
// name, check whether this is a deduction guide declaration.
CXXScopeSpec SS;
@@ -4137,12 +4171,9 @@
}
};
- if (MayBeTypeSpecifier()) {
+ if (!getLangOpts().CPlusPlus && MayBeTypeSpecifier()) {
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
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_auto, Loc, PrevSpec,
DiagID, Policy);
@@ -4700,7 +4731,8 @@
DS.SetRangeEnd(ConsumedEnd.isValid() ? ConsumedEnd : Tok.getLocation());
// If the specifier wasn't legal, issue a diagnostic.
- if (isInvalid) {
+ // Skip diagnostic if 'auto' conflict will be handled in Finish()
+ if (isInvalid && !DS.hasConflictingTypeSpecifier()) {
assert(PrevSpec && "Method did not return previous specifier!");
assert(DiagID);
diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp
index 2add7c6..3c882bc 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -759,6 +759,7 @@
if (TypeSpecType == TST_error)
return false;
if (TypeSpecType != TST_unspecified) {
+ setConflictingTypeSpecifier(T, TagKwLoc, TagNameLoc, Rep);
PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType, Policy);
DiagID = diag::err_invalid_decl_spec_combination;
return true;
@@ -790,6 +791,7 @@
if (TypeSpecType == TST_error)
return false;
if (TypeSpecType != TST_unspecified) {
+ setConflictingTypeSpecifier(T, Loc, Rep);
PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType, Policy);
DiagID = diag::err_invalid_decl_spec_combination;
return true;
@@ -822,6 +824,7 @@
if (TypeSpecType == TST_error)
return false;
if (TypeSpecType != TST_unspecified) {
+ setConflictingTypeSpecifier(T, TagKwLoc, TagNameLoc, Rep, Owned);
PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType, Policy);
DiagID = diag::err_invalid_decl_spec_combination;
return true;
@@ -852,6 +855,7 @@
if (TypeSpecType == TST_error)
return false;
if (TypeSpecType != TST_unspecified) {
+ setConflictingTypeSpecifier(T, Loc);
PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType, Policy);
DiagID = diag::err_invalid_decl_spec_combination;
return true;
@@ -1212,6 +1216,164 @@
<< Hints[4] << Hints[5] << Hints[6] << Hints[7];
}
+ // If 'auto' type specifier is combined with another type specifier, we need
+ // to handle it based on the language:
+ // - In C++11+: Emit error (cannot combine type specifiers)
+ // - In C23: Convert 'auto' to storage class (valid)
+ // - In OpenCL: Convert 'auto' to storage class, then OpenCL will reject it
+ // Handle both cases:
+ // - "auto int" (TypeSpecType == TST_auto, ConflictingTypeSpecifier ==
+ // TST_int)
+ // - "int auto" (TypeSpecType == TST_int, ConflictingTypeSpecifier ==
+ // TST_auto)
+ if (ConflictingTypeSpecifier != TST_unspecified) {
+ // Special case: "auto auto" - duplicate 'auto', emit error
+ if (TypeSpecType == TST_auto && ConflictingTypeSpecifier == TST_auto) {
+ // Both are 'auto', emit "cannot combine with previous 'auto' declaration
+ // specifier" error This matches GCC's "duplicate 'auto'" behavior Note:
+ // We keep TypeSpecType = TST_auto (don't set to TST_error) so that later
+ // checks in SemaType.cpp can emit "not allowed in function prototype" and
+ // "not allowed in function return type" errors as needed.
+ const char *PrevSpec = "auto";
+ unsigned DiagID = diag::err_invalid_decl_spec_combination;
+ S.Diag(ConflictingTypeSpecifierLoc, DiagID) << PrevSpec << PrevSpec;
+ // Clear the conflict tracking but keep TypeSpecType = TST_auto
+ ConflictingTypeSpecifier = TST_unspecified;
+ ConflictingTypeSpecifierLoc = SourceLocation();
+ } else if ((S.getLangOpts().C23 && !S.getLangOpts().CPlusPlus) ||
+ (S.getLangOpts().CPlusPlus && !S.getLangOpts().CPlusPlus11)) {
+ // In C23 or C++98, convert 'auto' to storage class specifier
+ if (TypeSpecType == TST_auto) {
+ // "auto int" case: Convert 'auto' to storage class specifier
+ StorageClassSpec = SCS_auto;
+ StorageClassSpecLoc = TSTLoc;
+ TypeSpecType = ConflictingTypeSpecifier;
+ TSTLoc = ConflictingTypeSpecifierLoc;
+ // Clear the conflict tracking
+ ConflictingTypeSpecifier = TST_unspecified;
+ ConflictingTypeSpecifierLoc = SourceLocation();
+ } else if (ConflictingTypeSpecifier == TST_auto) {
+ // "int auto" case: Convert 'auto' to storage class specifier
+ // In C23, if 'constexpr' is present, treat 'auto' as a type specifier
+ // conflict with 'int' rather than converting it to storage class, so we
+ // emit the "cannot combine with previous 'int' declaration specifier"
+ // error and mark the type as error to prevent further processing.
+ // Otherwise, convert 'auto' to storage class specifier (no type
+ // conflict error).
+ if (S.getLangOpts().C23 && !S.getLangOpts().CPlusPlus &&
+ getConstexprSpecifier() != ConstexprSpecKind::Unspecified) {
+ // constexpr int auto: treat as type specifier conflict
+ const char *PrevSpec =
+ getSpecifierName((TST)TypeSpecType, S.getPrintingPolicy());
+ unsigned DiagID = diag::err_invalid_decl_spec_combination;
+ S.Diag(ConflictingTypeSpecifierLoc, DiagID) << PrevSpec << "auto";
+ TypeSpecType = TST_error;
+ ConflictingTypeSpecifier = TST_unspecified;
+ ConflictingTypeSpecifierLoc = SourceLocation();
+ return;
+ }
+ // int auto (without constexpr): Convert 'auto' to storage class
+ // specifier. No type conflict error - auto is treated as storage class,
+ // not type specifier.
+ StorageClassSpec = SCS_auto;
+ StorageClassSpecLoc = ConflictingTypeSpecifierLoc;
+ // TypeSpecType already has the correct type (e.g., TST_int)
+ // Clear the conflict tracking
+ ConflictingTypeSpecifier = TST_unspecified;
+ ConflictingTypeSpecifierLoc = SourceLocation();
+ }
+ } else if (S.getLangOpts().OpenCL) {
+ // For OpenCL (C or C++), convert 'auto' to storage class specifier first
+ // (OpenCL will then reject it via SetStorageClassSpec checks)
+ // This must come before the C++11+ check to handle OpenCL C++
+ if (TypeSpecType == TST_auto) {
+ // "auto int" case: Convert 'auto' to storage class specifier
+ // Use SetStorageClassSpec to trigger OpenCL-specific error checking
+ const char *PrevSpec = nullptr;
+ unsigned DiagID = 0;
+ if (SetStorageClassSpec(
+ S, SCS_auto, TSTLoc, PrevSpec, DiagID,
+ S.getPrintingPolicy())) { // OpenCL rejected it, emit the error
+ // with version string
+ if (S.getLangOpts().OpenCL && S.getLangOpts().CPlusPlus) {
+ S.Diag(TSTLoc, DiagID)
+ << S.getLangOpts().getOpenCLVersionString() << PrevSpec << 1;
+ } else {
+ S.Diag(TSTLoc, DiagID) << PrevSpec;
+ }
+ TypeSpecType = TST_error;
+ } else {
+ StorageClassSpec = SCS_auto;
+ StorageClassSpecLoc = TSTLoc;
+ TypeSpecType = ConflictingTypeSpecifier;
+ TSTLoc = ConflictingTypeSpecifierLoc;
+ }
+ // Clear the conflict tracking
+ ConflictingTypeSpecifier = TST_unspecified;
+ ConflictingTypeSpecifierLoc = SourceLocation();
+ } else if (ConflictingTypeSpecifier == TST_auto) {
+ // "int auto" case: Convert 'auto' to storage class specifier
+ // Use SetStorageClassSpec to trigger OpenCL-specific error checking
+ const char *PrevSpec = nullptr;
+ unsigned DiagID = 0;
+ if (SetStorageClassSpec(S, SCS_auto, ConflictingTypeSpecifierLoc,
+ PrevSpec, DiagID, S.getPrintingPolicy())) {
+ // OpenCL rejected it, emit the error with version string
+ if (S.getLangOpts().OpenCL && S.getLangOpts().CPlusPlus) {
+ S.Diag(ConflictingTypeSpecifierLoc, DiagID)
+ << S.getLangOpts().getOpenCLVersionString() << PrevSpec << 1;
+ } else {
+ S.Diag(ConflictingTypeSpecifierLoc, DiagID) << PrevSpec;
+ }
+ TypeSpecType = TST_error;
+ } else {
+ StorageClassSpec = SCS_auto;
+ StorageClassSpecLoc = ConflictingTypeSpecifierLoc;
+ }
+ // Clear the conflict tracking
+ ConflictingTypeSpecifier = TST_unspecified;
+ ConflictingTypeSpecifierLoc = SourceLocation();
+ }
+ } else if (S.getLangOpts().CPlusPlus && S.getLangOpts().CPlusPlus11 &&
+ !S.getLangOpts().C23) {
+ // In C++11+ (but not C23 or OpenCL), emit error (cannot combine type
+ // specifiers)
+ if (TypeSpecType == TST_auto) {
+ // "auto int" case
+ S.Diag(ConflictingTypeSpecifierLoc, diag::err_auto_type_specifier)
+ << FixItHint::CreateRemoval(ConflictingTypeSpecifierLoc);
+ } else if (ConflictingTypeSpecifier == TST_auto) {
+ // "int auto" case
+ S.Diag(ConflictingTypeSpecifierLoc, diag::err_auto_type_specifier)
+ << FixItHint::CreateRemoval(ConflictingTypeSpecifierLoc);
+ }
+ // Mark as error to prevent further processing
+ TypeSpecType = TST_error;
+ } else if (!S.getLangOpts().CPlusPlus) {
+ // For C, C23, etc., convert 'auto' to storage class specifier
+ // (This is already handled above for C23, but keep for other C dialects)
+ // In C, C23, OpenCL, etc., convert 'auto' to storage class specifier
+ if (TypeSpecType == TST_auto) {
+ // "auto int" case: Convert 'auto' to storage class specifier
+ StorageClassSpec = SCS_auto;
+ StorageClassSpecLoc = TSTLoc;
+ TypeSpecType = ConflictingTypeSpecifier;
+ TSTLoc = ConflictingTypeSpecifierLoc;
+ // Clear the conflict tracking
+ ConflictingTypeSpecifier = TST_unspecified;
+ ConflictingTypeSpecifierLoc = SourceLocation();
+ } else if (ConflictingTypeSpecifier == TST_auto) {
+ // "int auto" case: Convert 'auto' to storage class specifier
+ StorageClassSpec = SCS_auto;
+ StorageClassSpecLoc = ConflictingTypeSpecifierLoc;
+ // TypeSpecType already has the correct type (e.g., TST_int)
+ // Clear the conflict tracking
+ ConflictingTypeSpecifier = TST_unspecified;
+ ConflictingTypeSpecifierLoc = SourceLocation();
+ }
+ }
+ }
+
// Validate and finalize AltiVec vector declspec.
if (TypeAltiVecVector) {
// No vector long long without VSX (or ZVector).
@@ -1440,6 +1602,7 @@
S.getLangOpts().getHLSLVersion() < LangOptions::HLSL_202y &&
TypeSpecType == TST_auto)
S.Diag(TSTLoc, diag::ext_hlsl_auto_type_specifier) << /*HLSL*/ 1;
+ // Emit warning for 'auto' storage class in pre-C++11 dialects
if (S.getLangOpts().CPlusPlus && !S.getLangOpts().CPlusPlus11 &&
StorageClassSpec == SCS_auto)
S.Diag(StorageClassSpecLoc, diag::warn_auto_storage_class)
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 723a796..3a3f752 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
@@ -1,66 +1,75 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify=cxx98 -std=c++98 %s
+// RUN: %clang_cc1 -fsyntax-only -verify=cxx11 -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify=cxx17 -std=c++17 %s
// 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}}
-#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}}
-#endif
+auto int ao;
+// cxx11-error@-1 {{'auto' cannot be combined with a type specifier}}
+// cxx17-error@-2 {{'auto' cannot be combined with a type specifier}}
+// cxx98-error@-3 {{illegal storage class on file-scoped variable}}
-auto void af(); // expected-error {{illegal storage class on function}}
-#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}}
-#endif
+auto void af();
+// cxx11-error@-1 {{'auto' cannot be combined with a type specifier}}
+// cxx17-error@-2 {{'auto' cannot be combined with a type specifier}}
+// cxx98-error@-3 {{illegal storage class on function}}
-register int ro; // expected-error {{illegal storage class on file-scoped variable}}
-#if __cplusplus >= 201703L
-// expected-error@-2 {{ISO C++17 does not allow 'register' storage class specifier}}
-#elif __cplusplus >= 201103L
-// expected-warning@-4 {{'register' storage class specifier is deprecated}}
-#endif
+register int ro;
+// cxx98-error@-1 {{illegal storage class on file-scoped variable}}
+// cxx11-error@-2 {{illegal storage class on file-scoped variable}}
+// cxx11-warning@-3 {{'register' storage class specifier is deprecated and incompatible with C++17}}
+// cxx17-error@-4 {{illegal storage class on file-scoped variable}}
+// cxx17-error@-5 {{ISO C++17 does not allow 'register' storage class specifier}}
-register void rf(); // expected-error {{illegal storage class on function}}
+register void rf();
+// cxx98-error@-1 {{illegal storage class on function}}
+// cxx11-error@-2 {{illegal storage class on function}}
+// cxx17-error@-3 {{illegal storage class on function}}
struct S {
- auto int ao; // expected-error {{storage class specified for a member declaration}}
-#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}}
-#endif
- auto void af(); // expected-error {{storage class specified for a member declaration}}
-#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}}
-#endif
+ auto int ao;
+ // cxx11-error@-1 {{'auto' cannot be combined with a type specifier}}
+ // cxx17-error@-2 {{'auto' cannot be combined with a type specifier}}
+ // cxx98-error@-3 {{storage class specified for a member declaration}}
+ auto void af();
+ // cxx11-error@-1 {{'auto' cannot be combined with a type specifier}}
+ // cxx17-error@-2 {{'auto' cannot be combined with a type specifier}}
+ // cxx98-error@-3 {{storage class specified for a member declaration}}
- register int ro; // expected-error {{storage class specified for a member declaration}}
- register void rf(); // expected-error {{storage class specified for a member declaration}}
+ register int ro;
+ // cxx98-error@-1 {{storage class specified for a member declaration}}
+ // cxx11-error@-2 {{storage class specified for a member declaration}}
+ // cxx17-error@-3 {{storage class specified for a member declaration}}
+ register void rf();
+ // cxx98-error@-1 {{storage class specified for a member declaration}}
+ // cxx11-error@-2 {{storage class specified for a member declaration}}
+ // cxx17-error@-3 {{storage class specified for a member declaration}}
};
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@-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-warning@-6 {{'register' storage class specifier is deprecated}}
-#endif
+ // cxx17-error@-1 {{'auto' cannot be combined with a type specifier}}
+ // cxx17-error@-2 {{ISO C++17 does not allow 'register' storage class specifier}}
+ // cxx11-error@-3 {{'auto' cannot be combined with a type specifier}}
+ // cxx11-warning@-4 {{'register' storage class specifier is deprecated and incompatible with C++17}}
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}}
-#endif
- auto void abf(); // expected-error {{illegal storage class on function}}
-#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}}
-#endif
+ // cxx11-error@-1 {{'auto' cannot be combined with a type specifier}}
+ // cxx17-error@-2 {{'auto' cannot be combined with a type specifier}}
+ auto void abf();
+ // cxx11-error@-1 {{'auto' cannot be combined with a type specifier}}
+ // cxx11-warning@-2 {{empty parentheses interpreted as a function declaration}}
+ // cxx11-note@-3 {{replace parentheses with an initializer to declare a variable}}
+ // cxx17-error@-4 {{'auto' cannot be combined with a type specifier}}
+ // cxx17-warning@-5 {{empty parentheses interpreted as a function declaration}}
+ // cxx17-note@-6 {{replace parentheses with an initializer to declare a variable}}
+ // cxx98-error@-7 {{illegal storage class on function}}
register int rbo;
-#if __cplusplus >= 201703L
-// expected-error@-2 {{ISO C++17 does not allow 'register' storage class specifier}}
-#elif __cplusplus >= 201103L
-// expected-warning@-4 {{'register' storage class specifier is deprecated}}
-#endif
+ // cxx17-error@-1 {{ISO C++17 does not allow 'register' storage class specifier}}
+ // cxx11-warning@-2 {{'register' storage class specifier is deprecated and incompatible with C++17}}
- register void rbf(); // expected-error {{illegal storage class on function}}
+ register void rbf();
+ // cxx98-error@-1 {{illegal storage class on function}}
+ // cxx11-error@-2 {{illegal storage class on function}}
+ // cxx17-error@-3 {{illegal storage class on function}}
}
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 e8f1215..880014d 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,13 @@
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;
+#if __cplusplus >= 201103L
+ // expected-error@-2 {{'auto' cannot be combined with a type specifier}}
+#else
+ // expected-warning@-4 {{storage class}}
+ // expected-error@-5 {{file-scope}}
+#endif
static_assert(is_same<decltype(x), int>(), "");
static_assert(is_same<decltype(v), const int*>(), "");
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 a590f8e..7577a78 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
@@ -62,11 +62,11 @@
{
auto l = [](auto (*)(auto)) { }; //expected-error{{'auto' not allowed}} expected-warning {{'auto' parameters are a C++20 extension}}
//FIXME: These diagnostics might need some work.
- 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 ()'}} \
- expected-warning {{'auto' parameters are a C++20 extension}}
+ auto l2 = [](char auto::*pm) { }; //expected-error{{'auto' cannot be combined with a type 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 ()'}} \
+ expected-warning {{'auto' parameters are a C++20 extension}}
}
}
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 440c782..8d77655 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 @@
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/dcl/dcl.fct/p17.cpp b/clang/test/CXX/dcl/dcl.fct/p17.cpp
index d837d09..971edc7 100644
--- a/clang/test/CXX/dcl/dcl.fct/p17.cpp
+++ b/clang/test/CXX/dcl/dcl.fct/p17.cpp
@@ -135,7 +135,7 @@
struct AStruct {};
-void a_struct(AStruct auto); // expected-error {{cannot combine with previous 'type-name' declaration specifier}}
+void a_struct(AStruct auto); // expected-error {{'auto' cannot be combined with a type specifier}}
}
#if __cplusplus >= 202002L
diff --git a/clang/test/CXX/drs/cwg3xx.cpp b/clang/test/CXX/drs/cwg3xx.cpp
index 10bf57e..05db042 100644
--- a/clang/test/CXX/drs/cwg3xx.cpp
+++ b/clang/test/CXX/drs/cwg3xx.cpp
@@ -1704,14 +1704,16 @@
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}}
+ auto int a(); // #cwg396-a
+ // cxx98-error@#cwg396-a {{illegal storage class on function}}
+ // since-cxx11-error@#cwg396-a {{'auto' cannot be combined with a type specifier}}
+ // since-cxx11-warning@#cwg396-a {{empty parentheses interpreted as a function declaration}}
+ // since-cxx11-note@#cwg396-a {{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}}
- // expected-error@-2 {{redefinition of 'i'}}
- // expected-note@#cwg396-i {{previous definition is here}}
+ auto int (i); // #cwg396-auto-i
+ // cxx98-error@#cwg396-auto-i {{redefinition of 'i'}}
+ // cxx98-note@#cwg396-i {{previous definition is here}}
+ // since-cxx11-error@#cwg396-auto-i {{'auto' cannot be combined with a type specifier}}
}
} // namespace cwg396
diff --git a/clang/test/Parser/c2x-auto.c b/clang/test/Parser/c2x-auto.c
index dc0e1f3..145e6f8 100644
--- a/clang/test/Parser/c2x-auto.c
+++ b/clang/test/Parser/c2x-auto.c
@@ -167,6 +167,13 @@
int auto d2 = 0;
}
+void typedef_type_specifiers(void) {
+ typedef int MyInt;
+
+ auto MyInt a = 0;
+ MyInt auto b = 0;
+}
+
void t2() {
auto long long a1 = 0;
long auto long a2 = 0;
diff --git a/clang/test/SemaCXX/auto-cxx0x.cpp b/clang/test/SemaCXX/auto-cxx0x.cpp
index f429beb..00ede33 100644
--- a/clang/test/SemaCXX/auto-cxx0x.cpp
+++ b/clang/test/SemaCXX/auto-cxx0x.cpp
@@ -1,8 +1,14 @@
// 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}}
- int auto b; // expected-error {{cannot combine with previous 'int' declaration specifier}}
+ auto int a; // expected-error {{'auto' cannot be combined with a type specifier}}
+ int auto b; // expected-error {{'auto' cannot be combined with a type specifier}}
+ unsigned auto int x; // expected-error {{'auto' cannot be combined with a type specifier}} expected-error-re {{'{{.*}}' cannot be signed or unsigned}}
+ signed auto int s; // expected-error {{'auto' cannot be combined with a type specifier}} expected-error-re {{'{{.*}}' cannot be signed or unsigned}}
+ auto double y; // expected-error {{'auto' cannot be combined with a type specifier}}
+ auto float z; // expected-error {{'auto' cannot be combined with a type specifier}}
+ long auto int l; // expected-error {{'auto' cannot be combined with a type specifier}} expected-error-re {{'long {{.*}}' is invalid}}
+ auto int arr[10]; // expected-error {{'auto' cannot be combined with a type specifier}}
}
typedef auto PR25449(); // expected-error {{'auto' not allowed in typedef}}
diff --git a/clang/test/SemaCXX/class.cpp b/clang/test/SemaCXX/class.cpp
index f1e02d5..71c71a1a 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}}
-#if __cplusplus <= 199711L
- // expected-warning@-2 {{'auto' storage class specifier is redundant}}
+ auto int errx;
+#if __cplusplus >= 201103L
+ // expected-error@-2 {{'auto' cannot be combined with a type specifier}}
#else
- // expected-warning@-4 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}}
+ // expected-error@-4 {{storage class specified for a member declaration}}
+ // expected-warning@-5 {{'auto' storage class specifier is redundant}}
#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 fb63da9..2dbcae1 100644
--- a/clang/test/SemaCXX/static-data-member.cpp
+++ b/clang/test/SemaCXX/static-data-member.cpp
@@ -13,7 +13,12 @@
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