Skip to content

[Clang] Revert "Reject auto combined with type specifiers in C++ (#208552)" and follow-ups commits - #215320

Merged
to268 merged 11 commits into
llvm:mainfrom
to268:revert-auto-ts-cxx-commits
Aug 11, 2026
Merged

[Clang] Revert "Reject auto combined with type specifiers in C++ (#208552)" and follow-ups commits#215320
to268 merged 11 commits into
llvm:mainfrom
to268:revert-auto-ts-cxx-commits

Conversation

@to268

@to268 to268 commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

The initial fix for #164273 had several follow up commits to fix the initial implementation. This fix induces a lot of regressions and a refactoring of the area is required in order to fix the issue in a later release.

This reverts the following commits (from oldest to latest):
72af746
226acaf
d592aa5
34436db
de4b1f5

@to268
to268 requested a review from AaronBallman August 10, 2026 16:09
@to268
to268 requested a review from Endilll as a code owner August 10, 2026 16:10
@llvmorg-github-actions llvmorg-github-actions Bot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Aug 10, 2026
@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-clang

Author: Tony Guillot (to268)

Changes

The initial fix for #164273 had several follow up commits to fix the initial implementation. This fix induces a lot of regressions and a refactoring of the area is required in order to fix the issue in a later release.

This reverts the following commits (from oldest to latest):
72af746
226acaf
d592aa5
de4b1f5


Patch is 38.95 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/215320.diff

15 Files Affected:

  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (-2)
  • (modified) clang/include/clang/Sema/DeclSpec.h (+2-51)
  • (modified) clang/lib/Parse/ParseDecl.cpp (+7-39)
  • (modified) clang/lib/Sema/DeclSpec.cpp (+1-181)
  • (modified) clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp (+48-57)
  • (modified) clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp (+1-7)
  • (modified) clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-generic-lambda-1y.cpp (+6-6)
  • (modified) clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp (+1-6)
  • (modified) clang/test/CXX/dcl/dcl.fct/p17.cpp (+1-1)
  • (removed) clang/test/CXX/dcl/dcl.spec/dcl.type/dcl.type.general/p2.cpp (-11)
  • (modified) clang/test/CXX/drs/cwg3xx.cpp (+7-9)
  • (modified) clang/test/Parser/c2x-auto.c (-7)
  • (modified) clang/test/SemaCXX/auto-cxx0x.cpp (+2-36)
  • (modified) clang/test/SemaCXX/class.cpp (+4-5)
  • (modified) clang/test/SemaCXX/static-data-member.cpp (+1-6)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 51fe39f3733b1..72d1f0ecb92f1 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2775,8 +2775,6 @@ 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 659bc06a257b5..e6dc6831d893f 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -363,10 +363,6 @@ 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)
@@ -431,50 +427,10 @@ class DeclSpec {
   SourceLocation FS_explicitCloseParenLoc;
   SourceLocation FS_forceinlineLoc;
   SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc;
-  SourceLocation TQ_pipeLoc, ConflictingTypeSpecifierLoc;
+  SourceLocation TQ_pipeLoc;
 
   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;
 
@@ -518,15 +474,13 @@ 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), ConflictingTypeSpecifierLoc(), writtenBS(),
-        ObjCQualifiers(nullptr) {}
+        Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {}
 
   // storage-class-specifier
   SCS getStorageClassSpec() const { return (SCS)StorageClassSpec; }
@@ -566,9 +520,6 @@ 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 2aaeba9225c38..2bbc76fc7c4df 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -3778,9 +3778,7 @@ 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'.
-      // 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)
+      if (DS.hasTypeSpecifier())
         goto DoneWithDeclSpec;
 
       // If the token is an identifier named "__declspec" and Microsoft
