[clang] [clang] Reject 'auto' storage class with type specifier in C++ (PR #166004)
Osama Abdelkader via cfe-commits
cfe-commits at lists.llvm.org
Tue Dec 16 13:48:17 PST 2025
https://github.com/osamakader updated https://github.com/llvm/llvm-project/pull/166004
>From 5cf310db78bfebaf000e8e7118cc1bbcb714e2b1 Mon Sep 17 00:00:00 2001
From: Osama Abdelkader <osama.abdelkader at gmail.com>
Date: Tue, 16 Dec 2025 21:25:35 +0100
Subject: [PATCH] Fix handling of 'auto' type specifier conflicts
This commit fixes several issues related to 'auto' type specifier handling.
Core implementation:
- Add ConflictingTypeSpecifier flag to DeclSpec to track when 'auto' conflicts
with another type specifier (e.g., 'auto int' or 'int auto'). This allows
Finish() to properly handle the conflict based on language mode rather than
immediately emitting an error during parsing.
1. Fix 'auto auto' duplicate detection: Keep TypeSpecType as TST_auto after
emitting error so subsequent checks can emit function-specific errors
(e.g., 'not allowed in function prototype').
2. Fix OpenCL 'auto' and 'register' storage class errors: Reorder checks to
prioritize OpenCL-specific handling before C++11+ checks. Use
SetStorageClassSpec to trigger OpenCL validation and emit version-specific
error messages.
3. Fix C23 'int auto' and 'constexpr int auto' handling:
- For 'constexpr int auto': Treat 'auto' as type specifier conflict (don't
convert to storage class), emit single error message.
- For 'int auto' without constexpr: Convert 'auto' to storage class specifier
(no type conflict error), only emit 'illegal storage class' if applicable.
4. Fix template parameter parsing: Skip type name lookup when 'auto' is set
in template parameter context to avoid false ambiguity errors for
placeholder variables.
5. Fix concept constraint syntax: Detect 'C<T> auto' pattern and skip type
conflict detection for concept constraints.
6. Update test expectations: Align error messages and line numbers with
actual diagnostic locations and behavior.
Signed-off-by: Osama Abdelkader <osama.abdelkader at gmail.com>
---
.../clang/Basic/DiagnosticSemaKinds.td | 2 +
clang/include/clang/Sema/DeclSpec.h | 12 +-
clang/lib/Parse/ParseDecl.cpp | 97 ++++++++-
clang/lib/Sema/DeclSpec.cpp | 203 ++++++++++++++++++
.../test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp | 105 ++++-----
.../dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp | 8 +-
.../dcl.spec.auto/p3-generic-lambda-1y.cpp | 2 +-
.../dcl.spec/dcl.type/dcl.spec.auto/p3.cpp | 7 +-
clang/test/CXX/drs/cwg3xx.cpp | 18 +-
clang/test/Parser/c2x-auto.c | 4 +-
clang/test/SemaCXX/auto-cxx0x.cpp | 4 +-
clang/test/SemaCXX/class.cpp | 9 +-
clang/test/SemaCXX/static-data-member.cpp | 7 +-
13 files changed, 402 insertions(+), 76 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 381d1fb063eba..8c18ab6e1292a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2725,6 +2725,8 @@ def err_decltype_auto_invalid : Error<
"'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 43a48c92fc305..bc794b82f4e6b 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -415,6 +415,12 @@ class DeclSpec {
SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc;
SourceLocation TQ_pipeLoc;
+ // Track conflicting type specifier when 'auto' is set (for Finish()
+ // detection)
+ LLVM_PREFERRED_TYPE(TST)
+ unsigned ConflictingTypeSpecifier : 7;
+ SourceLocation ConflictingTypeSpecifierLoc;
+
WrittenBuiltinSpecs writtenBS;
void SaveWrittenBuiltinSpecs();
@@ -465,7 +471,8 @@ class DeclSpec {
FS_noreturn_specified(false), FriendSpecifiedFirst(false),
ConstexprSpecifier(
static_cast<unsigned>(ConstexprSpecKind::Unspecified)),
- Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {}
+ Attrs(attrFactory), ConflictingTypeSpecifier(TST_unspecified),
+ ConflictingTypeSpecifierLoc(), writtenBS(), ObjCQualifiers(nullptr) {}
// storage-class-specifier
SCS getStorageClassSpec() const { return (SCS)StorageClassSpec; }
@@ -505,6 +512,9 @@ class DeclSpec {
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 8688ccf41acb5..03b72bed1ccd8 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -3753,7 +3753,9 @@ void Parser::ParseDeclarationSpecifiers(
// 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
@@ -3838,6 +3840,14 @@ void Parser::ParseDeclarationSpecifiers(
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,
@@ -3845,11 +3855,16 @@ void Parser::ParseDeclarationSpecifiers(
// 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()) {
@@ -3861,6 +3876,47 @@ void Parser::ParseDeclarationSpecifiers(
goto DoneWithDeclSpec;
}
+ // If 'auto' is set and this identifier is a type name, check if it's
+ // followed by declarator tokens (like '=', '(', '[', etc.). If so, this
+ // identifier is likely the variable name, not a type specifier, so we
+ // should stop parsing declaration specifiers.
+ // Also check for concept constraint syntax (C<T> auto param) where
+ // the identifier before 'auto' might be a concept, not a type conflict.
+ // Also check for template parameters (template<auto V>) and lambda
+ // parameters
+ // ([](auto c)) where the identifier is a parameter name, not a type
+ // conflict.
+ if (DS.getTypeSpecType() == DeclSpec::TST_auto && TypeRep) {
+ // Check if the next token indicates this is a declarator
+ tok::TokenKind NextKind = NextToken().getKind();
+ if (NextKind == tok::equal || NextKind == tok::l_paren ||
+ NextKind == tok::l_square || NextKind == tok::amp ||
+ NextKind == tok::ampamp || NextKind == tok::star ||
+ NextKind == tok::coloncolon || NextKind == tok::comma ||
+ NextKind == tok::semi || NextKind == tok::colon ||
+ NextKind == tok::greater || NextKind == tok::r_paren ||
+ NextKind == 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;
+ }
+ // Check for concept constraint syntax: C<T> auto param)
+ // If the identifier is followed by 'auto' and then an identifier that's
+ // followed by ')', this might be concept syntax, not a type conflict.
+ if (NextKind == tok::identifier) {
+ // Look ahead to see if this is followed by ')' (function parameter)
+ Token AfterNext = GetLookAheadToken(2);
+ if (AfterNext.is(tok::r_paren)) {
+ // This might be concept constraint syntax, skip conflict detection
+ 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;
@@ -4094,15 +4150,33 @@ void Parser::ParseDeclarationSpecifiers(
break;
case tok::kw_auto:
if (getLangOpts().CPlusPlus11 || getLangOpts().C23) {
- if (isKnownToBeTypeSpecifier(GetLookAheadToken(1))) {
- 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);
+ // Check for concept constraint syntax: C<T> auto param
+ // If there's already a type specifier, check if the previous token was
+ // '>' by looking at the source text before 'auto'.
+ if (DS.getTypeSpecType() != DeclSpec::TST_unspecified &&
+ Loc.isValid()) {
+ const SourceManager &SM = PP.getSourceManager();
+ const char *CharData = SM.getCharacterData(Loc);
+ if (CharData) {
+ // Look backwards for '>' token, skipping whitespace
+ const char *Cur = CharData - 1;
+ const char *BufferStart =
+ SM.getBufferData(SM.getFileID(Loc)).data();
+ while (Cur >= BufferStart && (*Cur == ' ' || *Cur == '\t' ||
+ *Cur == '\n' || *Cur == '\r')) {
+ --Cur;
+ }
+ if (Cur >= BufferStart && *Cur == '>') {
+ // This is concept constraint syntax (C<T> auto), don't treat as
+ // conflict. The concept constraint will be handled elsewhere
+ goto DoneWithDeclSpec;
+ }
+ }
+ }
+ // Always set 'auto' as a type specifier in C++11+ and C23.
+ // Conflicts will be detected in DeclSpec::Finish().
+ isInvalid = DS.SetTypeSpecType(DeclSpec::TST_auto, Loc, PrevSpec,
+ DiagID, Policy);
} else
isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc,
PrevSpec, DiagID, Policy);
@@ -4632,7 +4706,8 @@ void Parser::ParseDeclarationSpecifiers(
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 9da3d0d2ef599..a942ffc59243e 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -747,6 +747,17 @@ bool DeclSpec::SetTypeSpecType(TST T, SourceLocation TagKwLoc,
if (TypeSpecType == TST_error)
return false;
if (TypeSpecType != TST_unspecified) {
+ // 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 = TagKwLoc;
+ } else if (T == TST_auto) {
+ ConflictingTypeSpecifier = TST_auto;
+ ConflictingTypeSpecifierLoc = TagKwLoc;
+ }
PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType, Policy);
DiagID = diag::err_invalid_decl_spec_combination;
return true;
@@ -778,6 +789,17 @@ bool DeclSpec::SetTypeSpecType(TST T, SourceLocation Loc,
if (TypeSpecType == TST_error)
return false;
if (TypeSpecType != TST_unspecified) {
+ // 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;
+ }
PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType, Policy);
DiagID = diag::err_invalid_decl_spec_combination;
return true;
@@ -810,6 +832,17 @@ bool DeclSpec::SetTypeSpecType(TST T, SourceLocation TagKwLoc,
if (TypeSpecType == TST_error)
return false;
if (TypeSpecType != TST_unspecified) {
+ // 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 = TagKwLoc;
+ } else if (T == TST_auto) {
+ ConflictingTypeSpecifier = TST_auto;
+ ConflictingTypeSpecifierLoc = TagKwLoc;
+ }
PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType, Policy);
DiagID = diag::err_invalid_decl_spec_combination;
return true;
@@ -840,6 +873,17 @@ bool DeclSpec::SetTypeSpecType(TST T, SourceLocation Loc,
if (TypeSpecType == TST_error)
return false;
if (TypeSpecType != TST_unspecified) {
+ // 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;
+ }
PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType, Policy);
DiagID = diag::err_invalid_decl_spec_combination;
return true;
@@ -1167,6 +1211,164 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy &Policy) {
<< 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).
@@ -1396,6 +1598,7 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy &Policy) {
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 723a79628116c..3a3f752a9e93b 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 at -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 at -1 {{'auto' cannot be combined with a type specifier}}
+// cxx17-error at -2 {{'auto' cannot be combined with a type specifier}}
+// cxx98-error at -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 at -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 at -1 {{'auto' cannot be combined with a type specifier}}
+// cxx17-error at -2 {{'auto' cannot be combined with a type specifier}}
+// cxx98-error at -3 {{illegal storage class on function}}
-register int ro; // expected-error {{illegal storage class on file-scoped variable}}
-#if __cplusplus >= 201703L
-// expected-error at -2 {{ISO C++17 does not allow 'register' storage class specifier}}
-#elif __cplusplus >= 201103L
-// expected-warning at -4 {{'register' storage class specifier is deprecated}}
-#endif
+register int ro;
+// cxx98-error at -1 {{illegal storage class on file-scoped variable}}
+// cxx11-error at -2 {{illegal storage class on file-scoped variable}}
+// cxx11-warning at -3 {{'register' storage class specifier is deprecated and incompatible with C++17}}
+// cxx17-error at -4 {{illegal storage class on file-scoped variable}}
+// cxx17-error at -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 at -1 {{illegal storage class on function}}
+// cxx11-error at -2 {{illegal storage class on function}}
+// cxx17-error at -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 at -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 at -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 at -1 {{'auto' cannot be combined with a type specifier}}
+ // cxx17-error at -2 {{'auto' cannot be combined with a type specifier}}
+ // cxx98-error at -3 {{storage class specified for a member declaration}}
+ auto void af();
+ // cxx11-error at -1 {{'auto' cannot be combined with a type specifier}}
+ // cxx17-error at -2 {{'auto' cannot be combined with a type specifier}}
+ // cxx98-error at -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 at -1 {{storage class specified for a member declaration}}
+ // cxx11-error at -2 {{storage class specified for a member declaration}}
+ // cxx17-error at -3 {{storage class specified for a member declaration}}
+ register void rf();
+ // cxx98-error at -1 {{storage class specified for a member declaration}}
+ // cxx11-error at -2 {{storage class specified for a member declaration}}
+ // cxx17-error at -3 {{storage class specified for a member declaration}}
};
void foo(auto int ap, register int rp) {
-#if __cplusplus >= 201703L
-// expected-warning at -2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}}
-// expected-error at -3 {{ISO C++17 does not allow 'register' storage class specifier}}
-#elif __cplusplus >= 201103L
-// expected-warning at -5 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}}
-// expected-warning at -6 {{'register' storage class specifier is deprecated}}
-#endif
+ // cxx17-error at -1 {{'auto' cannot be combined with a type specifier}}
+ // cxx17-error at -2 {{ISO C++17 does not allow 'register' storage class specifier}}
+ // cxx11-error at -3 {{'auto' cannot be combined with a type specifier}}
+ // cxx11-warning at -4 {{'register' storage class specifier is deprecated and incompatible with C++17}}
auto int abo;
-#if __cplusplus >= 201103L // C++11 or later
-// expected-warning at -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 at -2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}}
-#endif
+ // cxx11-error at -1 {{'auto' cannot be combined with a type specifier}}
+ // cxx17-error at -2 {{'auto' cannot be combined with a type specifier}}
+ auto void abf();
+ // cxx11-error at -1 {{'auto' cannot be combined with a type specifier}}
+ // cxx11-warning at -2 {{empty parentheses interpreted as a function declaration}}
+ // cxx11-note at -3 {{replace parentheses with an initializer to declare a variable}}
+ // cxx17-error at -4 {{'auto' cannot be combined with a type specifier}}
+ // cxx17-warning at -5 {{empty parentheses interpreted as a function declaration}}
+ // cxx17-note at -6 {{replace parentheses with an initializer to declare a variable}}
+ // cxx98-error at -7 {{illegal storage class on function}}
register int rbo;
-#if __cplusplus >= 201703L
-// expected-error at -2 {{ISO C++17 does not allow 'register' storage class specifier}}
-#elif __cplusplus >= 201103L
-// expected-warning at -4 {{'register' storage class specifier is deprecated}}
-#endif
+ // cxx17-error at -1 {{ISO C++17 does not allow 'register' storage class specifier}}
+ // cxx11-warning at -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 at -1 {{illegal storage class on function}}
+ // cxx11-error at -2 {{illegal storage class on function}}
+ // cxx17-error at -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 e8f12156a4242..880014d591706 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 @@ 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;
+#if __cplusplus >= 201103L
+ // expected-error at -2 {{'auto' cannot be combined with a type specifier}}
+#else
+ // expected-warning at -4 {{storage class}}
+ // expected-error at -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 07bc884e7d5ee..aa64331950460 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,7 +63,7 @@ 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}}\
+ 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}}\
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 at -2 {{'auto' cannot be combined with a type specifier}}
+#else
+ // expected-warning at -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..52bc92820176e 100644
--- a/clang/test/CXX/drs/cwg3xx.cpp
+++ b/clang/test/CXX/drs/cwg3xx.cpp
@@ -1732,13 +1732,23 @@ namespace cwg395 { // cwg395: 3.0
namespace cwg396 { // cwg396: 3.0
void f() {
auto int a();
- // since-cxx11-error at -1 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}}
- // expected-error at -2 {{illegal storage class on function}}
+#if __cplusplus >= 201103L
+ // expected-error at -2 {{'auto' cannot be combined with a type specifier}}
+ // expected-warning at -3 {{empty parentheses interpreted as a function declaration}}
+ // expected-note at -4 {{replace parentheses with an initializer to declare a variable}}
+#else
+ // expected-error at -6 {{illegal storage class on function}}
+ // since-cxx11-error at -7 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}}
+#endif
int (i); // #cwg396-i
auto int (i);
- // since-cxx11-error at -1 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}}
- // expected-error at -2 {{redefinition of 'i'}}
+#if __cplusplus >= 201103L
+ // expected-error at -2 {{'auto' cannot be combined with a type specifier}}
+#else
+ // expected-error at -4 {{redefinition of 'i'}}
// expected-note@#cwg396-i {{previous definition is here}}
+ // since-cxx11-error at -5 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}}
+#endif
}
} // namespace cwg396
diff --git a/clang/test/Parser/c2x-auto.c b/clang/test/Parser/c2x-auto.c
index 7f80b0717ab25..941a99344fc83 100644
--- a/clang/test/Parser/c2x-auto.c
+++ b/clang/test/Parser/c2x-auto.c
@@ -144,7 +144,7 @@ constexpr int auto a2 = 0; // c23-error {{cannot combine with previous 'int' dec
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() {
@@ -155,5 +155,5 @@ void f() {
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;
}
diff --git a/clang/test/SemaCXX/auto-cxx0x.cpp b/clang/test/SemaCXX/auto-cxx0x.cpp
index 07687b6066790..594cbf40f7179 100644
--- a/clang/test/SemaCXX/auto-cxx0x.cpp
+++ b/clang/test/SemaCXX/auto-cxx0x.cpp
@@ -1,8 +1,8 @@
// 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}}
}
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 f1e02d5158aac..71c71a1acc807 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 at -2 {{'auto' storage class specifier is redundant}}
+ auto int errx;
+#if __cplusplus >= 201103L
+ // expected-error at -2 {{'auto' cannot be combined with a type specifier}}
#else
- // expected-warning at -4 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}}
+ // expected-error at -4 {{storage class specified for a member declaration}}
+ // expected-warning at -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 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 at -2 {{'auto' cannot be combined with a type specifier}}
+#else
+// expected-error at -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
More information about the cfe-commits
mailing list