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 <[email protected]> Date: Tue, 16 Dec 2025 21:25:35 +0100 Subject: [PATCH 01/10] 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 <[email protected]> --- .../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@-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 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@-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 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@-2 {{'auto' cannot be combined with a type specifier}} +#else + // expected-warning@-4 {{'auto' storage class specifier}} +#endif same<__typeof(x), int> xHasTypeInt; same<__typeof(v), const int*> vHasTypeConstIntPtr; diff --git a/clang/test/CXX/drs/cwg3xx.cpp b/clang/test/CXX/drs/cwg3xx.cpp index 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@-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}} +#if __cplusplus >= 201103L + // expected-error@-2 {{'auto' cannot be combined with a type specifier}} + // expected-warning@-3 {{empty parentheses interpreted as a function declaration}} + // expected-note@-4 {{replace parentheses with an initializer to declare a variable}} +#else + // expected-error@-6 {{illegal storage class on function}} + // since-cxx11-error@-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@-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'}} +#if __cplusplus >= 201103L + // expected-error@-2 {{'auto' cannot be combined with a type specifier}} +#else + // expected-error@-4 {{redefinition of 'i'}} // expected-note@#cwg396-i {{previous definition is here}} + // since-cxx11-error@-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@-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 fb63da9b40099..2dbcae1745e5b 100644 --- a/clang/test/SemaCXX/static-data-member.cpp +++ b/clang/test/SemaCXX/static-data-member.cpp @@ -13,7 +13,12 @@ double ABC::a = 1.0; extern double ABC::b = 1.0; // expected-error {{static data member definition cannot specify a storage class}} static double ABC::c = 1.0; // expected-error {{'static' can only be specified inside the class definition}} __private_extern__ double ABC::d = 1.0; // expected-error {{static data member definition cannot specify a storage class}} -auto double ABC::e = 1.0; // expected-error {{static data member definition cannot specify a storage class}} +auto double ABC::e = 1.0; +#if __cplusplus >= 201103L +// expected-error@-2 {{'auto' cannot be combined with a type specifier}} +#else +// expected-error@-4 {{static data member definition cannot specify a storage class}} +#endif #if __cplusplus < 201703L register double ABC::f = 1.0; // expected-error {{static data member definition cannot specify a storage class}} #endif >From b842181fe6ab357b301bfe06d9d909c01dc16bab Mon Sep 17 00:00:00 2001 From: Osama Abdelkader <[email protected]> Date: Sat, 14 Mar 2026 22:02:07 +0100 Subject: [PATCH 02/10] Add more auto type tests, and some refactoring Signed-off-by: Osama Abdelkader <[email protected]> --- 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 <[email protected]> Date: Sat, 14 Mar 2026 22:14:56 +0100 Subject: [PATCH 03/10] Fix DeclSpec merge conflict Signed-off-by: Osama Abdelkader <[email protected]> --- 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 <[email protected]> Date: Sat, 14 Mar 2026 22:27:37 +0100 Subject: [PATCH 04/10] Fix format error Signed-off-by: Osama Abdelkader <[email protected]> --- 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 <[email protected]> Date: Sat, 14 Mar 2026 22:32:50 +0100 Subject: [PATCH 05/10] Reordering constructor initializers to match member declaration order Signed-off-by: Osama Abdelkader <[email protected]> --- 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 <[email protected]> Date: Sun, 10 May 2026 16:22:46 +0200 Subject: [PATCH 06/10] clean up ParseDecl auto type part Signed-off-by: Osama Abdelkader <[email protected]> --- 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 <[email protected]> Date: Sun, 10 May 2026 16:23:45 +0200 Subject: [PATCH 07/10] use since-cxx11-error for auto type in cwg3xx.cpp test Signed-off-by: Osama Abdelkader <[email protected]> --- 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@-2 {{'auto' cannot be combined with a type specifier}} - // expected-warning@-3 {{empty parentheses interpreted as a function declaration}} - // expected-note@-4 {{replace parentheses with an initializer to declare a variable}} -#else - // expected-error@-6 {{illegal storage class on function}} - // since-cxx11-error@-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@-2 {{'auto' cannot be combined with a type specifier}} -#else - // expected-error@-4 {{redefinition of 'i'}} - // expected-note@#cwg396-i {{previous definition is here}} - // since-cxx11-error@-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 >From a13b98edc4daae2ad694864d5b1ca0253dc92348 Mon Sep 17 00:00:00 2001 From: Osama Abdelkader <[email protected]> Date: Sat, 23 May 2026 18:26:01 +0200 Subject: [PATCH 08/10] refactor conflicting type tracking Signed-off-by: Osama Abdelkader <[email protected]> --- clang/include/clang/Sema/DeclSpec.h | 40 ++++++++++++++++++++++++ clang/lib/Sema/DeclSpec.cpp | 48 +++-------------------------- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index e6a6338cfaed2..3e76bfc920152 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -434,6 +434,46 @@ class DeclSpec { 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; diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index cb4d416490dea..17409acf0c3ed 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -759,17 +759,7 @@ 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; - } + setConflictingTypeSpecifier(T, TagKwLoc, TagNameLoc, Rep); PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType, Policy); DiagID = diag::err_invalid_decl_spec_combination; return true; @@ -801,17 +791,7 @@ 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; - } + setConflictingTypeSpecifier(T, Loc, Rep); PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType, Policy); DiagID = diag::err_invalid_decl_spec_combination; return true; @@ -844,17 +824,7 @@ 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; - } + setConflictingTypeSpecifier(T, TagKwLoc, TagNameLoc, Rep, Owned); PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType, Policy); DiagID = diag::err_invalid_decl_spec_combination; return true; @@ -885,17 +855,7 @@ 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; - } + setConflictingTypeSpecifier(T, Loc); PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType, Policy); DiagID = diag::err_invalid_decl_spec_combination; return true; >From 92611c2bcc17c59560bbee0e318d75f176be59a3 Mon Sep 17 00:00:00 2001 From: Osama Abdelkader <[email protected]> Date: Sat, 23 May 2026 19:51:47 +0200 Subject: [PATCH 09/10] add c2x auto with typedef specifier Signed-off-by: Osama Abdelkader <[email protected]> --- clang/test/Parser/c2x-auto.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/clang/test/Parser/c2x-auto.c b/clang/test/Parser/c2x-auto.c index d60c96e2b168f..8d40ec0e28267 100644 --- a/clang/test/Parser/c2x-auto.c +++ b/clang/test/Parser/c2x-auto.c @@ -168,6 +168,13 @@ void t1() { 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; >From 5dc842ef83911ea9549aa480a040ddfdef19e775 Mon Sep 17 00:00:00 2001 From: Osama Abdelkader <[email protected]> Date: Sat, 23 May 2026 19:52:52 +0200 Subject: [PATCH 10/10] add release note - reject auto with type spec in c++ Signed-off-by: Osama Abdelkader <[email protected]> --- clang/docs/ReleaseNotes.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index c17143e3c0398..0ec0d27a756d8 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -54,6 +54,9 @@ C++ Specific Potentially Breaking Changes 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``. + ABI Changes in This Version --------------------------- _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
