[clang] [clang-tools-extra] [Clang] Add __type_list_dedup builtin to deduplicate types in templat… (PR #106730)

Ilya Biryukov via cfe-commits cfe-commits at lists.llvm.org
Wed Sep 4 06:18:22 PDT 2024


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

>From a46885df62ff64f355abb010f778d84309acd10f 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 1/3] [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 89bb5768dbd40d..1960ec1e99f652 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -401,6 +401,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;
@@ -608,6 +611,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;
@@ -1115,6 +1119,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
   ExternCContextDecl *getExternCContextDecl() const;
   BuiltinTemplateDecl *getMakeIntegerSeqDecl() const;
   BuiltinTemplateDecl *getTypePackElementDecl() const;
+  BuiltinTemplateDecl *getTypeListDedupDecl() const;
 
   // Builtin Types.
   CanQualType VoidTy;
@@ -2006,6 +2011,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 c61234aa4d1af1..a8490153193250 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 fa850409ba1210..7d97558ff0a87a 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -5476,6 +5476,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 7a6a64529f52ec..cf75caca67088b 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 bf6b53700d90eb..9ae4292858cd9e 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 d02557d581db131fe8d672177381f176ea53d6c2 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 2/3] 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 a8490153193250..aaea59eb9962c4 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 9ae4292858cd9e..cace516263a78d 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 3365026d8a4015e0cafb76a8444bd6387411788f 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 3/3] 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.



More information about the cfe-commits mailing list