Skip to content

[clang] improved preservation of template keyword #133610

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 1, 2025
Merged

Conversation

mizvekov
Copy link
Contributor

This makes it so clang can better represent the abscence of the template keyword for a template which cannot be resolved due to a dependent prefix.

The tracking of the template keyword is removed from the NestedNameSpecifier, and added to the last type node which had it missing, the DependentTemplateSpecializationType (DTST).

The DTST and the DependentTemplateName are refactored to share most of their implementation.

That refactoring along with the removal of the TypeSpecWithTemplate kind from the nested name specifiers amounts to a large amount of code removed, making this patch a small performance improvement overall.

image

This will also enable future further simplifications.

As a drive-by, this removes some special cases from the type printer, allowing template arguments within NestedNameSpecifiers to not have their nested names suppressed when printing.

@mizvekov mizvekov self-assigned this Mar 30, 2025
@llvmbot llvmbot added clang Clang issues not falling into any other category clang-tools-extra libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. clangd clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:modules C++20 modules and Clang Header Modules clang:as-a-library libclang and C++ API coroutines C++20 coroutines labels Mar 30, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 30, 2025

@llvm/pr-subscribers-clang-tidy
@llvm/pr-subscribers-clangd
@llvm/pr-subscribers-clang-tools-extra

@llvm/pr-subscribers-libcxx

Author: Matheus Izvekov (mizvekov)

Changes

This makes it so clang can better represent the abscence of the template keyword for a template which cannot be resolved due to a dependent prefix.

The tracking of the template keyword is removed from the NestedNameSpecifier, and added to the last type node which had it missing, the DependentTemplateSpecializationType (DTST).

The DTST and the DependentTemplateName are refactored to share most of their implementation.

That refactoring along with the removal of the TypeSpecWithTemplate kind from the nested name specifiers amounts to a large amount of code removed, making this patch a small performance improvement overall.

image

This will also enable future further simplifications.

As a drive-by, this removes some special cases from the type printer, allowing template arguments within NestedNameSpecifiers to not have their nested names suppressed when printing.


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

66 Files Affected:

  • (modified) clang-tools-extra/clangd/AST.cpp (+1-2)
  • (modified) clang-tools-extra/clangd/CodeComplete.cpp (-1)
  • (modified) clang-tools-extra/clangd/DumpAST.cpp (-1)
  • (modified) clang-tools-extra/clangd/FindTarget.cpp (-1)
  • (modified) clang-tools-extra/include-cleaner/lib/WalkAST.cpp (-1)
  • (modified) clang/docs/ReleaseNotes.rst (+4)
  • (modified) clang/include/clang/AST/ASTContext.h (+9-11)
  • (modified) clang/include/clang/AST/ASTImporter.h (+8)
  • (modified) clang/include/clang/AST/ASTNodeTraverser.h (+1-2)
  • (modified) clang/include/clang/AST/AbstractBasicReader.h (+1-3)
  • (modified) clang/include/clang/AST/AbstractBasicWriter.h (-1)
  • (modified) clang/include/clang/AST/NestedNameSpecifier.h (+5-15)
  • (modified) clang/include/clang/AST/ODRHash.h (+1)
  • (modified) clang/include/clang/AST/PropertiesBase.td (+8-7)
  • (modified) clang/include/clang/AST/RecursiveASTVisitor.h (+2-3)
  • (modified) clang/include/clang/AST/TemplateName.h (+56-77)
  • (modified) clang/include/clang/AST/Type.h (+8-14)
  • (modified) clang/include/clang/AST/TypeLoc.h (+5-4)
  • (modified) clang/include/clang/AST/TypeProperties.td (+25-22)
  • (modified) clang/include/clang/Sema/DeclSpec.h (+1-2)
  • (modified) clang/lib/AST/ASTContext.cpp (+81-133)
  • (modified) clang/lib/AST/ASTImporter.cpp (+22-41)
  • (modified) clang/lib/AST/ASTStructuralEquivalence.cpp (+19-18)
  • (modified) clang/lib/AST/ItaniumMangle.cpp (+22-13)
  • (modified) clang/lib/AST/NestedNameSpecifier.cpp (+23-79)
  • (modified) clang/lib/AST/ODRHash.cpp (+15-4)
  • (modified) clang/lib/AST/QualTypeNames.cpp (+3-5)
  • (modified) clang/lib/AST/TemplateName.cpp (+58-9)
  • (modified) clang/lib/AST/TextNodeDumper.cpp (-4)
  • (modified) clang/lib/AST/Type.cpp (+10-17)
  • (modified) clang/lib/AST/TypeLoc.cpp (+3-2)
  • (modified) clang/lib/AST/TypePrinter.cpp (+7-5)
  • (modified) clang/lib/ExtractAPI/DeclarationFragments.cpp (-7)
  • (modified) clang/lib/Index/IndexTypeSourceInfo.cpp (-1)
  • (modified) clang/lib/Parse/ParseExprCXX.cpp (+4-4)
  • (modified) clang/lib/Sema/DeclSpec.cpp (+3-3)
  • (modified) clang/lib/Sema/HeuristicResolver.cpp (+3-3)
  • (modified) clang/lib/Sema/SemaCXXScopeSpec.cpp (+12-19)
  • (modified) clang/lib/Sema/SemaCodeComplete.cpp (+3-3)
  • (modified) clang/lib/Sema/SemaCoroutine.cpp (+1-2)
  • (modified) clang/lib/Sema/SemaDecl.cpp (+12-12)
  • (modified) clang/lib/Sema/SemaDeclCXX.cpp (+1-2)
  • (modified) clang/lib/Sema/SemaExpr.cpp (+1-1)
  • (modified) clang/lib/Sema/SemaExprCXX.cpp (-1)
  • (modified) clang/lib/Sema/SemaLookup.cpp (+1-3)
  • (modified) clang/lib/Sema/SemaTemplate.cpp (+19-22)
  • (modified) clang/lib/Sema/SemaTemplateDeduction.cpp (+2-1)
  • (modified) clang/lib/Sema/TreeTransform.h (+73-76)
  • (modified) clang/lib/Serialization/ASTReader.cpp (+2-8)
  • (modified) clang/lib/Serialization/ASTWriter.cpp (-2)
  • (modified) clang/lib/Tooling/Syntax/BuildTree.cpp (-2)
  • (modified) clang/test/AST/ast-dump-decl.cpp (+10-10)
  • (modified) clang/test/AST/ast-dump-expr.cpp (+1-2)
  • (modified) clang/test/AST/ast-dump-templates.cpp (+3-3)
  • (modified) clang/test/CXX/class.access/p6.cpp (+2-2)
  • (modified) clang/test/CXX/drs/cwg2xx.cpp (+2-2)
  • (modified) clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp (+1-1)
  • (modified) clang/test/SemaCXX/static-assert.cpp (+3-1)
  • (modified) clang/test/SemaTemplate/aggregate-deduction-candidate.cpp (+4-4)
  • (modified) clang/test/SemaTemplate/dependent-template-recover.cpp (+18)
  • (modified) clang/test/SemaTemplate/instantiate-requires-expr.cpp (+2-2)
  • (modified) clang/tools/libclang/CIndex.cpp (-2)
  • (modified) libcxx/test/std/containers/sequences/array/array.overview/nttp.verify.cpp (+1-1)
  • (modified) libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr.verify.cpp (+1-1)
  • (modified) libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr.verify.cpp (+1-1)
  • (modified) libcxx/test/std/utilities/utility/pairs/pairs.pair/nttp.verify.cpp (+2-1)
diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index f3eee1c6335f9..66b587f00ff4a 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -119,8 +119,7 @@ getQualification(ASTContext &Context, const DeclContext *DestContext,
       // There can't be any more tag parents after hitting a namespace.
       assert(!ReachedNS);
       (void)ReachedNS;
-      NNS = NestedNameSpecifier::Create(Context, nullptr, false,
-                                        TD->getTypeForDecl());
+      NNS = NestedNameSpecifier::Create(Context, nullptr, TD->getTypeForDecl());
     } else if (auto *NSD = llvm::dyn_cast<NamespaceDecl>(CurContext)) {
       ReachedNS = true;
       NNS = NestedNameSpecifier::Create(Context, nullptr, NSD);
diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp
index 008cc96c91996..0eb196fbad46a 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -1467,7 +1467,6 @@ bool allowIndex(CodeCompletionContext &CC) {
     return true;
   case NestedNameSpecifier::Super:
   case NestedNameSpecifier::TypeSpec:
-  case NestedNameSpecifier::TypeSpecWithTemplate:
   // Unresolved inside a template.
   case NestedNameSpecifier::Identifier:
     return false;
diff --git a/clang-tools-extra/clangd/DumpAST.cpp b/clang-tools-extra/clangd/DumpAST.cpp
index e605f82e91fe4..584bb1f088380 100644
--- a/clang-tools-extra/clangd/DumpAST.cpp
+++ b/clang-tools-extra/clangd/DumpAST.cpp
@@ -157,7 +157,6 @@ class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> {
       NNS_KIND(Identifier);
       NNS_KIND(Namespace);
       NNS_KIND(TypeSpec);
-      NNS_KIND(TypeSpecWithTemplate);
       NNS_KIND(Global);
       NNS_KIND(Super);
       NNS_KIND(NamespaceAlias);
diff --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp
index bb4c91b831354..62f220b32bd10 100644
--- a/clang-tools-extra/clangd/FindTarget.cpp
+++ b/clang-tools-extra/clangd/FindTarget.cpp
@@ -500,7 +500,6 @@ struct TargetFinder {
       }
       return;
     case NestedNameSpecifier::TypeSpec:
-    case NestedNameSpecifier::TypeSpecWithTemplate:
       add(QualType(NNS->getAsType(), 0), Flags);
       return;
     case NestedNameSpecifier::Global:
diff --git a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
index 7a140c991925c..dff0c711f04c5 100644
--- a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
+++ b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
@@ -144,7 +144,6 @@ class ASTWalker : public RecursiveASTVisitor<ASTWalker> {
     case NestedNameSpecifier::Global:
       return true;
     case NestedNameSpecifier::TypeSpec:
-    case NestedNameSpecifier::TypeSpecWithTemplate:
     case NestedNameSpecifier::Super:
     case NestedNameSpecifier::Identifier:
       return false;
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index eaecead0e6b9d..3ae852d59bbf8 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -275,6 +275,10 @@ Improvements to Clang's diagnostics
 - Diagnostics on chained comparisons (``a < b < c``) are now an error by default. This can be disabled with
   ``-Wno-error=parentheses``.
 - Clang now better preserves the sugared types of pointers to member.
+- Clang now better preserves the presence of the template keyword with dependent
+  prefixes.
+- When printing types for diagnostics, clang now doesn't suppress the scopes of
+  template arguments contained within nested names.
 - The ``-Wshift-bool`` warning has been added to warn about shifting a boolean. (#GH28334)
 - Fixed diagnostics adding a trailing ``::`` when printing some source code
   constructs, like base classes.
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index af8c49e99a7ce..6fbe170e17242 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1837,15 +1837,15 @@ class ASTContext : public RefCountedBase<ASTContext> {
                              TagDecl *OwnedTagDecl = nullptr) const;
   QualType getDependentNameType(ElaboratedTypeKeyword Keyword,
                                 NestedNameSpecifier *NNS,
-                                const IdentifierInfo *Name,
-                                QualType Canon = QualType()) const;
+                                const IdentifierInfo *Name) const;
 
   QualType getDependentTemplateSpecializationType(
-      ElaboratedTypeKeyword Keyword, NestedNameSpecifier *NNS,
-      const IdentifierInfo *Name, ArrayRef<TemplateArgumentLoc> Args) const;
-  QualType getDependentTemplateSpecializationType(
-      ElaboratedTypeKeyword Keyword, NestedNameSpecifier *NNS,
-      const IdentifierInfo *Name, ArrayRef<TemplateArgument> Args) const;
+      ElaboratedTypeKeyword Keyword, const DependentTemplateStorage &Name,
+      ArrayRef<TemplateArgumentLoc> Args) const;
+  QualType
+  getDependentTemplateSpecializationType(ElaboratedTypeKeyword Keyword,
+                                         const DependentTemplateStorage &Name,
+                                         ArrayRef<TemplateArgument> Args) const;
 
   TemplateArgument getInjectedTemplateArg(NamedDecl *ParamDecl) const;
 
@@ -2394,10 +2394,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
                                         bool TemplateKeyword,
                                         TemplateName Template) const;
 
-  TemplateName getDependentTemplateName(NestedNameSpecifier *NNS,
-                                        const IdentifierInfo *Name) const;
-  TemplateName getDependentTemplateName(NestedNameSpecifier *NNS,
-                                        OverloadedOperatorKind Operator) const;
+  TemplateName
+  getDependentTemplateName(const DependentTemplateStorage &Name) const;
   TemplateName
   getSubstTemplateTemplateParm(TemplateName replacement, Decl *AssociatedDecl,
                                unsigned Index,
diff --git a/clang/include/clang/AST/ASTImporter.h b/clang/include/clang/AST/ASTImporter.h
index 8c3fa842ab8b9..a2550716e3c7f 100644
--- a/clang/include/clang/AST/ASTImporter.h
+++ b/clang/include/clang/AST/ASTImporter.h
@@ -446,6 +446,14 @@ class TypeSourceInfo;
     /// returns nullptr only if the FromId was nullptr.
     IdentifierInfo *Import(const IdentifierInfo *FromId);
 
+    /// Import the given identifier or overloaded operator from the "from"
+    /// context into the "to" context.
+    ///
+    /// \returns The equivalent identifier or overloaded operator in the "to"
+    /// context.
+    IdentifierOrOverloadedOperator
+    Import(IdentifierOrOverloadedOperator FromIO);
+
     /// Import the given Objective-C selector from the "from"
     /// context into the "to" context.
     ///
diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index 83a6b77704f34..f086d8134a64b 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -396,8 +396,7 @@ class ASTNodeTraverser
     // FIXME: Provide a NestedNameSpecifier visitor.
     NestedNameSpecifier *Qualifier = T->getQualifier();
     if (NestedNameSpecifier::SpecifierKind K = Qualifier->getKind();
-        K == NestedNameSpecifier::TypeSpec ||
-        K == NestedNameSpecifier::TypeSpecWithTemplate)
+        K == NestedNameSpecifier::TypeSpec)
       Visit(Qualifier->getAsType());
     if (T->isSugared())
       Visit(T->getMostRecentCXXRecordDecl()->getTypeForDecl());
diff --git a/clang/include/clang/AST/AbstractBasicReader.h b/clang/include/clang/AST/AbstractBasicReader.h
index 4b627c65e276b..5ab438715ecf7 100644
--- a/clang/include/clang/AST/AbstractBasicReader.h
+++ b/clang/include/clang/AST/AbstractBasicReader.h
@@ -279,10 +279,8 @@ class DataStreamBasicReader : public BasicReaderBase<Impl> {
         continue;
 
       case NestedNameSpecifier::TypeSpec:
-      case NestedNameSpecifier::TypeSpecWithTemplate:
         cur = NestedNameSpecifier::Create(ctx, cur,
-                          kind == NestedNameSpecifier::TypeSpecWithTemplate,
-                          asImpl().readQualType().getTypePtr());
+                                          asImpl().readQualType().getTypePtr());
         continue;
 
       case NestedNameSpecifier::Global:
diff --git a/clang/include/clang/AST/AbstractBasicWriter.h b/clang/include/clang/AST/AbstractBasicWriter.h
index b941add8bde88..f65d94abc2ff1 100644
--- a/clang/include/clang/AST/AbstractBasicWriter.h
+++ b/clang/include/clang/AST/AbstractBasicWriter.h
@@ -260,7 +260,6 @@ class DataStreamBasicWriter : public BasicWriterBase<Impl> {
         continue;
 
       case NestedNameSpecifier::TypeSpec:
-      case NestedNameSpecifier::TypeSpecWithTemplate:
         asImpl().writeQualType(QualType(NNS->getAsType(), 0));
         continue;
 
diff --git a/clang/include/clang/AST/NestedNameSpecifier.h b/clang/include/clang/AST/NestedNameSpecifier.h
index 273e73e7c1e95..d7da3272d0943 100644
--- a/clang/include/clang/AST/NestedNameSpecifier.h
+++ b/clang/include/clang/AST/NestedNameSpecifier.h
@@ -52,8 +52,7 @@ class NestedNameSpecifier : public llvm::FoldingSetNode {
   enum StoredSpecifierKind {
     StoredIdentifier = 0,
     StoredDecl = 1,
-    StoredTypeSpec = 2,
-    StoredTypeSpecWithTemplate = 3
+    StoredTypeSpec = 2
   };
 
   /// The nested name specifier that precedes this nested name
@@ -89,10 +88,6 @@ class NestedNameSpecifier : public llvm::FoldingSetNode {
     /// A type, stored as a Type*.
     TypeSpec,
 
-    /// A type that was preceded by the 'template' keyword,
-    /// stored as a Type*.
-    TypeSpecWithTemplate,
-
     /// The global specifier '::'. There is no stored value.
     Global,
 
@@ -137,9 +132,8 @@ class NestedNameSpecifier : public llvm::FoldingSetNode {
                                      const NamespaceAliasDecl *Alias);
 
   /// Builds a nested name specifier that names a type.
-  static NestedNameSpecifier *Create(const ASTContext &Context,
-                                     NestedNameSpecifier *Prefix,
-                                     bool Template, const Type *T);
+  static NestedNameSpecifier *
+  Create(const ASTContext &Context, NestedNameSpecifier *Prefix, const Type *T);
 
   /// Builds a specifier that consists of just an identifier.
   ///
@@ -194,8 +188,7 @@ class NestedNameSpecifier : public llvm::FoldingSetNode {
 
   /// Retrieve the type stored in this nested name specifier.
   const Type *getAsType() const {
-    if (Prefix.getInt() == StoredTypeSpec ||
-        Prefix.getInt() == StoredTypeSpecWithTemplate)
+    if (Prefix.getInt() == StoredTypeSpec)
       return (const Type *)Specifier;
 
     return nullptr;
@@ -401,13 +394,10 @@ class NestedNameSpecifierLocBuilder {
   /// \param Context The AST context in which this nested-name-specifier
   /// resides.
   ///
-  /// \param TemplateKWLoc The location of the 'template' keyword, if present.
-  ///
   /// \param TL The TypeLoc that describes the type preceding the '::'.
   ///
   /// \param ColonColonLoc The location of the trailing '::'.
-  void Extend(ASTContext &Context, SourceLocation TemplateKWLoc, TypeLoc TL,
-              SourceLocation ColonColonLoc);
+  void Extend(ASTContext &Context, TypeLoc TL, SourceLocation ColonColonLoc);
 
   /// Extend the current nested-name-specifier by another
   /// nested-name-specifier component of the form 'identifier::'.
diff --git a/clang/include/clang/AST/ODRHash.h b/clang/include/clang/AST/ODRHash.h
index a1caa6d39a87c..a923901b32dc0 100644
--- a/clang/include/clang/AST/ODRHash.h
+++ b/clang/include/clang/AST/ODRHash.h
@@ -94,6 +94,7 @@ class ODRHash {
   void AddStmt(const Stmt *S);
   void AddIdentifierInfo(const IdentifierInfo *II);
   void AddNestedNameSpecifier(const NestedNameSpecifier *NNS);
+  void AddDependentTemplateName(const DependentTemplateStorage &Name);
   void AddTemplateName(TemplateName Name);
   void AddDeclarationName(DeclarationName Name, bool TreatAsDecl = false);
   void AddTemplateArgument(TemplateArgument TA);
diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td
index 42883b6419261..178308a24e1a0 100644
--- a/clang/include/clang/AST/PropertiesBase.td
+++ b/clang/include/clang/AST/PropertiesBase.td
@@ -692,25 +692,26 @@ let Class = PropertyTypeCase<TemplateName, "QualifiedTemplate"> in {
 let Class = PropertyTypeCase<TemplateName, "DependentTemplate"> in {
   def : ReadHelper<[{
     auto dtn = node.getAsDependentTemplateName();
+    auto name = dtn->getName();
   }]>;
   def : Property<"qualifier", NestedNameSpecifier> {
     let Read = [{ dtn->getQualifier() }];
   }
   def : Property<"identifier", Optional<Identifier>> {
-    let Read = [{ makeOptionalFromPointer(
-                    dtn->isIdentifier()
-                      ? dtn->getIdentifier()
-                      : nullptr) }];
+    let Read = [{ makeOptionalFromPointer(name.getIdentifier()) }];
   }
   def : Property<"operatorKind", OverloadedOperatorKind> {
     let Conditional = [{ !identifier }];
-    let Read = [{ dtn->getOperator() }];
+    let Read = [{ name.getOperator() }];
+  }
+  def : Property<"HasTemplateKeyword", Bool> {
+    let Read = [{ dtn->hasTemplateKeyword() }];
   }
   def : Creator<[{
     if (identifier) {
-      return ctx.getDependentTemplateName(qualifier, *identifier);
+      return ctx.getDependentTemplateName({qualifier, *identifier, HasTemplateKeyword});
     } else {
-      return ctx.getDependentTemplateName(qualifier, *operatorKind);
+      return ctx.getDependentTemplateName({qualifier, *operatorKind, HasTemplateKeyword});
     }
   }]>;
 }
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 0d5d515c0e6f7..0530996ed20d3 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -795,7 +795,6 @@ bool RecursiveASTVisitor<Derived>::TraverseNestedNameSpecifier(
     return true;
 
   case NestedNameSpecifier::TypeSpec:
-  case NestedNameSpecifier::TypeSpecWithTemplate:
     TRY_TO(TraverseType(QualType(NNS->getAsType(), 0)));
   }
 
@@ -820,7 +819,6 @@ bool RecursiveASTVisitor<Derived>::TraverseNestedNameSpecifierLoc(
     return true;
 
   case NestedNameSpecifier::TypeSpec:
-  case NestedNameSpecifier::TypeSpecWithTemplate:
     TRY_TO(TraverseTypeLoc(NNS.getTypeLoc()));
     break;
   }
@@ -1172,7 +1170,8 @@ DEF_TRAVERSE_TYPE(DependentNameType,
                   { TRY_TO(TraverseNestedNameSpecifier(T->getQualifier())); })
 
 DEF_TRAVERSE_TYPE(DependentTemplateSpecializationType, {
-  TRY_TO(TraverseNestedNameSpecifier(T->getQualifier()));
+  const DependentTemplateStorage &S = T->getDependentTemplateName();
+  TRY_TO(TraverseNestedNameSpecifier(S.getQualifier()));
   TRY_TO(TraverseTemplateArguments(T->template_arguments()));
 })
 
diff --git a/clang/include/clang/AST/TemplateName.h b/clang/include/clang/AST/TemplateName.h
index ce97f834bfc1d..1a56133b72d6e 100644
--- a/clang/include/clang/AST/TemplateName.h
+++ b/clang/include/clang/AST/TemplateName.h
@@ -16,6 +16,7 @@
 #include "clang/AST/DependenceFlags.h"
 #include "clang/AST/NestedNameSpecifier.h"
 #include "clang/Basic/LLVM.h"
+#include "clang/Basic/OperatorKinds.h"
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/PointerIntPair.h"
 #include "llvm/ADT/PointerUnion.h"
@@ -537,6 +538,35 @@ class QualifiedTemplateName : public llvm::FoldingSetNode {
   }
 };
 
+struct IdentifierOrOverloadedOperator {
+  IdentifierOrOverloadedOperator() = default;
+  IdentifierOrOverloadedOperator(const IdentifierInfo *II);
+  IdentifierOrOverloadedOperator(OverloadedOperatorKind OOK);
+
+  /// Returns the identifier to which this template name refers.
+  const IdentifierInfo *getIdentifier() const {
+    if (getOperator() != OO_None)
+      return nullptr;
+    return reinterpret_cast<const IdentifierInfo *>(PtrOrOp);
+  }
+
+  /// Return the overloaded operator to which this template name refers.
+  OverloadedOperatorKind getOperator() const {
+    uintptr_t OOK = -PtrOrOp;
+    return OOK < NUM_OVERLOADED_OPERATORS ? OverloadedOperatorKind(OOK)
+                                          : OO_None;
+  }
+
+  void Profile(llvm::FoldingSetNodeID &ID) const;
+
+  bool operator==(const IdentifierOrOverloadedOperator &Other) const {
+    return PtrOrOp == Other.PtrOrOp;
+  };
+
+private:
+  uintptr_t PtrOrOp = 0;
+};
+
 /// Represents a dependent template name that cannot be
 /// resolved prior to template instantiation.
 ///
@@ -545,104 +575,53 @@ class QualifiedTemplateName : public llvm::FoldingSetNode {
 /// DependentTemplateName can refer to "MetaFun::template apply",
 /// where "MetaFun::" is the nested name specifier and "apply" is the
 /// template name referenced. The "template" keyword is implied.
-class DependentTemplateName : public llvm::FoldingSetNode {
-  friend class ASTContext;
-
+class DependentTemplateStorage {
   /// The nested name specifier that qualifies the template
   /// name.
   ///
   /// The bit stored in this qualifier describes whether the \c Name field
-  /// is interpreted as an IdentifierInfo pointer (when clear) or as an
-  /// overloaded operator kind (when set).
+  /// was preceeded by a template keyword.
   llvm::PointerIntPair<NestedNameSpecifier *, 1, bool> Qualifier;
 
   /// The dependent template name.
-  union {
-    /// The identifier template name.
-    ///
-    /// Only valid when the bit on \c Qualifier is clear.
-    const IdentifierInfo *Identifier;
-
-    /// The overloaded operator name.
-    ///
-    /// Only valid when the bit on \c Qualifier is set.
-    OverloadedOperatorKind Operator;
-  };
-
-  /// The canonical template name to which this dependent
-  /// template name refers.
-  ///
-  /// The canonical template name for a dependent template name is
-  /// another dependent template name whose nested name specifier is
-  /// canonical.
-  TemplateName CanonicalTemplateName;
-
-  DependentTemplateName(NestedNameSpecifier *Qualifier,
-                        const IdentifierInfo *Identifier)
-      : Qualifier(Qualifier, false), Identifier(Identifier),
-        CanonicalTemplateName(this) {}
-
-  DependentTemplateName(NestedNameSpecifier *Qualifier,
-                        const IdentifierInfo *Identifier,
-                        TemplateName Canon)
-      : Qualifier(Qualifier, false), Identifier(Identifier),
-        CanonicalTemplateName(Canon) {}
-
-  DependentTemplateName(NestedNameSpecifier *Qualifier,
-                        OverloadedOperatorKind Operator)
-      : Qualifier(Qualifier, true), Operator(Operator),
-        CanonicalTemplateName(this) {}
-
-  DependentTemplateName(NestedNameSpecifier *Qualifier,
-                        OverloadedOperatorKind Operator,
-                        TemplateName Canon)
-       : Qualifier(Qualifier, true), Operator(Operator),
-         CanonicalTemplateName(Canon) {}
+  IdentifierOrOverloadedOperator Name;
 
 public:
+  DependentTemplateStorage(NestedNameSpecifier *Qualifier,
+                           IdentifierOrOverloadedOperator Name,
+                           bool HasTemplateKeyword);
+
   /// Return the nested name specifier that qualifies this name.
   NestedNameSpecifier *getQualifier() const { return Qualifier.getPointer(); }
 
-  /// Determine whether this template name refers to an identifier.
-  bool isIdentifier() const { return !Qualifier.getInt(); }
+  IdentifierOrOverloadedOperator getName() const { return Name; }
 
-  /// Returns the identifier to which this template name refers.
-  const IdentifierInfo *getIdentifier() const {
-    assert(isIdentifier() && "Template name isn't an identifier?");
-    return Identifier;
-  }
-
-  /// Determine whether this template name refers to an overloaded
-  /// operator.
-  bool isOverloadedOperator() const { return Qualifier.getInt(); }
+  /// Was this template name was preceeded by the template keyword?
+  bool hasTemplateKeyword() const { return Qualifier.getInt(); }
 
-  /// Return the overloaded operator to which this template name refers.
-  OverloadedOperatorKind getOperator() const {
-    assert(isOverloadedOperator() &&
-           "Template name isn't an overloaded operator?");
-    return Operator;
-  }
+  TemplateNameDependence getDependence() const;
 
-  void Profile(llvm::FoldingSe...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Mar 30, 2025

@llvm/pr-subscribers-clang-modules

Author: Matheus Izvekov (mizvekov)

Changes

This makes it so clang can better represent the abscence of the template keyword for a template which cannot be resolved due to a dependent prefix.

The tracking of the template keyword is removed from the NestedNameSpecifier, and added to the last type node which had it missing, the DependentTemplateSpecializationType (DTST).

The DTST and the DependentTemplateName are refactored to share most of their implementation.

That refactoring along with the removal of the TypeSpecWithTemplate kind from the nested name specifiers amounts to a large amount of code removed, making this patch a small performance improvement overall.

image

This will also enable future further simplifications.

As a drive-by, this removes some special cases from the type printer, allowing template arguments within NestedNameSpecifiers to not have their nested names suppressed when printing.


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

66 Files Affected:

  • (modified) clang-tools-extra/clangd/AST.cpp (+1-2)
  • (modified) clang-tools-extra/clangd/CodeComplete.cpp (-1)
  • (modified) clang-tools-extra/clangd/DumpAST.cpp (-1)
  • (modified) clang-tools-extra/clangd/FindTarget.cpp (-1)
  • (modified) clang-tools-extra/include-cleaner/lib/WalkAST.cpp (-1)
  • (modified) clang/docs/ReleaseNotes.rst (+4)
  • (modified) clang/include/clang/AST/ASTContext.h (+9-11)
  • (modified) clang/include/clang/AST/ASTImporter.h (+8)
  • (modified) clang/include/clang/AST/ASTNodeTraverser.h (+1-2)
  • (modified) clang/include/clang/AST/AbstractBasicReader.h (+1-3)
  • (modified) clang/include/clang/AST/AbstractBasicWriter.h (-1)
  • (modified) clang/include/clang/AST/NestedNameSpecifier.h (+5-15)
  • (modified) clang/include/clang/AST/ODRHash.h (+1)
  • (modified) clang/include/clang/AST/PropertiesBase.td (+8-7)
  • (modified) clang/include/clang/AST/RecursiveASTVisitor.h (+2-3)
  • (modified) clang/include/clang/AST/TemplateName.h (+56-77)
  • (modified) clang/include/clang/AST/Type.h (+8-14)
  • (modified) clang/include/clang/AST/TypeLoc.h (+5-4)
  • (modified) clang/include/clang/AST/TypeProperties.td (+25-22)
  • (modified) clang/include/clang/Sema/DeclSpec.h (+1-2)
  • (modified) clang/lib/AST/ASTContext.cpp (+81-133)
  • (modified) clang/lib/AST/ASTImporter.cpp (+22-41)
  • (modified) clang/lib/AST/ASTStructuralEquivalence.cpp (+19-18)
  • (modified) clang/lib/AST/ItaniumMangle.cpp (+22-13)
  • (modified) clang/lib/AST/NestedNameSpecifier.cpp (+23-79)
  • (modified) clang/lib/AST/ODRHash.cpp (+15-4)
  • (modified) clang/lib/AST/QualTypeNames.cpp (+3-5)
  • (modified) clang/lib/AST/TemplateName.cpp (+58-9)
  • (modified) clang/lib/AST/TextNodeDumper.cpp (-4)
  • (modified) clang/lib/AST/Type.cpp (+10-17)
  • (modified) clang/lib/AST/TypeLoc.cpp (+3-2)
  • (modified) clang/lib/AST/TypePrinter.cpp (+7-5)
  • (modified) clang/lib/ExtractAPI/DeclarationFragments.cpp (-7)
  • (modified) clang/lib/Index/IndexTypeSourceInfo.cpp (-1)
  • (modified) clang/lib/Parse/ParseExprCXX.cpp (+4-4)
  • (modified) clang/lib/Sema/DeclSpec.cpp (+3-3)
  • (modified) clang/lib/Sema/HeuristicResolver.cpp (+3-3)
  • (modified) clang/lib/Sema/SemaCXXScopeSpec.cpp (+12-19)
  • (modified) clang/lib/Sema/SemaCodeComplete.cpp (+3-3)
  • (modified) clang/lib/Sema/SemaCoroutine.cpp (+1-2)
  • (modified) clang/lib/Sema/SemaDecl.cpp (+12-12)
  • (modified) clang/lib/Sema/SemaDeclCXX.cpp (+1-2)
  • (modified) clang/lib/Sema/SemaExpr.cpp (+1-1)
  • (modified) clang/lib/Sema/SemaExprCXX.cpp (-1)
  • (modified) clang/lib/Sema/SemaLookup.cpp (+1-3)
  • (modified) clang/lib/Sema/SemaTemplate.cpp (+19-22)
  • (modified) clang/lib/Sema/SemaTemplateDeduction.cpp (+2-1)
  • (modified) clang/lib/Sema/TreeTransform.h (+73-76)
  • (modified) clang/lib/Serialization/ASTReader.cpp (+2-8)
  • (modified) clang/lib/Serialization/ASTWriter.cpp (-2)
  • (modified) clang/lib/Tooling/Syntax/BuildTree.cpp (-2)
  • (modified) clang/test/AST/ast-dump-decl.cpp (+10-10)
  • (modified) clang/test/AST/ast-dump-expr.cpp (+1-2)
  • (modified) clang/test/AST/ast-dump-templates.cpp (+3-3)
  • (modified) clang/test/CXX/class.access/p6.cpp (+2-2)
  • (modified) clang/test/CXX/drs/cwg2xx.cpp (+2-2)
  • (modified) clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp (+1-1)
  • (modified) clang/test/SemaCXX/static-assert.cpp (+3-1)
  • (modified) clang/test/SemaTemplate/aggregate-deduction-candidate.cpp (+4-4)
  • (modified) clang/test/SemaTemplate/dependent-template-recover.cpp (+18)
  • (modified) clang/test/SemaTemplate/instantiate-requires-expr.cpp (+2-2)
  • (modified) clang/tools/libclang/CIndex.cpp (-2)
  • (modified) libcxx/test/std/containers/sequences/array/array.overview/nttp.verify.cpp (+1-1)
  • (modified) libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr.verify.cpp (+1-1)
  • (modified) libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr.verify.cpp (+1-1)
  • (modified) libcxx/test/std/utilities/utility/pairs/pairs.pair/nttp.verify.cpp (+2-1)
diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index f3eee1c6335f9..66b587f00ff4a 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -119,8 +119,7 @@ getQualification(ASTContext &Context, const DeclContext *DestContext,
       // There can't be any more tag parents after hitting a namespace.
       assert(!ReachedNS);
       (void)ReachedNS;
-      NNS = NestedNameSpecifier::Create(Context, nullptr, false,
-                                        TD->getTypeForDecl());
+      NNS = NestedNameSpecifier::Create(Context, nullptr, TD->getTypeForDecl());
     } else if (auto *NSD = llvm::dyn_cast<NamespaceDecl>(CurContext)) {
       ReachedNS = true;
       NNS = NestedNameSpecifier::Create(Context, nullptr, NSD);
diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp
index 008cc96c91996..0eb196fbad46a 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -1467,7 +1467,6 @@ bool allowIndex(CodeCompletionContext &CC) {
     return true;
   case NestedNameSpecifier::Super:
   case NestedNameSpecifier::TypeSpec:
-  case NestedNameSpecifier::TypeSpecWithTemplate:
   // Unresolved inside a template.
   case NestedNameSpecifier::Identifier:
     return false;
diff --git a/clang-tools-extra/clangd/DumpAST.cpp b/clang-tools-extra/clangd/DumpAST.cpp
index e605f82e91fe4..584bb1f088380 100644
--- a/clang-tools-extra/clangd/DumpAST.cpp
+++ b/clang-tools-extra/clangd/DumpAST.cpp
@@ -157,7 +157,6 @@ class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> {
       NNS_KIND(Identifier);
       NNS_KIND(Namespace);
       NNS_KIND(TypeSpec);
-      NNS_KIND(TypeSpecWithTemplate);
       NNS_KIND(Global);
       NNS_KIND(Super);
       NNS_KIND(NamespaceAlias);
diff --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp
index bb4c91b831354..62f220b32bd10 100644
--- a/clang-tools-extra/clangd/FindTarget.cpp
+++ b/clang-tools-extra/clangd/FindTarget.cpp
@@ -500,7 +500,6 @@ struct TargetFinder {
       }
       return;
     case NestedNameSpecifier::TypeSpec:
-    case NestedNameSpecifier::TypeSpecWithTemplate:
       add(QualType(NNS->getAsType(), 0), Flags);
       return;
     case NestedNameSpecifier::Global:
diff --git a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
index 7a140c991925c..dff0c711f04c5 100644
--- a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
+++ b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
@@ -144,7 +144,6 @@ class ASTWalker : public RecursiveASTVisitor<ASTWalker> {
     case NestedNameSpecifier::Global:
       return true;
     case NestedNameSpecifier::TypeSpec:
-    case NestedNameSpecifier::TypeSpecWithTemplate:
     case NestedNameSpecifier::Super:
     case NestedNameSpecifier::Identifier:
       return false;
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index eaecead0e6b9d..3ae852d59bbf8 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -275,6 +275,10 @@ Improvements to Clang's diagnostics
 - Diagnostics on chained comparisons (``a < b < c``) are now an error by default. This can be disabled with
   ``-Wno-error=parentheses``.
 - Clang now better preserves the sugared types of pointers to member.
+- Clang now better preserves the presence of the template keyword with dependent
+  prefixes.
+- When printing types for diagnostics, clang now doesn't suppress the scopes of
+  template arguments contained within nested names.
 - The ``-Wshift-bool`` warning has been added to warn about shifting a boolean. (#GH28334)
 - Fixed diagnostics adding a trailing ``::`` when printing some source code
   constructs, like base classes.
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index af8c49e99a7ce..6fbe170e17242 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1837,15 +1837,15 @@ class ASTContext : public RefCountedBase<ASTContext> {
                              TagDecl *OwnedTagDecl = nullptr) const;
   QualType getDependentNameType(ElaboratedTypeKeyword Keyword,
                                 NestedNameSpecifier *NNS,
-                                const IdentifierInfo *Name,
-                                QualType Canon = QualType()) const;
+                                const IdentifierInfo *Name) const;
 
   QualType getDependentTemplateSpecializationType(
-      ElaboratedTypeKeyword Keyword, NestedNameSpecifier *NNS,
-      const IdentifierInfo *Name, ArrayRef<TemplateArgumentLoc> Args) const;
-  QualType getDependentTemplateSpecializationType(
-      ElaboratedTypeKeyword Keyword, NestedNameSpecifier *NNS,
-      const IdentifierInfo *Name, ArrayRef<TemplateArgument> Args) const;
+      ElaboratedTypeKeyword Keyword, const DependentTemplateStorage &Name,
+      ArrayRef<TemplateArgumentLoc> Args) const;
+  QualType
+  getDependentTemplateSpecializationType(ElaboratedTypeKeyword Keyword,
+                                         const DependentTemplateStorage &Name,
+                                         ArrayRef<TemplateArgument> Args) const;
 
   TemplateArgument getInjectedTemplateArg(NamedDecl *ParamDecl) const;
 
@@ -2394,10 +2394,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
                                         bool TemplateKeyword,
                                         TemplateName Template) const;
 
-  TemplateName getDependentTemplateName(NestedNameSpecifier *NNS,
-                                        const IdentifierInfo *Name) const;
-  TemplateName getDependentTemplateName(NestedNameSpecifier *NNS,
-                                        OverloadedOperatorKind Operator) const;
+  TemplateName
+  getDependentTemplateName(const DependentTemplateStorage &Name) const;
   TemplateName
   getSubstTemplateTemplateParm(TemplateName replacement, Decl *AssociatedDecl,
                                unsigned Index,
diff --git a/clang/include/clang/AST/ASTImporter.h b/clang/include/clang/AST/ASTImporter.h
index 8c3fa842ab8b9..a2550716e3c7f 100644
--- a/clang/include/clang/AST/ASTImporter.h
+++ b/clang/include/clang/AST/ASTImporter.h
@@ -446,6 +446,14 @@ class TypeSourceInfo;
     /// returns nullptr only if the FromId was nullptr.
     IdentifierInfo *Import(const IdentifierInfo *FromId);
 
+    /// Import the given identifier or overloaded operator from the "from"
+    /// context into the "to" context.
+    ///
+    /// \returns The equivalent identifier or overloaded operator in the "to"
+    /// context.
+    IdentifierOrOverloadedOperator
+    Import(IdentifierOrOverloadedOperator FromIO);
+
     /// Import the given Objective-C selector from the "from"
     /// context into the "to" context.
     ///
diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index 83a6b77704f34..f086d8134a64b 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -396,8 +396,7 @@ class ASTNodeTraverser
     // FIXME: Provide a NestedNameSpecifier visitor.
     NestedNameSpecifier *Qualifier = T->getQualifier();
     if (NestedNameSpecifier::SpecifierKind K = Qualifier->getKind();
-        K == NestedNameSpecifier::TypeSpec ||
-        K == NestedNameSpecifier::TypeSpecWithTemplate)
+        K == NestedNameSpecifier::TypeSpec)
       Visit(Qualifier->getAsType());
     if (T->isSugared())
       Visit(T->getMostRecentCXXRecordDecl()->getTypeForDecl());
diff --git a/clang/include/clang/AST/AbstractBasicReader.h b/clang/include/clang/AST/AbstractBasicReader.h
index 4b627c65e276b..5ab438715ecf7 100644
--- a/clang/include/clang/AST/AbstractBasicReader.h
+++ b/clang/include/clang/AST/AbstractBasicReader.h
@@ -279,10 +279,8 @@ class DataStreamBasicReader : public BasicReaderBase<Impl> {
         continue;
 
       case NestedNameSpecifier::TypeSpec:
-      case NestedNameSpecifier::TypeSpecWithTemplate:
         cur = NestedNameSpecifier::Create(ctx, cur,
-                          kind == NestedNameSpecifier::TypeSpecWithTemplate,
-                          asImpl().readQualType().getTypePtr());
+                                          asImpl().readQualType().getTypePtr());
         continue;
 
       case NestedNameSpecifier::Global:
diff --git a/clang/include/clang/AST/AbstractBasicWriter.h b/clang/include/clang/AST/AbstractBasicWriter.h
index b941add8bde88..f65d94abc2ff1 100644
--- a/clang/include/clang/AST/AbstractBasicWriter.h
+++ b/clang/include/clang/AST/AbstractBasicWriter.h
@@ -260,7 +260,6 @@ class DataStreamBasicWriter : public BasicWriterBase<Impl> {
         continue;
 
       case NestedNameSpecifier::TypeSpec:
-      case NestedNameSpecifier::TypeSpecWithTemplate:
         asImpl().writeQualType(QualType(NNS->getAsType(), 0));
         continue;
 
diff --git a/clang/include/clang/AST/NestedNameSpecifier.h b/clang/include/clang/AST/NestedNameSpecifier.h
index 273e73e7c1e95..d7da3272d0943 100644
--- a/clang/include/clang/AST/NestedNameSpecifier.h
+++ b/clang/include/clang/AST/NestedNameSpecifier.h
@@ -52,8 +52,7 @@ class NestedNameSpecifier : public llvm::FoldingSetNode {
   enum StoredSpecifierKind {
     StoredIdentifier = 0,
     StoredDecl = 1,
-    StoredTypeSpec = 2,
-    StoredTypeSpecWithTemplate = 3
+    StoredTypeSpec = 2
   };
 
   /// The nested name specifier that precedes this nested name
@@ -89,10 +88,6 @@ class NestedNameSpecifier : public llvm::FoldingSetNode {
     /// A type, stored as a Type*.
     TypeSpec,
 
-    /// A type that was preceded by the 'template' keyword,
-    /// stored as a Type*.
-    TypeSpecWithTemplate,
-
     /// The global specifier '::'. There is no stored value.
     Global,
 
@@ -137,9 +132,8 @@ class NestedNameSpecifier : public llvm::FoldingSetNode {
                                      const NamespaceAliasDecl *Alias);
 
   /// Builds a nested name specifier that names a type.
-  static NestedNameSpecifier *Create(const ASTContext &Context,
-                                     NestedNameSpecifier *Prefix,
-                                     bool Template, const Type *T);
+  static NestedNameSpecifier *
+  Create(const ASTContext &Context, NestedNameSpecifier *Prefix, const Type *T);
 
   /// Builds a specifier that consists of just an identifier.
   ///
@@ -194,8 +188,7 @@ class NestedNameSpecifier : public llvm::FoldingSetNode {
 
   /// Retrieve the type stored in this nested name specifier.
   const Type *getAsType() const {
-    if (Prefix.getInt() == StoredTypeSpec ||
-        Prefix.getInt() == StoredTypeSpecWithTemplate)
+    if (Prefix.getInt() == StoredTypeSpec)
       return (const Type *)Specifier;
 
     return nullptr;
@@ -401,13 +394,10 @@ class NestedNameSpecifierLocBuilder {
   /// \param Context The AST context in which this nested-name-specifier
   /// resides.
   ///
-  /// \param TemplateKWLoc The location of the 'template' keyword, if present.
-  ///
   /// \param TL The TypeLoc that describes the type preceding the '::'.
   ///
   /// \param ColonColonLoc The location of the trailing '::'.
-  void Extend(ASTContext &Context, SourceLocation TemplateKWLoc, TypeLoc TL,
-              SourceLocation ColonColonLoc);
+  void Extend(ASTContext &Context, TypeLoc TL, SourceLocation ColonColonLoc);
 
   /// Extend the current nested-name-specifier by another
   /// nested-name-specifier component of the form 'identifier::'.
diff --git a/clang/include/clang/AST/ODRHash.h b/clang/include/clang/AST/ODRHash.h
index a1caa6d39a87c..a923901b32dc0 100644
--- a/clang/include/clang/AST/ODRHash.h
+++ b/clang/include/clang/AST/ODRHash.h
@@ -94,6 +94,7 @@ class ODRHash {
   void AddStmt(const Stmt *S);
   void AddIdentifierInfo(const IdentifierInfo *II);
   void AddNestedNameSpecifier(const NestedNameSpecifier *NNS);
+  void AddDependentTemplateName(const DependentTemplateStorage &Name);
   void AddTemplateName(TemplateName Name);
   void AddDeclarationName(DeclarationName Name, bool TreatAsDecl = false);
   void AddTemplateArgument(TemplateArgument TA);
diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td
index 42883b6419261..178308a24e1a0 100644
--- a/clang/include/clang/AST/PropertiesBase.td
+++ b/clang/include/clang/AST/PropertiesBase.td
@@ -692,25 +692,26 @@ let Class = PropertyTypeCase<TemplateName, "QualifiedTemplate"> in {
 let Class = PropertyTypeCase<TemplateName, "DependentTemplate"> in {
   def : ReadHelper<[{
     auto dtn = node.getAsDependentTemplateName();
+    auto name = dtn->getName();
   }]>;
   def : Property<"qualifier", NestedNameSpecifier> {
     let Read = [{ dtn->getQualifier() }];
   }
   def : Property<"identifier", Optional<Identifier>> {
-    let Read = [{ makeOptionalFromPointer(
-                    dtn->isIdentifier()
-                      ? dtn->getIdentifier()
-                      : nullptr) }];
+    let Read = [{ makeOptionalFromPointer(name.getIdentifier()) }];
   }
   def : Property<"operatorKind", OverloadedOperatorKind> {
     let Conditional = [{ !identifier }];
-    let Read = [{ dtn->getOperator() }];
+    let Read = [{ name.getOperator() }];
+  }
+  def : Property<"HasTemplateKeyword", Bool> {
+    let Read = [{ dtn->hasTemplateKeyword() }];
   }
   def : Creator<[{
     if (identifier) {
-      return ctx.getDependentTemplateName(qualifier, *identifier);
+      return ctx.getDependentTemplateName({qualifier, *identifier, HasTemplateKeyword});
     } else {
-      return ctx.getDependentTemplateName(qualifier, *operatorKind);
+      return ctx.getDependentTemplateName({qualifier, *operatorKind, HasTemplateKeyword});
     }
   }]>;
 }
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 0d5d515c0e6f7..0530996ed20d3 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -795,7 +795,6 @@ bool RecursiveASTVisitor<Derived>::TraverseNestedNameSpecifier(
     return true;
 
   case NestedNameSpecifier::TypeSpec:
-  case NestedNameSpecifier::TypeSpecWithTemplate:
     TRY_TO(TraverseType(QualType(NNS->getAsType(), 0)));
   }
 
@@ -820,7 +819,6 @@ bool RecursiveASTVisitor<Derived>::TraverseNestedNameSpecifierLoc(
     return true;
 
   case NestedNameSpecifier::TypeSpec:
-  case NestedNameSpecifier::TypeSpecWithTemplate:
     TRY_TO(TraverseTypeLoc(NNS.getTypeLoc()));
     break;
   }
@@ -1172,7 +1170,8 @@ DEF_TRAVERSE_TYPE(DependentNameType,
                   { TRY_TO(TraverseNestedNameSpecifier(T->getQualifier())); })
 
 DEF_TRAVERSE_TYPE(DependentTemplateSpecializationType, {
-  TRY_TO(TraverseNestedNameSpecifier(T->getQualifier()));
+  const DependentTemplateStorage &S = T->getDependentTemplateName();
+  TRY_TO(TraverseNestedNameSpecifier(S.getQualifier()));
   TRY_TO(TraverseTemplateArguments(T->template_arguments()));
 })
 
diff --git a/clang/include/clang/AST/TemplateName.h b/clang/include/clang/AST/TemplateName.h
index ce97f834bfc1d..1a56133b72d6e 100644
--- a/clang/include/clang/AST/TemplateName.h
+++ b/clang/include/clang/AST/TemplateName.h
@@ -16,6 +16,7 @@
 #include "clang/AST/DependenceFlags.h"
 #include "clang/AST/NestedNameSpecifier.h"
 #include "clang/Basic/LLVM.h"
+#include "clang/Basic/OperatorKinds.h"
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/PointerIntPair.h"
 #include "llvm/ADT/PointerUnion.h"
@@ -537,6 +538,35 @@ class QualifiedTemplateName : public llvm::FoldingSetNode {
   }
 };
 
+struct IdentifierOrOverloadedOperator {
+  IdentifierOrOverloadedOperator() = default;
+  IdentifierOrOverloadedOperator(const IdentifierInfo *II);
+  IdentifierOrOverloadedOperator(OverloadedOperatorKind OOK);
+
+  /// Returns the identifier to which this template name refers.
+  const IdentifierInfo *getIdentifier() const {
+    if (getOperator() != OO_None)
+      return nullptr;
+    return reinterpret_cast<const IdentifierInfo *>(PtrOrOp);
+  }
+
+  /// Return the overloaded operator to which this template name refers.
+  OverloadedOperatorKind getOperator() const {
+    uintptr_t OOK = -PtrOrOp;
+    return OOK < NUM_OVERLOADED_OPERATORS ? OverloadedOperatorKind(OOK)
+                                          : OO_None;
+  }
+
+  void Profile(llvm::FoldingSetNodeID &ID) const;
+
+  bool operator==(const IdentifierOrOverloadedOperator &Other) const {
+    return PtrOrOp == Other.PtrOrOp;
+  };
+
+private:
+  uintptr_t PtrOrOp = 0;
+};
+
 /// Represents a dependent template name that cannot be
 /// resolved prior to template instantiation.
 ///
@@ -545,104 +575,53 @@ class QualifiedTemplateName : public llvm::FoldingSetNode {
 /// DependentTemplateName can refer to "MetaFun::template apply",
 /// where "MetaFun::" is the nested name specifier and "apply" is the
 /// template name referenced. The "template" keyword is implied.
-class DependentTemplateName : public llvm::FoldingSetNode {
-  friend class ASTContext;
-
+class DependentTemplateStorage {
   /// The nested name specifier that qualifies the template
   /// name.
   ///
   /// The bit stored in this qualifier describes whether the \c Name field
-  /// is interpreted as an IdentifierInfo pointer (when clear) or as an
-  /// overloaded operator kind (when set).
+  /// was preceeded by a template keyword.
   llvm::PointerIntPair<NestedNameSpecifier *, 1, bool> Qualifier;
 
   /// The dependent template name.
-  union {
-    /// The identifier template name.
-    ///
-    /// Only valid when the bit on \c Qualifier is clear.
-    const IdentifierInfo *Identifier;
-
-    /// The overloaded operator name.
-    ///
-    /// Only valid when the bit on \c Qualifier is set.
-    OverloadedOperatorKind Operator;
-  };
-
-  /// The canonical template name to which this dependent
-  /// template name refers.
-  ///
-  /// The canonical template name for a dependent template name is
-  /// another dependent template name whose nested name specifier is
-  /// canonical.
-  TemplateName CanonicalTemplateName;
-
-  DependentTemplateName(NestedNameSpecifier *Qualifier,
-                        const IdentifierInfo *Identifier)
-      : Qualifier(Qualifier, false), Identifier(Identifier),
-        CanonicalTemplateName(this) {}
-
-  DependentTemplateName(NestedNameSpecifier *Qualifier,
-                        const IdentifierInfo *Identifier,
-                        TemplateName Canon)
-      : Qualifier(Qualifier, false), Identifier(Identifier),
-        CanonicalTemplateName(Canon) {}
-
-  DependentTemplateName(NestedNameSpecifier *Qualifier,
-                        OverloadedOperatorKind Operator)
-      : Qualifier(Qualifier, true), Operator(Operator),
-        CanonicalTemplateName(this) {}
-
-  DependentTemplateName(NestedNameSpecifier *Qualifier,
-                        OverloadedOperatorKind Operator,
-                        TemplateName Canon)
-       : Qualifier(Qualifier, true), Operator(Operator),
-         CanonicalTemplateName(Canon) {}
+  IdentifierOrOverloadedOperator Name;
 
 public:
+  DependentTemplateStorage(NestedNameSpecifier *Qualifier,
+                           IdentifierOrOverloadedOperator Name,
+                           bool HasTemplateKeyword);
+
   /// Return the nested name specifier that qualifies this name.
   NestedNameSpecifier *getQualifier() const { return Qualifier.getPointer(); }
 
-  /// Determine whether this template name refers to an identifier.
-  bool isIdentifier() const { return !Qualifier.getInt(); }
+  IdentifierOrOverloadedOperator getName() const { return Name; }
 
-  /// Returns the identifier to which this template name refers.
-  const IdentifierInfo *getIdentifier() const {
-    assert(isIdentifier() && "Template name isn't an identifier?");
-    return Identifier;
-  }
-
-  /// Determine whether this template name refers to an overloaded
-  /// operator.
-  bool isOverloadedOperator() const { return Qualifier.getInt(); }
+  /// Was this template name was preceeded by the template keyword?
+  bool hasTemplateKeyword() const { return Qualifier.getInt(); }
 
-  /// Return the overloaded operator to which this template name refers.
-  OverloadedOperatorKind getOperator() const {
-    assert(isOverloadedOperator() &&
-           "Template name isn't an overloaded operator?");
-    return Operator;
-  }
+  TemplateNameDependence getDependence() const;
 
-  void Profile(llvm::FoldingSe...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Mar 30, 2025

@llvm/pr-subscribers-coroutines

Author: Matheus Izvekov (mizvekov)

Changes

This makes it so clang can better represent the abscence of the template keyword for a template which cannot be resolved due to a dependent prefix.

The tracking of the template keyword is removed from the NestedNameSpecifier, and added to the last type node which had it missing, the DependentTemplateSpecializationType (DTST).

The DTST and the DependentTemplateName are refactored to share most of their implementation.

That refactoring along with the removal of the TypeSpecWithTemplate kind from the nested name specifiers amounts to a large amount of code removed, making this patch a small performance improvement overall.

image

This will also enable future further simplifications.

As a drive-by, this removes some special cases from the type printer, allowing template arguments within NestedNameSpecifiers to not have their nested names suppressed when printing.


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

66 Files Affected:

  • (modified) clang-tools-extra/clangd/AST.cpp (+1-2)
  • (modified) clang-tools-extra/clangd/CodeComplete.cpp (-1)
  • (modified) clang-tools-extra/clangd/DumpAST.cpp (-1)
  • (modified) clang-tools-extra/clangd/FindTarget.cpp (-1)
  • (modified) clang-tools-extra/include-cleaner/lib/WalkAST.cpp (-1)
  • (modified) clang/docs/ReleaseNotes.rst (+4)
  • (modified) clang/include/clang/AST/ASTContext.h (+9-11)
  • (modified) clang/include/clang/AST/ASTImporter.h (+8)
  • (modified) clang/include/clang/AST/ASTNodeTraverser.h (+1-2)
  • (modified) clang/include/clang/AST/AbstractBasicReader.h (+1-3)
  • (modified) clang/include/clang/AST/AbstractBasicWriter.h (-1)
  • (modified) clang/include/clang/AST/NestedNameSpecifier.h (+5-15)
  • (modified) clang/include/clang/AST/ODRHash.h (+1)
  • (modified) clang/include/clang/AST/PropertiesBase.td (+8-7)
  • (modified) clang/include/clang/AST/RecursiveASTVisitor.h (+2-3)
  • (modified) clang/include/clang/AST/TemplateName.h (+56-77)
  • (modified) clang/include/clang/AST/Type.h (+8-14)
  • (modified) clang/include/clang/AST/TypeLoc.h (+5-4)
  • (modified) clang/include/clang/AST/TypeProperties.td (+25-22)
  • (modified) clang/include/clang/Sema/DeclSpec.h (+1-2)
  • (modified) clang/lib/AST/ASTContext.cpp (+81-133)
  • (modified) clang/lib/AST/ASTImporter.cpp (+22-41)
  • (modified) clang/lib/AST/ASTStructuralEquivalence.cpp (+19-18)
  • (modified) clang/lib/AST/ItaniumMangle.cpp (+22-13)
  • (modified) clang/lib/AST/NestedNameSpecifier.cpp (+23-79)
  • (modified) clang/lib/AST/ODRHash.cpp (+15-4)
  • (modified) clang/lib/AST/QualTypeNames.cpp (+3-5)
  • (modified) clang/lib/AST/TemplateName.cpp (+58-9)
  • (modified) clang/lib/AST/TextNodeDumper.cpp (-4)
  • (modified) clang/lib/AST/Type.cpp (+10-17)
  • (modified) clang/lib/AST/TypeLoc.cpp (+3-2)
  • (modified) clang/lib/AST/TypePrinter.cpp (+7-5)
  • (modified) clang/lib/ExtractAPI/DeclarationFragments.cpp (-7)
  • (modified) clang/lib/Index/IndexTypeSourceInfo.cpp (-1)
  • (modified) clang/lib/Parse/ParseExprCXX.cpp (+4-4)
  • (modified) clang/lib/Sema/DeclSpec.cpp (+3-3)
  • (modified) clang/lib/Sema/HeuristicResolver.cpp (+3-3)
  • (modified) clang/lib/Sema/SemaCXXScopeSpec.cpp (+12-19)
  • (modified) clang/lib/Sema/SemaCodeComplete.cpp (+3-3)
  • (modified) clang/lib/Sema/SemaCoroutine.cpp (+1-2)
  • (modified) clang/lib/Sema/SemaDecl.cpp (+12-12)
  • (modified) clang/lib/Sema/SemaDeclCXX.cpp (+1-2)
  • (modified) clang/lib/Sema/SemaExpr.cpp (+1-1)
  • (modified) clang/lib/Sema/SemaExprCXX.cpp (-1)
  • (modified) clang/lib/Sema/SemaLookup.cpp (+1-3)
  • (modified) clang/lib/Sema/SemaTemplate.cpp (+19-22)
  • (modified) clang/lib/Sema/SemaTemplateDeduction.cpp (+2-1)
  • (modified) clang/lib/Sema/TreeTransform.h (+73-76)
  • (modified) clang/lib/Serialization/ASTReader.cpp (+2-8)
  • (modified) clang/lib/Serialization/ASTWriter.cpp (-2)
  • (modified) clang/lib/Tooling/Syntax/BuildTree.cpp (-2)
  • (modified) clang/test/AST/ast-dump-decl.cpp (+10-10)
  • (modified) clang/test/AST/ast-dump-expr.cpp (+1-2)
  • (modified) clang/test/AST/ast-dump-templates.cpp (+3-3)
  • (modified) clang/test/CXX/class.access/p6.cpp (+2-2)
  • (modified) clang/test/CXX/drs/cwg2xx.cpp (+2-2)
  • (modified) clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp (+1-1)
  • (modified) clang/test/SemaCXX/static-assert.cpp (+3-1)
  • (modified) clang/test/SemaTemplate/aggregate-deduction-candidate.cpp (+4-4)
  • (modified) clang/test/SemaTemplate/dependent-template-recover.cpp (+18)
  • (modified) clang/test/SemaTemplate/instantiate-requires-expr.cpp (+2-2)
  • (modified) clang/tools/libclang/CIndex.cpp (-2)
  • (modified) libcxx/test/std/containers/sequences/array/array.overview/nttp.verify.cpp (+1-1)
  • (modified) libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr.verify.cpp (+1-1)
  • (modified) libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr.verify.cpp (+1-1)
  • (modified) libcxx/test/std/utilities/utility/pairs/pairs.pair/nttp.verify.cpp (+2-1)
diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index f3eee1c6335f9..66b587f00ff4a 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -119,8 +119,7 @@ getQualification(ASTContext &Context, const DeclContext *DestContext,
       // There can't be any more tag parents after hitting a namespace.
       assert(!ReachedNS);
       (void)ReachedNS;
-      NNS = NestedNameSpecifier::Create(Context, nullptr, false,
-                                        TD->getTypeForDecl());
+      NNS = NestedNameSpecifier::Create(Context, nullptr, TD->getTypeForDecl());
     } else if (auto *NSD = llvm::dyn_cast<NamespaceDecl>(CurContext)) {
       ReachedNS = true;
       NNS = NestedNameSpecifier::Create(Context, nullptr, NSD);
diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp
index 008cc96c91996..0eb196fbad46a 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -1467,7 +1467,6 @@ bool allowIndex(CodeCompletionContext &CC) {
     return true;
   case NestedNameSpecifier::Super:
   case NestedNameSpecifier::TypeSpec:
-  case NestedNameSpecifier::TypeSpecWithTemplate:
   // Unresolved inside a template.
   case NestedNameSpecifier::Identifier:
     return false;
diff --git a/clang-tools-extra/clangd/DumpAST.cpp b/clang-tools-extra/clangd/DumpAST.cpp
index e605f82e91fe4..584bb1f088380 100644
--- a/clang-tools-extra/clangd/DumpAST.cpp
+++ b/clang-tools-extra/clangd/DumpAST.cpp
@@ -157,7 +157,6 @@ class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> {
       NNS_KIND(Identifier);
       NNS_KIND(Namespace);
       NNS_KIND(TypeSpec);
-      NNS_KIND(TypeSpecWithTemplate);
       NNS_KIND(Global);
       NNS_KIND(Super);
       NNS_KIND(NamespaceAlias);
diff --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp
index bb4c91b831354..62f220b32bd10 100644
--- a/clang-tools-extra/clangd/FindTarget.cpp
+++ b/clang-tools-extra/clangd/FindTarget.cpp
@@ -500,7 +500,6 @@ struct TargetFinder {
       }
       return;
     case NestedNameSpecifier::TypeSpec:
-    case NestedNameSpecifier::TypeSpecWithTemplate:
       add(QualType(NNS->getAsType(), 0), Flags);
       return;
     case NestedNameSpecifier::Global:
diff --git a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
index 7a140c991925c..dff0c711f04c5 100644
--- a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
+++ b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
@@ -144,7 +144,6 @@ class ASTWalker : public RecursiveASTVisitor<ASTWalker> {
     case NestedNameSpecifier::Global:
       return true;
     case NestedNameSpecifier::TypeSpec:
-    case NestedNameSpecifier::TypeSpecWithTemplate:
     case NestedNameSpecifier::Super:
     case NestedNameSpecifier::Identifier:
       return false;
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index eaecead0e6b9d..3ae852d59bbf8 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -275,6 +275,10 @@ Improvements to Clang's diagnostics
 - Diagnostics on chained comparisons (``a < b < c``) are now an error by default. This can be disabled with
   ``-Wno-error=parentheses``.
 - Clang now better preserves the sugared types of pointers to member.
+- Clang now better preserves the presence of the template keyword with dependent
+  prefixes.
+- When printing types for diagnostics, clang now doesn't suppress the scopes of
+  template arguments contained within nested names.
 - The ``-Wshift-bool`` warning has been added to warn about shifting a boolean. (#GH28334)
 - Fixed diagnostics adding a trailing ``::`` when printing some source code
   constructs, like base classes.
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index af8c49e99a7ce..6fbe170e17242 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1837,15 +1837,15 @@ class ASTContext : public RefCountedBase<ASTContext> {
                              TagDecl *OwnedTagDecl = nullptr) const;
   QualType getDependentNameType(ElaboratedTypeKeyword Keyword,
                                 NestedNameSpecifier *NNS,
-                                const IdentifierInfo *Name,
-                                QualType Canon = QualType()) const;
+                                const IdentifierInfo *Name) const;
 
   QualType getDependentTemplateSpecializationType(
-      ElaboratedTypeKeyword Keyword, NestedNameSpecifier *NNS,
-      const IdentifierInfo *Name, ArrayRef<TemplateArgumentLoc> Args) const;
-  QualType getDependentTemplateSpecializationType(
-      ElaboratedTypeKeyword Keyword, NestedNameSpecifier *NNS,
-      const IdentifierInfo *Name, ArrayRef<TemplateArgument> Args) const;
+      ElaboratedTypeKeyword Keyword, const DependentTemplateStorage &Name,
+      ArrayRef<TemplateArgumentLoc> Args) const;
+  QualType
+  getDependentTemplateSpecializationType(ElaboratedTypeKeyword Keyword,
+                                         const DependentTemplateStorage &Name,
+                                         ArrayRef<TemplateArgument> Args) const;
 
   TemplateArgument getInjectedTemplateArg(NamedDecl *ParamDecl) const;
 
@@ -2394,10 +2394,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
                                         bool TemplateKeyword,
                                         TemplateName Template) const;
 
-  TemplateName getDependentTemplateName(NestedNameSpecifier *NNS,
-                                        const IdentifierInfo *Name) const;
-  TemplateName getDependentTemplateName(NestedNameSpecifier *NNS,
-                                        OverloadedOperatorKind Operator) const;
+  TemplateName
+  getDependentTemplateName(const DependentTemplateStorage &Name) const;
   TemplateName
   getSubstTemplateTemplateParm(TemplateName replacement, Decl *AssociatedDecl,
                                unsigned Index,
diff --git a/clang/include/clang/AST/ASTImporter.h b/clang/include/clang/AST/ASTImporter.h
index 8c3fa842ab8b9..a2550716e3c7f 100644
--- a/clang/include/clang/AST/ASTImporter.h
+++ b/clang/include/clang/AST/ASTImporter.h
@@ -446,6 +446,14 @@ class TypeSourceInfo;
     /// returns nullptr only if the FromId was nullptr.
     IdentifierInfo *Import(const IdentifierInfo *FromId);
 
+    /// Import the given identifier or overloaded operator from the "from"
+    /// context into the "to" context.
+    ///
+    /// \returns The equivalent identifier or overloaded operator in the "to"
+    /// context.
+    IdentifierOrOverloadedOperator
+    Import(IdentifierOrOverloadedOperator FromIO);
+
     /// Import the given Objective-C selector from the "from"
     /// context into the "to" context.
     ///
diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index 83a6b77704f34..f086d8134a64b 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -396,8 +396,7 @@ class ASTNodeTraverser
     // FIXME: Provide a NestedNameSpecifier visitor.
     NestedNameSpecifier *Qualifier = T->getQualifier();
     if (NestedNameSpecifier::SpecifierKind K = Qualifier->getKind();
-        K == NestedNameSpecifier::TypeSpec ||
-        K == NestedNameSpecifier::TypeSpecWithTemplate)
+        K == NestedNameSpecifier::TypeSpec)
       Visit(Qualifier->getAsType());
     if (T->isSugared())
       Visit(T->getMostRecentCXXRecordDecl()->getTypeForDecl());
diff --git a/clang/include/clang/AST/AbstractBasicReader.h b/clang/include/clang/AST/AbstractBasicReader.h
index 4b627c65e276b..5ab438715ecf7 100644
--- a/clang/include/clang/AST/AbstractBasicReader.h
+++ b/clang/include/clang/AST/AbstractBasicReader.h
@@ -279,10 +279,8 @@ class DataStreamBasicReader : public BasicReaderBase<Impl> {
         continue;
 
       case NestedNameSpecifier::TypeSpec:
-      case NestedNameSpecifier::TypeSpecWithTemplate:
         cur = NestedNameSpecifier::Create(ctx, cur,
-                          kind == NestedNameSpecifier::TypeSpecWithTemplate,
-                          asImpl().readQualType().getTypePtr());
+                                          asImpl().readQualType().getTypePtr());
         continue;
 
       case NestedNameSpecifier::Global:
diff --git a/clang/include/clang/AST/AbstractBasicWriter.h b/clang/include/clang/AST/AbstractBasicWriter.h
index b941add8bde88..f65d94abc2ff1 100644
--- a/clang/include/clang/AST/AbstractBasicWriter.h
+++ b/clang/include/clang/AST/AbstractBasicWriter.h
@@ -260,7 +260,6 @@ class DataStreamBasicWriter : public BasicWriterBase<Impl> {
         continue;
 
       case NestedNameSpecifier::TypeSpec:
-      case NestedNameSpecifier::TypeSpecWithTemplate:
         asImpl().writeQualType(QualType(NNS->getAsType(), 0));
         continue;
 
diff --git a/clang/include/clang/AST/NestedNameSpecifier.h b/clang/include/clang/AST/NestedNameSpecifier.h
index 273e73e7c1e95..d7da3272d0943 100644
--- a/clang/include/clang/AST/NestedNameSpecifier.h
+++ b/clang/include/clang/AST/NestedNameSpecifier.h
@@ -52,8 +52,7 @@ class NestedNameSpecifier : public llvm::FoldingSetNode {
   enum StoredSpecifierKind {
     StoredIdentifier = 0,
     StoredDecl = 1,
-    StoredTypeSpec = 2,
-    StoredTypeSpecWithTemplate = 3
+    StoredTypeSpec = 2
   };
 
   /// The nested name specifier that precedes this nested name
@@ -89,10 +88,6 @@ class NestedNameSpecifier : public llvm::FoldingSetNode {
     /// A type, stored as a Type*.
     TypeSpec,
 
-    /// A type that was preceded by the 'template' keyword,
-    /// stored as a Type*.
-    TypeSpecWithTemplate,
-
     /// The global specifier '::'. There is no stored value.
     Global,
 
@@ -137,9 +132,8 @@ class NestedNameSpecifier : public llvm::FoldingSetNode {
                                      const NamespaceAliasDecl *Alias);
 
   /// Builds a nested name specifier that names a type.
-  static NestedNameSpecifier *Create(const ASTContext &Context,
-                                     NestedNameSpecifier *Prefix,
-                                     bool Template, const Type *T);
+  static NestedNameSpecifier *
+  Create(const ASTContext &Context, NestedNameSpecifier *Prefix, const Type *T);
 
   /// Builds a specifier that consists of just an identifier.
   ///
@@ -194,8 +188,7 @@ class NestedNameSpecifier : public llvm::FoldingSetNode {
 
   /// Retrieve the type stored in this nested name specifier.
   const Type *getAsType() const {
-    if (Prefix.getInt() == StoredTypeSpec ||
-        Prefix.getInt() == StoredTypeSpecWithTemplate)
+    if (Prefix.getInt() == StoredTypeSpec)
       return (const Type *)Specifier;
 
     return nullptr;
@@ -401,13 +394,10 @@ class NestedNameSpecifierLocBuilder {
   /// \param Context The AST context in which this nested-name-specifier
   /// resides.
   ///
-  /// \param TemplateKWLoc The location of the 'template' keyword, if present.
-  ///
   /// \param TL The TypeLoc that describes the type preceding the '::'.
   ///
   /// \param ColonColonLoc The location of the trailing '::'.
-  void Extend(ASTContext &Context, SourceLocation TemplateKWLoc, TypeLoc TL,
-              SourceLocation ColonColonLoc);
+  void Extend(ASTContext &Context, TypeLoc TL, SourceLocation ColonColonLoc);
 
   /// Extend the current nested-name-specifier by another
   /// nested-name-specifier component of the form 'identifier::'.
diff --git a/clang/include/clang/AST/ODRHash.h b/clang/include/clang/AST/ODRHash.h
index a1caa6d39a87c..a923901b32dc0 100644
--- a/clang/include/clang/AST/ODRHash.h
+++ b/clang/include/clang/AST/ODRHash.h
@@ -94,6 +94,7 @@ class ODRHash {
   void AddStmt(const Stmt *S);
   void AddIdentifierInfo(const IdentifierInfo *II);
   void AddNestedNameSpecifier(const NestedNameSpecifier *NNS);
+  void AddDependentTemplateName(const DependentTemplateStorage &Name);
   void AddTemplateName(TemplateName Name);
   void AddDeclarationName(DeclarationName Name, bool TreatAsDecl = false);
   void AddTemplateArgument(TemplateArgument TA);
diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td
index 42883b6419261..178308a24e1a0 100644
--- a/clang/include/clang/AST/PropertiesBase.td
+++ b/clang/include/clang/AST/PropertiesBase.td
@@ -692,25 +692,26 @@ let Class = PropertyTypeCase<TemplateName, "QualifiedTemplate"> in {
 let Class = PropertyTypeCase<TemplateName, "DependentTemplate"> in {
   def : ReadHelper<[{
     auto dtn = node.getAsDependentTemplateName();
+    auto name = dtn->getName();
   }]>;
   def : Property<"qualifier", NestedNameSpecifier> {
     let Read = [{ dtn->getQualifier() }];
   }
   def : Property<"identifier", Optional<Identifier>> {
-    let Read = [{ makeOptionalFromPointer(
-                    dtn->isIdentifier()
-                      ? dtn->getIdentifier()
-                      : nullptr) }];
+    let Read = [{ makeOptionalFromPointer(name.getIdentifier()) }];
   }
   def : Property<"operatorKind", OverloadedOperatorKind> {
     let Conditional = [{ !identifier }];
-    let Read = [{ dtn->getOperator() }];
+    let Read = [{ name.getOperator() }];
+  }
+  def : Property<"HasTemplateKeyword", Bool> {
+    let Read = [{ dtn->hasTemplateKeyword() }];
   }
   def : Creator<[{
     if (identifier) {
-      return ctx.getDependentTemplateName(qualifier, *identifier);
+      return ctx.getDependentTemplateName({qualifier, *identifier, HasTemplateKeyword});
     } else {
-      return ctx.getDependentTemplateName(qualifier, *operatorKind);
+      return ctx.getDependentTemplateName({qualifier, *operatorKind, HasTemplateKeyword});
     }
   }]>;
 }
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 0d5d515c0e6f7..0530996ed20d3 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -795,7 +795,6 @@ bool RecursiveASTVisitor<Derived>::TraverseNestedNameSpecifier(
     return true;
 
   case NestedNameSpecifier::TypeSpec:
-  case NestedNameSpecifier::TypeSpecWithTemplate:
     TRY_TO(TraverseType(QualType(NNS->getAsType(), 0)));
   }
 
@@ -820,7 +819,6 @@ bool RecursiveASTVisitor<Derived>::TraverseNestedNameSpecifierLoc(
     return true;
 
   case NestedNameSpecifier::TypeSpec:
-  case NestedNameSpecifier::TypeSpecWithTemplate:
     TRY_TO(TraverseTypeLoc(NNS.getTypeLoc()));
     break;
   }
@@ -1172,7 +1170,8 @@ DEF_TRAVERSE_TYPE(DependentNameType,
                   { TRY_TO(TraverseNestedNameSpecifier(T->getQualifier())); })
 
 DEF_TRAVERSE_TYPE(DependentTemplateSpecializationType, {
-  TRY_TO(TraverseNestedNameSpecifier(T->getQualifier()));
+  const DependentTemplateStorage &S = T->getDependentTemplateName();
+  TRY_TO(TraverseNestedNameSpecifier(S.getQualifier()));
   TRY_TO(TraverseTemplateArguments(T->template_arguments()));
 })
 
diff --git a/clang/include/clang/AST/TemplateName.h b/clang/include/clang/AST/TemplateName.h
index ce97f834bfc1d..1a56133b72d6e 100644
--- a/clang/include/clang/AST/TemplateName.h
+++ b/clang/include/clang/AST/TemplateName.h
@@ -16,6 +16,7 @@
 #include "clang/AST/DependenceFlags.h"
 #include "clang/AST/NestedNameSpecifier.h"
 #include "clang/Basic/LLVM.h"
+#include "clang/Basic/OperatorKinds.h"
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/PointerIntPair.h"
 #include "llvm/ADT/PointerUnion.h"
@@ -537,6 +538,35 @@ class QualifiedTemplateName : public llvm::FoldingSetNode {
   }
 };
 
+struct IdentifierOrOverloadedOperator {
+  IdentifierOrOverloadedOperator() = default;
+  IdentifierOrOverloadedOperator(const IdentifierInfo *II);
+  IdentifierOrOverloadedOperator(OverloadedOperatorKind OOK);
+
+  /// Returns the identifier to which this template name refers.
+  const IdentifierInfo *getIdentifier() const {
+    if (getOperator() != OO_None)
+      return nullptr;
+    return reinterpret_cast<const IdentifierInfo *>(PtrOrOp);
+  }
+
+  /// Return the overloaded operator to which this template name refers.
+  OverloadedOperatorKind getOperator() const {
+    uintptr_t OOK = -PtrOrOp;
+    return OOK < NUM_OVERLOADED_OPERATORS ? OverloadedOperatorKind(OOK)
+                                          : OO_None;
+  }
+
+  void Profile(llvm::FoldingSetNodeID &ID) const;
+
+  bool operator==(const IdentifierOrOverloadedOperator &Other) const {
+    return PtrOrOp == Other.PtrOrOp;
+  };
+
+private:
+  uintptr_t PtrOrOp = 0;
+};
+
 /// Represents a dependent template name that cannot be
 /// resolved prior to template instantiation.
 ///
@@ -545,104 +575,53 @@ class QualifiedTemplateName : public llvm::FoldingSetNode {
 /// DependentTemplateName can refer to "MetaFun::template apply",
 /// where "MetaFun::" is the nested name specifier and "apply" is the
 /// template name referenced. The "template" keyword is implied.
-class DependentTemplateName : public llvm::FoldingSetNode {
-  friend class ASTContext;
-
+class DependentTemplateStorage {
   /// The nested name specifier that qualifies the template
   /// name.
   ///
   /// The bit stored in this qualifier describes whether the \c Name field
-  /// is interpreted as an IdentifierInfo pointer (when clear) or as an
-  /// overloaded operator kind (when set).
+  /// was preceeded by a template keyword.
   llvm::PointerIntPair<NestedNameSpecifier *, 1, bool> Qualifier;
 
   /// The dependent template name.
-  union {
-    /// The identifier template name.
-    ///
-    /// Only valid when the bit on \c Qualifier is clear.
-    const IdentifierInfo *Identifier;
-
-    /// The overloaded operator name.
-    ///
-    /// Only valid when the bit on \c Qualifier is set.
-    OverloadedOperatorKind Operator;
-  };
-
-  /// The canonical template name to which this dependent
-  /// template name refers.
-  ///
-  /// The canonical template name for a dependent template name is
-  /// another dependent template name whose nested name specifier is
-  /// canonical.
-  TemplateName CanonicalTemplateName;
-
-  DependentTemplateName(NestedNameSpecifier *Qualifier,
-                        const IdentifierInfo *Identifier)
-      : Qualifier(Qualifier, false), Identifier(Identifier),
-        CanonicalTemplateName(this) {}
-
-  DependentTemplateName(NestedNameSpecifier *Qualifier,
-                        const IdentifierInfo *Identifier,
-                        TemplateName Canon)
-      : Qualifier(Qualifier, false), Identifier(Identifier),
-        CanonicalTemplateName(Canon) {}
-
-  DependentTemplateName(NestedNameSpecifier *Qualifier,
-                        OverloadedOperatorKind Operator)
-      : Qualifier(Qualifier, true), Operator(Operator),
-        CanonicalTemplateName(this) {}
-
-  DependentTemplateName(NestedNameSpecifier *Qualifier,
-                        OverloadedOperatorKind Operator,
-                        TemplateName Canon)
-       : Qualifier(Qualifier, true), Operator(Operator),
-         CanonicalTemplateName(Canon) {}
+  IdentifierOrOverloadedOperator Name;
 
 public:
+  DependentTemplateStorage(NestedNameSpecifier *Qualifier,
+                           IdentifierOrOverloadedOperator Name,
+                           bool HasTemplateKeyword);
+
   /// Return the nested name specifier that qualifies this name.
   NestedNameSpecifier *getQualifier() const { return Qualifier.getPointer(); }
 
-  /// Determine whether this template name refers to an identifier.
-  bool isIdentifier() const { return !Qualifier.getInt(); }
+  IdentifierOrOverloadedOperator getName() const { return Name; }
 
-  /// Returns the identifier to which this template name refers.
-  const IdentifierInfo *getIdentifier() const {
-    assert(isIdentifier() && "Template name isn't an identifier?");
-    return Identifier;
-  }
-
-  /// Determine whether this template name refers to an overloaded
-  /// operator.
-  bool isOverloadedOperator() const { return Qualifier.getInt(); }
+  /// Was this template name was preceeded by the template keyword?
+  bool hasTemplateKeyword() const { return Qualifier.getInt(); }
 
-  /// Return the overloaded operator to which this template name refers.
-  OverloadedOperatorKind getOperator() const {
-    assert(isOverloadedOperator() &&
-           "Template name isn't an overloaded operator?");
-    return Operator;
-  }
+  TemplateNameDependence getDependence() const;
 
-  void Profile(llvm::FoldingSe...
[truncated]

@mizvekov mizvekov force-pushed the users/mizvekov/dtst branch 2 times, most recently from 3d58492 to ce6a2db Compare March 30, 2025 19:24
@mizvekov mizvekov changed the base branch from main to users/mizvekov/subst_final March 31, 2025 03:15
@mizvekov mizvekov force-pushed the users/mizvekov/dtst branch from ce6a2db to 6e31920 Compare March 31, 2025 03:16
@zwuis
Copy link
Contributor

zwuis commented Mar 31, 2025

I tested this branch locally. It fixes #43179, #68670 and #92757.

@mizvekov
Copy link
Contributor Author

@zwuis thanks for finding these.

I removed that assertion because it was not relevant anymore at that point where it existed.

However, we should still not have an ElaboratedType in a NestedNameSpecifier, although this patch moves us in the direction where that will not be the case anymore.

Copy link
Contributor

@zyn0217 zyn0217 left a comment

Choose a reason for hiding this comment

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

Thanks for the improvement.

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

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

Ooof... this is a big patch again. Seems like you have a minimum patch size of 50 files these days :D Reviewed best I can, but didn't see anything problematic best I could tell. I question why we are now a regex match on some tests, else LGTM.

This makes it so clang can better represent the abscence of
the template keyword for a template which cannot be resolved
due to a dependent prefix.

The tracking of the template keyword is removed from the
NestedNameSpecifier, and added to the last type node which had
it missing, the DependentTemplateSpecializationType (DTST).

The DTST and the DependentTemplateName are refactored to share
most of their implementation.

That refactoring along with the removal of the TypeSpecWithTemplate kind
from the nested name specifiers amounts to a large amount of code
removed, making this patch a small performance improvement overall.

This will also enable future further simplifications.

As a drive-by, this removes some special cases from the type printer,
allowing template arguments within NestedNameSpecifiers to not have
their nested names suppressed when printing.
@mizvekov
Copy link
Contributor Author

mizvekov commented Apr 7, 2025

@DKLoehr this will be fixed here: #134748

mizvekov added a commit that referenced this pull request Apr 8, 2025
This changes the transform for DTST so it rebuilds the node if any of
the template arguments changed.

This fixes a regression reported here:
#133610 (comment)
which was introduced by #133610

There are no release notes since the regression was never released.
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Apr 8, 2025
…(#134748)

This changes the transform for DTST so it rebuilds the node if any of
the template arguments changed.

This fixes a regression reported here:
llvm/llvm-project#133610 (comment)
which was introduced by llvm/llvm-project#133610

There are no release notes since the regression was never released.
@alexfh
Copy link
Contributor

alexfh commented Apr 8, 2025

My test case: https://gcc.godbolt.org/z/a5no94d39

$ cat q.cc
namespace ns {
enum class E : unsigned char {
  KA,
  kB,
};
template <typename P>
struct A {
  using Parent = P;
  struct B;
  using typename Parent::Res;
  template <E e>
  using Source = typename Parent::template Source<e>;
  Res M(Source<E::KA>, const typename B::S& event);
  Res M(Source<E::kB>, const typename B::S& event);
};
}
$ ./clang-bad -fsyntax-only -std=c++20 q.cc
q.cc:14:7: error: class member cannot be redeclared
   14 |   Res M(Source<E::kB>, const typename B::S& event);
      |       ^
q.cc:13:7: note: previous declaration is here
   13 |   Res M(Source<E::KA>, const typename B::S& event);
      |       ^
1 error generated.

@alexfh
Copy link
Contributor

alexfh commented Apr 8, 2025

#134748 doesn't fix errors in the original code, unfortunately. Now we're getting out-of-line definition of 'X' does not match any declaration in 'Y'. It's hard to tell whether the original code is correct, but I suppose that the purpose of this change was not to make Clang stricter?

I'm re-running reduction with Clang after #134748

@slackito
Copy link
Collaborator

slackito commented Apr 8, 2025

I have a reduced test case for the mangling issue mentioned here. It's still a bit long (118 lines), so apologies for that.

hash_mangling.txt

Rename the attached file to hash_mangling.ii (github wouldn't let me attach it with a .ii extension), then build with clang++ -c hash_mangling.ii. With an affected version of clang (just checked at HEAD) the resulting object file contains the wrong mangled name:

$ llvm-readelf -s -W hash_mangling.o | grep hashable
    33: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT   UND _ZN4absl18container_internal13AbslHashValueINS_13hash_internal15MixingHashStateEEEN12_GLOBAL__N_19enable_ifIXsrNT_S6_11is_hashableIiEE5valueES6_E4typeES6_NS0_12raw_hash_setINS0_17FlatHashSetPolicyEiEE

(note the spurious S6_ mentioned in the comment above)

With a good version of clang, the output looks like this instead

    33: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT   UND _ZN4absl18container_internal13AbslHashValueINS_13hash_internal15MixingHashStateEEEN12_GLOBAL__N_19enable_ifIXsrNT_11is_hashableIiEE5valueES6_E4typeES6_NS0_12raw_hash_setINS0_17FlatHashSetPolicyEiEE

@mizvekov
Copy link
Contributor Author

mizvekov commented Apr 8, 2025

Ack, I see. I will try to reduce this example further.

It's not likely I will have a solution in less than 2 hours this time due to personal commitments, so feel free to revert if this is blocking you.

In any case I will pick this up tomorrow.

@alexfh
Copy link
Contributor

alexfh commented Apr 9, 2025

#134748 doesn't fix errors in the original code, unfortunately. Now we're getting out-of-line definition of 'X' does not match any declaration in 'Y'. It's hard to tell whether the original code is correct, but I suppose that the purpose of this change was not to make Clang stricter?

I'm re-running reduction with Clang after #134748

Reduced test case (https://gcc.godbolt.org/z/qezPYcW93):

$ cat q3.cc
template <typename T>
class SM;
enum class S { kA };
template <typename T>
struct N {
  using State = S;
  using P = SM<N>;
  struct E;
  template <typename P::template FromSources<State::kA> = 0>
  void F(const typename E::S& e);
};
template <typename T>
template <typename N<T>::P::template FromSources<N<T>::State::kA>>
inline void N<T>::F(const typename E::S& e) {}
$ ./clang-bad2 -fsyntax-only -std=c++20 q3.cc
q3.cc:14:19: error: out-of-line definition of 'F' does not match any declaration in 'N<T>'
   14 | inline void N<T>::F(const typename E::S& e) {}
      |                   ^
q3.cc:5:8: note: N defined here
    5 | struct N {
      |        ^
1 error generated.
$ ./clang-good -fsyntax-only -std=c++20 q3.cc

aelovikov-intel added a commit to intel/llvm that referenced this pull request Apr 9, 2025
mizvekov added a commit that referenced this pull request Apr 9, 2025
A NestedNameSpecifier of TypeSpec kind can be non-dependent even if its
prefix is dependent, when for example the prefix is an injected class type
but the type itself is a simple alias to a non-dependent type.

This issue was a bit hard to observe because if its an alias to
a class type, then we (for some unknown reason) ignored that the
NNS was dependent in the first place, which wouldn't happen
with an enum type.

This could have been a workaround for previous dependency bugs,
and is not relevant anymore for any of the test cases in the
tree, so this patch also removes that.

The other kinds of dependencies are still relevant.
If the prefix contains an unexpanded pack, then this NNS is still
unexpanded, and likewise for errors.

This fixes a regression reported here: #133610 (comment)
which was introduced by #133610

There are no release notes since the regression was never released.
mizvekov added a commit that referenced this pull request Apr 9, 2025
A NestedNameSpecifier of TypeSpec kind can be non-dependent even if its
prefix is dependent, when for example the prefix is an injected class type
but the type itself is a simple alias to a non-dependent type.

This issue was a bit hard to observe because if its an alias to
a class type, then we (for some unknown reason) ignored that the
NNS was dependent in the first place, which wouldn't happen
with an enum type.

This could have been a workaround for previous dependency bugs,
and is not relevant anymore for any of the test cases in the
tree, so this patch also removes that.

The other kinds of dependencies are still relevant.
If the prefix contains an unexpanded pack, then this NNS is still
unexpanded, and likewise for errors.

This fixes a regression reported here: #133610 (comment)
which was introduced by #133610

There are no release notes since the regression was never released.
@mizvekov
Copy link
Contributor Author

mizvekov commented Apr 9, 2025

@alexfh the last one will be fixed here: #135067

mizvekov added a commit that referenced this pull request Apr 9, 2025
A NestedNameSpecifier of TypeSpec kind can be non-dependent even if its
prefix is dependent, when for example the prefix is an injected class
type but the type itself is a simple alias to a non-dependent type.

This issue was a bit hard to observe because if it is an alias to a
class type, then we (for some unknown reason) ignored that the NNS was
dependent in the first place, which wouldn't happen with an enum type.

This could have been a workaround for previous dependency bugs, and is
not relevant anymore for any of the test cases in the tree, so this
patch also removes that.

The other kinds of dependencies are still relevant. If the prefix
contains an unexpanded pack, then this NNS is still unexpanded, and
likewise for errors.

This fixes a regression reported here:
#133610 (comment)
which was introduced by #133610

There are no release notes since the regression was never released.
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Apr 9, 2025
…35067)

A NestedNameSpecifier of TypeSpec kind can be non-dependent even if its
prefix is dependent, when for example the prefix is an injected class
type but the type itself is a simple alias to a non-dependent type.

This issue was a bit hard to observe because if it is an alias to a
class type, then we (for some unknown reason) ignored that the NNS was
dependent in the first place, which wouldn't happen with an enum type.

This could have been a workaround for previous dependency bugs, and is
not relevant anymore for any of the test cases in the tree, so this
patch also removes that.

The other kinds of dependencies are still relevant. If the prefix
contains an unexpanded pack, then this NNS is still unexpanded, and
likewise for errors.

This fixes a regression reported here:
llvm/llvm-project#133610 (comment)
which was introduced by llvm/llvm-project#133610

There are no release notes since the regression was never released.
mizvekov added a commit that referenced this pull request Apr 10, 2025
This fixes a regression introduced in
#133610 (comment)
which was reported here #133610 (comment)

When mangling a dependent template specialization appearing
within an unresolved prefix, translate the dtst back to
a dependent template name including the prefix, and mangle
following the nested unresolved-type production.

There are no release notes, since this regression was never released.
@mizvekov
Copy link
Contributor Author

@slackito this will be fixed here: #135111

mizvekov added a commit that referenced this pull request Apr 10, 2025
…35111)

This fixes a regression introduced in
#133610 which was reported here
#133610 (comment)

When mangling a dependent template specialization appearing within an
unresolved prefix, translate the dtst back to a dependent template name
including the prefix, and mangle following the nested unresolved-type
production.

There are no release notes, since this regression was never released.
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Apr 10, 2025
…angling (#135111)

This fixes a regression introduced in
llvm/llvm-project#133610 which was reported here
llvm/llvm-project#133610 (comment)

When mangling a dependent template specialization appearing within an
unresolved prefix, translate the dtst back to a dependent template name
including the prefix, and mangle following the nested unresolved-type
production.

There are no release notes, since this regression was never released.
@slackito
Copy link
Collaborator

@slackito this will be fixed here: #135111

Thanks for the quick fix!

AllinLeeYL pushed a commit to AllinLeeYL/llvm-project that referenced this pull request Apr 10, 2025
A NestedNameSpecifier of TypeSpec kind can be non-dependent even if its
prefix is dependent, when for example the prefix is an injected class
type but the type itself is a simple alias to a non-dependent type.

This issue was a bit hard to observe because if it is an alias to a
class type, then we (for some unknown reason) ignored that the NNS was
dependent in the first place, which wouldn't happen with an enum type.

This could have been a workaround for previous dependency bugs, and is
not relevant anymore for any of the test cases in the tree, so this
patch also removes that.

The other kinds of dependencies are still relevant. If the prefix
contains an unexpanded pack, then this NNS is still unexpanded, and
likewise for errors.

This fixes a regression reported here:
llvm#133610 (comment)
which was introduced by llvm#133610

There are no release notes since the regression was never released.
AllinLeeYL pushed a commit to AllinLeeYL/llvm-project that referenced this pull request Apr 10, 2025
…vm#135111)

This fixes a regression introduced in
llvm#133610 which was reported here
llvm#133610 (comment)

When mangling a dependent template specialization appearing within an
unresolved prefix, translate the dtst back to a dependent template name
including the prefix, and mangle following the nested unresolved-type
production.

There are no release notes, since this regression was never released.
var-const pushed a commit to ldionne/llvm-project that referenced this pull request Apr 17, 2025
A NestedNameSpecifier of TypeSpec kind can be non-dependent even if its
prefix is dependent, when for example the prefix is an injected class
type but the type itself is a simple alias to a non-dependent type.

This issue was a bit hard to observe because if it is an alias to a
class type, then we (for some unknown reason) ignored that the NNS was
dependent in the first place, which wouldn't happen with an enum type.

This could have been a workaround for previous dependency bugs, and is
not relevant anymore for any of the test cases in the tree, so this
patch also removes that.

The other kinds of dependencies are still relevant. If the prefix
contains an unexpanded pack, then this NNS is still unexpanded, and
likewise for errors.

This fixes a regression reported here:
llvm#133610 (comment)
which was introduced by llvm#133610

There are no release notes since the regression was never released.
var-const pushed a commit to ldionne/llvm-project that referenced this pull request Apr 17, 2025
…vm#135111)

This fixes a regression introduced in
llvm#133610 which was reported here
llvm#133610 (comment)

When mangling a dependent template specialization appearing within an
unresolved prefix, translate the dtst back to a dependent template name
including the prefix, and mangle following the nested unresolved-type
production.

There are no release notes, since this regression was never released.
mizvekov added a commit that referenced this pull request Apr 17, 2025
This fixes a regression introduced in #133610 which was reported here #133610 (comment)
and in #136119

This redoes previous attempt in #135111

When mangling a DTST which appears in the prefix,
the template name is not actually relevant, as its
prefix is part of the nested name anyway, and a
substitution is not allowed at that position in any case.

Fixes #136119
mizvekov added a commit that referenced this pull request Apr 17, 2025
…36201)

This fixes a regression introduced in #133610 which was reported here
#133610 (comment) and in #136119

This redoes previous attempt in #135111

When mangling a DTST which appears in the prefix,
the template name is not actually relevant, as its prefix is part of the
nested name anyway, and a
substitution is not allowed at that position in any case.

Fixes #136119
jsji pushed a commit to intel/llvm that referenced this pull request Apr 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:as-a-library libclang and C++ API clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:modules C++20 modules and Clang Header Modules clang Clang issues not falling into any other category clang-tidy clang-tools-extra clangd coroutines C++20 coroutines libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.