[clang] [clang-tools-extra] [llvm] [Clang] Add __type_list_dedup builtin to deduplicate types in template arguments (PR #106730)

Ilya Biryukov via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 13 06:19:24 PDT 2024


https://github.com/ilya-biryukov updated https://github.com/llvm/llvm-project/pull/106730

>From a68fcabba1b67b03bcea88678e509be122d1197d Mon Sep 17 00:00:00 2001
From: Ilya Biryukov <ibiryukov at google.com>
Date: Fri, 23 Aug 2024 17:27:26 +0200
Subject: [PATCH 01/11] [Clang] Add __type_list_dedup builtin to deduplicate
 types in template arguments

This allows to deduplicate the type lists efficiently in C++. It is
possible to achieve the same effect via template metaprogramming, but
performance of the resulting code is much lower than in the compiler.

We have observed that this code is quite common in our internal codebase
and this builtin allows to have significant savings (up to 25% of
compile time on targets that already take 3 minutes to compile).
The same builtin is also used widely enough in the codebase that we
expect a savings from a long tail of uses, too, although it is hard to
put an estimate on this number in advance.

The implementation aims to be as simple as possible and relies on the
exsisting machinery for builtin templates.
---
 .../clangd/unittests/FindTargetTests.cpp      |  6 ++++
 clang/include/clang/AST/ASTContext.h          | 11 ++++++
 clang/include/clang/AST/DeclID.h              |  3 ++
 clang/include/clang/Basic/Builtins.h          |  5 ++-
 clang/lib/AST/ASTContext.cpp                  |  8 +++++
 clang/lib/AST/ASTImporter.cpp                 |  3 ++
 clang/lib/AST/DeclTemplate.cpp                | 31 ++++++++++++++++
 clang/lib/Lex/PPMacroExpansion.cpp            |  1 +
 clang/lib/Sema/SemaLookup.cpp                 |  7 +++-
 clang/lib/Sema/SemaTemplate.cpp               | 36 +++++++++++++++++--
 clang/lib/Serialization/ASTReader.cpp         |  6 ++++
 clang/lib/Serialization/ASTWriter.cpp         |  3 ++
 .../test/Import/builtin-template/Inputs/S.cpp |  7 ++++
 clang/test/Import/builtin-template/test.cpp   | 10 +++++-
 clang/test/PCH/type_list_dedup.cpp            | 20 +++++++++++
 .../SemaTemplate/temp-param-list-dedup.cpp    | 33 +++++++++++++++++
 16 files changed, 185 insertions(+), 5 deletions(-)
 create mode 100644 clang/test/PCH/type_list_dedup.cpp
 create mode 100644 clang/test/SemaTemplate/temp-param-list-dedup.cpp

diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
index 3220a5a6a98250..d9f788cbf2a8a2 100644
--- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
+++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
@@ -731,6 +731,12 @@ TEST_F(TargetDeclTest, BuiltinTemplates) {
     using type_pack_element = [[__type_pack_element]]<N, Pack...>;
   )cpp";
   EXPECT_DECLS("TemplateSpecializationTypeLoc", );
+
+  Code = R"cpp(
+    template <template <class...> Templ, class... Types>
+    using type_list_dedup = [[__type_list_dedup]]<Templ, Types...>;
+  )cpp";
+  EXPECT_DECLS("TemplateSpecializationTypeLoc", );
 }
 
 TEST_F(TargetDeclTest, MemberOfTemplate) {
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 168bdca3c880b2..88d70e784cb71e 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -403,6 +403,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// The identifier '__type_pack_element'.
   mutable IdentifierInfo *TypePackElementName = nullptr;
 
+  /// The identifier '__type_list_dedup'.
+  mutable IdentifierInfo *TypeListDedupName = nullptr;
+
   QualType ObjCConstantStringType;
   mutable RecordDecl *CFConstantStringTagDecl = nullptr;
   mutable TypedefDecl *CFConstantStringTypeDecl = nullptr;
@@ -610,6 +613,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
   mutable ExternCContextDecl *ExternCContext = nullptr;
   mutable BuiltinTemplateDecl *MakeIntegerSeqDecl = nullptr;
   mutable BuiltinTemplateDecl *TypePackElementDecl = nullptr;
+  mutable BuiltinTemplateDecl *TypeListDedupDecl = nullptr;
 
   /// The associated SourceManager object.
   SourceManager &SourceMgr;
@@ -1117,6 +1121,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
   ExternCContextDecl *getExternCContextDecl() const;
   BuiltinTemplateDecl *getMakeIntegerSeqDecl() const;
   BuiltinTemplateDecl *getTypePackElementDecl() const;
+  BuiltinTemplateDecl *getTypeListDedupDecl() const;
 
   // Builtin Types.
   CanQualType VoidTy;
@@ -2008,6 +2013,12 @@ class ASTContext : public RefCountedBase<ASTContext> {
     return TypePackElementName;
   }
 
+  IdentifierInfo *getTypeListDedupName() const {
+    if (!TypeListDedupName)
+      TypeListDedupName = &Idents.get("__type_list_dedup");
+    return TypeListDedupName;
+  }
+
   /// Retrieve the Objective-C "instancetype" type, if already known;
   /// otherwise, returns a NULL type;
   QualType getObjCInstanceType() {
diff --git a/clang/include/clang/AST/DeclID.h b/clang/include/clang/AST/DeclID.h
index 81454a247229f5..41cecf1b8a9ebf 100644
--- a/clang/include/clang/AST/DeclID.h
+++ b/clang/include/clang/AST/DeclID.h
@@ -83,6 +83,9 @@ enum PredefinedDeclIDs {
   /// The internal '__type_pack_element' template.
   PREDEF_DECL_TYPE_PACK_ELEMENT_ID,
 
+  /// The internal '__type_list_dedup' template.
+  PREDEF_DECL_TYPE_LIST_DEDUP_ID,
+
   /// The number of declaration IDs that are predefined.
   NUM_PREDEF_DECL_IDS
 };
diff --git a/clang/include/clang/Basic/Builtins.h b/clang/include/clang/Basic/Builtins.h
index e85ec5b2dca14e..018996c0da527e 100644
--- a/clang/include/clang/Basic/Builtins.h
+++ b/clang/include/clang/Basic/Builtins.h
@@ -309,7 +309,10 @@ enum BuiltinTemplateKind : int {
   BTK__make_integer_seq,
 
   /// This names the __type_pack_element BuiltinTemplateDecl.
-  BTK__type_pack_element
+  BTK__type_pack_element,
+
+  /// This names the __type_list_dedup BuiltinTemplateDecl.
+  BTK__type_list_dedup,
 };
 
 } // end namespace clang
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 8ece39a3830468..3ae2b623f67aba 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -1170,6 +1170,14 @@ ASTContext::getTypePackElementDecl() const {
   return TypePackElementDecl;
 }
 
+BuiltinTemplateDecl *
+ASTContext::getTypeListDedupDecl() const {
+  if (!TypeListDedupDecl)
+    TypeListDedupDecl =
+        buildBuiltinTemplateDecl(BTK__type_list_dedup, getTypeListDedupName());
+  return TypeListDedupDecl;
+}
+
 RecordDecl *ASTContext::buildImplicitRecord(StringRef Name,
                                             RecordDecl::TagKind TK) const {
   SourceLocation Loc;
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index c2fb7dddcfc637..edf258f262e85d 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -5467,6 +5467,9 @@ ExpectedDecl ASTNodeImporter::VisitBuiltinTemplateDecl(BuiltinTemplateDecl *D) {
   case BuiltinTemplateKind::BTK__type_pack_element:
     ToD = Importer.getToContext().getTypePackElementDecl();
     break;
+  case BuiltinTemplateKind::BTK__type_list_dedup:
+    ToD = Importer.getToContext().getTypeListDedupDecl();
+    break;
   }
   assert(ToD && "BuiltinTemplateDecl of unsupported kind!");
   Importer.MapImported(D, ToD);
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 976b3a3e1ecedb..727f9a0751cf82 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1608,6 +1608,35 @@ createTypePackElementParameterList(const ASTContext &C, DeclContext *DC) {
                                        nullptr);
 }
 
+static TemplateParameterList *
+createTypeListDedupParameterList(const ASTContext &C, DeclContext *DC) {
+  // template <typename ...> typename Templ
+  auto *InnerTs = TemplateTypeParmDecl::Create(
+      C, DC, SourceLocation(), SourceLocation(), /*Depth=*/1, /*Position=*/0,
+      /*Id=*/nullptr, /*Typename=*/true, /*ParameterPack=*/true,
+      /*HasTypeConstraint=*/false);
+  InnerTs->setImplicit(true);
+  auto *TemplateParamList = TemplateParameterList::Create(
+      C, SourceLocation(), SourceLocation(), {InnerTs}, SourceLocation(),
+      /*RequiresClause=*/nullptr);
+  auto *Template = TemplateTemplateParmDecl::Create(
+      C, DC, SourceLocation(), /*Depth=*/0,
+      /*Position=*/0, /*ParameterPack=*/false, /*Id=*/nullptr,
+      /*Typename=*/true, TemplateParamList);
+  Template->setImplicit(true);
+
+  // typename ...Ts
+  auto *Ts = TemplateTypeParmDecl::Create(
+      C, DC, SourceLocation(), SourceLocation(), /*Depth=*/0, /*Position=*/1,
+      /*Id=*/nullptr, /*Typename=*/true, /*ParameterPack=*/true,
+      /*HasTypeConstraint=*/false);
+  Ts->setImplicit(true);
+  return TemplateParameterList::Create(
+      C, SourceLocation(), SourceLocation(),
+      llvm::ArrayRef<NamedDecl *>({Template, Ts}), SourceLocation(),
+      /*RequiresClause=*/nullptr);
+}
+
 static TemplateParameterList *createBuiltinTemplateParameterList(
     const ASTContext &C, DeclContext *DC, BuiltinTemplateKind BTK) {
   switch (BTK) {
@@ -1615,6 +1644,8 @@ static TemplateParameterList *createBuiltinTemplateParameterList(
     return createMakeIntegerSeqParameterList(C, DC);
   case BTK__type_pack_element:
     return createTypePackElementParameterList(C, DC);
+  case BTK__type_list_dedup:
+    return createTypeListDedupParameterList(C, DC);
   }
 
   llvm_unreachable("unhandled BuiltinTemplateKind!");
diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp
index 1d671ab72b0c03..f2d091ab29082e 100644
--- a/clang/lib/Lex/PPMacroExpansion.cpp
+++ b/clang/lib/Lex/PPMacroExpansion.cpp
@@ -1836,6 +1836,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
               // Report builtin templates as being builtins.
               .Case("__make_integer_seq", getLangOpts().CPlusPlus)
               .Case("__type_pack_element", getLangOpts().CPlusPlus)
+              .Case("__type_list_dedup", getLangOpts().CPlusPlus)
               // Likewise for some builtin preprocessor macros.
               // FIXME: This is inconsistent; we usually suggest detecting
               // builtin macros via #ifdef. Don't add more cases here.
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index d3d4bf27ae7283..f064495037a558 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -928,10 +928,15 @@ bool Sema::LookupBuiltin(LookupResult &R) {
         if (II == getASTContext().getMakeIntegerSeqName()) {
           R.addDecl(getASTContext().getMakeIntegerSeqDecl());
           return true;
-        } else if (II == getASTContext().getTypePackElementName()) {
+        }
+        if (II == getASTContext().getTypePackElementName()) {
           R.addDecl(getASTContext().getTypePackElementDecl());
           return true;
         }
+        if (II == getASTContext().getTypeListDedupName()) {
+          R.addDecl(getASTContext().getTypeListDedupDecl());
+          return true;
+        }
       }
 
       // Check if this is an OpenCL Builtin, and if so, insert its overloads.
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index e5ea02a919f4eb..aebaaa62bdc744 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -17,7 +17,9 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/TemplateBase.h"
 #include "clang/AST/TemplateName.h"
+#include "clang/AST/TypeOrdering.h"
 #include "clang/AST/TypeVisitor.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/DiagnosticSema.h"
@@ -38,9 +40,11 @@
 #include "clang/Sema/Template.h"
 #include "clang/Sema/TemplateDeduction.h"
 #include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/SmallBitVector.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/raw_ostream.h"
 
 #include <iterator>
 #include <optional>
@@ -3132,12 +3136,12 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD,
                                        TemplateLoc, SyntheticTemplateArgs);
   }
 
-  case BTK__type_pack_element:
+  case BTK__type_pack_element: {
     // Specializations of
     //    __type_pack_element<Index, T_1, ..., T_N>
     // are treated like T_Index.
     assert(Converted.size() == 2 &&
-      "__type_pack_element should be given an index and a parameter pack");
+           "__type_pack_element should be given an index and a parameter pack");
 
     TemplateArgument IndexArg = Converted[0], Ts = Converted[1];
     if (IndexArg.isDependent() || Ts.isDependent())
@@ -3158,6 +3162,34 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD,
     int64_t N = Index.getExtValue();
     return Ts.getPackAsArray()[N].getAsType();
   }
+  case BTK__type_list_dedup: {
+    assert(Converted.size() == 2 &&
+           "__type_list_dedup should be given a template and a parameter pack");
+    TemplateArgument Template = Converted[0];
+    TemplateArgument Ts = Converted[1];
+    if (Template.isDependent() || Ts.isDependent())
+      return Context.getCanonicalTemplateSpecializationType(TemplateName(BTD),
+                                                            Converted);
+
+    assert(Template.getKind() == clang::TemplateArgument::Template);
+    assert(Ts.getKind() == clang::TemplateArgument::Pack);
+    TemplateArgumentListInfo SyntheticTemplateArgs;
+    llvm::DenseSet<QualType> Seen;
+    // Synthesize a new template argument list, removing duplicates.
+    for (auto T : Ts.getPackAsArray()) {
+      assert(T.getKind() == clang::TemplateArgument::Type);
+      if (!Seen.insert(T.getAsType().getCanonicalType()).second)
+        continue;
+      SyntheticTemplateArgs.addArgument(TemplateArgumentLoc(
+          TemplateArgument(T),
+          SemaRef.Context.getTrivialTypeSourceInfo(
+              T.getAsType(),
+              /*FIXME: add location*/ SourceLocation())));
+    }
+    return SemaRef.CheckTemplateIdType(Template.getAsTemplate(), TemplateLoc,
+                                       SyntheticTemplateArgs);
+  }
+  }
   llvm_unreachable("unexpected BuiltinTemplateDecl!");
 }
 
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index e5a1e20a265616..78cb449d60c474 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -23,6 +23,7 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclFriend.h"
 #include "clang/AST/DeclGroup.h"
