[clang] [clang] Reject 'auto' storage class with type specifier in C++ (PR #166004)
Osama Abdelkader via cfe-commits
cfe-commits at lists.llvm.org
Sun May 10 07:25:16 PDT 2026
https://github.com/osamakader updated https://github.com/llvm/llvm-project/pull/166004
>From b953ef3b73c99738144568b70a396b5cfd80ebaa 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 1/7] 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 | 13 +-
clang/lib/Parse/ParseDecl.cpp | 66 +++++-
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 | 10 +-
.../dcl.spec/dcl.type/dcl.spec.auto/p3.cpp | 7 +-
clang/test/CXX/drs/cwg3xx.cpp | 18 +-
clang/test/SemaCXX/auto-cxx0x.cpp | 4 +-
clang/test/SemaCXX/class.cpp | 9 +-
clang/test/SemaCXX/static-data-member.cpp | 7 +-
12 files changed, 378 insertions(+), 74 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c638c23f24bb5..e022cc3de7612 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2749,6 +2749,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 61706bc8f4229..5e0b7345dd899 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -428,6 +428,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();
@@ -472,14 +478,14 @@ class DeclSpec {
TypeSpecSign(static_cast<unsigned>(TypeSpecifierSign::Unspecified)),
TypeSpecType(TST_unspecified), TypeAltiVecVector(false),
TypeAltiVecPixel(false), TypeAltiVecBool(false), TypeSpecOwned(false),
- TypeSpecPipe(false), TypeSpecSat(false), ConstrainedAuto(false),
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), ConflictingTypeSpecifier(TST_unspecified),
+ ConflictingTypeSpecifierLoc(), writtenBS(), ObjCQualifiers(nullptr) {}
// storage-class-specifier
SCS getStorageClassSpec() const { return (SCS)StorageClassSpec; }
@@ -519,6 +525,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 55ea562faacaa..4ada0589a363c 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -3775,7 +3775,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
@@ -3860,6 +3862,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,
@@ -3867,11 +3877,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()) {
@@ -3883,6 +3898,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;
@@ -4137,12 +4193,9 @@ void Parser::ParseDeclarationSpecifiers(
}
};
- 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 +4753,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 660b1805c450e..cb4d416490dea 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -759,6 +759,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;
@@ -790,6 +801,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;
@@ -822,6 +844,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;
@@ -852,6 +885,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;
@@ -1198,6 +1242,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).
@@ -1426,6 +1628,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 ca8d50fe22728..240c673f6b0db 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,10 +63,10 @@ int main()
auto l = [](auto
(*)(auto)) { }; //expected-error{{'auto' not allowed}}
//FIXME: These diagnostics might need some work.
- auto l2 = [](char auto::*pm) { }; // expected-error {{cannot combine with previous '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 ()'}}
+ 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 ()'}}
}
}
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 10bf57e422f33..923ee43a9ca80 100644
--- a/clang/test/CXX/drs/cwg3xx.cpp
+++ b/clang/test/CXX/drs/cwg3xx.cpp
@@ -1705,13 +1705,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/SemaCXX/auto-cxx0x.cpp b/clang/test/SemaCXX/auto-cxx0x.cpp
index f429bebb9941a..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
>From b842181fe6ab357b301bfe06d9d909c01dc16bab Mon Sep 17 00:00:00 2001
From: Osama Abdelkader <osama.abdelkader at gmail.com>
Date: Sat, 14 Mar 2026 22:02:07 +0100
Subject: [PATCH 2/7] Add more auto type tests, and some refactoring
Signed-off-by: Osama Abdelkader <osama.abdelkader at gmail.com>
---
clang/include/clang/Sema/DeclSpec.h | 11 ++++-------
clang/lib/Parse/ParseDecl.cpp | 15 ++++++---------
clang/test/SemaCXX/auto-cxx0x.cpp | 6 ++++++
3 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 5e0b7345dd899..36260dd69be31 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -362,6 +362,9 @@ class DeclSpec {
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)
@@ -426,13 +429,7 @@ class DeclSpec {
SourceLocation FS_explicitCloseParenLoc;
SourceLocation FS_forceinlineLoc;
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;
+ SourceLocation TQ_pipeLoc, ConflictingTypeSpecifierLoc;
WrittenBuiltinSpecs writtenBS;
void SaveWrittenBuiltinSpecs();
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 4ada0589a363c..82877b47614d9 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -3910,14 +3910,11 @@ void Parser::ParseDeclarationSpecifiers(
// 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) {
+ 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).
@@ -3929,7 +3926,7 @@ void Parser::ParseDeclarationSpecifiers(
// 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) {
+ if (Next.is(tok::identifier)) {
// Look ahead to see if this is followed by ')' (function parameter)
Token AfterNext = GetLookAheadToken(2);
if (AfterNext.is(tok::r_paren)) {
diff --git a/clang/test/SemaCXX/auto-cxx0x.cpp b/clang/test/SemaCXX/auto-cxx0x.cpp
index 594cbf40f7179..00ede3358b6b4 100644
--- a/clang/test/SemaCXX/auto-cxx0x.cpp
+++ b/clang/test/SemaCXX/auto-cxx0x.cpp
@@ -3,6 +3,12 @@
void f() {
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}}
>From db20607696a44513edd0f93f396f94cda342efb9 Mon Sep 17 00:00:00 2001
From: Osama Abdelkader <osama.abdelkader at gmail.com>
Date: Sat, 14 Mar 2026 22:14:56 +0100
Subject: [PATCH 3/7] Fix DeclSpec merge conflict
Signed-off-by: Osama Abdelkader <osama.abdelkader at gmail.com>
---
clang/include/clang/Sema/DeclSpec.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 36260dd69be31..721da6e623333 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -475,6 +475,7 @@ class DeclSpec {
TypeSpecSign(static_cast<unsigned>(TypeSpecifierSign::Unspecified)),
TypeSpecType(TST_unspecified), TypeAltiVecVector(false),
TypeAltiVecPixel(false), TypeAltiVecBool(false), TypeSpecOwned(false),
+ TypeSpecPipe(false), TypeSpecSat(false), ConstrainedAuto(false),
TypeQualifiers(TQ_unspecified),
OB_state(static_cast<unsigned>(OverflowBehaviorState::Unspecified)),
FS_inline_specified(false), FS_forceinline_specified(false),
>From 954cc290d922fcd672abbba13b6b7a7fcbbd4250 Mon Sep 17 00:00:00 2001
From: Osama Abdelkader <osama.abdelkader at gmail.com>
Date: Sat, 14 Mar 2026 22:27:37 +0100
Subject: [PATCH 4/7] Fix format error
Signed-off-by: Osama Abdelkader <osama.abdelkader at gmail.com>
---
clang/include/clang/Sema/DeclSpec.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 721da6e623333..b3c9a9c0750f6 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -362,7 +362,8 @@ class DeclSpec {
unsigned TypeSpecSat : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned ConstrainedAuto : 1;
- // Track conflicting type specifier when 'auto' is set (for Finish() detection)
+ // Track conflicting type specifier when 'auto' is set (for Finish()
+ // detection)
LLVM_PREFERRED_TYPE(TST)
unsigned ConflictingTypeSpecifier : 7;
>From 6cc608c1c1f901310250c35b36846c285ec8f079 Mon Sep 17 00:00:00 2001
From: Osama Abdelkader <osama.abdelkader at gmail.com>
Date: Sat, 14 Mar 2026 22:32:50 +0100
Subject: [PATCH 5/7] Reordering constructor initializers to match member
declaration order
Signed-off-by: Osama Abdelkader <osama.abdelkader at gmail.com>
---
clang/include/clang/Sema/DeclSpec.h | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index b3c9a9c0750f6..e6a6338cfaed2 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -477,14 +477,15 @@ class DeclSpec {
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), ConflictingTypeSpecifier(TST_unspecified),
- ConflictingTypeSpecifierLoc(), writtenBS(), ObjCQualifiers(nullptr) {}
+ Attrs(attrFactory), ConflictingTypeSpecifierLoc(), writtenBS(),
+ ObjCQualifiers(nullptr) {}
// storage-class-specifier
SCS getStorageClassSpec() const { return (SCS)StorageClassSpec; }
>From db56433a7095ff65ebe72be336c9e5bb28a766d7 Mon Sep 17 00:00:00 2001
From: Osama Abdelkader <osama.abdelkader at gmail.com>
Date: Sun, 10 May 2026 16:22:46 +0200
Subject: [PATCH 6/7] clean up ParseDecl auto type part
Signed-off-by: Osama Abdelkader <osama.abdelkader at gmail.com>
---
clang/lib/Parse/ParseDecl.cpp | 25 +++----------------------
1 file changed, 3 insertions(+), 22 deletions(-)
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 82877b47614d9..6dc07ee3ddd21 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -3898,18 +3898,10 @@ 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 '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) {
- // Check if the next token indicates this is a declarator
Token Next = NextToken();
if (Next.isOneOf(tok::equal, tok::l_paren, tok::l_square, tok::amp,
tok::ampamp, tok::star, tok::coloncolon, tok::comma,
@@ -3923,17 +3915,6 @@ void Parser::ParseDeclarationSpecifiers(
// 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 (Next.is(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
>From d27dc824ee6d9072fbaf28fae9f06c97d0ccd4bf Mon Sep 17 00:00:00 2001
From: Osama Abdelkader <osama.abdelkader at gmail.com>
Date: Sun, 10 May 2026 16:23:45 +0200
Subject: [PATCH 7/7] use since-cxx11-error for auto type in cwg3xx.cpp test
Signed-off-by: Osama Abdelkader <osama.abdelkader at gmail.com>
---
clang/test/CXX/drs/cwg3xx.cpp | 26 +++++++++-----------------
1 file changed, 9 insertions(+), 17 deletions(-)
diff --git a/clang/test/CXX/drs/cwg3xx.cpp b/clang/test/CXX/drs/cwg3xx.cpp
index 923ee43a9ca80..05db04255780b 100644
--- a/clang/test/CXX/drs/cwg3xx.cpp
+++ b/clang/test/CXX/drs/cwg3xx.cpp
@@ -1704,24 +1704,16 @@ namespace cwg395 { // cwg395: 3.0
namespace cwg396 { // cwg396: 3.0
void f() {
- auto int a();
-#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
+ 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);
-#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
+ 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
More information about the cfe-commits
mailing list