https://github.com/osamakader updated https://github.com/llvm/llvm-project/pull/166004
>From 5cf310db78bfebaf000e8e7118cc1bbcb714e2b1 Mon Sep 17 00:00:00 2001 From: Osama Abdelkader <[email protected]> Date: Tue, 16 Dec 2025 21:25:35 +0100 Subject: [PATCH] Fix handling of 'auto' type specifier conflicts This commit fixes several issues related to 'auto' type specifier handling. Core implementation: - Add ConflictingTypeSpecifier flag to DeclSpec to track when 'auto' conflicts with another type specifier (e.g., 'auto int' or 'int auto'). This allows Finish() to properly handle the conflict based on language mode rather than immediately emitting an error during parsing. 1. Fix 'auto auto' duplicate detection: Keep TypeSpecType as TST_auto after emitting error so subsequent checks can emit function-specific errors (e.g., 'not allowed in function prototype'). 2. Fix OpenCL 'auto' and 'register' storage class errors: Reorder checks to prioritize OpenCL-specific handling before C++11+ checks. Use SetStorageClassSpec to trigger OpenCL validation and emit version-specific error messages. 3. Fix C23 'int auto' and 'constexpr int auto' handling: - For 'constexpr int auto': Treat 'auto' as type specifier conflict (don't convert to storage class), emit single error message. - For 'int auto' without constexpr: Convert 'auto' to storage class specifier (no type conflict error), only emit 'illegal storage class' if applicable. 4. Fix template parameter parsing: Skip type name lookup when 'auto' is set in template parameter context to avoid false ambiguity errors for placeholder variables. 5. Fix concept constraint syntax: Detect 'C<T> auto' pattern and skip type conflict detection for concept constraints. 6. Update test expectations: Align error messages and line numbers with actual diagnostic locations and behavior. Signed-off-by: Osama Abdelkader <[email protected]> --- .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/include/clang/Sema/DeclSpec.h | 12 +- clang/lib/Parse/ParseDecl.cpp | 97 ++++++++- clang/lib/Sema/DeclSpec.cpp | 203 ++++++++++++++++++ .../test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp | 105 ++++----- .../dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp | 8 +- .../dcl.spec.auto/p3-generic-lambda-1y.cpp | 2 +- .../dcl.spec/dcl.type/dcl.spec.auto/p3.cpp | 7 +- clang/test/CXX/drs/cwg3xx.cpp | 18 +- clang/test/Parser/c2x-auto.c | 4 +- clang/test/SemaCXX/auto-cxx0x.cpp | 4 +- clang/test/SemaCXX/class.cpp | 9 +- clang/test/SemaCXX/static-data-member.cpp | 7 +- 13 files changed, 402 insertions(+), 76 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 381d1fb063eba..8c18ab6e1292a 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2725,6 +2725,8 @@ def err_decltype_auto_invalid : Error< "'decltype(auto)' not allowed here">; def err_decltype_auto_cannot_be_combined : Error< "'decltype(auto)' cannot be combined with other type specifiers">; +def err_auto_type_specifier : Error< + "'auto' cannot be combined with a type specifier">; def err_decltype_auto_function_declarator_not_declaration : Error< "'decltype(auto)' can only be used as a return type " "in a function declaration">; diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index 43a48c92fc305..bc794b82f4e6b 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -415,6 +415,12 @@ class DeclSpec { SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc; SourceLocation TQ_pipeLoc; + // Track conflicting type specifier when 'auto' is set (for Finish() + // detection) + LLVM_PREFERRED_TYPE(TST) + unsigned ConflictingTypeSpecifier : 7; + SourceLocation ConflictingTypeSpecifierLoc; + WrittenBuiltinSpecs writtenBS; void SaveWrittenBuiltinSpecs(); @@ -465,7 +471,8 @@ class DeclSpec { FS_noreturn_specified(false), FriendSpecifiedFirst(false), ConstexprSpecifier( static_cast<unsigned>(ConstexprSpecKind::Unspecified)), - Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {} + Attrs(attrFactory), ConflictingTypeSpecifier(TST_unspecified), + ConflictingTypeSpecifierLoc(), writtenBS(), ObjCQualifiers(nullptr) {} // storage-class-specifier SCS getStorageClassSpec() const { return (SCS)StorageClassSpec; } @@ -505,6 +512,9 @@ class DeclSpec { return static_cast<TypeSpecifierSign>(TypeSpecSign); } TST getTypeSpecType() const { return (TST)TypeSpecType; } + bool hasConflictingTypeSpecifier() const { + return ConflictingTypeSpecifier != TST_unspecified; + } bool isTypeAltiVecVector() const { return TypeAltiVecVector; } bool isTypeAltiVecPixel() const { return TypeAltiVecPixel; } bool isTypeAltiVecBool() const { return TypeAltiVecBool; } diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 8688ccf41acb5..03b72bed1ccd8 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -3753,7 +3753,9 @@ void Parser::ParseDeclarationSpecifiers( // This identifier can only be a typedef name if we haven't already seen // a type-specifier. Without this check we misparse: // typedef int X; struct Y { short X; }; as 'short int'. - if (DS.hasTypeSpecifier()) + // However, if 'auto' is set, we need to check if this identifier is a + // type name to detect conflicts (e.g., "auto MyInt"). + if (DS.hasTypeSpecifier() && DS.getTypeSpecType() != DeclSpec::TST_auto) goto DoneWithDeclSpec; // If the token is an identifier named "__declspec" and Microsoft @@ -3838,6 +3840,14 @@ void Parser::ParseDeclarationSpecifiers( DS.isFriendSpecified())) goto DoneWithDeclSpec; + // If 'auto' is set and we're in a template parameter context, the + // identifier is always the parameter name, not a type specifier, so skip + // type name lookup to avoid false ambiguity errors. + if (DS.getTypeSpecType() == DeclSpec::TST_auto && + DSContext == DeclSpecContext::DSC_template_param) { + goto DoneWithDeclSpec; + } + ParsedType TypeRep = Actions.getTypeName( *Tok.getIdentifierInfo(), Tok.getLocation(), getCurScope(), nullptr, false, false, nullptr, false, false, @@ -3845,11 +3855,16 @@ void Parser::ParseDeclarationSpecifiers( // If this is not a typedef name, don't parse it as part of the declspec, // it must be an implicit int or an error. + // However, if 'auto' is already set, we can't have an implicit int. if (!TypeRep) { if (TryAnnotateTypeConstraint()) goto DoneWithDeclSpec; if (Tok.isNot(tok::identifier)) continue; + // If 'auto' is set, the identifier must be a type name or it's an + // error. Don't try to parse it as implicit int. + if (DS.getTypeSpecType() == DeclSpec::TST_auto) + goto DoneWithDeclSpec; ParsedAttributes Attrs(AttrFactory); if (ParseImplicitInt(DS, nullptr, TemplateInfo, AS, DSContext, Attrs)) { if (!Attrs.empty()) { @@ -3861,6 +3876,47 @@ void Parser::ParseDeclarationSpecifiers( goto DoneWithDeclSpec; } + // If 'auto' is set and this identifier is a type name, check if it's + // followed by declarator tokens (like '=', '(', '[', etc.). If so, this + // identifier is likely the variable name, not a type specifier, so we + // should stop parsing declaration specifiers. + // Also check for concept constraint syntax (C<T> auto param) where + // the identifier before 'auto' might be a concept, not a type conflict. + // Also check for template parameters (template<auto V>) and lambda + // parameters + // ([](auto c)) where the identifier is a parameter name, not a type + // conflict. + if (DS.getTypeSpecType() == DeclSpec::TST_auto && TypeRep) { + // Check if the next token indicates this is a declarator + tok::TokenKind NextKind = NextToken().getKind(); + if (NextKind == tok::equal || NextKind == tok::l_paren || + NextKind == tok::l_square || NextKind == tok::amp || + NextKind == tok::ampamp || NextKind == tok::star || + NextKind == tok::coloncolon || NextKind == tok::comma || + NextKind == tok::semi || NextKind == tok::colon || + NextKind == tok::greater || NextKind == tok::r_paren || + NextKind == tok::arrow) { + // This identifier is likely the variable/parameter name, stop parsing + // decl specifiers. Note: ':' is for range-based for loops: + // for (auto Arg: x). + // Note: '>' is for template parameters: template<auto V> + // Note: ')' is for function/lambda parameters: [](auto c) + // Note: '->' is for lambda return types: [](auto c) -> int + goto DoneWithDeclSpec; + } + // Check for concept constraint syntax: C<T> auto param) + // If the identifier is followed by 'auto' and then an identifier that's + // followed by ')', this might be concept syntax, not a type conflict. + if (NextKind == tok::identifier) { + // Look ahead to see if this is followed by ')' (function parameter) + Token AfterNext = GetLookAheadToken(2); + if (AfterNext.is(tok::r_paren)) { + // This might be concept constraint syntax, skip conflict detection + goto DoneWithDeclSpec; + } + } + } + // Likewise, if this is a context where the identifier could be a template // name, check whether this is a deduction guide declaration. CXXScopeSpec SS; @@ -4094,15 +4150,33 @@ void Parser::ParseDeclarationSpecifiers( break; case tok::kw_auto: if (getLangOpts().CPlusPlus11 || getLangOpts().C23) { - if (isKnownToBeTypeSpecifier(GetLookAheadToken(1))) { - isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc, - PrevSpec, DiagID, Policy); - if (!isInvalid && !getLangOpts().C23) - Diag(Tok, diag::ext_auto_storage_class) - << FixItHint::CreateRemoval(DS.getStorageClassSpecLoc()); - } else - isInvalid = DS.SetTypeSpecType(DeclSpec::TST_auto, Loc, PrevSpec, - DiagID, Policy); + // Check for concept constraint syntax: C<T> auto param + // If there's already a type specifier, check if the previous token was + // '>' by looking at the source text before 'auto'. + if (DS.getTypeSpecType() != DeclSpec::TST_unspecified && + Loc.isValid()) { + const SourceManager &SM = PP.getSourceManager(); + const char *CharData = SM.getCharacterData(Loc); + if (CharData) { + // Look backwards for '>' token, skipping whitespace + const char *Cur = CharData - 1; + const char *BufferStart = + SM.getBufferData(SM.getFileID(Loc)).data(); + while (Cur >= BufferStart && (*Cur == ' ' || *Cur == '\t' || + *Cur == '\n' || *Cur == '\r')) { + --Cur; + } + if (Cur >= BufferStart && *Cur == '>') { + // This is concept constraint syntax (C<T> auto), don't treat as + // conflict. The concept constraint will be handled elsewhere + goto DoneWithDeclSpec; + } + } + } + // Always set 'auto' as a type specifier in C++11+ and C23. + // Conflicts will be detected in DeclSpec::Finish(). + isInvalid = DS.SetTypeSpecType(DeclSpec::TST_auto, Loc, PrevSpec, + DiagID, Policy); } else isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc, PrevSpec, DiagID, Policy); @@ -4632,7 +4706,8 @@ void Parser::ParseDeclarationSpecifiers( DS.SetRangeEnd(ConsumedEnd.isValid() ? ConsumedEnd : Tok.getLocation()); // If the specifier wasn't legal, issue a diagnostic. - if (isInvalid) { + // Skip diagnostic if 'auto' conflict will be handled in Finish() + if (isInvalid && !DS.hasConflictingTypeSpecifier()) { assert(PrevSpec && "Method did not return previous specifier!"); assert(DiagID); diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index 9da3d0d2ef599..a942ffc59243e 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -747,6 +747,17 @@ bool DeclSpec::SetTypeSpecType(TST T, SourceLocation TagKwLoc, if (TypeSpecType == TST_error) return false; if (TypeSpecType != TST_unspecified) { + // Store conflicting type specifier for Finish() to detect: + // - If 'auto' is already set, store the conflicting type (e.g., "auto int") + // - If 'auto' is being set after another type, store TST_auto (e.g., "int + // auto") + if (TypeSpecType == TST_auto) { + ConflictingTypeSpecifier = T; + ConflictingTypeSpecifierLoc = TagKwLoc; + } else if (T == TST_auto) { + ConflictingTypeSpecifier = TST_auto; + ConflictingTypeSpecifierLoc = TagKwLoc; + } PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType, Policy); DiagID = diag::err_invalid_decl_spec_combination; return true; @@ -778,6 +789,17 @@ bool DeclSpec::SetTypeSpecType(TST T, SourceLocation Loc, if (TypeSpecType == TST_error) return false; if (TypeSpecType != TST_unspecified) { + // Store conflicting type specifier for Finish() to detect: + // - If 'auto' is already set, store the conflicting type (e.g., "auto int") + // - If 'auto' is being set after another type, store TST_auto (e.g., "int + // auto") + if (TypeSpecType == TST_auto) { + ConflictingTypeSpecifier = T; + ConflictingTypeSpecifierLoc = Loc; + } else if (T == TST_auto) { + ConflictingTypeSpecifier = TST_auto; + ConflictingTypeSpecifierLoc = Loc; + } PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType, Policy); DiagID = diag::err_invalid_decl_spec_combination; return true; @@ -810,6 +832,17 @@ bool DeclSpec::SetTypeSpecType(TST T, SourceLocation TagKwLoc, if (TypeSpecType == TST_error) return false; if (TypeSpecType != TST_unspecified) { + // Store conflicting type specifier for Finish() to detect: + // - If 'auto' is already set, store the conflicting type (e.g., "auto int") + // - If 'auto' is being set after another type, store TST_auto (e.g., "int + // auto") + if (TypeSpecType == TST_auto) { + ConflictingTypeSpecifier = T; + ConflictingTypeSpecifierLoc = TagKwLoc; + } else if (T == TST_auto) { + ConflictingTypeSpecifier = TST_auto; + ConflictingTypeSpecifierLoc = TagKwLoc; + } PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType, Policy); DiagID = diag::err_invalid_decl_spec_combination; return true; @@ -840,6 +873,17 @@ bool DeclSpec::SetTypeSpecType(TST T, SourceLocation Loc, if (TypeSpecType == TST_error) return false; if (TypeSpecType != TST_unspecified) { + // Store conflicting type specifier for Finish() to detect: + // - If 'auto' is already set, store the conflicting type (e.g., "auto int") + // - If 'auto' is being set after another type, store TST_auto (e.g., "int + // auto") + if (TypeSpecType == TST_auto) { + ConflictingTypeSpecifier = T; + ConflictingTypeSpecifierLoc = Loc; + } else if (T == TST_auto) { + ConflictingTypeSpecifier = TST_auto; + ConflictingTypeSpecifierLoc = Loc; + } PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType, Policy); DiagID = diag::err_invalid_decl_spec_combination; return true; @@ -1167,6 +1211,164 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy &Policy) { << Hints[4] << Hints[5] << Hints[6] << Hints[7]; } + // If 'auto' type specifier is combined with another type specifier, we need + // to handle it based on the language: + // - In C++11+: Emit error (cannot combine type specifiers) + // - In C23: Convert 'auto' to storage class (valid) + // - In OpenCL: Convert 'auto' to storage class, then OpenCL will reject it + // Handle both cases: + // - "auto int" (TypeSpecType == TST_auto, ConflictingTypeSpecifier == + // TST_int) + // - "int auto" (TypeSpecType == TST_int, ConflictingTypeSpecifier == + // TST_auto) + if (ConflictingTypeSpecifier != TST_unspecified) { + // Special case: "auto auto" - duplicate 'auto', emit error + if (TypeSpecType == TST_auto && ConflictingTypeSpecifier == TST_auto) { + // Both are 'auto', emit "cannot combine with previous 'auto' declaration + // specifier" error This matches GCC's "duplicate 'auto'" behavior Note: + // We keep TypeSpecType = TST_auto (don't set to TST_error) so that later + // checks in SemaType.cpp can emit "not allowed in function prototype" and + // "not allowed in function return type" errors as needed. + const char *PrevSpec = "auto"; + unsigned DiagID = diag::err_invalid_decl_spec_combination; + S.Diag(ConflictingTypeSpecifierLoc, DiagID) << PrevSpec << PrevSpec; + // Clear the conflict tracking but keep TypeSpecType = TST_auto + ConflictingTypeSpecifier = TST_unspecified; + ConflictingTypeSpecifierLoc = SourceLocation(); + } else if ((S.getLangOpts().C23 && !S.getLangOpts().CPlusPlus) || + (S.getLangOpts().CPlusPlus && !S.getLangOpts().CPlusPlus11)) { + // In C23 or C++98, convert 'auto' to storage class specifier + if (TypeSpecType == TST_auto) { + // "auto int" case: Convert 'auto' to storage class specifier + StorageClassSpec = SCS_auto; + StorageClassSpecLoc = TSTLoc; + TypeSpecType = ConflictingTypeSpecifier; + TSTLoc = ConflictingTypeSpecifierLoc; + // Clear the conflict tracking + ConflictingTypeSpecifier = TST_unspecified; + ConflictingTypeSpecifierLoc = SourceLocation(); + } else if (ConflictingTypeSpecifier == TST_auto) { + // "int auto" case: Convert 'auto' to storage class specifier + // In C23, if 'constexpr' is present, treat 'auto' as a type specifier + // conflict with 'int' rather than converting it to storage class, so we + // emit the "cannot combine with previous 'int' declaration specifier" + // error and mark the type as error to prevent further processing. + // Otherwise, convert 'auto' to storage class specifier (no type + // conflict error). + if (S.getLangOpts().C23 && !S.getLangOpts().CPlusPlus && + getConstexprSpecifier() != ConstexprSpecKind::Unspecified) { + // constexpr int auto: treat as type specifier conflict + const char *PrevSpec = + getSpecifierName((TST)TypeSpecType, S.getPrintingPolicy()); + unsigned DiagID = diag::err_invalid_decl_spec_combination; + S.Diag(ConflictingTypeSpecifierLoc, DiagID) << PrevSpec << "auto"; + TypeSpecType = TST_error; + ConflictingTypeSpecifier = TST_unspecified; + ConflictingTypeSpecifierLoc = SourceLocation(); + return; + } + // int auto (without constexpr): Convert 'auto' to storage class + // specifier. No type conflict error - auto is treated as storage class, + // not type specifier. + StorageClassSpec = SCS_auto; + StorageClassSpecLoc = ConflictingTypeSpecifierLoc; + // TypeSpecType already has the correct type (e.g., TST_int) + // Clear the conflict tracking + ConflictingTypeSpecifier = TST_unspecified; + ConflictingTypeSpecifierLoc = SourceLocation(); + } + } else if (S.getLangOpts().OpenCL) { + // For OpenCL (C or C++), convert 'auto' to storage class specifier first + // (OpenCL will then reject it via SetStorageClassSpec checks) + // This must come before the C++11+ check to handle OpenCL C++ + if (TypeSpecType == TST_auto) { + // "auto int" case: Convert 'auto' to storage class specifier + // Use SetStorageClassSpec to trigger OpenCL-specific error checking + const char *PrevSpec = nullptr; + unsigned DiagID = 0; + if (SetStorageClassSpec( + S, SCS_auto, TSTLoc, PrevSpec, DiagID, + S.getPrintingPolicy())) { // OpenCL rejected it, emit the error + // with version string + if (S.getLangOpts().OpenCL && S.getLangOpts().CPlusPlus) { + S.Diag(TSTLoc, DiagID) + << S.getLangOpts().getOpenCLVersionString() << PrevSpec << 1; + } else { + S.Diag(TSTLoc, DiagID) << PrevSpec; + } + TypeSpecType = TST_error; + } else { + StorageClassSpec = SCS_auto; + StorageClassSpecLoc = TSTLoc; + TypeSpecType = ConflictingTypeSpecifier; + TSTLoc = ConflictingTypeSpecifierLoc; + } + // Clear the conflict tracking + ConflictingTypeSpecifier = TST_unspecified; + ConflictingTypeSpecifierLoc = SourceLocation(); + } else if (ConflictingTypeSpecifier == TST_auto) { + // "int auto" case: Convert 'auto' to storage class specifier + // Use SetStorageClassSpec to trigger OpenCL-specific error checking + const char *PrevSpec = nullptr; + unsigned DiagID = 0; + if (SetStorageClassSpec(S, SCS_auto, ConflictingTypeSpecifierLoc, + PrevSpec, DiagID, S.getPrintingPolicy())) { + // OpenCL rejected it, emit the error with version string + if (S.getLangOpts().OpenCL && S.getLangOpts().CPlusPlus) { + S.Diag(ConflictingTypeSpecifierLoc, DiagID) + << S.getLangOpts().getOpenCLVersionString() << PrevSpec << 1; + } else { + S.Diag(ConflictingTypeSpecifierLoc, DiagID) << PrevSpec; + } + TypeSpecType = TST_error; + } else { + StorageClassSpec = SCS_auto; + StorageClassSpecLoc = ConflictingTypeSpecifierLoc; + } + // Clear the conflict tracking + ConflictingTypeSpecifier = TST_unspecified; + ConflictingTypeSpecifierLoc = SourceLocation(); + } + } else if (S.getLangOpts().CPlusPlus && S.getLangOpts().CPlusPlus11 && + !S.getLangOpts().C23) { + // In C++11+ (but not C23 or OpenCL), emit error (cannot combine type + // specifiers) + if (TypeSpecType == TST_auto) { + // "auto int" case + S.Diag(ConflictingTypeSpecifierLoc, diag::err_auto_type_specifier) + << FixItHint::CreateRemoval(ConflictingTypeSpecifierLoc); + } else if (ConflictingTypeSpecifier == TST_auto) { + // "int auto" case + S.Diag(ConflictingTypeSpecifierLoc, diag::err_auto_type_specifier) + << FixItHint::CreateRemoval(ConflictingTypeSpecifierLoc); + } + // Mark as error to prevent further processing + TypeSpecType = TST_error; + } else if (!S.getLangOpts().CPlusPlus) { + // For C, C23, etc., convert 'auto' to storage class specifier + // (This is already handled above for C23, but keep for other C dialects) + // In C, C23, OpenCL, etc., convert 'auto' to storage class specifier + if (TypeSpecType == TST_auto) { + // "auto int" case: Convert 'auto' to storage class specifier + StorageClassSpec = SCS_auto; + StorageClassSpecLoc = TSTLoc; + TypeSpecType = ConflictingTypeSpecifier; + TSTLoc = ConflictingTypeSpecifierLoc; + // Clear the conflict tracking + ConflictingTypeSpecifier = TST_unspecified; + ConflictingTypeSpecifierLoc = SourceLocation(); + } else if (ConflictingTypeSpecifier == TST_auto) { + // "int auto" case: Convert 'auto' to storage class specifier + StorageClassSpec = SCS_auto; + StorageClassSpecLoc = ConflictingTypeSpecifierLoc; + // TypeSpecType already has the correct type (e.g., TST_int) + // Clear the conflict tracking + ConflictingTypeSpecifier = TST_unspecified; + ConflictingTypeSpecifierLoc = SourceLocation(); + } + } + } + // Validate and finalize AltiVec vector declspec. if (TypeAltiVecVector) { // No vector long long without VSX (or ZVector). @@ -1396,6 +1598,7 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy &Policy) { S.getLangOpts().getHLSLVersion() < LangOptions::HLSL_202y && TypeSpecType == TST_auto) S.Diag(TSTLoc, diag::ext_hlsl_auto_type_specifier) << /*HLSL*/ 1; + // Emit warning for 'auto' storage class in pre-C++11 dialects if (S.getLangOpts().CPlusPlus && !S.getLangOpts().CPlusPlus11 && StorageClassSpec == SCS_auto) S.Diag(StorageClassSpecLoc, diag::warn_auto_storage_class) diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp index 723a79628116c..3a3f752a9e93b 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp @@ -1,66 +1,75 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s -// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s -// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify=cxx98 -std=c++98 %s +// RUN: %clang_cc1 -fsyntax-only -verify=cxx11 -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify=cxx17 -std=c++17 %s // The auto or register specifiers can be applied only to names of objects // declared in a block (6.3) or to function parameters (8.4). -auto int ao; // expected-error {{illegal storage class on file-scoped variable}} -#if __cplusplus >= 201103L // C++11 or later -// expected-warning@-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 07bc884e7d5ee..aa64331950460 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-generic-lambda-1y.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-generic-lambda-1y.cpp @@ -63,7 +63,7 @@ int main() auto l = [](auto (*)(auto)) { }; //expected-error{{'auto' not allowed}} //FIXME: These diagnostics might need some work. - auto l2 = [](char auto::*pm) { }; //expected-error{{cannot combine with previous}}\ + auto l2 = [](char auto::*pm) { }; //expected-error{{'auto' cannot be combined with a type specifier}}\ expected-error{{'pm' does not point into a class}} auto l3 = [](char (auto::*pmf)()) { }; //expected-error{{'auto' not allowed}}\ expected-error{{'pmf' does not point into a class}}\ diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp index 440c78201293b..8d7765523461c 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp @@ -42,7 +42,12 @@ void p3example() { static auto y = 0.0; // In C++98: 'auto' storage class specifier is redundant and incompatible with C++0x // In C++0x: 'auto' storage class specifier is not permitted in C++0x, and will not be supported in future releases - auto int r; // expected-warning {{'auto' storage class specifier}} + auto int r; +#if __cplusplus >= 201103L + // expected-error@-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 bbd87c060801a..52bc92820176e 100644 --- a/clang/test/CXX/drs/cwg3xx.cpp +++ b/clang/test/CXX/drs/cwg3xx.cpp @@ -1732,13 +1732,23 @@ namespace cwg395 { // cwg395: 3.0 namespace cwg396 { // cwg396: 3.0 void f() { auto int a(); - // since-cxx11-error@-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/Parser/c2x-auto.c b/clang/test/Parser/c2x-auto.c index 7f80b0717ab25..941a99344fc83 100644 --- a/clang/test/Parser/c2x-auto.c +++ b/clang/test/Parser/c2x-auto.c @@ -144,7 +144,7 @@ constexpr int auto a2 = 0; // c23-error {{cannot combine with previous 'int' dec auto int b1 = 0; // c23-error {{illegal storage class on file-scoped variable}} \ c17-error {{illegal storage class on file-scoped variable}} -int auto b2 = 0; // c23-error {{cannot combine with previous 'int' declaration specifier}} \ +int auto b2 = 0; // c23-error {{illegal storage class on file-scoped variable}} \ c17-error {{illegal storage class on file-scoped variable}} void f() { @@ -155,5 +155,5 @@ void f() { c17-error {{use of undeclared identifier 'constexpr'}} auto int d1 = 0; - int auto d2 = 0; // c23-error {{cannot combine with previous 'int' declaration specifier}} + int auto d2 = 0; } diff --git a/clang/test/SemaCXX/auto-cxx0x.cpp b/clang/test/SemaCXX/auto-cxx0x.cpp index 07687b6066790..594cbf40f7179 100644 --- a/clang/test/SemaCXX/auto-cxx0x.cpp +++ b/clang/test/SemaCXX/auto-cxx0x.cpp @@ -1,8 +1,8 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11 // RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++1y void f() { - auto int a; // expected-warning {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} - int auto b; // expected-error{{cannot combine with previous 'int' declaration specifier}} + auto int a; // expected-error {{'auto' cannot be combined with a type specifier}} + int auto b; // expected-error {{'auto' cannot be combined with a type specifier}} } typedef auto PR25449(); // expected-error {{'auto' not allowed in typedef}} diff --git a/clang/test/SemaCXX/class.cpp b/clang/test/SemaCXX/class.cpp index f1e02d5158aac..71c71a1acc807 100644 --- a/clang/test/SemaCXX/class.cpp +++ b/clang/test/SemaCXX/class.cpp @@ -2,11 +2,12 @@ // RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx98 -Wc++11-compat %s -std=c++98 class C { public: - auto int errx; // expected-error {{storage class specified for a member declaration}} -#if __cplusplus <= 199711L - // expected-warning@-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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