@@ -3865,27 +3863,6 @@ 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;
-      }
-
-      // If 'auto' is set and the next token indicates this identifier is the
-      // declarator-id, stop parsing declaration specifiers before doing type
-      // lookup. Looking up the declarator-id can produce bogus ambiguity errors
-      // when a variable name matches a type brought in by a using-directive.
-      if (DS.getTypeSpecType() == DeclSpec::TST_auto) {
-        Token Next = NextToken();
-        if (Next.isOneOf(tok::equal, tok::l_paren, tok::l_square, tok::l_brace,
-                         tok::amp, tok::ampamp, tok::star, tok::coloncolon,
-                         tok::comma, tok::semi, tok::colon, tok::greater,
-                         tok::r_paren, tok::arrow))
-          goto DoneWithDeclSpec;
-      }
-
       ParsedType TypeRep = Actions.getTypeName(
           *Tok.getIdentifierInfo(), Tok.getLocation(), getCurScope(), nullptr,
           false, false, nullptr, false, false,
@@ -3893,16 +3870,11 @@ 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()) {
@@ -4168,18 +4140,15 @@ void Parser::ParseDeclarationSpecifiers(
           }
         };
 
-        if (!getLangOpts().CPlusPlus && MayBeTypeSpecifier()) {
+        if (MayBeTypeSpecifier()) {
           isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc,
                                              PrevSpec, DiagID, Policy);
-        } else {
-          if (getLangOpts().CPlusPlus11 &&
-              NextToken().isOneOf(tok::kw_class, tok::kw_struct,
-                                  tok::kw___interface, tok::kw_union,
-                                  tok::kw_enum))
-            Diag(Loc, diag::ext_auto_storage_class);
+          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);
-        }
       } else
         isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc,
                                            PrevSpec, DiagID, Policy);