+#include "clang/AST/DeclID.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DeclarationName.h"
@@ -7897,6 +7898,11 @@ Decl *ASTReader::getPredefinedDecl(PredefinedDeclIDs ID) {
       return Context.TypePackElementDecl;
     NewLoaded = Context.getTypePackElementDecl();
     break;
+  case PREDEF_DECL_TYPE_LIST_DEDUP_ID:
+    if (Context.TypeListDedupDecl)
+      return Context.TypeListDedupDecl;
+    NewLoaded = Context.getTypeListDedupDecl();
+    break;
   case NUM_PREDEF_DECL_IDS:
     llvm_unreachable("Invalid decl ID");
     break;
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index c6289493fce1de..65a37f895a666b 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -22,6 +22,7 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclContextInternals.h"
 #include "clang/AST/DeclFriend.h"
+#include "clang/AST/DeclID.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DeclarationName.h"
@@ -5051,6 +5052,8 @@ void ASTWriter::PrepareWritingSpecialDecls(Sema &SemaRef) {
                      PREDEF_DECL_CF_CONSTANT_STRING_TAG_ID);
   RegisterPredefDecl(Context.TypePackElementDecl,
                      PREDEF_DECL_TYPE_PACK_ELEMENT_ID);
+  RegisterPredefDecl(Context.TypeListDedupDecl,
+                     PREDEF_DECL_TYPE_LIST_DEDUP_ID);
 
   const TranslationUnitDecl *TU = Context.getTranslationUnitDecl();
 
diff --git a/clang/test/Import/builtin-template/Inputs/S.cpp b/clang/test/Import/builtin-template/Inputs/S.cpp
index d5c9a9ae0309d3..6cde0732d3719f 100644
--- a/clang/test/Import/builtin-template/Inputs/S.cpp
+++ b/clang/test/Import/builtin-template/Inputs/S.cpp
@@ -14,3 +14,10 @@ using TypePackElement = __type_pack_element<i, T...>;
 
 template <int i>
 struct X;
+
+
+template <template <class...> class Templ, class...Types>
+using TypeListDedup = __type_list_dedup<Templ, Types...>;
+
+template <class ...Ts>
+struct TypeList {};
diff --git a/clang/test/Import/builtin-template/test.cpp b/clang/test/Import/builtin-template/test.cpp
index 590efad0c71dca..ca67457d73f2b6 100644
--- a/clang/test/Import/builtin-template/test.cpp
+++ b/clang/test/Import/builtin-template/test.cpp
@@ -1,9 +1,11 @@
 // RUN: clang-import-test -dump-ast -import %S/Inputs/S.cpp -expression %s -Xcc -DSEQ | FileCheck --check-prefix=CHECK-SEQ %s
 // RUN: clang-import-test -dump-ast -import %S/Inputs/S.cpp -expression %s -Xcc -DPACK | FileCheck --check-prefix=CHECK-PACK %s
-// RUN: clang-import-test -dump-ast -import %S/Inputs/S.cpp -expression %s -Xcc -DPACK -Xcc -DSEQ | FileCheck --check-prefixes=CHECK-SEQ,CHECK-PACK %s
+// RUN: clang-import-test -dump-ast -import %S/Inputs/S.cpp -expression %s -Xcc -DDEDUP | FileCheck --check-prefix=CHECK-DEDUP %s
+// RUN: clang-import-test -dump-ast -import %S/Inputs/S.cpp -expression %s -Xcc -DPACK -Xcc -DSEQ -Xcc -DDEDUP | FileCheck --check-prefixes=CHECK-SEQ,CHECK-PACK,CHECK-DEDUP %s
 
 // CHECK-SEQ:  BuiltinTemplateDecl {{.+}} <<invalid sloc>> <invalid sloc> implicit __make_integer_seq{{$}}
 // CHECK-PACK: BuiltinTemplateDecl {{.+}} <<invalid sloc>> <invalid sloc> implicit __type_pack_element{{$}}
+// CHECK-DEDUP: BuiltinTemplateDecl {{.+}} <<invalid sloc>> <invalid sloc> implicit __type_list_dedup{{$}}
 
 void expr() {
 #ifdef SEQ
@@ -20,4 +22,10 @@ void expr() {
   static_assert(__is_same(TypePackElement<0, X<0>, X<1>>, X<0>), "");
   static_assert(__is_same(TypePackElement<1, X<0>, X<1>>, X<1>), "");
 #endif
+
+#ifdef DEDUP
+  static_assert(__is_same(TypeListDedup<TypeList>, TypeList<>), "");
+  static_assert(__is_same(TypeListDedup<TypeList, int, double, int>, TypeList<int, double>), "");
+  static_assert(__is_same(TypeListDedup<TypeList, X<0>, X<1>, X<1>, X<2>, X<0>>, TypeList<X<0>, X<1>, X<2>>), "");
+#endif
 }
diff --git a/clang/test/PCH/type_list_dedup.cpp b/clang/test/PCH/type_list_dedup.cpp
new file mode 100644
index 00000000000000..a84dc340160edf
--- /dev/null
+++ b/clang/test/PCH/type_list_dedup.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -std=c++14 -x c++-header %s -emit-pch -o %t.pch
+// RUN: %clang_cc1 -std=c++14 -x c++ /dev/null -include-pch %t.pch
+
+// RUN: %clang_cc1 -std=c++14 -x c++-header %s -emit-pch -fpch-instantiate-templates -o %t.pch
+// RUN: %clang_cc1 -std=c++14 -x c++ /dev/null -include-pch %t.pch
+
+template <template <class...> class Templ, class...Types>
+using TypeListDedup = __type_list_dedup<Templ, Types...>;
+
+template <class ...Ts>
+struct TypeList {};
+
+template <int i>
+struct X {};
+
+void fn1() {
+  TypeList<int, double> l1 = TypeListDedup<TypeList, int, double, int>{};
+  TypeList<> l2 = TypeListDedup<TypeList>{};
+  TypeList<X<0>, X<1>> x1 = TypeListDedup<TypeList, X<0>, X<1>, X<0>, X<1>>{};
+}
diff --git a/clang/test/SemaTemplate/temp-param-list-dedup.cpp b/clang/test/SemaTemplate/temp-param-list-dedup.cpp
new file mode 100644
index 00000000000000..d3aa1b8d1ec45f
--- /dev/null
+++ b/clang/test/SemaTemplate/temp-param-list-dedup.cpp
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 %s -verify
+// expected-no-diagnostics
+
+template <typename...> struct TypeList;
+
+static_assert(__is_same(
+  __type_list_dedup<TypeList, int, int*, int, double, float>,
+  TypeList<int, int*, double, float>));
+
+template <template<typename ...> typename Templ, typename ...Types>
+struct Dependent {
+  using empty_list = __type_list_dedup<Templ>;
+  using same = __type_list_dedup<Templ, Types...>;
+  using twice = __type_list_dedup<Templ, Types..., Types...>;
+  using dep_only_types = __type_list_dedup<TypeList, Types...>;
+  using dep_only_template = __type_list_dedup<Templ, int, double, int>;
+}; 
+
+static_assert(__is_same(Dependent<TypeList>::empty_list, TypeList<>));
+static_assert(__is_same(Dependent<TypeList>::same, TypeList<>));
+static_assert(__is_same(Dependent<TypeList>::twice, TypeList<>));
+static_assert(__is_same(Dependent<TypeList>::dep_only_types, TypeList<>));
+static_assert(__is_same(Dependent<TypeList>::dep_only_template, TypeList<int, double>));
+
+static_assert(__is_same(Dependent<TypeList, int*, double*, int*>::empty_list, TypeList<>));
+static_assert(__is_same(Dependent<TypeList, int*, double*, int*>::same, TypeList<int*, double*>));
+static_assert(__is_same(Dependent<TypeList, int*, double*, int*>::twice, TypeList<int*, double*>));
+static_assert(__is_same(Dependent<TypeList, int*, double*, int*>::dep_only_types, TypeList<int*, double*>));
+static_assert(__is_same(Dependent<TypeList, int*, double*, int*>::dep_only_template, TypeList<int, double>));
+
+// FIXME: tests for using template type alias as a template
+// FIXME: tests for errors
+// FIXME: tests for locations

>From 53d24e00543c9fdf763b9b95ca9296368fc647e5 Mon Sep 17 00:00:00 2001
From: Ilya Biryukov <ibiryukov at google.com>
Date: Fri, 30 Aug 2024 15:59:58 +0200
Subject: [PATCH 02/11] fixup! [Clang] Add __type_list_dedup builtin to
 deduplicate types in template arguments

reformat the code
---
 clang/lib/AST/ASTContext.cpp          | 3 +--
 clang/lib/Sema/SemaTemplate.cpp       | 7 +++----
 clang/lib/Serialization/ASTWriter.cpp | 3 +--
 3 files changed, 5 insertions(+), 8 deletions(-)

diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 3ae2b623f67aba..cdd06fbaff42db 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -1170,8 +1170,7 @@ ASTContext::getTypePackElementDecl() const {
   return TypePackElementDecl;
 }
 
-BuiltinTemplateDecl *
-ASTContext::getTypeListDedupDecl() const {
+BuiltinTemplateDecl *ASTContext::getTypeListDedupDecl() const {
   if (!TypeListDedupDecl)
     TypeListDedupDecl =
         buildBuiltinTemplateDecl(BTK__type_list_dedup, getTypeListDedupName());
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index aebaaa62bdc744..b74feef1503143 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3181,10 +3181,9 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD,
       if (!Seen.insert(T.getAsType().getCanonicalType()).second)
         continue;
       SyntheticTemplateArgs.addArgument(TemplateArgumentLoc(
-          TemplateArgument(T),
-          SemaRef.Context.getTrivialTypeSourceInfo(
-              T.getAsType(),
-              /*FIXME: add location*/ SourceLocation())));
+          TemplateArgument(T), SemaRef.Context.getTrivialTypeSourceInfo(
+                                   T.getAsType(),
+                                   /*FIXME: add location*/ SourceLocation())));
     }
     return SemaRef.CheckTemplateIdType(Template.getAsTemplate(), TemplateLoc,
                                        SyntheticTemplateArgs);
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 65a37f895a666b..9a1c67e33894bd 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -5052,8 +5052,7 @@ void ASTWriter::PrepareWritingSpecialDecls(Sema &SemaRef) {
                      PREDEF_DECL_CF_CONSTANT_STRING_TAG_ID);
   RegisterPredefDecl(Context.TypePackElementDecl,
                      PREDEF_DECL_TYPE_PACK_ELEMENT_ID);
-  RegisterPredefDecl(Context.TypeListDedupDecl,
-                     PREDEF_DECL_TYPE_LIST_DEDUP_ID);
+  RegisterPredefDecl(Context.TypeListDedupDecl, PREDEF_DECL_TYPE_LIST_DEDUP_ID);
 
   const TranslationUnitDecl *TU = Context.getTranslationUnitDecl();
 

>From 06261d3ab8c03ef2cc7a762b5c8def725fa85ab5 Mon Sep 17 00:00:00 2001
From: Ilya Biryukov <ibiryukov at google.com>
Date: Wed, 4 Sep 2024 15:14:13 +0200
Subject: [PATCH 03/11] fixup! [Clang] Add __type_list_dedup builtin to
 deduplicate types in template arguments

Add more tests.
---
 .../SemaTemplate/temp-param-list-dedup.cpp    | 32 ++++++++++++++++---
 1 file changed, 28 insertions(+), 4 deletions(-)

diff --git a/clang/test/SemaTemplate/temp-param-list-dedup.cpp b/clang/test/SemaTemplate/temp-param-list-dedup.cpp
index d3aa1b8d1ec45f..7a9f6c79419445 100644
--- a/clang/test/SemaTemplate/temp-param-list-dedup.cpp
+++ b/clang/test/SemaTemplate/temp-param-list-dedup.cpp
@@ -1,5 +1,4 @@
 // RUN: %clang_cc1 %s -verify
-// expected-no-diagnostics
 
 template <typename...> struct TypeList;
 
@@ -28,6 +27,31 @@ static_assert(__is_same(Dependent<TypeList, int*, double*, int*>::twice, TypeLis
 static_assert(__is_same(Dependent<TypeList, int*, double*, int*>::dep_only_types, TypeList<int*, double*>));
 static_assert(__is_same(Dependent<TypeList, int*, double*, int*>::dep_only_template, TypeList<int, double>));
 
-// FIXME: tests for using template type alias as a template
-// FIXME: tests for errors
-// FIXME: tests for locations
+
+template <class ...T>
+using Twice = TypeList<T..., T...>;
+
+static_assert(__is_same(__type_list_dedup<Twice, int, double, int>, TypeList<int, double, int, double>));
+
+
+template <int...> struct IntList;
+// Wrong kinds of template arguments.
+__type_list_dedup<IntList>* wrong_template; // expected-error {{template template argument has different template parameters than its corresponding template template parameter}}
+                                            // expected-note at -3 {{template parameter has a different kind in template argument}}
+__type_list_dedup<TypeList, 1, 2, 3>* wrong_template_args; // expected-error  {{template argument for template type parameter must be a type}}
+                                                           // expected-note@* {{previous template template parameter is here}}
+                                                           // expected-note@* {{template parameter from hidden source}}
+__type_list_dedup<> not_enough_args; // expected-error {{too few template arguments for template '__type_list_dedup'}}
+                                     // expected-note@* {{template declaration from hidden source}}
+__type_list_dedup missing_template_args; // expected-error {{use of template '__type_list_dedup' requires template arguments}}
+                                         // expected-note@* {{template declaration from hidden source}}
+
+// Direct recursive use will fail because the signature of template parameters does not match.
+// The intention for this test is to anticipate a failure mode where compiler may start running the deduplication recursively, going into
+// an infinite loop without diagnosing an error.
+// Currently, the type checking prevents us from it, but if the builtin becomes more generic, we need to be aware of it.
+__type_list_dedup<__type_list_dedup, int, int, double>; // expected-error {{template template argument has different template parameters than its corresponding template template parameter}}
+                                                        // expected-note@* {{template parameter has a different kind in template argument}}
+                                                        // expected-note@* {{previous template template parameter is here}}
+
+// FIXME: tests for locations of template arguments, ideally we should point into the original locations of the template arguments.

>From cfa8d4f0279d68ef33592504c63fb7aaeb80ceb3 Mon Sep 17 00:00:00 2001
From: Ilya Biryukov <ibiryukov at google.com>
Date: Wed, 4 Sep 2024 18:29:55 +0200
Subject: [PATCH 04/11] fixup! [Clang] Add __type_list_dedup builtin to
 deduplicate types in template arguments

Fix the clangd tests
---
 clang-tools-extra/clangd/unittests/FindTargetTests.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
index d9f788cbf2a8a2..391f627654d9c2 100644
--- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
+++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
@@ -733,7 +733,7 @@ TEST_F(TargetDeclTest, BuiltinTemplates) {
   EXPECT_DECLS("TemplateSpecializationTypeLoc", );
 
   Code = R"cpp(
-    template <template <class...> Templ, class... Types>
+    template <template <class...> class Templ, class... Types>
     using type_list_dedup = [[__type_list_dedup]]<Templ, Types...>;
   )cpp";
   EXPECT_DECLS("TemplateSpecializationTypeLoc", );

>From 1ad5858abbb28b0f139c5b13b04acc62364b9e3c Mon Sep 17 00:00:00 2001
From: Ilya Biryukov <ibiryukov at google.com>
Date: Tue, 10 Sep 2024 11:00:34 +0200
Subject: [PATCH 05/11] fixup! [Clang] Add __type_list_dedup builtin to
 deduplicate types in template arguments

Remove accidental include
---
 clang/lib/Sema/SemaTemplate.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index b74feef1503143..87a8f3300178c6 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -44,7 +44,6 @@
 #include "llvm/ADT/SmallBitVector.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/raw_ostream.h"
 
 #include <iterator>
 #include <optional>

>From 52482d31ef06da32620e89aeb233e03cebb25a44 Mon Sep 17 00:00:00 2001
From: Ilya Biryukov <ibiryukov at google.com>
Date: Fri, 13 Sep 2024 14:46:15 +0200
Subject: [PATCH 06/11] fixup! [Clang] Add __type_list_dedup builtin to
 deduplicate types in template arguments

Rename type_list_dedup to __builtin_type_pack_dedup
---
 .../clangd/unittests/FindTargetTests.cpp      |  2 +-
 clang/include/clang/AST/ASTContext.h          | 16 ++++++-------
 clang/include/clang/AST/DeclID.h              |  4 ++--
 clang/include/clang/Basic/Builtins.h          |  4 ++--
 clang/lib/AST/ASTContext.cpp                  | 10 ++++----
 clang/lib/AST/ASTImporter.cpp                 |  4 ++--
 clang/lib/AST/DeclTemplate.cpp                |  6 ++---
 clang/lib/Lex/PPMacroExpansion.cpp            |  2 +-
 clang/lib/Sema/SemaLookup.cpp                 |  4 ++--
 clang/lib/Sema/SemaTemplate.cpp               |  4 ++--
 clang/lib/Serialization/ASTReader.cpp         |  8 +++----
 clang/lib/Serialization/ASTWriter.cpp         |  2 +-
 .../test/Import/builtin-template/Inputs/S.cpp |  2 +-
 clang/test/Import/builtin-template/test.cpp   |  8 +++----
 ...ype_list_dedup.cpp => type_pack_dedup.cpp} |  8 +++----
 .../SemaTemplate/temp-param-list-dedup.cpp    | 24 +++++++++----------
 16 files changed, 54 insertions(+), 54 deletions(-)
 rename clang/test/PCH/{type_list_dedup.cpp => type_pack_dedup.cpp} (66%)

diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
index 391f627654d9c2..955ac69851f640 100644
--- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
+++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
@@ -734,7 +734,7 @@ TEST_F(TargetDeclTest, BuiltinTemplates) {
 
   Code = R"cpp(
     template <template <class...> class Templ, class... Types>
-    using type_list_dedup = [[__type_list_dedup]]<Templ, Types...>;
+    using __builtin_type_pack_dedup = [[__builtin_type_pack_dedup]]<Templ, Types...>;
   )cpp";
   EXPECT_DECLS("TemplateSpecializationTypeLoc", );
 }
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 88d70e784cb71e..a526b4a0f2e193 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -403,8 +403,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// The identifier '__type_pack_element'.
   mutable IdentifierInfo *TypePackElementName = nullptr;
 
-  /// The identifier '__type_list_dedup'.
-  mutable IdentifierInfo *TypeListDedupName = nullptr;
+  /// The identifier '__builtin_type_pack_dedup'.
+  mutable IdentifierInfo *TypePackDedupName = nullptr;
 
   QualType ObjCConstantStringType;
   mutable RecordDecl *CFConstantStringTagDecl = nullptr;
@@ -613,7 +613,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
   mutable ExternCContextDecl *ExternCContext = nullptr;
   mutable BuiltinTemplateDecl *MakeIntegerSeqDecl = nullptr;
   mutable BuiltinTemplateDecl *TypePackElementDecl = nullptr;
-  mutable BuiltinTemplateDecl *TypeListDedupDecl = nullptr;
+  mutable BuiltinTemplateDecl *TypePackDedupDecl = nullptr;
 
   /// The associated SourceManager object.
   SourceManager &SourceMgr;
@@ -1121,7 +1121,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
   ExternCContextDecl *getExternCContextDecl() const;
   BuiltinTemplateDecl *getMakeIntegerSeqDecl() const;
   BuiltinTemplateDecl *getTypePackElementDecl() const;
-  BuiltinTemplateDecl *getTypeListDedupDecl() const;
+  BuiltinTemplateDecl *getTypePackDedupDecl() const;
 
   // Builtin Types.
   CanQualType VoidTy;
@@ -2013,10 +2013,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
     return TypePackElementName;
   }
 