@@ -4734,8 +4703,7 @@ void Parser::ParseDeclarationSpecifiers(
     DS.SetRangeEnd(ConsumedEnd.isValid() ? ConsumedEnd : Tok.getLocation());
 
     // If the specifier wasn't legal, issue a diagnostic.
-    // Skip diagnostic if 'auto' conflict will be handled in Finish()
-    if (isInvalid && !DS.hasConflictingTypeSpecifier()) {
+    if (isInvalid) {
       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 1492bbf60a373..c13d5c8345604 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -431,7 +431,7 @@ void DeclSpec::forEachQualifier(
 }
 
 bool DeclSpec::hasTagDefinition() const {
-  if (!TypeSpecOwned || !isDeclRep((TST)TypeSpecType))
+  if (!TypeSpecOwned)
     return false;
   return cast<TagDecl>(getRepAsDecl())->isCompleteDefinition();
 }
@@ -759,7 +759,6 @@ bool DeclSpec::SetTypeSpecType(TST T, SourceLocation TagKwLoc,
   if (TypeSpecType == TST_error)
     return false;
   if (TypeSpecType != TST_unspecified) {
-    setConflictingTypeSpecifier(T, TagKwLoc, TagNameLoc, Rep);
     PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType, Policy);
     DiagID = diag::err_invalid_decl_spec_combination;
     return true;
@@ -791,7 +790,6 @@ bool DeclSpec::SetTypeSpecType(TST T, SourceLocation Loc,
   if (TypeSpecType == TST_error)
     return false;
   if (TypeSpecType != TST_unspecified) {
-    setConflictingTypeSpecifier(T, Loc, Rep);
     PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType, Policy);
     DiagID = diag::err_invalid_decl_spec_combination;
     return true;
@@ -824,7 +822,6 @@ bool DeclSpec::SetTypeSpecType(TST T, SourceLocation TagKwLoc,
   if (TypeSpecType == TST_error)
     return false;
   if (TypeSpecType != TST_unspecified) {
-    setConflictingTypeSpecifier(T, TagKwLoc, TagNameLoc, Rep, Owned);
     PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType, Policy);
     DiagID = diag::err_invalid_decl_spec_combination;
     return true;
@@ -855,7 +852,6 @@ bool DeclSpec::SetTypeSpecType(TST T, SourceLocation Loc,
   if (TypeSpecType == TST_error)
     return false;
   if (TypeSpecType != TST_unspecified) {
-    setConflictingTypeSpecifier(T, Loc);
     PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType, Policy);
     DiagID = diag::err_invalid_decl_spec_combination;
     return true;
@@ -1216,181 +1212,6 @@ void DeclSpec::CheckTypeSpec(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.
-        // But typedef + any storage-class-specifier is unconditionally invalid
-        // per [dcl.stc]p1, regardless of C++ version.
-        if (StorageClassSpec == SCS_typedef) {
-          S.Diag(TSTLoc, diag::err_invalid_decl_spec_combination)
-              << "typedef" << FixItHint::CreateRemoval(TSTLoc);
-          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
-        // 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. But typedef + any storage-class-specifier is
-        // unconditionally invalid per [dcl.stc]p1.
-        if (StorageClassSpec == SCS_typedef) {
-          S.Diag(ConflictingTypeSpecifierLoc,
-                 diag::err_invalid_decl_spec_combination)
-              << "typedef"
-              << FixItHint::CreateRemoval(ConflictingTypeSpecifierLoc);
-          TypeSpecType = TST_error;
-        } else {
-          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;
-      TypeSpecOwned = false;
-    } 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 = SourceLocat...
[truncated]

@to268

to268 commented Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

It should be noted that the ReleaseNotes.md entry is not covered by this PR and should be removed separately on the release branch.

- Clang now rejects C++ declarations that combine the `auto` type specifier
with another type specifier, such as `auto int`.

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff origin/main HEAD --extensions c,h,cpp -- clang/include/clang/Sema/DeclSpec.h clang/lib/Parse/ParseDecl.cpp clang/lib/Sema/DeclSpec.cpp clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-generic-lambda-1y.cpp clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp clang/test/CXX/dcl/dcl.fct/p17.cpp clang/test/CXX/drs/cwg3xx.cpp clang/test/Parser/c2x-auto.c clang/test/SemaCXX/auto-cxx0x.cpp clang/test/SemaCXX/auto-cxx98.cpp clang/test/SemaCXX/class.cpp clang/test/SemaCXX/static-data-member.cpp --diff_from_common_commit

⚠️
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing origin/main to the base branch/commit you want to compare against.
⚠️

View the diff from clang-format here.
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 2bbc76fc7..e5b46da65 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4145,7 +4145,7 @@ void Parser::ParseDeclarationSpecifiers(
                                              PrevSpec, DiagID, Policy);
           if (!isInvalid && !getLangOpts().C23)
             Diag(Tok, diag::ext_auto_storage_class)
-              << FixItHint::CreateRemoval(DS.getStorageClassSpecLoc());
+                << FixItHint::CreateRemoval(DS.getStorageClassSpecLoc());
         } else
           isInvalid = DS.SetTypeSpecType(DeclSpec::TST_auto, Loc, PrevSpec,
                                          DiagID, Policy);

@AaronBallman AaronBallman left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I manually verified that all four commits are properly reverted by this single PR, so this LGTM (and needs to be applied to the 23.x release). Thank you for the quick turnaround on this revert!

@AaronBallman AaronBallman left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, but four reverts might not be enough. Pre-commit CI found this failure:

   ******************** TEST 'Clang :: SemaCXX/auto-cxx98.cpp' FAILED ********************
  Exit Code: 1
  
  Command Output (stdout):
  --
  # RUN: at line 1
  /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/clang -cc1 -internal-isystem /home/gha/actions-runner/_work/llvm-project/llvm-project/build/lib/clang/24/include -nostdsysteminc -fsyntax-only -verify /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/SemaCXX/auto-cxx98.cpp -std=c++98 -Wc++11-compat
  # executed command: /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/clang -cc1 -internal-isystem /home/gha/actions-runner/_work/llvm-project/llvm-project/build/lib/clang/24/include -nostdsysteminc -fsyntax-only -verify /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/SemaCXX/auto-cxx98.cpp -std=c++98 -Wc++11-compat
  # .---command stderr------------
  # | error: 'expected-error' diagnostics expected but not seen: 
  # |   File /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/SemaCXX/auto-cxx98.cpp Line 14: cannot combine with previous 'typedef' declaration specifier
  # |   File /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/SemaCXX/auto-cxx98.cpp Line 15: cannot combine with previous 'typedef' declaration specifier
  # | error: 'expected-error' diagnostics seen but not expected: 
  # |   File /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/SemaCXX/auto-cxx98.cpp Line 14: cannot combine with previous 'auto' declaration specifier
  # |   File /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/SemaCXX/auto-cxx98.cpp Line 14: 'auto' not allowed in typedef
  # |   File /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/SemaCXX/auto-cxx98.cpp Line 15: cannot combine with previous 'auto' declaration specifier
  # |   File /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/SemaCXX/auto-cxx98.cpp Line 15: 'auto' not allowed in typedef
  # | error: 'expected-warning' diagnostics seen but not expected: 
  # |   File /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/SemaCXX/auto-cxx98.cpp Line 14: 'auto' type specifier is a C++11 extension
  # |   File /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/SemaCXX/auto-cxx98.cpp Line 15: 'auto' type specifier is a C++11 extension
  # | 8 errors generated.
  # `-----------------------------
  # error: command failed with exit status: 1
  
  --

that file was not updated by any of the links commits.

@AaronBallman

Copy link
Copy Markdown
Contributor

Ah it looks like we need to revert 34436db as well, the functional changes are already covered by the removed code in DeclSpec.cpp, but the test case needs to have its changes backed out.

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

🐧 Linux x64 Test Results

  • 121308 tests passed
  • 4997 tests skipped

✅ The build succeeded and all tests passed.

@Endilll Endilll left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes to C++ DR tests LGTM

@AaronBallman AaronBallman left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thank you! We still need to cherry-pick to 23.x

@tru tru moved this from Needs Triage to Needs Backport PR in LLVM Release Status Aug 11, 2026
@to268
to268 merged commit 6d1b926 into llvm:main Aug 11, 2026
11 of 12 checks passed
@github-project-automation github-project-automation Bot moved this from Needs Backport PR to Done in LLVM Release Status Aug 11, 2026
@to268
to268 deleted the revert-auto-ts-cxx-commits branch August 11, 2026 13:48
hulxv pushed a commit that referenced this pull request Aug 12, 2026
…8552)" and follow-ups commits (#215320)

The initial fix for #164273 had several follow up commits to fix the
initial implementation. This fix induces a lot of regressions and a
refactoring of the area is required in order to fix the issue in a later
release.

This reverts the following commits (from oldest to latest):
72af746
226acaf
d592aa5
34436db
de4b1f5
dyung pushed a commit to to268/llvm-project that referenced this pull request Aug 15, 2026
…m#208552)" and follow-ups commits (llvm#215320)

The initial fix for llvm#164273 had several follow up commits to fix the
initial implementation. This fix induces a lot of regressions and a
refactoring of the area is required in order to fix the issue in a later
release.

This reverts the following commits (from oldest to latest):
72af746
226acaf
d592aa5
34436db
de4b1f5
zhangweize9-cyber pushed a commit to zhangweize9-cyber/llvm-project that referenced this pull request Aug 16, 2026
…m#208552)" and follow-ups commits (llvm#215320)

The initial fix for llvm#164273 had several follow up commits to fix the
initial implementation. This fix induces a lot of regressions and a
refactoring of the area is required in order to fix the issue in a later
release.

This reverts the following commits (from oldest to latest):
72af746
226acaf
d592aa5
34436db
de4b1f5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category

Projects

Development

Successfully merging this pull request may close these issues.

4 participants