-  IdentifierInfo *getTypeListDedupName() const {
-    if (!TypeListDedupName)
-      TypeListDedupName = &Idents.get("__type_list_dedup");
-    return TypeListDedupName;
+  IdentifierInfo *getTypePackDedupName() const {
+    if (!TypePackDedupName)
+      TypePackDedupName = &Idents.get("__builtin_type_pack_dedup");
+    return TypePackDedupName;
   }
 
   /// Retrieve the Objective-C "instancetype" type, if already known;
diff --git a/clang/include/clang/AST/DeclID.h b/clang/include/clang/AST/DeclID.h
index 41cecf1b8a9ebf..a1f5d5aa36ad0f 100644
--- a/clang/include/clang/AST/DeclID.h
+++ b/clang/include/clang/AST/DeclID.h
@@ -83,8 +83,8 @@ enum PredefinedDeclIDs {
   /// The internal '__type_pack_element' template.
   PREDEF_DECL_TYPE_PACK_ELEMENT_ID,
 
-  /// The internal '__type_list_dedup' template.
-  PREDEF_DECL_TYPE_LIST_DEDUP_ID,
+  /// The internal '__builtin_type_pack_dedup' template.
+  PREDEF_DECL___builtin_type_pack_dedup_ID,
 
   /// The number of declaration IDs that are predefined.
   NUM_PREDEF_DECL_IDS
diff --git a/clang/include/clang/Basic/Builtins.h b/clang/include/clang/Basic/Builtins.h
index 018996c0da527e..7a326a26d598e8 100644
--- a/clang/include/clang/Basic/Builtins.h
+++ b/clang/include/clang/Basic/Builtins.h
@@ -311,8 +311,8 @@ enum BuiltinTemplateKind : int {
   /// This names the __type_pack_element BuiltinTemplateDecl.
   BTK__type_pack_element,
 
-  /// This names the __type_list_dedup BuiltinTemplateDecl.
-  BTK__type_list_dedup,
+  /// This names the __builtin_type_pack_dedup BuiltinTemplateDecl.
+  BTK__type_pack_dedup,
 };
 
 } // end namespace clang
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index cdd06fbaff42db..f257ea41b71bc5 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -1170,11 +1170,11 @@ ASTContext::getTypePackElementDecl() const {
   return TypePackElementDecl;
 }
 
-BuiltinTemplateDecl *ASTContext::getTypeListDedupDecl() const {
-  if (!TypeListDedupDecl)
-    TypeListDedupDecl =
-        buildBuiltinTemplateDecl(BTK__type_list_dedup, getTypeListDedupName());
-  return TypeListDedupDecl;
+BuiltinTemplateDecl *ASTContext::getTypePackDedupDecl() const {
+  if (!TypePackDedupDecl)
+    TypePackDedupDecl =
+        buildBuiltinTemplateDecl(BTK__type_pack_dedup, getTypePackDedupName());
+  return TypePackDedupDecl;
 }
 
 RecordDecl *ASTContext::buildImplicitRecord(StringRef Name,
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index edf258f262e85d..dd920820c10d5c 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -5467,8 +5467,8 @@ ExpectedDecl ASTNodeImporter::VisitBuiltinTemplateDecl(BuiltinTemplateDecl *D) {
   case BuiltinTemplateKind::BTK__type_pack_element:
     ToD = Importer.getToContext().getTypePackElementDecl();
     break;
-  case BuiltinTemplateKind::BTK__type_list_dedup:
-    ToD = Importer.getToContext().getTypeListDedupDecl();
+  case BuiltinTemplateKind::BTK__type_pack_dedup:
+    ToD = Importer.getToContext().getTypePackDedupDecl();
     break;
   }
   assert(ToD && "BuiltinTemplateDecl of unsupported kind!");
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 727f9a0751cf82..42e6b397ce3a8f 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1609,7 +1609,7 @@ createTypePackElementParameterList(const ASTContext &C, DeclContext *DC) {
 }
 
 static TemplateParameterList *
-createTypeListDedupParameterList(const ASTContext &C, DeclContext *DC) {
+createTypePackDedupParameterList(const ASTContext &C, DeclContext *DC) {
   // template <typename ...> typename Templ
   auto *InnerTs = TemplateTypeParmDecl::Create(
       C, DC, SourceLocation(), SourceLocation(), /*Depth=*/1, /*Position=*/0,
@@ -1644,8 +1644,8 @@ static TemplateParameterList *createBuiltinTemplateParameterList(
     return createMakeIntegerSeqParameterList(C, DC);
   case BTK__type_pack_element:
     return createTypePackElementParameterList(C, DC);
-  case BTK__type_list_dedup:
-    return createTypeListDedupParameterList(C, DC);
+  case BTK__type_pack_dedup:
+    return createTypePackDedupParameterList(C, DC);
   }
 
   llvm_unreachable("unhandled BuiltinTemplateKind!");
diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp
index f2d091ab29082e..6aeebdc3de4707 100644
--- a/clang/lib/Lex/PPMacroExpansion.cpp
+++ b/clang/lib/Lex/PPMacroExpansion.cpp
@@ -1836,7 +1836,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
               // Report builtin templates as being builtins.
               .Case("__make_integer_seq", getLangOpts().CPlusPlus)
               .Case("__type_pack_element", getLangOpts().CPlusPlus)
-              .Case("__type_list_dedup", getLangOpts().CPlusPlus)
+              .Case("__builtin_type_pack_dedup", getLangOpts().CPlusPlus)
               // Likewise for some builtin preprocessor macros.
               // FIXME: This is inconsistent; we usually suggest detecting
               // builtin macros via #ifdef. Don't add more cases here.
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index f064495037a558..d4e97e8ded01eb 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -933,8 +933,8 @@ bool Sema::LookupBuiltin(LookupResult &R) {
           R.addDecl(getASTContext().getTypePackElementDecl());
           return true;
         }
-        if (II == getASTContext().getTypeListDedupName()) {
-          R.addDecl(getASTContext().getTypeListDedupDecl());
+        if (II == getASTContext().getTypePackDedupName()) {
+          R.addDecl(getASTContext().getTypePackDedupDecl());
           return true;
         }
       }
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 87a8f3300178c6..bb48c3f6799565 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3161,9 +3161,9 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD,
     int64_t N = Index.getExtValue();
     return Ts.getPackAsArray()[N].getAsType();
   }
-  case BTK__type_list_dedup: {
+  case BTK__type_pack_dedup: {
     assert(Converted.size() == 2 &&
-           "__type_list_dedup should be given a template and a parameter pack");
+           "__builtin_type_pack_dedup should be given a template and a parameter pack");
     TemplateArgument Template = Converted[0];
     TemplateArgument Ts = Converted[1];
     if (Template.isDependent() || Ts.isDependent())
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 78cb449d60c474..a0bc6917839bcc 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -7898,10 +7898,10 @@ Decl *ASTReader::getPredefinedDecl(PredefinedDeclIDs ID) {
       return Context.TypePackElementDecl;
     NewLoaded = Context.getTypePackElementDecl();
     break;
-  case PREDEF_DECL_TYPE_LIST_DEDUP_ID:
-    if (Context.TypeListDedupDecl)
-      return Context.TypeListDedupDecl;
-    NewLoaded = Context.getTypeListDedupDecl();
+  case PREDEF_DECL___builtin_type_pack_dedup_ID:
+    if (Context.TypePackDedupDecl)
+      return Context.TypePackDedupDecl;
+    NewLoaded = Context.getTypePackDedupDecl();
     break;
   case NUM_PREDEF_DECL_IDS:
     llvm_unreachable("Invalid decl ID");
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 9a1c67e33894bd..515af4efbdd70a 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -5052,7 +5052,7 @@ void ASTWriter::PrepareWritingSpecialDecls(Sema &SemaRef) {
                      PREDEF_DECL_CF_CONSTANT_STRING_TAG_ID);
   RegisterPredefDecl(Context.TypePackElementDecl,
                      PREDEF_DECL_TYPE_PACK_ELEMENT_ID);
-  RegisterPredefDecl(Context.TypeListDedupDecl, PREDEF_DECL_TYPE_LIST_DEDUP_ID);
+  RegisterPredefDecl(Context.TypePackDedupDecl, PREDEF_DECL___builtin_type_pack_dedup_ID);
 
   const TranslationUnitDecl *TU = Context.getTranslationUnitDecl();
 
diff --git a/clang/test/Import/builtin-template/Inputs/S.cpp b/clang/test/Import/builtin-template/Inputs/S.cpp
index 6cde0732d3719f..6f0aa5165187dc 100644
--- a/clang/test/Import/builtin-template/Inputs/S.cpp
+++ b/clang/test/Import/builtin-template/Inputs/S.cpp
@@ -17,7 +17,7 @@ struct X;
 
 
 template <template <class...> class Templ, class...Types>
-using TypeListDedup = __type_list_dedup<Templ, Types...>;
+using TypePackDedup = __builtin_type_pack_dedup<Templ, Types...>;
 
 template <class ...Ts>
 struct TypeList {};
diff --git a/clang/test/Import/builtin-template/test.cpp b/clang/test/Import/builtin-template/test.cpp
index ca67457d73f2b6..e03716d926553b 100644
--- a/clang/test/Import/builtin-template/test.cpp
+++ b/clang/test/Import/builtin-template/test.cpp
@@ -5,7 +5,7 @@
 
 // CHECK-SEQ:  BuiltinTemplateDecl {{.+}} <<invalid sloc>> <invalid sloc> implicit __make_integer_seq{{$}}
 // CHECK-PACK: BuiltinTemplateDecl {{.+}} <<invalid sloc>> <invalid sloc> implicit __type_pack_element{{$}}
-// CHECK-DEDUP: BuiltinTemplateDecl {{.+}} <<invalid sloc>> <invalid sloc> implicit __type_list_dedup{{$}}
+// CHECK-DEDUP: BuiltinTemplateDecl {{.+}} <<invalid sloc>> <invalid sloc> implicit __builtin_type_pack_dedup{{$}}
 
 void expr() {
 #ifdef SEQ
@@ -24,8 +24,8 @@ void expr() {
 #endif
 
 #ifdef DEDUP
-  static_assert(__is_same(TypeListDedup<TypeList>, TypeList<>), "");
-  static_assert(__is_same(TypeListDedup<TypeList, int, double, int>, TypeList<int, double>), "");
-  static_assert(__is_same(TypeListDedup<TypeList, X<0>, X<1>, X<1>, X<2>, X<0>>, TypeList<X<0>, X<1>, X<2>>), "");
+  static_assert(__is_same(TypePackDedup<TypeList>, TypeList<>), "");
+  static_assert(__is_same(TypePackDedup<TypeList, int, double, int>, TypeList<int, double>), "");
+  static_assert(__is_same(TypePackDedup<TypeList, X<0>, X<1>, X<1>, X<2>, X<0>>, TypeList<X<0>, X<1>, X<2>>), "");
 #endif
 }
diff --git a/clang/test/PCH/type_list_dedup.cpp b/clang/test/PCH/type_pack_dedup.cpp
similarity index 66%
rename from clang/test/PCH/type_list_dedup.cpp
rename to clang/test/PCH/type_pack_dedup.cpp
index a84dc340160edf..a1544aad79039a 100644
--- a/clang/test/PCH/type_list_dedup.cpp
+++ b/clang/test/PCH/type_pack_dedup.cpp
@@ -5,7 +5,7 @@
 // RUN: %clang_cc1 -std=c++14 -x c++ /dev/null -include-pch %t.pch
 
 template <template <class...> class Templ, class...Types>
-using TypeListDedup = __type_list_dedup<Templ, Types...>;
+using TypePackDedup = __builtin_type_pack_dedup<Templ, Types...>;
 
 template <class ...Ts>
 struct TypeList {};
@@ -14,7 +14,7 @@ template <int i>
 struct X {};
 
 void fn1() {
-  TypeList<int, double> l1 = TypeListDedup<TypeList, int, double, int>{};
-  TypeList<> l2 = TypeListDedup<TypeList>{};
-  TypeList<X<0>, X<1>> x1 = TypeListDedup<TypeList, X<0>, X<1>, X<0>, X<1>>{};
+  TypeList<int, double> l1 = TypePackDedup<TypeList, int, double, int>{};
+  TypeList<> l2 = TypePackDedup<TypeList>{};
+  TypeList<X<0>, X<1>> x1 = TypePackDedup<TypeList, X<0>, X<1>, X<0>, X<1>>{};
 }
diff --git a/clang/test/SemaTemplate/temp-param-list-dedup.cpp b/clang/test/SemaTemplate/temp-param-list-dedup.cpp
index 7a9f6c79419445..b1b01cc150c7e6 100644
--- a/clang/test/SemaTemplate/temp-param-list-dedup.cpp
+++ b/clang/test/SemaTemplate/temp-param-list-dedup.cpp
@@ -3,16 +3,16 @@
 template <typename...> struct TypeList;
 
 static_assert(__is_same(
-  __type_list_dedup<TypeList, int, int*, int, double, float>,
+  __builtin_type_pack_dedup<TypeList, int, int*, int, double, float>,
   TypeList<int, int*, double, float>));
 
 template <template<typename ...> typename Templ, typename ...Types>
 struct Dependent {
-  using empty_list = __type_list_dedup<Templ>;
-  using same = __type_list_dedup<Templ, Types...>;
-  using twice = __type_list_dedup<Templ, Types..., Types...>;
-  using dep_only_types = __type_list_dedup<TypeList, Types...>;
-  using dep_only_template = __type_list_dedup<Templ, int, double, int>;
+  using empty_list = __builtin_type_pack_dedup<Templ>;
+  using same = __builtin_type_pack_dedup<Templ, Types...>;
+  using twice = __builtin_type_pack_dedup<Templ, Types..., Types...>;
+  using dep_only_types = __builtin_type_pack_dedup<TypeList, Types...>;
+  using dep_only_template = __builtin_type_pack_dedup<Templ, int, double, int>;
 }; 
 
 static_assert(__is_same(Dependent<TypeList>::empty_list, TypeList<>));
@@ -31,26 +31,26 @@ static_assert(__is_same(Dependent<TypeList, int*, double*, int*>::dep_only_templ
 template <class ...T>
 using Twice = TypeList<T..., T...>;
 
-static_assert(__is_same(__type_list_dedup<Twice, int, double, int>, TypeList<int, double, int, double>));
+static_assert(__is_same(__builtin_type_pack_dedup<Twice, int, double, int>, TypeList<int, double, int, double>));
 
 
 template <int...> struct IntList;
 // Wrong kinds of template arguments.
-__type_list_dedup<IntList>* wrong_template; // expected-error {{template template argument has different template parameters than its corresponding template template parameter}}
+__builtin_type_pack_dedup<IntList>* wrong_template; // expected-error {{template template argument has different template parameters than its corresponding template template parameter}}
                                             // expected-note at -3 {{template parameter has a different kind in template argument}}
-__type_list_dedup<TypeList, 1, 2, 3>* wrong_template_args; // expected-error  {{template argument for template type parameter must be a type}}
+__builtin_type_pack_dedup<TypeList, 1, 2, 3>* wrong_template_args; // expected-error  {{template argument for template type parameter must be a type}}
                                                            // expected-note@* {{previous template template parameter is here}}
                                                            // expected-note@* {{template parameter from hidden source}}
-__type_list_dedup<> not_enough_args; // expected-error {{too few template arguments for template '__type_list_dedup'}}
+__builtin_type_pack_dedup<> not_enough_args; // expected-error {{too few template arguments for template '__builtin_type_pack_dedup'}}
                                      // expected-note@* {{template declaration from hidden source}}
-__type_list_dedup missing_template_args; // expected-error {{use of template '__type_list_dedup' requires template arguments}}
+__builtin_type_pack_dedup missing_template_args; // expected-error {{use of template '__builtin_type_pack_dedup' requires template arguments}}
                                          // expected-note@* {{template declaration from hidden source}}
 
 // Direct recursive use will fail because the signature of template parameters does not match.
 // The intention for this test is to anticipate a failure mode where compiler may start running the deduplication recursively, going into
 // an infinite loop without diagnosing an error.
 // Currently, the type checking prevents us from it, but if the builtin becomes more generic, we need to be aware of it.
-__type_list_dedup<__type_list_dedup, int, int, double>; // expected-error {{template template argument has different template parameters than its corresponding template template parameter}}
+__builtin_type_pack_dedup<__builtin_type_pack_dedup, int, int, double>; // expected-error {{template template argument has different template parameters than its corresponding template template parameter}}
                                                         // expected-note@* {{template parameter has a different kind in template argument}}
                                                         // expected-note@* {{previous template template parameter is here}}
 

>From 49fcad874052c2d415319e66577642e785d872a7 Mon Sep 17 00:00:00 2001
From: Ilya Biryukov <ibiryukov at google.com>
Date: Fri, 13 Sep 2024 14:53:42 +0200
Subject: [PATCH 07/11] fixup! [Clang] Add __type_list_dedup builtin to
 deduplicate types in template arguments

Added a test for a canonical and qualified types and their potentially
non-canonical counterparts.
---
 ...ch-Do-not-deserialize-all-lazy-templ.patch | 2459 +++++++++++++++++
 clang/test/PCH/type_pack_dedup.cpp            |   23 +
 2 files changed, 2482 insertions(+)
 create mode 100644 0001-D41416-modules-pch-Do-not-deserialize-all-lazy-templ.patch

diff --git a/0001-D41416-modules-pch-Do-not-deserialize-all-lazy-templ.patch b/0001-D41416-modules-pch-Do-not-deserialize-all-lazy-templ.patch
new file mode 100644
index 00000000000000..a8bff964167e44
--- /dev/null
+++ b/0001-D41416-modules-pch-Do-not-deserialize-all-lazy-templ.patch
@@ -0,0 +1,2459 @@
+From 13a1b0f11e48af879ba042ca3dcfd99a6a6d864d Mon Sep 17 00:00:00 2001
+From: Vassil Vassilev <v.g.vassilev at gmail.com>
+Date: Sun, 7 Jan 2018 15:16:11 +0200
+Subject: [PATCH] D41416: [modules] [pch] Do not deserialize all lazy template
+ specializations when looking for one.
+
+---
+ clang/include/clang/AST/DeclTemplate.h        |  23 +-
+ clang/include/clang/AST/ExternalASTSource.h   |  15 +
+ .../clang/Sema/MultiplexExternalSemaSource.h  |   6 +
+ .../include/clang/Serialization/ASTBitCodes.h |   6 +
+ clang/include/clang/Serialization/ASTReader.h |  34 +-
+ clang/include/clang/Serialization/ASTWriter.h |  15 +
+ clang/lib/AST/DeclTemplate.cpp                | 108 +++--
+ clang/lib/AST/ExternalASTSource.cpp           |   9 +
+ clang/lib/AST/ODRHash.cpp                     |  23 +-
+ .../lib/Sema/MultiplexExternalSemaSource.cpp  |  17 +
+ clang/lib/Serialization/ASTCommon.h           |   1 -
+ clang/lib/Serialization/ASTReader.cpp         | 189 +++++++-
+ clang/lib/Serialization/ASTReaderDecl.cpp     | 105 ++---
+ clang/lib/Serialization/ASTReaderInternals.h  | 123 ++++++
+ clang/lib/Serialization/ASTWriter.cpp         | 182 +++++++-
+ clang/lib/Serialization/ASTWriterDecl.cpp     |  73 ++--
+ clang/lib/Serialization/CMakeLists.txt        |   1 +
+ .../lib/Serialization/MultiOnDiskHashTable.h  |   4 +-
+ .../Serialization/TemplateArgumentHasher.cpp  | 405 ++++++++++++++++++
+ .../Serialization/TemplateArgumentHasher.h    |  34 ++
+ clang/test/Modules/odr_hash.cpp               |   4 +-
+ .../Modules/recursive-instantiations.cppm     |  40 ++
+ .../test/OpenMP/target_parallel_ast_print.cpp |   4 -
+ clang/test/OpenMP/target_teams_ast_print.cpp  |   4 -
+ clang/test/OpenMP/task_ast_print.cpp          |   4 -
+ clang/test/OpenMP/teams_ast_print.cpp         |   4 -
+ clang/unittests/Serialization/CMakeLists.txt  |   1 +
+ .../Serialization/LoadSpecLazilyTest.cpp      | 260 +++++++++++
+ 28 files changed, 1515 insertions(+), 179 deletions(-)
+ create mode 100644 clang/lib/Serialization/TemplateArgumentHasher.cpp
+ create mode 100644 clang/lib/Serialization/TemplateArgumentHasher.h
+ create mode 100644 clang/test/Modules/recursive-instantiations.cppm
+ create mode 100644 clang/unittests/Serialization/LoadSpecLazilyTest.cpp
+
+diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
+index 687715a22e9f..97e42299ab89 100644
+--- a/clang/include/clang/AST/DeclTemplate.h
++++ b/clang/include/clang/AST/DeclTemplate.h
+@@ -729,6 +729,7 @@ class RedeclarableTemplateDecl : public TemplateDecl,
+   }
+ 
+   void anchor() override;
++
+ protected:
+   template <typename EntryType> struct SpecEntryTraits {
+     using DeclType = EntryType;
+@@ -769,13 +770,22 @@ protected:
+     return SpecIterator<EntryType>(isEnd ? Specs.end() : Specs.begin());
+   }
+ 
+-  void loadLazySpecializationsImpl() const;
++  void loadLazySpecializationsImpl(bool OnlyPartial = false) const;
++
++  bool loadLazySpecializationsImpl(llvm::ArrayRef<TemplateArgument> Args,
++                                   TemplateParameterList *TPL = nullptr) const;
+ 
+   template <class EntryType, typename ...ProfileArguments>
+   typename SpecEntryTraits<EntryType>::DeclType*
+   findSpecializationImpl(llvm::FoldingSetVector<EntryType> &Specs,
+                          void *&InsertPos, ProfileArguments &&...ProfileArgs);
+ 
++  template <class EntryType, typename... ProfileArguments>
++  typename SpecEntryTraits<EntryType>::DeclType *
++  findSpecializationLocally(llvm::FoldingSetVector<EntryType> &Specs,
++                            void *&InsertPos,
++                            ProfileArguments &&...ProfileArgs);
++
+   template <class Derived, class EntryType>
+   void addSpecializationImpl(llvm::FoldingSetVector<EntryType> &Specs,
+                              EntryType *Entry, void *InsertPos);
+@@ -791,13 +801,6 @@ protected:
+     llvm::PointerIntPair<RedeclarableTemplateDecl*, 1, bool>
+       InstantiatedFromMember;
+ 
+-    /// If non-null, points to an array of specializations (including
+-    /// partial specializations) known only by their external declaration IDs.
+-    ///
+-    /// The first value in the array is the number of specializations/partial
+-    /// specializations that follow.
+-    GlobalDeclID *LazySpecializations = nullptr;
+-
+     /// The set of "injected" template arguments used within this
+     /// template.
+     ///
+@@ -2279,7 +2282,7 @@ public:
+   friend class TemplateDeclInstantiator;
+ 
+   /// Load any lazily-loaded specializations from the external source.
+-  void LoadLazySpecializations() const;
++  void LoadLazySpecializations(bool OnlyPartial = false) const;
+ 
+   /// Get the underlying class declarations of the template.
+   CXXRecordDecl *getTemplatedDecl() const {
+@@ -3023,7 +3026,7 @@ public:
+   friend class ASTDeclWriter;
+ 
+   /// Load any lazily-loaded specializations from the external source.
+-  void LoadLazySpecializations() const;
++  void LoadLazySpecializations(bool OnlyPartial = false) const;
+ 
+   /// Get the underlying variable declarations of the template.
+   VarDecl *getTemplatedDecl() const {
+diff --git a/clang/include/clang/AST/ExternalASTSource.h b/clang/include/clang/AST/ExternalASTSource.h
+index 385c32edbae0..2b73b543fabb 100644
+--- a/clang/include/clang/AST/ExternalASTSource.h
++++ b/clang/include/clang/AST/ExternalASTSource.h
+@@ -150,6 +150,21 @@ public:
+   virtual bool
+   FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name);
+ 
++  /// Load all the external specializations for the Decl \param D if \param
++  /// OnlyPartial is false. Otherwise, load all the external **partial**
++  /// specializations for the \param D.
++  ///
++  /// Return true if any new specializations get loaded. Return false otherwise.
++  virtual bool LoadExternalSpecializations(const Decl *D, bool OnlyPartial);
++
++  /// Load all the specializations for the Decl \param D with the same template
++  /// args specified by \param TemplateArgs.
++  ///
++  /// Return true if any new specializations get loaded. Return false otherwise.
++  virtual bool
++  LoadExternalSpecializations(const Decl *D,
++                              ArrayRef<TemplateArgument> TemplateArgs);
++
+   /// Ensures that the table of all visible declarations inside this
+   /// context is up to date.
+   ///
+diff --git a/clang/include/clang/Sema/MultiplexExternalSemaSource.h b/clang/include/clang/Sema/MultiplexExternalSemaSource.h
+index 3d1906d86992..0c92c52854c9 100644
+--- a/clang/include/clang/Sema/MultiplexExternalSemaSource.h
++++ b/clang/include/clang/Sema/MultiplexExternalSemaSource.h
+@@ -97,6 +97,12 @@ public:
+   bool FindExternalVisibleDeclsByName(const DeclContext *DC,
+                                       DeclarationName Name) override;
+ 
++  bool LoadExternalSpecializations(const Decl *D, bool OnlyPartial) override;
++
++  bool
++  LoadExternalSpecializations(const Decl *D,
++                              ArrayRef<TemplateArgument> TemplateArgs) override;
++
+   /// Ensures that the table of all visible declarations inside this
+   /// context is up to date.
+   void completeVisibleDeclsMap(const DeclContext *DC) override;
+diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
+index 4410df296d8e..5451119dc589 100644
+--- a/clang/include/clang/Serialization/ASTBitCodes.h
++++ b/clang/include/clang/Serialization/ASTBitCodes.h
+@@ -724,6 +724,9 @@ enum ASTRecordTypes {
+ 
+   /// Record code for vtables to emit.
+   VTABLES_TO_EMIT = 70,
++
++  /// Record code for updated specialization
++  CXX_ADDED_TEMPLATE_SPECIALIZATION = 71,
+ };
+ 
+ /// Record types used within a source manager block.
+@@ -1490,6 +1493,9 @@ enum DeclCode {
+   /// A HLSLBufferDecl record.
+   DECL_HLSL_BUFFER,
+ 
++  // A decls specilization record.
++  DECL_SPECIALIZATIONS,
++
+   /// An ImplicitConceptSpecializationDecl record.
+   DECL_IMPLICIT_CONCEPT_SPECIALIZATION,
+ 
+diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h
+index 898f4392465f..709464fd2cdb 100644
+--- a/clang/include/clang/Serialization/ASTReader.h
++++ b/clang/include/clang/Serialization/ASTReader.h
+@@ -352,6 +352,9 @@ class ASTIdentifierLookupTrait;
+ /// The on-disk hash table(s) used for DeclContext name lookup.
+ struct DeclContextLookupTable;
+ 
++/// The on-disk hash table(s) used for specialization decls.
++struct LazySpecializationInfoLookupTable;
++
+ } // namespace reader
+ 
+ } // namespace serialization
+@@ -618,20 +621,29 @@ private:
+   llvm::DenseMap<const DeclContext *,
+                  serialization::reader::DeclContextLookupTable> Lookups;
+ 
++  /// Map from decls to specialized decls.
++  llvm::DenseMap<const Decl *,
++                 serialization::reader::LazySpecializationInfoLookupTable>
++      SpecializationsLookups;
++
+   // Updates for visible decls can occur for other contexts than just the
+   // TU, and when we read those update records, the actual context may not
+   // be available yet, so have this pending map using the ID as a key. It
+-  // will be realized when the context is actually loaded.
+-  struct PendingVisibleUpdate {
++  // will be realized when the data is actually loaded.
++  struct UpdateData {
+     ModuleFile *Mod;
+     const unsigned char *Data;
+   };
+-  using DeclContextVisibleUpdates = SmallVector<PendingVisibleUpdate, 1>;
++  using DeclContextVisibleUpdates = SmallVector<UpdateData, 1>;
+ 
+   /// Updates to the visible declarations of declaration contexts that
+   /// haven't been loaded yet.
+   llvm::DenseMap<GlobalDeclID, DeclContextVisibleUpdates> PendingVisibleUpdates;
+ 
++  using SpecializationsUpdate = SmallVector<UpdateData, 1>;
++  llvm::DenseMap<GlobalDeclID, SpecializationsUpdate>
++      PendingSpecializationsUpdates;
++
+   /// The set of C++ or Objective-C classes that have forward
+   /// declarations that have not yet been linked to their definitions.
+   llvm::SmallPtrSet<Decl *, 4> PendingDefinitions;
+@@ -664,6 +676,11 @@ private:
+                                      llvm::BitstreamCursor &Cursor,
+                                      uint64_t Offset, GlobalDeclID ID);
+ 
++  bool ReadSpecializations(ModuleFile &M, llvm::BitstreamCursor &Cursor,
++                           uint64_t Offset, Decl *D);
++  void AddSpecializations(const Decl *D, const unsigned char *Data,
++                          ModuleFile &M);
++
+   /// A vector containing identifiers that have already been
+   /// loaded.
+   ///
+@@ -1363,6 +1380,11 @@ public:
+   const serialization::reader::DeclContextLookupTable *
+   getLoadedLookupTables(DeclContext *Primary) const;
+ 
++  /// Get the loaded specializations lookup tables for \p D,
++  /// if any.
++  serialization::reader::LazySpecializationInfoLookupTable *
++  getLoadedSpecializationsLookupTables(const Decl *D);
++
+ private:
+   struct ImportedModule {
+     ModuleFile *Mod;
+@@ -2020,6 +2042,12 @@ public:
+                                       unsigned BlockID,
+                                       uint64_t *StartOfBlockOffset = nullptr);
+ 
++  bool LoadExternalSpecializations(const Decl *D, bool OnlyPartial) override;
++
++  bool
++  LoadExternalSpecializations(const Decl *D,
++                              ArrayRef<TemplateArgument> TemplateArgs) override;
++
+   /// Finds all the visible declarations with a given name.
+   /// The current implementation of this method just loads the entire
+   /// lookup table as unmaterialized references.
+diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h
+index 10a50b711043..f5f54313e29d 100644
+--- a/clang/include/clang/Serialization/ASTWriter.h
++++ b/clang/include/clang/Serialization/ASTWriter.h
+@@ -418,6 +418,12 @@ private:
+   /// Only meaningful for reduced BMI.
+   DeclUpdateMap DeclUpdatesFromGMF;
+ 
++  /// Mapping from decl templates and its new specialization in the
++  /// current TU.
++  using SpecializationUpdateMap =
++      llvm::MapVector<const NamedDecl *, SmallVector<const Decl *>>;
++  SpecializationUpdateMap SpecializationsUpdates;
++
+   using FirstLatestDeclMap = llvm::DenseMap<Decl *, Decl *>;
+ 
+   /// Map of first declarations from a chained PCH that point to the
+@@ -569,6 +575,12 @@ private:
+ 
+   bool isLookupResultExternal(StoredDeclsList &Result, DeclContext *DC);
+ 
++  void GenerateSpecializationInfoLookupTable(
++      const NamedDecl *D, llvm::SmallVectorImpl<const Decl *> &Specializations,
++      llvm::SmallVectorImpl<char> &LookupTable);
++  uint64_t WriteSpecializationInfoLookupTable(
++      const NamedDecl *D, llvm::SmallVectorImpl<const Decl *> &Specializations);
++
+   void GenerateNameLookupTable(const DeclContext *DC,
+                                llvm::SmallVectorImpl<char> &LookupTable);
+   uint64_t WriteDeclContextLexicalBlock(ASTContext &Context,
+@@ -584,6 +596,7 @@ private:
+   void WriteDeclAndTypes(ASTContext &Context);
+   void PrepareWritingSpecialDecls(Sema &SemaRef);
+   void WriteSpecialDeclRecords(Sema &SemaRef);
++  void WriteSpecializationsUpdates();
+   void WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord);
+   void WriteDeclContextVisibleUpdate(const DeclContext *DC);
+   void WriteFPPragmaOptions(const FPOptionsOverride &Opts);
+@@ -610,6 +623,8 @@ private:
+   unsigned DeclEnumAbbrev = 0;
+   unsigned DeclObjCIvarAbbrev = 0;
+   unsigned DeclCXXMethodAbbrev = 0;
++  unsigned DeclSpecializationsAbbrev = 0;
++
+   unsigned DeclDependentNonTemplateCXXMethodAbbrev = 0;
+   unsigned DeclTemplateCXXMethodAbbrev = 0;
+   unsigned DeclMemberSpecializedCXXMethodAbbrev = 0;
+diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
+index 976b3a3e1ece..4e21c813a002 100644
+--- a/clang/lib/AST/DeclTemplate.cpp
++++ b/clang/lib/AST/DeclTemplate.cpp
+@@ -16,7 +16,9 @@
+ #include "clang/AST/DeclCXX.h"
+ #include "clang/AST/DeclarationName.h"
+ #include "clang/AST/Expr.h"
++#include "clang/AST/ExprCXX.h"
+ #include "clang/AST/ExternalASTSource.h"
++#include "clang/AST/ODRHash.h"
+ #include "clang/AST/TemplateBase.h"
+ #include "clang/AST/TemplateName.h"
+ #include "clang/AST/Type.h"
+@@ -340,26 +342,39 @@ RedeclarableTemplateDecl::CommonBase *RedeclarableTemplateDecl::getCommonPtr() c
+   return Common;
+ }
+ 
+-void RedeclarableTemplateDecl::loadLazySpecializationsImpl() const {
+-  // Grab the most recent declaration to ensure we've loaded any lazy
+-  // redeclarations of this template.
+-  CommonBase *CommonBasePtr = getMostRecentDecl()->getCommonPtr();
+-  if (CommonBasePtr->LazySpecializations) {
+-    ASTContext &Context = getASTContext();
+-    GlobalDeclID *Specs = CommonBasePtr->LazySpecializations;
+-    CommonBasePtr->LazySpecializations = nullptr;
+-    unsigned SpecSize = (*Specs++).getRawValue();
+-    for (unsigned I = 0; I != SpecSize; ++I)
+-      (void)Context.getExternalSource()->GetExternalDecl(Specs[I]);
+-  }
++void RedeclarableTemplateDecl::loadLazySpecializationsImpl(
++    bool OnlyPartial /*=false*/) const {
++  auto *ExternalSource = getASTContext().getExternalSource();
++  if (!ExternalSource)
++    return;
++
++  ExternalSource->LoadExternalSpecializations(this->getCanonicalDecl(),
++                                              OnlyPartial);
++  return;
+ }
+ 
+-template<class EntryType, typename... ProfileArguments>
++bool RedeclarableTemplateDecl::loadLazySpecializationsImpl(
++    ArrayRef<TemplateArgument> Args, TemplateParameterList *TPL) const {
++  auto *ExternalSource = getASTContext().getExternalSource();
++  if (!ExternalSource)
++    return false;
++
++  // If TPL is not null, it implies that we're loading specializations for
++  // partial templates. We need to load all specializations in such cases.
++  if (TPL)
++    return ExternalSource->LoadExternalSpecializations(this->getCanonicalDecl(),
++                                                       /*OnlyPartial=*/false);
++
++  return ExternalSource->LoadExternalSpecializations(this->getCanonicalDecl(),
++                                                     Args);
++}
++
++template <class EntryType, typename... ProfileArguments>
+ typename RedeclarableTemplateDecl::SpecEntryTraits<EntryType>::DeclType *
+-RedeclarableTemplateDecl::findSpecializationImpl(
++RedeclarableTemplateDecl::findSpecializationLocally(
+     llvm::FoldingSetVector<EntryType> &Specs, void *&InsertPos,
+-    ProfileArguments&&... ProfileArgs) {
+-  using SETraits = SpecEntryTraits<EntryType>;
++    ProfileArguments &&...ProfileArgs) {
++  using SETraits = RedeclarableTemplateDecl::SpecEntryTraits<EntryType>;
+ 
+   llvm::FoldingSetNodeID ID;
+   EntryType::Profile(ID, std::forward<ProfileArguments>(ProfileArgs)...,
+@@ -368,6 +383,24 @@ RedeclarableTemplateDecl::findSpecializationImpl(
+   return Entry ? SETraits::getDecl(Entry)->getMostRecentDecl() : nullptr;
+ }
+ 
++template <class EntryType, typename... ProfileArguments>
++typename RedeclarableTemplateDecl::SpecEntryTraits<EntryType>::DeclType *
++RedeclarableTemplateDecl::findSpecializationImpl(
++    llvm::FoldingSetVector<EntryType> &Specs, void *&InsertPos,
++    ProfileArguments &&...ProfileArgs) {
++
++  if (auto *Found = findSpecializationLocally(
++          Specs, InsertPos, std::forward<ProfileArguments>(ProfileArgs)...))
++    return Found;
++
++  if (!loadLazySpecializationsImpl(
++          std::forward<ProfileArguments>(ProfileArgs)...))
++    return nullptr;
++
++  return findSpecializationLocally(
++      Specs, InsertPos, std::forward<ProfileArguments>(ProfileArgs)...);
++}
++
+ template<class Derived, class EntryType>
+ void RedeclarableTemplateDecl::addSpecializationImpl(
+     llvm::FoldingSetVector<EntryType> &Specializations, EntryType *Entry,
+@@ -376,10 +409,14 @@ void RedeclarableTemplateDecl::addSpecializationImpl(
+ 
+   if (InsertPos) {
+ #ifndef NDEBUG
++    auto Args = SETraits::getTemplateArgs(Entry);
++    // Due to hash collisions, it can happen that we load another template
++    // specialization with the same hash. This is fine, as long as the next
++    // call to findSpecializationImpl does not find a matching Decl for the
++    // template arguments.
++    loadLazySpecializationsImpl(Args);
+     void *CorrectInsertPos;
+-    assert(!findSpecializationImpl(Specializations,
+-                                   CorrectInsertPos,
+-                                   SETraits::getTemplateArgs(Entry)) &&
++    assert(!findSpecializationImpl(Specializations, CorrectInsertPos, Args) &&
+            InsertPos == CorrectInsertPos &&
+            "given incorrect InsertPos for specialization");
+ #endif
+@@ -453,12 +490,14 @@ FunctionTemplateDecl::getSpecializations() const {
+ FunctionDecl *
+ FunctionTemplateDecl::findSpecialization(ArrayRef<TemplateArgument> Args,
+                                          void *&InsertPos) {
+-  return findSpecializationImpl(getSpecializations(), InsertPos, Args);
++  auto *Common = getCommonPtr();
++  return findSpecializationImpl(Common->Specializations, InsertPos, Args);
+ }
+ 
+ void FunctionTemplateDecl::addSpecialization(
+       FunctionTemplateSpecializationInfo *Info, void *InsertPos) {
+-  addSpecializationImpl<FunctionTemplateDecl>(getSpecializations(), Info,
++  auto *Common = getCommonPtr();
++  addSpecializationImpl<FunctionTemplateDecl>(Common->Specializations, Info,
+                                               InsertPos);
+ }
+ 
+@@ -518,8 +557,9 @@ ClassTemplateDecl *ClassTemplateDecl::CreateDeserialized(ASTContext &C,
+                                        DeclarationName(), nullptr, nullptr);
+ }
+ 
+-void ClassTemplateDecl::LoadLazySpecializations() const {
+-  loadLazySpecializationsImpl();
++void ClassTemplateDecl::LoadLazySpecializations(
++    bool OnlyPartial /*=false*/) const {
++  loadLazySpecializationsImpl(OnlyPartial);
+ }
+ 
+ llvm::FoldingSetVector<ClassTemplateSpecializationDecl> &
+@@ -530,7 +570,7 @@ ClassTemplateDecl::getSpecializations() const {
+ 
+ llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl> &
+ ClassTemplateDecl::getPartialSpecializations() const {
+-  LoadLazySpecializations();
++  LoadLazySpecializations(/*PartialOnly = */ true);
+   return getCommonPtr()->PartialSpecializations;
+ }
+ 
+@@ -544,12 +584,15 @@ ClassTemplateDecl::newCommon(ASTContext &C) const {
+ ClassTemplateSpecializationDecl *
+ ClassTemplateDecl::findSpecialization(ArrayRef<TemplateArgument> Args,
+                                       void *&InsertPos) {
+-  return findSpecializationImpl(getSpecializations(), InsertPos, Args);
++  auto *Common = getCommonPtr();
++  return findSpecializationImpl(Common->Specializations, InsertPos, Args);
+ }
+ 
+ void ClassTemplateDecl::AddSpecialization(ClassTemplateSpecializationDecl *D,
+                                           void *InsertPos) {
+-  addSpecializationImpl<ClassTemplateDecl>(getSpecializations(), D, InsertPos);
++  auto *Common = getCommonPtr();
++  addSpecializationImpl<ClassTemplateDecl>(Common->Specializations, D,
++                                           InsertPos);
+ }
+ 
+ ClassTemplatePartialSpecializationDecl *
+@@ -1271,8 +1314,9 @@ VarTemplateDecl *VarTemplateDecl::CreateDeserialized(ASTContext &C,
+                                      DeclarationName(), nullptr, nullptr);
+ }
+ 
+-void VarTemplateDecl::LoadLazySpecializations() const {
+-  loadLazySpecializationsImpl();
++void VarTemplateDecl::LoadLazySpecializations(
++    bool OnlyPartial /*=false*/) const {
++  loadLazySpecializationsImpl(OnlyPartial);
+ }
+ 
+ llvm::FoldingSetVector<VarTemplateSpecializationDecl> &
+@@ -1283,7 +1327,7 @@ VarTemplateDecl::getSpecializations() const {
+ 
+ llvm::FoldingSetVector<VarTemplatePartialSpecializationDecl> &
+ VarTemplateDecl::getPartialSpecializations() const {
+-  LoadLazySpecializations();
++  LoadLazySpecializations(/*PartialOnly = */ true);
+   return getCommonPtr()->PartialSpecializations;
+ }
+ 
+@@ -1297,12 +1341,14 @@ VarTemplateDecl::newCommon(ASTContext &C) const {
+ VarTemplateSpecializationDecl *
+ VarTemplateDecl::findSpecialization(ArrayRef<TemplateArgument> Args,
+                                     void *&InsertPos) {
+-  return findSpecializationImpl(getSpecializations(), InsertPos, Args);
++  auto *Common = getCommonPtr();
++  return findSpecializationImpl(Common->Specializations, InsertPos, Args);
+ }
+ 
+ void VarTemplateDecl::AddSpecialization(VarTemplateSpecializationDecl *D,
+                                         void *InsertPos) {
+-  addSpecializationImpl<VarTemplateDecl>(getSpecializations(), D, InsertPos);
++  auto *Common = getCommonPtr();
++  addSpecializationImpl<VarTemplateDecl>(Common->Specializations, D, InsertPos);
+ }
+ 
+ VarTemplatePartialSpecializationDecl *
+diff --git a/clang/lib/AST/ExternalASTSource.cpp b/clang/lib/AST/ExternalASTSource.cpp
+index a5b6f80bde69..0eeb549d8cab 100644
+--- a/clang/lib/AST/ExternalASTSource.cpp
++++ b/clang/lib/AST/ExternalASTSource.cpp
+@@ -98,6 +98,15 @@ ExternalASTSource::FindExternalVisibleDeclsByName(const DeclContext *DC,
+   return false;
+ }
+ 
++bool ExternalASTSource::LoadExternalSpecializations(const Decl *D, bool) {
++  return false;
++}
++
++bool ExternalASTSource::LoadExternalSpecializations(
++    const Decl *D, ArrayRef<TemplateArgument>) {
++  return false;
++}
++
+ void ExternalASTSource::completeVisibleDeclsMap(const DeclContext *DC) {}
+ 
+ void ExternalASTSource::FindExternalLexicalDecls(
+diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
+index b748093831e3..ddfaa7e139a0 100644
+--- a/clang/lib/AST/ODRHash.cpp
++++ b/clang/lib/AST/ODRHash.cpp
+@@ -817,15 +817,20 @@ void ODRHash::AddDecl(const Decl *D) {
+ 
+   AddDeclarationName(ND->getDeclName());
+ 
+-  const auto *Specialization =
+-            dyn_cast<ClassTemplateSpecializationDecl>(D);
+-  AddBoolean(Specialization);
+-  if (Specialization) {
+-    const TemplateArgumentList &List = Specialization->getTemplateArgs();
+-    ID.AddInteger(List.size());
+-    for (const TemplateArgument &TA : List.asArray())
+-      AddTemplateArgument(TA);
+-  }
++  // If this was a specialization we should take into account its template
++  // arguments. This helps to reduce collisions coming when visiting template
++  // specialization types (eg. when processing type template arguments).
++  ArrayRef<TemplateArgument> Args;
++  if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D))
++    Args = CTSD->getTemplateArgs().asArray();
++  else if (auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(D))
++    Args = VTSD->getTemplateArgs().asArray();
++  else if (auto *FD = dyn_cast<FunctionDecl>(D))
++    if (FD->getTemplateSpecializationArgs())
++      Args = FD->getTemplateSpecializationArgs()->asArray();
++
++  for (auto &TA : Args)
++    AddTemplateArgument(TA);
+ }
+ 
+ namespace {
+diff --git a/clang/lib/Sema/MultiplexExternalSemaSource.cpp b/clang/lib/Sema/MultiplexExternalSemaSource.cpp
+index cd44483b5cbe..54944267b486 100644
+--- a/clang/lib/Sema/MultiplexExternalSemaSource.cpp
++++ b/clang/lib/Sema/MultiplexExternalSemaSource.cpp
+@@ -115,6 +115,23 @@ FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name) {
+   return AnyDeclsFound;
+ }
+ 
++bool MultiplexExternalSemaSource::LoadExternalSpecializations(
++    const Decl *D, bool OnlyPartial) {
++  bool Loaded = false;
++  for (size_t i = 0; i < Sources.size(); ++i)
++    Loaded |= Sources[i]->LoadExternalSpecializations(D, OnlyPartial);
++  return Loaded;
++}
++
++bool MultiplexExternalSemaSource::LoadExternalSpecializations(
++    const Decl *D, ArrayRef<TemplateArgument> TemplateArgs) {
++  bool AnyNewSpecsLoaded = false;
++  for (size_t i = 0; i < Sources.size(); ++i)
++    AnyNewSpecsLoaded |=
++        Sources[i]->LoadExternalSpecializations(D, TemplateArgs);
++  return AnyNewSpecsLoaded;
++}
++
+ void MultiplexExternalSemaSource::completeVisibleDeclsMap(const DeclContext *DC){
+   for(size_t i = 0; i < Sources.size(); ++i)
+     Sources[i]->completeVisibleDeclsMap(DC);
+diff --git a/clang/lib/Serialization/ASTCommon.h b/clang/lib/Serialization/ASTCommon.h
+index 0230908d3e05..0cef55c53ffc 100644
+--- a/clang/lib/Serialization/ASTCommon.h
++++ b/clang/lib/Serialization/ASTCommon.h
+@@ -23,7 +23,6 @@ namespace serialization {
+ 
+ enum DeclUpdateKind {
+   UPD_CXX_ADDED_IMPLICIT_MEMBER,
+-  UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION,
+   UPD_CXX_ADDED_ANONYMOUS_NAMESPACE,
+   UPD_CXX_ADDED_FUNCTION_DEFINITION,
+   UPD_CXX_ADDED_VAR_DEFINITION,
+diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
+index e5a1e20a2656..f9a74048cbbd 100644
+--- a/clang/lib/Serialization/ASTReader.cpp
++++ b/clang/lib/Serialization/ASTReader.cpp
+@@ -12,6 +12,7 @@
+ 
+ #include "ASTCommon.h"
+ #include "ASTReaderInternals.h"
++#include "TemplateArgumentHasher.h"
+ #include "clang/AST/ASTConsumer.h"
+ #include "clang/AST/ASTContext.h"
+ #include "clang/AST/ASTMutationListener.h"
+@@ -1292,6 +1293,43 @@ void ASTDeclContextNameLookupTrait::ReadDataInto(internal_key_type,
+   }
+ }
+ 
++ModuleFile *
++LazySpecializationInfoLookupTrait::ReadFileRef(const unsigned char *&d) {
++  using namespace llvm::support;
++
++  uint32_t ModuleFileID =
++      endian::readNext<uint32_t, llvm::endianness::little, unaligned>(d);
++  return Reader.getLocalModuleFile(F, ModuleFileID);
++}
++
++LazySpecializationInfoLookupTrait::internal_key_type
++LazySpecializationInfoLookupTrait::ReadKey(const unsigned char *d, unsigned) {
++  using namespace llvm::support;
++  return endian::readNext<uint32_t, llvm::endianness::little, unaligned>(d);
++}
++
++std::pair<unsigned, unsigned>
++LazySpecializationInfoLookupTrait::ReadKeyDataLength(const unsigned char *&d) {
++  return readULEBKeyDataLength(d);
++}
++
++void LazySpecializationInfoLookupTrait::ReadDataInto(internal_key_type,
++                                                     const unsigned char *d,
++                                                     unsigned DataLen,
++                                                     data_type_builder &Val) {
++  using namespace llvm::support;
++
++  for (unsigned NumDecls =
++           DataLen / serialization::reader::LazySpecializationInfo::Length;
++       NumDecls; --NumDecls) {
++    LocalDeclID LocalID =
++        LocalDeclID::get(Reader, F, endian::readNext<DeclID, llvm::endianness::little, unaligned>(d));
++    const bool IsPartial =
++        endian::readNext<bool, llvm::endianness::little, unaligned>(d);
++    Val.insert({Reader.getGlobalDeclID(F, LocalID), IsPartial});
++  }
++}
++
+ bool ASTReader::ReadLexicalDeclContextStorage(ModuleFile &M,
+                                               BitstreamCursor &Cursor,
+                                               uint64_t Offset,
+@@ -1376,7 +1414,49 @@ bool ASTReader::ReadVisibleDeclContextStorage(ModuleFile &M,
+   // We can't safely determine the primary context yet, so delay attaching the
+   // lookup table until we're done with recursive deserialization.
+   auto *Data = (const unsigned char*)Blob.data();
+-  PendingVisibleUpdates[ID].push_back(PendingVisibleUpdate{&M, Data});
++  PendingVisibleUpdates[ID].push_back(UpdateData{&M, Data});
++  return false;
++}
++
++void ASTReader::AddSpecializations(const Decl *D, const unsigned char *Data,
++                                   ModuleFile &M) {
++  D = D->getCanonicalDecl();
++  SpecializationsLookups[D].Table.add(
++      &M, Data, reader::LazySpecializationInfoLookupTrait(*this, M));
++}
++
++bool ASTReader::ReadSpecializations(ModuleFile &M, BitstreamCursor &Cursor,
++                                    uint64_t Offset, Decl *D) {
++  assert(Offset != 0);
++
++  SavedStreamPosition SavedPosition(Cursor);
++  if (llvm::Error Err = Cursor.JumpToBit(Offset)) {
++    Error(std::move(Err));
++    return true;
++  }
++
++  RecordData Record;
++  StringRef Blob;
++  Expected<unsigned> MaybeCode = Cursor.ReadCode();
++  if (!MaybeCode) {
++    Error(MaybeCode.takeError());
++    return true;
++  }
++  unsigned Code = MaybeCode.get();
++
++  Expected<unsigned> MaybeRecCode = Cursor.readRecord(Code, Record, &Blob);
++  if (!MaybeRecCode) {
++    Error(MaybeRecCode.takeError());
++    return true;
++  }
++  unsigned RecCode = MaybeRecCode.get();
++  if (RecCode != DECL_SPECIALIZATIONS) {
++    Error("Expected decl specs block");
++    return true;
++  }
++
++  auto *Data = (const unsigned char *)Blob.data();
++  AddSpecializations(D, Data, M);
+   return false;
+ }
+ 
+@@ -3465,7 +3545,20 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
+       unsigned Idx = 0;
+       GlobalDeclID ID = ReadDeclID(F, Record, Idx);
+       auto *Data = (const unsigned char*)Blob.data();
+-      PendingVisibleUpdates[ID].push_back(PendingVisibleUpdate{&F, Data});
++      PendingVisibleUpdates[ID].push_back(UpdateData{&F, Data});
++      // If we've already loaded the decl, perform the updates when we finish
++      // loading this block.
++      if (Decl *D = GetExistingDecl(ID))
++        PendingUpdateRecords.push_back(
++            PendingUpdateRecord(ID, D, /*JustLoaded=*/false));
++      break;
++    }
++
++    case CXX_ADDED_TEMPLATE_SPECIALIZATION: {
++      unsigned Idx = 0;
++      GlobalDeclID ID = ReadDeclID(F, Record, Idx);
++      auto *Data = (const unsigned char *)Blob.data();
++      PendingSpecializationsUpdates[ID].push_back(UpdateData{&F, Data});
+       // If we've already loaded the decl, perform the updates when we finish
+       // loading this block.
+       if (Decl *D = GetExistingDecl(ID))
+@@ -7646,13 +7739,28 @@ void ASTReader::CompleteRedeclChain(const Decl *D) {
+     }
+   }
+ 
+-  if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D))
+-    CTSD->getSpecializedTemplate()->LoadLazySpecializations();
+-  if (auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(D))
+-    VTSD->getSpecializedTemplate()->LoadLazySpecializations();
+-  if (auto *FD = dyn_cast<FunctionDecl>(D)) {
+-    if (auto *Template = FD->getPrimaryTemplate())
+-      Template->LoadLazySpecializations();
++  RedeclarableTemplateDecl *Template = nullptr;
++  ArrayRef<TemplateArgument> Args;
++  if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
++    Template = CTSD->getSpecializedTemplate();
++    Args = CTSD->getTemplateArgs().asArray();
++  } else if (auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(D)) {
++    Template = VTSD->getSpecializedTemplate();
++    Args = VTSD->getTemplateArgs().asArray();
++  } else if (auto *FD = dyn_cast<FunctionDecl>(D)) {
++    if (auto *Tmplt = FD->getPrimaryTemplate()) {
++      Template = Tmplt;
++      Args = FD->getTemplateSpecializationArgs()->asArray();
++    }
++  }
++
++  if (Template) {
++    // For partitial specialization, load all the specializations for safety.
++    if (isa<ClassTemplatePartialSpecializationDecl,
++            VarTemplatePartialSpecializationDecl>(D))
++      Template->loadLazySpecializationsImpl();
++    else
++      Template->loadLazySpecializationsImpl(Args);
+   }
+ }
+ 
+@@ -8027,6 +8135,62 @@ Stmt *ASTReader::GetExternalDeclStmt(uint64_t Offset) {
+   return ReadStmtFromStream(*Loc.F);
+ }
+ 
++bool ASTReader::LoadExternalSpecializations(const Decl *D, bool OnlyPartial) {
++  assert(D);
++
++  auto It = SpecializationsLookups.find(D);
++  if (It == SpecializationsLookups.end())
++    return false;
++
++  // Get Decl may violate the iterator from SpecializationsLookups so we store
++  // the DeclIDs in ahead.
++  llvm::SmallVector<serialization::reader::LazySpecializationInfo, 8> Infos =
++      It->second.Table.findAll();
++
++  // Since we've loaded all the specializations, we can erase it from
++  // the lookup table.
++  if (!OnlyPartial)
++    SpecializationsLookups.erase(It);
++
++  bool NewSpecsFound = false;
++  Deserializing LookupResults(this);
++  for (auto &Info : Infos)
++    if (!OnlyPartial || Info.IsPartial) {
++      if (GetExistingDecl(Info.ID))
++        continue;
++      NewSpecsFound = true;
++      GetDecl(Info.ID);
++    }
++
++  return NewSpecsFound;
++}
++
++bool ASTReader::LoadExternalSpecializations(
++    const Decl *D, ArrayRef<TemplateArgument> TemplateArgs) {
++  assert(D);
++
++  auto It = SpecializationsLookups.find(D);
++  if (It == SpecializationsLookups.end())
++    return false;
++
++  Deserializing LookupResults(this);
++  auto HashValue = StableHashForTemplateArguments(TemplateArgs);
++
++  // Get Decl may violate the iterator from SpecializationsLookups
++  llvm::SmallVector<serialization::reader::LazySpecializationInfo, 8> Infos =
++      It->second.Table.find(HashValue);
++
++  bool NewSpecsFound = false;
++  for (auto &Info : Infos) {
++    if (GetExistingDecl(Info.ID))
++      continue;
++    NewSpecsFound = true;
++    GetDecl(Info.ID);
++  }
++
++  return NewSpecsFound;
++}
++
+ void ASTReader::FindExternalLexicalDecls(
+     const DeclContext *DC, llvm::function_ref<bool(Decl::Kind)> IsKindWeWant,
+     SmallVectorImpl<Decl *> &Decls) {
+@@ -8206,6 +8370,13 @@ ASTReader::getLoadedLookupTables(DeclContext *Primary) const {
+   return I == Lookups.end() ? nullptr : &I->second;
+ }
+ 
++serialization::reader::LazySpecializationInfoLookupTable *
++ASTReader::getLoadedSpecializationsLookupTables(const Decl *D) {
++  assert(D->isCanonicalDecl());
++  auto I = SpecializationsLookups.find(D);
++  return I == SpecializationsLookups.end() ? nullptr : &I->second;
++}
++
+ /// Under non-PCH compilation the consumer receives the objc methods
+ /// before receiving the implementation, and codegen depends on this.
+ /// We simulate this by deserializing and passing to consumer the methods of the
+diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
+index 9272e23c7da3..eaffb549839c 100644
+--- a/clang/lib/Serialization/ASTReaderDecl.cpp
++++ b/clang/lib/Serialization/ASTReaderDecl.cpp
+@@ -185,12 +185,9 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> {
+ 
+   GlobalDeclID readDeclID() { return Record.readDeclID(); }
+ 
+-  std::string readString() { return Record.readString(); }
+-
+-  void readDeclIDList(SmallVectorImpl<GlobalDeclID> &IDs) {
+-    for (unsigned I = 0, Size = Record.readInt(); I != Size; ++I)
+-      IDs.push_back(readDeclID());
+-  }
++    std::string readString() {
++      return Record.readString();
++    }
+ 
+   Decl *readDecl() { return Record.readDecl(); }
+ 
+@@ -284,34 +281,10 @@ public:
+       : Reader(Reader), MergeImpl(Reader), Record(Record), Loc(Loc),
+         ThisDeclID(thisDeclID), ThisDeclLoc(ThisDeclLoc) {}
+ 
+-  template <typename T>
+-  static void AddLazySpecializations(T *D, SmallVectorImpl<GlobalDeclID> &IDs) {
+-    if (IDs.empty())
+-      return;
+-
+-    // FIXME: We should avoid this pattern of getting the ASTContext.
+-    ASTContext &C = D->getASTContext();
+-
+-    auto *&LazySpecializations = D->getCommonPtr()->LazySpecializations;
+-
+-    if (auto &Old = LazySpecializations) {
+-      IDs.insert(IDs.end(), Old + 1, Old + 1 + Old[0].getRawValue());
+-      llvm::sort(IDs);
+-      IDs.erase(std::unique(IDs.begin(), IDs.end()), IDs.end());
+-    }
+-
+-    auto *Result = new (C) GlobalDeclID[1 + IDs.size()];
+-    *Result = GlobalDeclID(IDs.size());
+-
+-    std::copy(IDs.begin(), IDs.end(), Result + 1);
+-
+-    LazySpecializations = Result;
+-  }
+-
+-  template <typename DeclT>
+-  static Decl *getMostRecentDeclImpl(Redeclarable<DeclT> *D);
+-  static Decl *getMostRecentDeclImpl(...);
+-  static Decl *getMostRecentDecl(Decl *D);
++    template <typename DeclT>
++    static Decl *getMostRecentDeclImpl(Redeclarable<DeclT> *D);
++    static Decl *getMostRecentDeclImpl(...);
++    static Decl *getMostRecentDecl(Decl *D);
+ 
+   template <typename DeclT>
+   static void attachPreviousDeclImpl(ASTReader &Reader, Redeclarable<DeclT> *D,
+@@ -335,7 +308,7 @@ public:
+   void ReadFunctionDefinition(FunctionDecl *FD);
+   void Visit(Decl *D);
+ 
+-  void UpdateDecl(Decl *D, SmallVectorImpl<GlobalDeclID> &);
++    void UpdateDecl(Decl *D);
+ 
+   static void setNextObjCCategory(ObjCCategoryDecl *Cat,
+                                   ObjCCategoryDecl *Next) {
+@@ -441,8 +414,11 @@ public:
+ 
+   std::pair<uint64_t, uint64_t> VisitDeclContext(DeclContext *DC);
+ 
+-  template <typename T>
+-  RedeclarableResult VisitRedeclarable(Redeclarable<T> *D);
++    void ReadSpecializations(ModuleFile &M, Decl *D,
++                             llvm::BitstreamCursor &Cursor);
++
++    template<typename T>
++    RedeclarableResult VisitRedeclarable(Redeclarable<T> *D);
+ 
+   template <typename T>
+   void mergeRedeclarable(Redeclarable<T> *D, RedeclarableResult &Redecl);
+@@ -2409,6 +2385,14 @@ void ASTDeclReader::VisitImplicitConceptSpecializationDecl(
+ void ASTDeclReader::VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D) {
+ }
+ 
++void ASTDeclReader::ReadSpecializations(ModuleFile &M, Decl *D,
++                                        llvm::BitstreamCursor &DeclsCursor) {
++  uint64_t Offset = ReadLocalOffset();
++  bool Failed = Reader.ReadSpecializations(M, DeclsCursor, Offset, D);
++  (void)Failed;
++  assert(!Failed);
++}
++
+ RedeclarableResult
+ ASTDeclReader::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) {
+   RedeclarableResult Redecl = VisitRedeclarable(D);
+@@ -2447,9 +2431,7 @@ void ASTDeclReader::VisitClassTemplateDecl(ClassTemplateDecl *D) {
+   if (ThisDeclID == Redecl.getFirstID()) {
+     // This ClassTemplateDecl owns a CommonPtr; read it to keep track of all of
+     // the specializations.
+-    SmallVector<GlobalDeclID, 32> SpecIDs;
+-    readDeclIDList(SpecIDs);
+-    ASTDeclReader::AddLazySpecializations(D, SpecIDs);
++    ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor);
+   }
+ 
+   if (D->getTemplatedDecl()->TemplateOrInstantiation) {
+@@ -2475,9 +2457,7 @@ void ASTDeclReader::VisitVarTemplateDecl(VarTemplateDecl *D) {
+   if (ThisDeclID == Redecl.getFirstID()) {
+     // This VarTemplateDecl owns a CommonPtr; read it to keep track of all of
+     // the specializations.
+-    SmallVector<GlobalDeclID, 32> SpecIDs;
+-    readDeclIDList(SpecIDs);
+-    ASTDeclReader::AddLazySpecializations(D, SpecIDs);
++    ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor);
+   }
+ }
+ 
+@@ -2576,9 +2556,7 @@ void ASTDeclReader::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) {
+ 
+   if (ThisDeclID == Redecl.getFirstID()) {
+     // This FunctionTemplateDecl owns a CommonPtr; read it.
+-    SmallVector<GlobalDeclID, 32> SpecIDs;
+-    readDeclIDList(SpecIDs);
+-    ASTDeclReader::AddLazySpecializations(D, SpecIDs);
++    ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor);
+   }
+ }
+ 
+@@ -3868,6 +3846,7 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
+   switch ((DeclCode)MaybeDeclCode.get()) {
+   case DECL_CONTEXT_LEXICAL:
+   case DECL_CONTEXT_VISIBLE:
++  case DECL_SPECIALIZATIONS:
+     llvm_unreachable("Record cannot be de-serialized with readDeclRecord");
+   case DECL_TYPEDEF:
+     D = TypedefDecl::CreateDeserialized(Context, ID);
+@@ -4278,8 +4257,6 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) {
+   ProcessingUpdatesRAIIObj ProcessingUpdates(*this);
+   DeclUpdateOffsetsMap::iterator UpdI = DeclUpdateOffsets.find(ID);
+ 
+-  SmallVector<GlobalDeclID, 8> PendingLazySpecializationIDs;
+-
+   if (UpdI != DeclUpdateOffsets.end()) {
+     auto UpdateOffsets = std::move(UpdI->second);
+     DeclUpdateOffsets.erase(UpdI);
+@@ -4316,7 +4293,7 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) {
+ 
+       ASTDeclReader Reader(*this, Record, RecordLocation(F, Offset), ID,
+                            SourceLocation());
+-      Reader.UpdateDecl(D, PendingLazySpecializationIDs);
++      Reader.UpdateDecl(D);
+ 
+       // We might have made this declaration interesting. If so, remember that
+       // we need to hand it off to the consumer.
+@@ -4326,17 +4303,6 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) {
+       }
+     }
+   }
+-  // Add the lazy specializations to the template.
+-  assert((PendingLazySpecializationIDs.empty() || isa<ClassTemplateDecl>(D) ||
+-          isa<FunctionTemplateDecl, VarTemplateDecl>(D)) &&
+-         "Must not have pending specializations");
+-  if (auto *CTD = dyn_cast<ClassTemplateDecl>(D))
+-    ASTDeclReader::AddLazySpecializations(CTD, PendingLazySpecializationIDs);
+-  else if (auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
+-    ASTDeclReader::AddLazySpecializations(FTD, PendingLazySpecializationIDs);
+-  else if (auto *VTD = dyn_cast<VarTemplateDecl>(D))
+-    ASTDeclReader::AddLazySpecializations(VTD, PendingLazySpecializationIDs);
+-  PendingLazySpecializationIDs.clear();
+ 
+   // Load the pending visible updates for this decl context, if it has any.
+   auto I = PendingVisibleUpdates.find(ID);
+@@ -4351,6 +4317,16 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) {
+           reader::ASTDeclContextNameLookupTrait(*this, *Update.Mod));
+     DC->setHasExternalVisibleStorage(true);
+   }
++
++  // Load the pending specializations update for this decl, if it has any.
++  if (auto I = PendingSpecializationsUpdates.find(ID);
++      I != PendingSpecializationsUpdates.end()) {
++    auto SpecializationUpdates = std::move(I->second);
++    PendingSpecializationsUpdates.erase(I);
++
++    for (const auto &Update : SpecializationUpdates)
++      AddSpecializations(D, Update.Data, *Update.Mod);
++  }
+ }
+ 
+ void ASTReader::loadPendingDeclChain(Decl *FirstLocal, uint64_t LocalOffset) {
+@@ -4543,9 +4519,7 @@ static void forAllLaterRedecls(DeclT *D, Fn F) {
+   }
+ }
+ 
+-void ASTDeclReader::UpdateDecl(
+-    Decl *D,
+-    llvm::SmallVectorImpl<GlobalDeclID> &PendingLazySpecializationIDs) {
++void ASTDeclReader::UpdateDecl(Decl *D) {
+   while (Record.getIdx() < Record.size()) {
+     switch ((DeclUpdateKind)Record.readInt()) {
+     case UPD_CXX_ADDED_IMPLICIT_MEMBER: {
+@@ -4556,11 +4530,6 @@ void ASTDeclReader::UpdateDecl(
+       break;
+     }
+ 
+-    case UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION:
+-      // It will be added to the template's lazy specialization set.
+-      PendingLazySpecializationIDs.push_back(readDeclID());
+-      break;
+-
+     case UPD_CXX_ADDED_ANONYMOUS_NAMESPACE: {
+       auto *Anon = readDeclAs<NamespaceDecl>();
+ 
+diff --git a/clang/lib/Serialization/ASTReaderInternals.h b/clang/lib/Serialization/ASTReaderInternals.h
+index 536b19f91691..0611255b7858 100644
+--- a/clang/lib/Serialization/ASTReaderInternals.h
++++ b/clang/lib/Serialization/ASTReaderInternals.h
+@@ -119,6 +119,101 @@ struct DeclContextLookupTable {
+   MultiOnDiskHashTable<ASTDeclContextNameLookupTrait> Table;
+ };
+ 
++struct LazySpecializationInfo {
++  // The Decl ID for the specialization.
++  GlobalDeclID ID;
++  // Whether or not this specialization is partial.
++  bool IsPartial;
++
++  bool operator==(const LazySpecializationInfo &Other) const {
++    assert(ID != Other.ID || IsPartial == Other.IsPartial);
++    return ID == Other.ID;
++  }
++  // Records the size record in OnDiskHashTable.
++  // sizeof() may return 8 due to align requirements.
++  static constexpr unsigned Length = sizeof(DeclID) + sizeof(IsPartial);
++};
++
++/// Class that performs lookup to specialized decls.
++class LazySpecializationInfoLookupTrait {
++  ASTReader &Reader;
++  ModuleFile &F;
++
++public:
++  // Maximum number of lookup tables we allow before condensing the tables.
++  static const int MaxTables = 4;
++
++  /// The lookup result is a list of global declaration IDs.
++  using data_type = SmallVector<LazySpecializationInfo, 4>;
++
++  struct data_type_builder {
++    data_type &Data;
++    llvm::DenseSet<LazySpecializationInfo> Found;
++
++    data_type_builder(data_type &D) : Data(D) {}
++
++    void insert(LazySpecializationInfo Info) {
++      // Just use a linear scan unless we have more than a few IDs.
++      if (Found.empty() && !Data.empty()) {
++        if (Data.size() <= 4) {
++          for (auto I : Found)
++            if (I == Info)
++              return;
++          Data.push_back(Info);
++          return;
++        }
++
++        // Switch to tracking found IDs in the set.
++        Found.insert(Data.begin(), Data.end());
++      }
++
++      if (Found.insert(Info).second)
++        Data.push_back(Info);
++    }
++  };
++  using hash_value_type = unsigned;
++  using offset_type = unsigned;
++  using file_type = ModuleFile *;
++
++  using external_key_type = unsigned;
++  using internal_key_type = unsigned;
++
++  explicit LazySpecializationInfoLookupTrait(ASTReader &Reader, ModuleFile &F)
++      : Reader(Reader), F(F) {}
++
++  static bool EqualKey(const internal_key_type &a, const internal_key_type &b) {
++    return a == b;
++  }
++
++  static hash_value_type ComputeHash(const internal_key_type &Key) {
++    return Key;
++  }
++
++  static internal_key_type GetInternalKey(const external_key_type &Name) {
++    return Name;
++  }
++
++  static std::pair<unsigned, unsigned>
++  ReadKeyDataLength(const unsigned char *&d);
++
++  internal_key_type ReadKey(const unsigned char *d, unsigned);
++
++  void ReadDataInto(internal_key_type, const unsigned char *d, unsigned DataLen,
++                    data_type_builder &Val);
++
++  static void MergeDataInto(const data_type &From, data_type_builder &To) {
++    To.Data.reserve(To.Data.size() + From.size());
++    for (LazySpecializationInfo Info : From)
++      To.insert(Info);
++  }
++
++  file_type ReadFileRef(const unsigned char *&d);
++};
++
++struct LazySpecializationInfoLookupTable {
++  MultiOnDiskHashTable<LazySpecializationInfoLookupTrait> Table;
++};
++
+ /// Base class for the trait describing the on-disk hash table for the
+ /// identifiers in an AST file.
+ ///
+@@ -291,4 +386,32 @@ using HeaderFileInfoLookupTable =
+ 
+ } // namespace clang
+ 
++namespace llvm {
++// ID is unique in LazySpecializationInfo, it is redundant to calculate
++// IsPartial.
++template <>
++struct DenseMapInfo<clang::serialization::reader::LazySpecializationInfo> {
++  using LazySpecializationInfo =
++      clang::serialization::reader::LazySpecializationInfo;
++  using Wrapped = DenseMapInfo<clang::serialization::DeclID>;
++
++  static inline LazySpecializationInfo getEmptyKey() {
++    return {(clang::GlobalDeclID)Wrapped::getEmptyKey(), false};
++  }
++
++  static inline LazySpecializationInfo getTombstoneKey() {
++    return {(clang::GlobalDeclID)Wrapped::getTombstoneKey(), false};
++  }
++
++  static unsigned getHashValue(const LazySpecializationInfo &Key) {
++    return Wrapped::getHashValue(Key.ID.getRawValue());
++  }
++
++  static bool isEqual(const LazySpecializationInfo &LHS,
++                      const LazySpecializationInfo &RHS) {
++    return LHS.ID == RHS.ID;
++  }
++};
++} // end namespace llvm
++
+ #endif // LLVM_CLANG_LIB_SERIALIZATION_ASTREADERINTERNALS_H
+diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
+index c6289493fce1..59a2dad2aa4a 100644
+--- a/clang/lib/Serialization/ASTWriter.cpp
++++ b/clang/lib/Serialization/ASTWriter.cpp
+@@ -13,6 +13,7 @@
+ #include "ASTCommon.h"
+ #include "ASTReaderInternals.h"
+ #include "MultiOnDiskHashTable.h"
++#include "TemplateArgumentHasher.h"
+ #include "clang/AST/ASTContext.h"
+ #include "clang/AST/ASTUnresolvedSet.h"
+ #include "clang/AST/AbstractTypeWriter.h"
+@@ -4125,6 +4126,159 @@ public:
+ 
+ } // namespace
+ 
++namespace {
++class LazySpecializationInfoLookupTrait {
++  ASTWriter &Writer;
++  llvm::SmallVector<serialization::reader::LazySpecializationInfo, 64> Specs;
++
++public:
++  using key_type = unsigned;
++  using key_type_ref = key_type;
++
++  /// A start and end index into Specs, representing a sequence of decls.
++  using data_type = std::pair<unsigned, unsigned>;
++  using data_type_ref = const data_type &;
++
++  using hash_value_type = unsigned;
++  using offset_type = unsigned;
++
++  explicit LazySpecializationInfoLookupTrait(ASTWriter &Writer)
++      : Writer(Writer) {}
++
++  template <typename Col> data_type getData(Col &&C) {
++    unsigned Start = Specs.size();
++    for (auto *D : C) {
++      bool IsPartial = isa<ClassTemplatePartialSpecializationDecl,
++                           VarTemplatePartialSpecializationDecl>(D);
++      NamedDecl *ND = getDeclForLocalLookup(
++                           Writer.getLangOpts(), const_cast<NamedDecl *>(D));
++      Specs.push_back({GlobalDeclID(Writer.GetDeclRef(ND).getRawValue()),
++                       IsPartial});
++    }
++    return std::make_pair(Start, Specs.size());
++  }
++
++  data_type ImportData(
++      const reader::LazySpecializationInfoLookupTrait::data_type &FromReader) {
++    unsigned Start = Specs.size();
++    for (auto ID : FromReader)
++      Specs.push_back(ID);
++    return std::make_pair(Start, Specs.size());
++  }
++
++  static bool EqualKey(key_type_ref a, key_type_ref b) { return a == b; }
++
++  hash_value_type ComputeHash(key_type Name) { return Name; }
++
++  void EmitFileRef(raw_ostream &Out, ModuleFile *F) const {
++    assert(Writer.hasChain() &&
++           "have reference to loaded module file but no chain?");
++
++    using namespace llvm::support;
++    endian::write<uint32_t>(Out, Writer.getChain()->getModuleFileID(F),
++                            llvm::endianness::little);
++  }
++
++  std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &Out,
++                                                  key_type HashValue,
++                                                  data_type_ref Lookup) {
++    // 4 bytes for each slot.
++    unsigned KeyLen = 4;
++    unsigned DataLen = serialization::reader::LazySpecializationInfo::Length *
++                       (Lookup.second - Lookup.first);
++
++    return emitULEBKeyDataLength(KeyLen, DataLen, Out);
++  }
++
++  void EmitKey(raw_ostream &Out, key_type HashValue, unsigned) {
++    using namespace llvm::support;
++
++    endian::Writer LE(Out, llvm::endianness::little);
++    LE.write<uint32_t>(HashValue);
++  }
++
++  void EmitData(raw_ostream &Out, key_type_ref, data_type Lookup,
++                unsigned DataLen) {
++    using namespace llvm::support;
++
++    endian::Writer LE(Out, llvm::endianness::little);
++    uint64_t Start = Out.tell();
++    (void)Start;
++    for (unsigned I = Lookup.first, N = Lookup.second; I != N; ++I) {
++      LE.write<DeclID>(Specs[I].ID.getRawValue());
++      LE.write<bool>(Specs[I].IsPartial);
++    }
++    assert(Out.tell() - Start == DataLen && "Data length is wrong");
++  }
++};
++
++unsigned CalculateODRHashForSpecs(const Decl *Spec) {
++  ArrayRef<TemplateArgument> Args;
++  if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(Spec))
++    Args = CTSD->getTemplateArgs().asArray();
++  else if (auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(Spec))
++    Args = VTSD->getTemplateArgs().asArray();
++  else if (auto *FD = dyn_cast<FunctionDecl>(Spec))
++    Args = FD->getTemplateSpecializationArgs()->asArray();
++  else
++    llvm_unreachable("New Specialization Kind?");
++
++  return StableHashForTemplateArguments(Args);
++}
++} // namespace
++
++void ASTWriter::GenerateSpecializationInfoLookupTable(
++    const NamedDecl *D, llvm::SmallVectorImpl<const Decl *> &Specializations,
++    llvm::SmallVectorImpl<char> &LookupTable) {
++  assert(D->isFirstDecl());
++
++  // Create the on-disk hash table representation.
++  MultiOnDiskHashTableGenerator<reader::LazySpecializationInfoLookupTrait,
++                                LazySpecializationInfoLookupTrait>
++      Generator;
++  LazySpecializationInfoLookupTrait Trait(*this);
++
++  llvm::DenseMap<unsigned, llvm::SmallVector<const NamedDecl *, 4>>
++      SpecializationMaps;
++
++  for (auto *Specialization : Specializations) {
++    unsigned HashedValue = CalculateODRHashForSpecs(Specialization);
++
++    auto Iter = SpecializationMaps.find(HashedValue);
++    if (Iter == SpecializationMaps.end())
++      Iter = SpecializationMaps
++                 .try_emplace(HashedValue,
++                              llvm::SmallVector<const NamedDecl *, 4>())
++                 .first;
++
++    Iter->second.push_back(cast<NamedDecl>(Specialization));
++  }
++
++  for (auto Iter : SpecializationMaps)
++    Generator.insert(Iter.first, Trait.getData(Iter.second), Trait);
++
++  auto *Lookups =
++      Chain ? Chain->getLoadedSpecializationsLookupTables(D) : nullptr;
++  // We can't OmitDataWithSameKeyInBase since the lazy load specialization
++  // mechanism assumes that different specializations can share the same
++  // hash key.
++  Generator.emit(LookupTable, Trait, Lookups ? &Lookups->Table : nullptr,
++                 /*OmitDataWithSameKeyInBase=*/false);
++}
++
++uint64_t ASTWriter::WriteSpecializationInfoLookupTable(
++    const NamedDecl *D, llvm::SmallVectorImpl<const Decl *> &Specializations) {
++
++  llvm::SmallString<4096> LookupTable;
++  GenerateSpecializationInfoLookupTable(D, Specializations, LookupTable);
++
++  uint64_t Offset = Stream.GetCurrentBitNo();
++  RecordData::value_type Record[] = {DECL_SPECIALIZATIONS};
++  Stream.EmitRecordWithBlob(DeclSpecializationsAbbrev, Record, LookupTable);
++
++  return Offset;
++}
++
+ bool ASTWriter::isLookupResultExternal(StoredDeclsList &Result,
+                                        DeclContext *DC) {
+   return Result.hasExternalDecls() &&
+@@ -5652,7 +5806,7 @@ void ASTWriter::WriteDeclAndTypes(ASTContext &Context) {
+   // Keep writing types, declarations, and declaration update records
+   // until we've emitted all of them.
+   RecordData DeclUpdatesOffsetsRecord;
+-  Stream.EnterSubblock(DECLTYPES_BLOCK_ID, /*bits for abbreviations*/5);
++  Stream.EnterSubblock(DECLTYPES_BLOCK_ID, /*bits for abbreviations*/6);
+   DeclTypesBlockStartOffset = Stream.GetCurrentBitNo();
+   WriteTypeAbbrevs();
+   WriteDeclAbbrevs();
+@@ -5706,6 +5860,9 @@ void ASTWriter::WriteDeclAndTypes(ASTContext &Context) {
+     Stream.EmitRecord(DELAYED_NAMESPACE_LEXICAL_VISIBLE_RECORD,
+                       DelayedNamespaceRecord);
+ 
++  if (!SpecializationsUpdates.empty())
++    WriteSpecializationsUpdates();
++
+   const TranslationUnitDecl *TU = Context.getTranslationUnitDecl();
+   // Create a lexical update block containing all of the declarations in the
+   // translation unit that do not come from other AST files.
+@@ -5749,6 +5906,27 @@ void ASTWriter::WriteDeclAndTypes(ASTContext &Context) {
+     WriteDeclContextVisibleUpdate(DC);
+ }
+ 
++void ASTWriter::WriteSpecializationsUpdates() {
++  auto Abv = std::make_shared<llvm::BitCodeAbbrev>();
++  Abv->Add(llvm::BitCodeAbbrevOp(CXX_ADDED_TEMPLATE_SPECIALIZATION));
++  Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::VBR, 6));
++  Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob));
++  auto UpdateSpecializationAbbrev = Stream.EmitAbbrev(std::move(Abv));
++
++  for (auto &SpecializationUpdate : SpecializationsUpdates) {
++    const NamedDecl *D = SpecializationUpdate.first;
++
++    llvm::SmallString<4096> LookupTable;
++    GenerateSpecializationInfoLookupTable(D, SpecializationUpdate.second,
++                                          LookupTable);
++
++    // Write the lookup table
++    RecordData::value_type Record[] = {CXX_ADDED_TEMPLATE_SPECIALIZATION,
++                                       getDeclID(D).getRawValue()};
++    Stream.EmitRecordWithBlob(UpdateSpecializationAbbrev, Record, LookupTable);
++  }
++}
++
+ void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) {
+   if (DeclUpdates.empty())
+     return;
+@@ -5777,12 +5955,10 @@ void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) {
+ 
+       switch (Kind) {
+       case UPD_CXX_ADDED_IMPLICIT_MEMBER:
+-      case UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION:
+       case UPD_CXX_ADDED_ANONYMOUS_NAMESPACE:
+         assert(Update.getDecl() && "no decl to add?");
+         Record.AddDeclRef(Update.getDecl());
+         break;
+-
+       case UPD_CXX_ADDED_FUNCTION_DEFINITION:
+       case UPD_CXX_ADDED_VAR_DEFINITION:
+         break;
+diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
+index 555f6325da64..9147c3683ad1 100644
+--- a/clang/lib/Serialization/ASTWriterDecl.cpp
++++ b/clang/lib/Serialization/ASTWriterDecl.cpp
+@@ -178,11 +178,12 @@ namespace clang {
+       Record.AddSourceLocation(typeParams->getRAngleLoc());
+     }
+ 
+-    /// Add to the record the first declaration from each module file that
+-    /// provides a declaration of D. The intent is to provide a sufficient
+-    /// set such that reloading this set will load all current redeclarations.
+-    void AddFirstDeclFromEachModule(const Decl *D, bool IncludeLocal) {
+-      llvm::MapVector<ModuleFile*, const Decl*> Firsts;
++    /// Collect the first declaration from each module file that provides a
++    /// declaration of D.
++    void CollectFirstDeclFromEachModule(
++        const Decl *D, bool IncludeLocal,
++        llvm::MapVector<ModuleFile *, const Decl *> &Firsts) {
++
+       // FIXME: We can skip entries that we know are implied by others.
+       for (const Decl *R = D->getMostRecentDecl(); R; R = R->getPreviousDecl()) {
+         if (R->isFromASTFile())
+@@ -190,10 +191,35 @@ namespace clang {
+         else if (IncludeLocal)
+           Firsts[nullptr] = R;
+       }
++    }
++
++    /// Add to the record the first declaration from each module file that
++    /// provides a declaration of D. The intent is to provide a sufficient
++    /// set such that reloading this set will load all current redeclarations.
++    void AddFirstDeclFromEachModule(const Decl *D, bool IncludeLocal) {
++      llvm::MapVector<ModuleFile *, const Decl *> Firsts;
++      CollectFirstDeclFromEachModule(D, IncludeLocal, Firsts);
++
+       for (const auto &F : Firsts)
+         Record.AddDeclRef(F.second);
+     }
+ 
++    /// Add to the record the first template specialization from each module
++    /// file that provides a declaration of D. We store the DeclId and an
++    /// ODRHash of the template arguments of D which should provide enough
++    /// information to load D only if the template instantiator needs it.
++    void AddFirstSpecializationDeclFromEachModule(
++        const Decl *D, llvm::SmallVectorImpl<const Decl *> &SpecsInMap) {
++      assert((isa<ClassTemplateSpecializationDecl>(D) ||
++              isa<VarTemplateSpecializationDecl>(D) || isa<FunctionDecl>(D)) &&
++             "Must not be called with other decls");
++      llvm::MapVector<ModuleFile *, const Decl *> Firsts;
++      CollectFirstDeclFromEachModule(D, /*IncludeLocal*/ true, Firsts);
++
++      for (const auto &F : Firsts)
++        SpecsInMap.push_back(F.second);
++    }
++
+     /// Get the specialization decl from an entry in the specialization list.
+     template <typename EntryType>
+     typename RedeclarableTemplateDecl::SpecEntryTraits<EntryType>::DeclType *
+@@ -206,7 +232,8 @@ namespace clang {
+     decltype(T::PartialSpecializations) &getPartialSpecializations(T *Common) {
+       return Common->PartialSpecializations;
+     }
+-    ArrayRef<Decl> getPartialSpecializations(FunctionTemplateDecl::Common *) {
++    MutableArrayRef<FunctionTemplateSpecializationInfo>
++    getPartialSpecializations(FunctionTemplateDecl::Common *) {
+       return std::nullopt;
+     }
+ 
+@@ -217,20 +244,12 @@ namespace clang {
+       // If we have any lazy specializations, and the external AST source is
+       // our chained AST reader, we can just write out the DeclIDs. Otherwise,
+       // we need to resolve them to actual declarations.
+-      if (Writer.Chain != Writer.Context->getExternalSource() &&
+-          Common->LazySpecializations) {
++      if (Writer.Chain != Writer.Context->getExternalSource() && Writer.Chain &&
++          Writer.Chain->getLoadedSpecializationsLookupTables(D)) {
+         D->LoadLazySpecializations();
+-        assert(!Common->LazySpecializations);
++        assert(!Writer.Chain->getLoadedSpecializationsLookupTables(D));
+       }
+ 
+-      ArrayRef<GlobalDeclID> LazySpecializations;
+-      if (auto *LS = Common->LazySpecializations)
+-        LazySpecializations = llvm::ArrayRef(LS + 1, LS[0].getRawValue());
+-
+-      // Add a slot to the record for the number of specializations.
+-      unsigned I = Record.size();
+-      Record.push_back(0);
+-
+       // AddFirstDeclFromEachModule might trigger deserialization, invalidating
+       // *Specializations iterators.
+       llvm::SmallVector<const Decl*, 16> Specs;
+@@ -239,16 +258,15 @@ namespace clang {
+       for (auto &Entry : getPartialSpecializations(Common))
+         Specs.push_back(getSpecializationDecl(Entry));
+ 
++      llvm::SmallVector<const Decl *, 16> SpecsInOnDiskMap = Specs;
++
+       for (auto *D : Specs) {
+         assert(D->isCanonicalDecl() && "non-canonical decl in set");
+-        AddFirstDeclFromEachModule(D, /*IncludeLocal*/true);
++        AddFirstSpecializationDeclFromEachModule(D, SpecsInOnDiskMap);
+       }
+-      Record.append(
+-          DeclIDIterator<GlobalDeclID, DeclID>(LazySpecializations.begin()),
+-          DeclIDIterator<GlobalDeclID, DeclID>(LazySpecializations.end()));
+ 
+-      // Update the size entry we added earlier.
+-      Record[I] = Record.size() - I - 1;
++      Record.AddOffset(
++          Writer.WriteSpecializationInfoLookupTable(D, SpecsInOnDiskMap));
+     }
+ 
+     /// Ensure that this template specialization is associated with the specified
+@@ -269,8 +287,8 @@ namespace clang {
+       if (Writer.getFirstLocalDecl(Specialization) != Specialization)
+         return;
+ 
+-      Writer.DeclUpdates[Template].push_back(ASTWriter::DeclUpdate(
+-          UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION, Specialization));
++      Writer.SpecializationsUpdates[cast<NamedDecl>(Template)].push_back(
++          cast<NamedDecl>(Specialization));
+     }
+   };
+ }
+@@ -2751,6 +2769,11 @@ void ASTWriter::WriteDeclAbbrevs() {
+   Abv->Add(BitCodeAbbrevOp(serialization::DECL_CONTEXT_VISIBLE));
+   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));
+   DeclContextVisibleLookupAbbrev = Stream.EmitAbbrev(std::move(Abv));
++
++  Abv = std::make_shared<BitCodeAbbrev>();
++  Abv->Add(BitCodeAbbrevOp(serialization::DECL_SPECIALIZATIONS));
++  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));
++  DeclSpecializationsAbbrev = Stream.EmitAbbrev(std::move(Abv));
+ }
+ 
+ /// isRequiredDecl - Check if this is a "required" Decl, which must be seen by
+diff --git a/clang/lib/Serialization/CMakeLists.txt b/clang/lib/Serialization/CMakeLists.txt
+index 99c47c15a2f4..b1fc0345047f 100644
+--- a/clang/lib/Serialization/CMakeLists.txt
++++ b/clang/lib/Serialization/CMakeLists.txt
+@@ -23,6 +23,7 @@ add_clang_library(clangSerialization
+   ModuleManager.cpp
+   PCHContainerOperations.cpp
+   ObjectFilePCHContainerReader.cpp
++  TemplateArgumentHasher.cpp
+ 
+   ADDITIONAL_HEADERS
+   ASTCommon.h
+diff --git a/clang/lib/Serialization/MultiOnDiskHashTable.h b/clang/lib/Serialization/MultiOnDiskHashTable.h
+index a0d75ec3a9e7..76910f4d1e21 100644
+--- a/clang/lib/Serialization/MultiOnDiskHashTable.h
++++ b/clang/lib/Serialization/MultiOnDiskHashTable.h
+@@ -306,7 +306,7 @@ public:
+   }
+ 
+   void emit(llvm::SmallVectorImpl<char> &Out, WriterInfo &Info,
+-            const BaseTable *Base) {
++            const BaseTable *Base, bool OmitDataWithSameKeyInBase = true) {
+     using namespace llvm::support;
+ 
+     llvm::raw_svector_ostream OutStream(Out);
+@@ -326,7 +326,7 @@ public:
+ 
+         // Add all merged entries from Base to the generator.
+         for (auto &KV : Merged->Data) {
+-          if (!Gen.contains(KV.first, Info))
++          if (!OmitDataWithSameKeyInBase || !Gen.contains(KV.first, Info))
+             Gen.insert(KV.first, Info.ImportData(KV.second), Info);
+         }
+       } else {
+diff --git a/clang/lib/Serialization/TemplateArgumentHasher.cpp b/clang/lib/Serialization/TemplateArgumentHasher.cpp
+new file mode 100644
+index 000000000000..ab7ea262cddf
+--- /dev/null
++++ b/clang/lib/Serialization/TemplateArgumentHasher.cpp
+@@ -0,0 +1,405 @@
++//===- TemplateArgumentHasher.cpp - Hash Template Arguments -----*- C++ -*-===//
++//
++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
++// See https://llvm.org/LICENSE.txt for license information.
++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
++//
++//===----------------------------------------------------------------------===//
++
++#include "TemplateArgumentHasher.h"
++#include "clang/AST/APValue.h"
++#include "clang/AST/Decl.h"
++#include "clang/AST/DeclCXX.h"
++#include "clang/AST/DeclTemplate.h"
++#include "clang/AST/DeclarationName.h"
++#include "clang/AST/TypeVisitor.h"
++#include "clang/Basic/IdentifierTable.h"
++#include "llvm/ADT/FoldingSet.h"
++
++using namespace clang;
++
++namespace {
++
++class TemplateArgumentHasher {
++  // If we bail out during the process of calculating hash values for
++  // template arguments for any reason. We're allowed to do it since
++  // TemplateArgumentHasher are only required to give the same hash value
++  // for the same template arguments, but not required to give different
++  // hash value for different template arguments.
++  //
++  // So in the worst case, it is still a valid implementation to give all
++  // inputs the same BailedOutValue as output.
++  bool BailedOut = false;
++  static constexpr unsigned BailedOutValue = 0x12345678;
++
++  llvm::FoldingSetNodeID ID;
++
++public:
++  TemplateArgumentHasher() = default;
++
++  void AddTemplateArgument(TemplateArgument TA);
++
++  void AddInteger(unsigned V) { ID.AddInteger(V); }
++
++  unsigned getValue() {
++    if (BailedOut)
++      return BailedOutValue;
++
++    return ID.computeStableHash();
++  }
++
++  void setBailedOut() { BailedOut = true; }
++
++  void AddType(const Type *T);
++  void AddQualType(QualType T);
++  void AddDecl(const Decl *D);
++  void AddStructuralValue(const APValue &);
++  void AddTemplateName(TemplateName Name);
++  void AddDeclarationName(DeclarationName Name);
++  void AddIdentifierInfo(const IdentifierInfo *II);
++};
++
++void TemplateArgumentHasher::AddTemplateArgument(TemplateArgument TA) {
++  const auto Kind = TA.getKind();
++  AddInteger(Kind);
++
++  switch (Kind) {
++  case TemplateArgument::Null:
++    llvm_unreachable("Expected valid TemplateArgument");
++  case TemplateArgument::Type:
++    AddQualType(TA.getAsType());
++    break;
++  case TemplateArgument::Declaration:
++    AddDecl(TA.getAsDecl());
++    break;
++  case TemplateArgument::NullPtr:
++    ID.AddPointer(nullptr);
++    break;
++  case TemplateArgument::Integral: {
++    // There are integrals (e.g.: _BitInt(128)) that cannot be represented as
++    // any builtin integral type, so we use the hash of APSInt instead.
++    TA.getAsIntegral().Profile(ID);
++    break;
++  }
++  case TemplateArgument::StructuralValue:
++    AddQualType(TA.getStructuralValueType());
++    AddStructuralValue(TA.getAsStructuralValue());
++    break;
++  case TemplateArgument::Template:
++  case TemplateArgument::TemplateExpansion:
++    AddTemplateName(TA.getAsTemplateOrTemplatePattern());
++    break;
++  case TemplateArgument::Expression:
++    // If we meet expression in template argument, it implies
++    // that the template is still dependent. It is meaningless
++    // to get a stable hash for the template. Bail out simply.
++    BailedOut = true;
++    break;
++  case TemplateArgument::Pack:
++    AddInteger(TA.pack_size());
++    for (auto SubTA : TA.pack_elements()) {
++      AddTemplateArgument(SubTA);
++    }
++    break;
++  }
++}
++
++void TemplateArgumentHasher::AddStructuralValue(const APValue &Value) {
++  auto Kind = Value.getKind();
++  AddInteger(Kind);
++
++  // 'APValue::Profile' uses pointer values to make hash for LValue and
++  // MemberPointer, but they differ from one compiler invocation to another.
++  // It may be difficult to handle such cases. Bail out simply.
++
++  if (Kind == APValue::LValue || Kind == APValue::MemberPointer) {
++    BailedOut = true;
++    return;
++  }
++
++  Value.Profile(ID);
++}
++
++void TemplateArgumentHasher::AddTemplateName(TemplateName Name) {
++  switch (Name.getKind()) {
++  case TemplateName::Template:
++    AddDecl(Name.getAsTemplateDecl());
++    break;
++  case TemplateName::QualifiedTemplate: {
++    QualifiedTemplateName *QTN = Name.getAsQualifiedTemplateName();
++    AddTemplateName(QTN->getUnderlyingTemplate());
++    break;
++  }
++  case TemplateName::OverloadedTemplate:
++  case TemplateName::AssumedTemplate:
++  case TemplateName::DependentTemplate:
++  case TemplateName::SubstTemplateTemplateParm:
++  case TemplateName::SubstTemplateTemplateParmPack:
++    BailedOut = true;
++    break;
++  case TemplateName::UsingTemplate:
++    UsingShadowDecl *USD = Name.getAsUsingShadowDecl();
++    if (USD)
++      AddDecl(USD->getTargetDecl());
++    else
++      BailedOut = true;
++    break;
++  }
++}
++
++void TemplateArgumentHasher::AddIdentifierInfo(const IdentifierInfo *II) {
++  assert(II && "Expecting non-null pointer.");
++  ID.AddString(II->getName());
++}
++
++void TemplateArgumentHasher::AddDeclarationName(DeclarationName Name) {
++  if (Name.isEmpty())
++    return;
++
++  switch (Name.getNameKind()) {
++  case DeclarationName::Identifier:
++    AddIdentifierInfo(Name.getAsIdentifierInfo());
++    break;
++  case DeclarationName::ObjCZeroArgSelector:
++  case DeclarationName::ObjCOneArgSelector:
++  case DeclarationName::ObjCMultiArgSelector:
++    BailedOut = true;
++    break;
++  case DeclarationName::CXXConstructorName:
++  case DeclarationName::CXXDestructorName:
++    AddQualType(Name.getCXXNameType());
++    break;
++  case DeclarationName::CXXOperatorName:
++    AddInteger(Name.getCXXOverloadedOperator());
++    break;
++  case DeclarationName::CXXLiteralOperatorName:
++    AddIdentifierInfo(Name.getCXXLiteralIdentifier());
++    break;
++  case DeclarationName::CXXConversionFunctionName:
++    AddQualType(Name.getCXXNameType());
++    break;
++  case DeclarationName::CXXUsingDirective:
++    break;
++  case DeclarationName::CXXDeductionGuideName: {
++    if (auto *Template = Name.getCXXDeductionGuideTemplate())
++      AddDecl(Template);
++  }
++  }
++}
++
++void TemplateArgumentHasher::AddDecl(const Decl *D) {
++  const NamedDecl *ND = dyn_cast<NamedDecl>(D);
++  if (!ND) {
++    BailedOut = true;
++    return;
++  }
++
++  AddDeclarationName(ND->getDeclName());
++}
++
++void TemplateArgumentHasher::AddQualType(QualType T) {
++  if (T.isNull()) {
++    BailedOut = true;
++    return;
++  }
++  SplitQualType split = T.split();
++  AddInteger(split.Quals.getAsOpaqueValue());
++  AddType(split.Ty);
++}
++
++// Process a Type pointer.  Add* methods call back into TemplateArgumentHasher
++// while Visit* methods process the relevant parts of the Type.
++// Any unhandled type will make the hash computation bail out.
++class TypeVisitorHelper : public TypeVisitor<TypeVisitorHelper> {
++  typedef TypeVisitor<TypeVisitorHelper> Inherited;
++  llvm::FoldingSetNodeID &ID;
++  TemplateArgumentHasher &Hash;
++
++public:
++  TypeVisitorHelper(llvm::FoldingSetNodeID &ID, TemplateArgumentHasher &Hash)
++      : ID(ID), Hash(Hash) {}
++
++  void AddDecl(const Decl *D) {
++    if (D)
++      Hash.AddDecl(D);
++    else
++      Hash.AddInteger(0);
++  }
++
++  void AddQualType(QualType T) { Hash.AddQualType(T); }
++
++  void AddType(const Type *T) {
++    if (T)
++      Hash.AddType(T);
++    else
++      Hash.AddInteger(0);
++  }
++
++  void VisitQualifiers(Qualifiers Quals) {
++    Hash.AddInteger(Quals.getAsOpaqueValue());
++  }
++
++  void Visit(const Type *T) { Inherited::Visit(T); }
++
++  // Unhandled types. Bail out simply.
++  void VisitType(const Type *T) { Hash.setBailedOut(); }
++
++  void VisitAdjustedType(const AdjustedType *T) {
++    AddQualType(T->getOriginalType());
++  }
++
++  void VisitDecayedType(const DecayedType *T) {
++    // getDecayedType and getPointeeType are derived from getAdjustedType
++    // and don't need to be separately processed.
++    VisitAdjustedType(T);
++  }
++
++  void VisitArrayType(const ArrayType *T) {
++    AddQualType(T->getElementType());
++    Hash.AddInteger(llvm::to_underlying(T->getSizeModifier()));
++    VisitQualifiers(T->getIndexTypeQualifiers());
++  }
++  void VisitConstantArrayType(const ConstantArrayType *T) {
++    T->getSize().Profile(ID);
++    VisitArrayType(T);
++  }
++
++  void VisitAttributedType(const AttributedType *T) {
++    Hash.AddInteger(T->getAttrKind());
++    AddQualType(T->getModifiedType());
++  }
++
++  void VisitBuiltinType(const BuiltinType *T) { Hash.AddInteger(T->getKind()); }
++
++  void VisitComplexType(const ComplexType *T) {
++    AddQualType(T->getElementType());
++  }
++
++  void VisitDecltypeType(const DecltypeType *T) {
++    AddQualType(T->getUnderlyingType());
++  }
++
++  void VisitDeducedType(const DeducedType *T) {
++    AddQualType(T->getDeducedType());
++  }
++
++  void VisitAutoType(const AutoType *T) { VisitDeducedType(T); }
++
++  void VisitDeducedTemplateSpecializationType(
++      const DeducedTemplateSpecializationType *T) {
++    Hash.AddTemplateName(T->getTemplateName());
++    VisitDeducedType(T);
++  }
++
++  void VisitFunctionType(const FunctionType *T) {
++    AddQualType(T->getReturnType());
++    T->getExtInfo().Profile(ID);
++    Hash.AddInteger(T->isConst());
++    Hash.AddInteger(T->isVolatile());
++    Hash.AddInteger(T->isRestrict());
++  }
++
++  void VisitFunctionNoProtoType(const FunctionNoProtoType *T) {
++    VisitFunctionType(T);
++  }
++
++  void VisitFunctionProtoType(const FunctionProtoType *T) {
++    Hash.AddInteger(T->getNumParams());
++    for (auto ParamType : T->getParamTypes())
++      AddQualType(ParamType);
++
++    VisitFunctionType(T);
++  }
++
++  void VisitMemberPointerType(const MemberPointerType *T) {
++    AddQualType(T->getPointeeType());
++    AddType(T->getClass());
++  }
++
++  void VisitPackExpansionType(const PackExpansionType *T) {
++    AddQualType(T->getPattern());
++  }
++
++  void VisitParenType(const ParenType *T) { AddQualType(T->getInnerType()); }
++
++  void VisitPointerType(const PointerType *T) {
++    AddQualType(T->getPointeeType());
++  }
++
++  void VisitReferenceType(const ReferenceType *T) {
++    AddQualType(T->getPointeeTypeAsWritten());
++  }
++
++  void VisitLValueReferenceType(const LValueReferenceType *T) {
++    VisitReferenceType(T);
++  }
++
++  void VisitRValueReferenceType(const RValueReferenceType *T) {
++    VisitReferenceType(T);
++  }
++
++  void
++  VisitSubstTemplateTypeParmPackType(const SubstTemplateTypeParmPackType *T) {
++    AddDecl(T->getAssociatedDecl());
++    Hash.AddTemplateArgument(T->getArgumentPack());
++  }
++
++  void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *T) {
++    AddDecl(T->getAssociatedDecl());
++    AddQualType(T->getReplacementType());
++  }
++
++  void VisitTagType(const TagType *T) { AddDecl(T->getDecl()); }
++
++  void VisitRecordType(const RecordType *T) { VisitTagType(T); }
++  void VisitEnumType(const EnumType *T) { VisitTagType(T); }
++
++  void VisitTemplateSpecializationType(const TemplateSpecializationType *T) {
++    Hash.AddInteger(T->template_arguments().size());
++    for (const auto &TA : T->template_arguments()) {
++      Hash.AddTemplateArgument(TA);
++    }
++    Hash.AddTemplateName(T->getTemplateName());
++  }
++
++  void VisitTemplateTypeParmType(const TemplateTypeParmType *T) {
++    Hash.AddInteger(T->getDepth());
++    Hash.AddInteger(T->getIndex());
++    Hash.AddInteger(T->isParameterPack());
++  }
++
++  void VisitTypedefType(const TypedefType *T) { AddDecl(T->getDecl()); }
++
++  void VisitElaboratedType(const ElaboratedType *T) {
++    AddQualType(T->getNamedType());
++  }
++
++  void VisitUnaryTransformType(const UnaryTransformType *T) {
++    AddQualType(T->getUnderlyingType());
++    AddQualType(T->getBaseType());
++  }
++
++  void VisitVectorType(const VectorType *T) {
++    AddQualType(T->getElementType());
++    Hash.AddInteger(T->getNumElements());
++    Hash.AddInteger(llvm::to_underlying(T->getVectorKind()));
++  }
++
++  void VisitExtVectorType(const ExtVectorType *T) { VisitVectorType(T); }
++};
++
++void TemplateArgumentHasher::AddType(const Type *T) {
++  assert(T && "Expecting non-null pointer.");
++  TypeVisitorHelper(ID, *this).Visit(T);
++}
++
++} // namespace
++
++unsigned clang::serialization::StableHashForTemplateArguments(
++    llvm::ArrayRef<TemplateArgument> Args) {
++  TemplateArgumentHasher Hasher;
++  Hasher.AddInteger(Args.size());
++  for (TemplateArgument Arg : Args)
++    Hasher.AddTemplateArgument(Arg);
++  return Hasher.getValue();
++}
+diff --git a/clang/lib/Serialization/TemplateArgumentHasher.h b/clang/lib/Serialization/TemplateArgumentHasher.h
+new file mode 100644
+index 000000000000..f23f1318afbb
+--- /dev/null
++++ b/clang/lib/Serialization/TemplateArgumentHasher.h
+@@ -0,0 +1,34 @@
++//===- TemplateArgumentHasher.h - Hash Template Arguments -------*- C++ -*-===//
++//
++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
++// See https://llvm.org/LICENSE.txt for license information.
++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
++//
++//===----------------------------------------------------------------------===//
++
++#include "clang/AST/TemplateBase.h"
++
++namespace clang {
++namespace serialization {
++
++/// Calculate a stable hash value for template arguments. We guarantee that
++/// the same template arguments must have the same hashed values. But we don't
++/// guarantee that the template arguments with the same hashed value are the
++/// same template arguments.
++///
++/// ODR hashing may not be the best mechanism to hash the template
++/// arguments. ODR hashing is (or perhaps, should be) about determining whether
++/// two things are spelled the same way and have the same meaning (as required
++/// by the C++ ODR), whereas what we want here is whether they have the same
++/// meaning regardless of spelling. Maybe we can get away with reusing ODR
++/// hashing anyway, on the basis that any canonical, non-dependent template
++/// argument should have the same (invented) spelling in every translation
++/// unit, but it is not sure that's true in all cases. There may still be cases
++/// where the canonical type includes some aspect of "whatever we saw first",
++/// in which case the ODR hash can differ across translation units for
++/// non-dependent, canonical template arguments that are spelled differently
++/// but have the same meaning. But it is not easy to raise examples.
++unsigned StableHashForTemplateArguments(llvm::ArrayRef<TemplateArgument> Args);
++
++} // namespace serialization
++} // namespace clang
+diff --git a/clang/test/Modules/odr_hash.cpp b/clang/test/Modules/odr_hash.cpp
+index fa8b2c81ab46..7cea3af3f41b 100644
+--- a/clang/test/Modules/odr_hash.cpp
++++ b/clang/test/Modules/odr_hash.cpp
+@@ -3084,8 +3084,8 @@ struct S5 {
+ };
+ #else
+ S5 s5;
+-// expected-error at second.h:* {{'PointersAndReferences::S5::x' from module 'SecondModule' is not present in definition of 'PointersAndReferences::S5' in module 'FirstModule'}}
+-// expected-note at first.h:* {{declaration of 'x' does not match}}
++// expected-error at first.h:* {{'PointersAndReferences::S5::x' from module 'FirstModule' is not present in definition of 'PointersAndReferences::S5' in module 'SecondModule'}}
++// expected-note at second.h:* {{declaration of 'x' does not match}}
+ #endif
+ 
+ #if defined(FIRST)
+diff --git a/clang/test/Modules/recursive-instantiations.cppm b/clang/test/Modules/recursive-instantiations.cppm
+new file mode 100644
+index 000000000000..d5854b0e647e
+--- /dev/null
++++ b/clang/test/Modules/recursive-instantiations.cppm
+@@ -0,0 +1,40 @@
++// RUN: rm -rf %t
++// RUN: mkdir -p %t
++// RUN: split-file %s %t
++//
++// RUN: %clang_cc1 -std=c++20 %t/type_traits.cppm -emit-module-interface -o %t/type_traits.pcm
++// RUN: %clang_cc1 -std=c++20 %t/test.cpp -fprebuilt-module-path=%t -verify
++
++//--- type_traits.cppm
++export module type_traits;
++
++export template <typename T>
++constexpr bool is_pod_v = __is_pod(T);
++
++//--- test.cpp
++// expected-no-diagnostics
++import type_traits;
++// Base is either void or wrapper<T>.
++template <class Base> struct wrapper : Base {};
++template <> struct wrapper<void> {};
++
++// wrap<0>::type<T> is wrapper<T>, wrap<1>::type<T> is wrapper<wrapper<T>>,
++// and so on.
++template <int N>
++struct wrap {
++  template <class Base>
++  using type = wrapper<typename wrap<N-1>::template type<Base>>;
++};
++
++template <>
++struct wrap<0> {
++  template <class Base>
++  using type = wrapper<Base>;
++};
++
++inline constexpr int kMaxRank = 40;
++template <int N, class Base = void>
++using rank = typename wrap<N>::template type<Base>;
++using rank_selector_t = rank<kMaxRank>;
++
++static_assert(is_pod_v<rank_selector_t>, "Must be POD");
+diff --git a/clang/test/OpenMP/target_parallel_ast_print.cpp b/clang/test/OpenMP/target_parallel_ast_print.cpp
+index 7e27ac7b92ca..3ee98bc525c1 100644
+--- a/clang/test/OpenMP/target_parallel_ast_print.cpp
++++ b/clang/test/OpenMP/target_parallel_ast_print.cpp
+@@ -38,10 +38,6 @@ struct S {
+ // CHECK:        static int TS;
+ // CHECK-NEXT:   #pragma omp threadprivate(S<int>::TS)
+ // CHECK-NEXT: }
+-// CHECK:      template<> struct S<char> {
+-// CHECK:        static char TS;
+-// CHECK-NEXT:   #pragma omp threadprivate(S<char>::TS)
+-// CHECK-NEXT: }
+ 
+ template <typename T, int C>
+ T tmain(T argc, T *argv) {
+diff --git a/clang/test/OpenMP/target_teams_ast_print.cpp b/clang/test/OpenMP/target_teams_ast_print.cpp
+index 8338f2a68f92..cc47ae92efac 100644
+--- a/clang/test/OpenMP/target_teams_ast_print.cpp
++++ b/clang/test/OpenMP/target_teams_ast_print.cpp
+@@ -40,10 +40,6 @@ struct S {
+ // CHECK:        static int TS;
+ // CHECK-NEXT:   #pragma omp threadprivate(S<int>::TS)
+ // CHECK-NEXT: }
+-// CHECK:      template<> struct S<long> {
+-// CHECK:        static long TS;
+-// CHECK-NEXT:   #pragma omp threadprivate(S<long>::TS)
+-// CHECK-NEXT: }
+ 
+ template <typename T, int C>
+ T tmain(T argc, T *argv) {
+diff --git a/clang/test/OpenMP/task_ast_print.cpp b/clang/test/OpenMP/task_ast_print.cpp
+index 2a6b8908a1e2..30fb7ab75cc8 100644
+--- a/clang/test/OpenMP/task_ast_print.cpp
++++ b/clang/test/OpenMP/task_ast_print.cpp
+@@ -87,10 +87,6 @@ struct S {
+ // CHECK:        static int TS;
+ // CHECK-NEXT:   #pragma omp threadprivate(S<int>::TS)
+ // CHECK-NEXT: }
+-// CHECK:      template<> struct S<long> {
+-// CHECK:        static long TS;
+-// CHECK-NEXT:   #pragma omp threadprivate(S<long>::TS)
+-// CHECK-NEXT: }
+ 
+ template <typename T, int C>
+ T tmain(T argc, T *argv) {
+diff --git a/clang/test/OpenMP/teams_ast_print.cpp b/clang/test/OpenMP/teams_ast_print.cpp
+index 0087f71ac9f7..597a9b2bdbdc 100644
+--- a/clang/test/OpenMP/teams_ast_print.cpp
++++ b/clang/test/OpenMP/teams_ast_print.cpp
+@@ -27,10 +27,6 @@ struct S {
+ // CHECK:        static int TS;
+ // CHECK-NEXT:   #pragma omp threadprivate(S<int>::TS)
+ // CHECK-NEXT: }
+-// CHECK:      template<> struct S<long> {
+-// CHECK:        static long TS;
+-// CHECK-NEXT:   #pragma omp threadprivate(S<long>::TS)
+-// CHECK-NEXT: }
+ 
+ template <typename T, int C>
+ T tmain(T argc, T *argv) {
+diff --git a/clang/unittests/Serialization/CMakeLists.txt b/clang/unittests/Serialization/CMakeLists.txt
+index e7eebd0cb982..e7005b5d511e 100644
+--- a/clang/unittests/Serialization/CMakeLists.txt
++++ b/clang/unittests/Serialization/CMakeLists.txt
+@@ -11,6 +11,7 @@ add_clang_unittest(SerializationTests
+   ModuleCacheTest.cpp
+   NoCommentsTest.cpp
+   PreambleInNamedModulesTest.cpp
++  LoadSpecLazilyTest.cpp
+   SourceLocationEncodingTest.cpp
+   VarDeclConstantInitTest.cpp
+   )
+diff --git a/clang/unittests/Serialization/LoadSpecLazilyTest.cpp b/clang/unittests/Serialization/LoadSpecLazilyTest.cpp
+new file mode 100644
+index 000000000000..76e3ccae3d3c
+--- /dev/null
++++ b/clang/unittests/Serialization/LoadSpecLazilyTest.cpp
+@@ -0,0 +1,260 @@
++//== unittests/Serialization/LoadSpecLazily.cpp ----------------------========//
++//
++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
++// See https://llvm.org/LICENSE.txt for license information.
++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
++//
++//===----------------------------------------------------------------------===//
++
++#include "clang/Frontend/CompilerInstance.h"
++#include "clang/Frontend/FrontendAction.h"
++#include "clang/Frontend/FrontendActions.h"
++#include "clang/Parse/ParseAST.h"
++#include "clang/Serialization/ASTDeserializationListener.h"
++#include "clang/Tooling/Tooling.h"
++#include "gtest/gtest.h"
++
++using namespace llvm;
++using namespace clang;
++using namespace clang::tooling;
++
++namespace {
++
++class LoadSpecLazilyTest : public ::testing::Test {
++  void SetUp() override {
++    ASSERT_FALSE(
++        sys::fs::createUniqueDirectory("load-spec-lazily-test", TestDir));
++  }
++
++  void TearDown() override { sys::fs::remove_directories(TestDir); }
++
++public:
++  SmallString<256> TestDir;
++
++  void addFile(StringRef Path, StringRef Contents) {
++    ASSERT_FALSE(sys::path::is_absolute(Path));
++
++    SmallString<256> AbsPath(TestDir);
++    sys::path::append(AbsPath, Path);
++
++    ASSERT_FALSE(
++        sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath)));
++
++    std::error_code EC;
++    llvm::raw_fd_ostream OS(AbsPath, EC);
++    ASSERT_FALSE(EC);
++    OS << Contents;
++  }
++
++  std::string GenerateModuleInterface(StringRef ModuleName,
++                                      StringRef Contents) {
++    std::string FileName = llvm::Twine(ModuleName + ".cppm").str();
++    addFile(FileName, Contents);
++
++    IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
++        CompilerInstance::createDiagnostics(new DiagnosticOptions());
++    CreateInvocationOptions CIOpts;
++    CIOpts.Diags = Diags;
++    CIOpts.VFS = llvm::vfs::createPhysicalFileSystem();
++
++    std::string CacheBMIPath =
++        llvm::Twine(TestDir + "/" + ModuleName + ".pcm").str();
++    std::string PrebuiltModulePath =
++        "-fprebuilt-module-path=" + TestDir.str().str();
++    const char *Args[] = {"clang++",
++                          "-std=c++20",
++                          "--precompile",
++                          PrebuiltModulePath.c_str(),
++                          "-working-directory",
++                          TestDir.c_str(),
++                          "-I",
++                          TestDir.c_str(),
++                          FileName.c_str(),
++                          "-o",
++                          CacheBMIPath.c_str()};
++    std::shared_ptr<CompilerInvocation> Invocation =
++        createInvocation(Args, CIOpts);
++    EXPECT_TRUE(Invocation);
++
++    CompilerInstance Instance;
++    Instance.setDiagnostics(Diags.get());
++    Instance.setInvocation(Invocation);
++    Instance.getFrontendOpts().OutputFile = CacheBMIPath;
++    GenerateModuleInterfaceAction Action;
++    EXPECT_TRUE(Instance.ExecuteAction(Action));
++    EXPECT_FALSE(Diags->hasErrorOccurred());
++
++    return CacheBMIPath;
++  }
++};
++
++enum class CheckingMode { Forbidden, Required };
++
++class DeclsReaderListener : public ASTDeserializationListener {
++  StringRef SpeficiedName;
++  CheckingMode Mode;
++
++  bool ReadedSpecifiedName = false;
++
++public:
++  void DeclRead(GlobalDeclID ID, const Decl *D) override {
++    auto *ND = dyn_cast<NamedDecl>(D);
++    if (!ND)
++      return;
++
++    ReadedSpecifiedName |= ND->getName().contains(SpeficiedName);
++    if (Mode == CheckingMode::Forbidden) {
++      EXPECT_FALSE(ReadedSpecifiedName);
++    }
++  }
++
++  DeclsReaderListener(StringRef SpeficiedName, CheckingMode Mode)
++      : SpeficiedName(SpeficiedName), Mode(Mode) {}
++
++  ~DeclsReaderListener() {
++    if (Mode == CheckingMode::Required) {
++      EXPECT_TRUE(ReadedSpecifiedName);
++    }
++  }
++};
++
++class LoadSpecLazilyConsumer : public ASTConsumer {
++  DeclsReaderListener Listener;
++
++public:
++  LoadSpecLazilyConsumer(StringRef SpecifiedName, CheckingMode Mode)
++      : Listener(SpecifiedName, Mode) {}
++
++  ASTDeserializationListener *GetASTDeserializationListener() override {
++    return &Listener;
++  }
++};
++
++class CheckLoadSpecLazilyAction : public ASTFrontendAction {
++  StringRef SpecifiedName;
++  CheckingMode Mode;
++
++public:
++  std::unique_ptr<ASTConsumer>
++  CreateASTConsumer(CompilerInstance &CI, StringRef /*Unused*/) override {
++    return std::make_unique<LoadSpecLazilyConsumer>(SpecifiedName, Mode);
++  }
++
++  CheckLoadSpecLazilyAction(StringRef SpecifiedName, CheckingMode Mode)
++      : SpecifiedName(SpecifiedName), Mode(Mode) {}
++};
++
++TEST_F(LoadSpecLazilyTest, BasicTest) {
++  GenerateModuleInterface("M", R"cpp(
++export module M;
++export template <class T>
++class A {};
++export class ShouldNotBeLoaded {};
++export class Temp {
++   A<ShouldNotBeLoaded> AS;
++};
++  )cpp");
++
++  const char *test_file_contents = R"cpp(
++import M;
++A<int> a;
++  )cpp";
++  std::string DepArg = "-fprebuilt-module-path=" + TestDir.str().str();
++  EXPECT_TRUE(
++      runToolOnCodeWithArgs(std::make_unique<CheckLoadSpecLazilyAction>(
++                                "ShouldNotBeLoaded", CheckingMode::Forbidden),
++                            test_file_contents,
++                            {
++                                "-std=c++20",
++                                DepArg.c_str(),
++                                "-I",
++                                TestDir.c_str(),
++                            },
++                            "test.cpp"));
++}
++
++TEST_F(LoadSpecLazilyTest, ChainedTest) {
++  GenerateModuleInterface("M", R"cpp(
++export module M;
++export template <class T>
++class A {};
++  )cpp");
++
++  GenerateModuleInterface("N", R"cpp(
++export module N;
++export import M;
++export class ShouldNotBeLoaded {};
++export class Temp {
++   A<ShouldNotBeLoaded> AS;
++};
++  )cpp");
++
++  const char *test_file_contents = R"cpp(
++import N;
++A<int> a;
++  )cpp";
++  std::string DepArg = "-fprebuilt-module-path=" + TestDir.str().str();
++  EXPECT_TRUE(
++      runToolOnCodeWithArgs(std::make_unique<CheckLoadSpecLazilyAction>(
++                                "ShouldNotBeLoaded", CheckingMode::Forbidden),
++                            test_file_contents,
++                            {
++                                "-std=c++20",
++                                DepArg.c_str(),
++                                "-I",
++                                TestDir.c_str(),
++                            },
++                            "test.cpp"));
++}
++
++/// Test that we won't crash due to we may invalidate the lazy specialization
++/// lookup table during the loading process.
++TEST_F(LoadSpecLazilyTest, ChainedTest2) {
++  GenerateModuleInterface("M", R"cpp(
++export module M;
++export template <class T>
++class A {};
++
++export class B {};
++
++export class C {
++  A<B> D;
++};
++  )cpp");
++
++  GenerateModuleInterface("N", R"cpp(
++export module N;
++export import M;
++export class MayBeLoaded {};
++
++export class Temp {
++   A<MayBeLoaded> AS;
++};
++
++export class ExportedClass {};
++
++export template<> class A<ExportedClass> {
++   A<MayBeLoaded> AS;
++   A<B>           AB;
++};
++  )cpp");
++
++  const char *test_file_contents = R"cpp(
++import N;
++Temp T;
++A<ExportedClass> a;
++  )cpp";
++  std::string DepArg = "-fprebuilt-module-path=" + TestDir.str().str();
++  EXPECT_TRUE(runToolOnCodeWithArgs(std::make_unique<CheckLoadSpecLazilyAction>(
++                                        "MayBeLoaded", CheckingMode::Required),
++                                    test_file_contents,
++                                    {
++                                        "-std=c++20",
++                                        DepArg.c_str(),
++                                        "-I",
++                                        TestDir.c_str(),
++                                    },
++                                    "test.cpp"));
++}
++
++} // namespace
+-- 
+2.46.0.469.g59c65b2a67-goog
+
diff --git a/clang/test/PCH/type_pack_dedup.cpp b/clang/test/PCH/type_pack_dedup.cpp
index a1544aad79039a..8bd724a16654fd 100644
--- a/clang/test/PCH/type_pack_dedup.cpp
+++ b/clang/test/PCH/type_pack_dedup.cpp
@@ -18,3 +18,26 @@ void fn1() {
   TypeList<> l2 = TypePackDedup<TypeList>{};
   TypeList<X<0>, X<1>> x1 = TypePackDedup<TypeList, X<0>, X<1>, X<0>, X<1>>{};
 }
+
+using Int = int;
+using CInt = const Int;
+using IntArray = Int[10];
+using CIntArray = Int[10];
+using IntPtr = int*;
+using CIntPtr = const int*;
+static_assert(
+  __is_same(
+    __builtin_type_pack_dedup<TypeList,
+      Int, int,
+      const int, const Int, CInt, const CInt,
+      IntArray, Int[10], int[10],
+      const IntArray, const int[10], CIntArray, const CIntArray,
+      IntPtr, int*,
+      const IntPtr, int* const,
+      CIntPtr, const int*,
+      const IntPtr*, int*const*,
+      CIntPtr*, const int**,
+      const CIntPtr*, const int* const*
+    >,
+    TypeList<int, const int, int[10], const int [10], int*, int* const, const int*, int*const *, const int**, const int*const*>),
+  "");

>From 3e51c6d21fe3fc7bdccfaad56ea9c0a8fe860a81 Mon Sep 17 00:00:00 2001
From: Ilya Biryukov <ibiryukov at google.com>
Date: Fri, 13 Sep 2024 15:05:29 +0200
Subject: [PATCH 08/11] fixup! [Clang] Add __type_list_dedup builtin to
 deduplicate types in template arguments

Use SmallDenseSet
---
 clang/lib/Sema/SemaTemplate.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index bb48c3f6799565..67c13e350ff7a9 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3173,7 +3173,7 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD,
     assert(Template.getKind() == clang::TemplateArgument::Template);
     assert(Ts.getKind() == clang::TemplateArgument::Pack);
     TemplateArgumentListInfo SyntheticTemplateArgs;
-    llvm::DenseSet<QualType> Seen;
+    llvm::SmallDenseSet<QualType> Seen;
     // Synthesize a new template argument list, removing duplicates.
     for (auto T : Ts.getPackAsArray()) {
       assert(T.getKind() == clang::TemplateArgument::Type);

>From f8a22337f2dd4d2444f89e76b0044e289bdf85f6 Mon Sep 17 00:00:00 2001
From: Ilya Biryukov <ibiryukov at google.com>
Date: Fri, 13 Sep 2024 15:10:30 +0200
Subject: [PATCH 09/11] fixup! [Clang] Add __type_list_dedup builtin to
 deduplicate types in template arguments

Remove the prefix from the FindTarget tests
---
 clang-tools-extra/clangd/unittests/FindTargetTests.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
index 955ac69851f640..90e825cb8874d2 100644
--- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
+++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
@@ -734,7 +734,7 @@ TEST_F(TargetDeclTest, BuiltinTemplates) {
 
   Code = R"cpp(
     template <template <class...> class Templ, class... Types>
-    using __builtin_type_pack_dedup = [[__builtin_type_pack_dedup]]<Templ, Types...>;
+    using type_pack_dedup = [[__builtin_type_pack_dedup]]<Templ, Types...>;
   )cpp";
   EXPECT_DECLS("TemplateSpecializationTypeLoc", );
 }

>From 86a1480155dd827c8966c1c651f92fabe7ffdabd Mon Sep 17 00:00:00 2001
From: Ilya Biryukov <ibiryukov at google.com>
Date: Fri, 13 Sep 2024 15:12:56 +0200
Subject: [PATCH 10/11] fixup! [Clang] Add __type_list_dedup builtin to
 deduplicate types in template arguments

Fix a macro name style
---
 clang/include/clang/AST/DeclID.h      | 2 +-
 clang/lib/Serialization/ASTReader.cpp | 2 +-
 clang/lib/Serialization/ASTWriter.cpp | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/include/clang/AST/DeclID.h b/clang/include/clang/AST/DeclID.h
index a1f5d5aa36ad0f..5deff676a3c947 100644
--- a/clang/include/clang/AST/DeclID.h
+++ b/clang/include/clang/AST/DeclID.h
@@ -84,7 +84,7 @@ enum PredefinedDeclIDs {
   PREDEF_DECL_TYPE_PACK_ELEMENT_ID,
 
   /// The internal '__builtin_type_pack_dedup' template.
-  PREDEF_DECL___builtin_type_pack_dedup_ID,
+  PREDEF_DECL_TYPE_PACK_DEDUP_ID,
 
   /// The number of declaration IDs that are predefined.
   NUM_PREDEF_DECL_IDS
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index a0bc6917839bcc..86bf9813d869f3 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -7898,7 +7898,7 @@ Decl *ASTReader::getPredefinedDecl(PredefinedDeclIDs ID) {
       return Context.TypePackElementDecl;
     NewLoaded = Context.getTypePackElementDecl();
     break;
-  case PREDEF_DECL___builtin_type_pack_dedup_ID:
+  case PREDEF_DECL_TYPE_PACK_DEDUP_ID:
     if (Context.TypePackDedupDecl)
       return Context.TypePackDedupDecl;
     NewLoaded = Context.getTypePackDedupDecl();
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 515af4efbdd70a..81ba2af1e90e61 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -5052,7 +5052,7 @@ void ASTWriter::PrepareWritingSpecialDecls(Sema &SemaRef) {
                      PREDEF_DECL_CF_CONSTANT_STRING_TAG_ID);
   RegisterPredefDecl(Context.TypePackElementDecl,
                      PREDEF_DECL_TYPE_PACK_ELEMENT_ID);
-  RegisterPredefDecl(Context.TypePackDedupDecl, PREDEF_DECL___builtin_type_pack_dedup_ID);
+  RegisterPredefDecl(Context.TypePackDedupDecl, PREDEF_DECL_TYPE_PACK_DEDUP_ID);
 
   const TranslationUnitDecl *TU = Context.getTranslationUnitDecl();
 

>From 93d2a473b5535151db17e6571d6caa8b8a396fdd Mon Sep 17 00:00:00 2001
From: Ilya Biryukov <ibiryukov at google.com>
Date: Fri, 13 Sep 2024 15:18:42 +0200
Subject: [PATCH 11/11] fixup! [Clang] Add __type_list_dedup builtin to
 deduplicate types in template arguments

Move the tests to a proper file
---
 clang/test/PCH/type_pack_dedup.cpp            | 23 -----------------
 ...st-dedup.cpp => temp-param-pack-dedup.cpp} | 25 +++++++++++++++++++
 2 files changed, 25 insertions(+), 23 deletions(-)
 rename clang/test/SemaTemplate/{temp-param-list-dedup.cpp => temp-param-pack-dedup.cpp} (81%)

diff --git a/clang/test/PCH/type_pack_dedup.cpp b/clang/test/PCH/type_pack_dedup.cpp
index 8bd724a16654fd..a1544aad79039a 100644
--- a/clang/test/PCH/type_pack_dedup.cpp
+++ b/clang/test/PCH/type_pack_dedup.cpp
@@ -18,26 +18,3 @@ void fn1() {
   TypeList<> l2 = TypePackDedup<TypeList>{};
   TypeList<X<0>, X<1>> x1 = TypePackDedup<TypeList, X<0>, X<1>, X<0>, X<1>>{};
 }
-
-using Int = int;
-using CInt = const Int;
-using IntArray = Int[10];
-using CIntArray = Int[10];
-using IntPtr = int*;
-using CIntPtr = const int*;
-static_assert(
-  __is_same(
-    __builtin_type_pack_dedup<TypeList,
-      Int, int,
-      const int, const Int, CInt, const CInt,
-      IntArray, Int[10], int[10],
-      const IntArray, const int[10], CIntArray, const CIntArray,
-      IntPtr, int*,
-      const IntPtr, int* const,
-      CIntPtr, const int*,
-      const IntPtr*, int*const*,
-      CIntPtr*, const int**,
-      const CIntPtr*, const int* const*
-    >,
-    TypeList<int, const int, int[10], const int [10], int*, int* const, const int*, int*const *, const int**, const int*const*>),
-  "");
diff --git a/clang/test/SemaTemplate/temp-param-list-dedup.cpp b/clang/test/SemaTemplate/temp-param-pack-dedup.cpp
similarity index 81%
rename from clang/test/SemaTemplate/temp-param-list-dedup.cpp
rename to clang/test/SemaTemplate/temp-param-pack-dedup.cpp
index b1b01cc150c7e6..a24237f30041a0 100644
--- a/clang/test/SemaTemplate/temp-param-list-dedup.cpp
+++ b/clang/test/SemaTemplate/temp-param-pack-dedup.cpp
@@ -54,4 +54,29 @@ __builtin_type_pack_dedup<__builtin_type_pack_dedup, int, int, double>; // expec
                                                         // expected-note@* {{template parameter has a different kind in template argument}}
                                                         // expected-note@* {{previous template template parameter is here}}
 
+// Make sure various canonical / non-canonical type representations do not affect results
+// of the deduplication and the qualifiers do end up creating different types when C++ requires it.
+using Int = int;
+using CInt = const Int;
+using IntArray = Int[10];
+using CIntArray = Int[10];
+using IntPtr = int*;
+using CIntPtr = const int*;
+static_assert(
+  __is_same(
+    __builtin_type_pack_dedup<TypeList,
+      Int, int,
+      const int, const Int, CInt, const CInt,
+      IntArray, Int[10], int[10],
+      const IntArray, const int[10], CIntArray, const CIntArray,
+      IntPtr, int*,
+      const IntPtr, int* const,
+      CIntPtr, const int*,
+      const IntPtr*, int*const*,
+      CIntPtr*, const int**,
+      const CIntPtr*, const int* const*
+    >,
+    TypeList<int, const int, int[10], const int [10], int*, int* const, const int*, int*const *, const int**, const int*const*>),
+  "");
+
 // FIXME: tests for locations of template arguments, ideally we should point into the original locations of the template arguments.



More information about the cfe-commits mailing list