]> granicus.if.org Git - clang/commitdiff
[Feature] Add a builtin for indexing into parameter packs. Patch by Louis Dionne.
authorEric Fiselier <eric@efcs.ca>
Fri, 1 Jul 2016 01:24:09 +0000 (01:24 +0000)
committerEric Fiselier <eric@efcs.ca>
Fri, 1 Jul 2016 01:24:09 +0000 (01:24 +0000)
This patch adds a __nth_element builtin that allows fetching the n-th type of a
parameter pack with very little compile-time overhead. The patch was inspired by
r252036 and r252115 by David Majnemer, which add a similar __make_integer_seq
builtin for efficiently creating a std::integer_sequence.

Reviewed as D15421. http://reviews.llvm.org/D15421

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@274316 91177308-0d34-0410-b5e6-96231b3b80d8

14 files changed:
include/clang/AST/ASTContext.h
include/clang/AST/DeclTemplate.h
include/clang/Basic/Builtins.h
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Serialization/ASTBitCodes.h
lib/AST/ASTContext.cpp
lib/AST/DeclTemplate.cpp
lib/Lex/PPMacroExpansion.cpp
lib/Sema/SemaLookup.cpp
lib/Sema/SemaTemplate.cpp
lib/Serialization/ASTReader.cpp
lib/Serialization/ASTWriter.cpp
test/PCH/type_pack_element.cpp [new file with mode: 0644]
test/SemaCXX/type_pack_element.cpp [new file with mode: 0644]

index 47f89c6361308a240c78261f92a29bc0515d045e..b4f232bbdc505b96be4ad774ff9336ace465ad77 100644 (file)
@@ -255,6 +255,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// The identifier '__make_integer_seq'.
   mutable IdentifierInfo *MakeIntegerSeqName = nullptr;
 
+  /// The identifier '__type_pack_element'.
+  mutable IdentifierInfo *TypePackElementName = nullptr;
+
   QualType ObjCConstantStringType;
   mutable RecordDecl *CFConstantStringTagDecl;
   mutable TypedefDecl *CFConstantStringTypeDecl;
@@ -410,6 +413,7 @@ private:
   TranslationUnitDecl *TUDecl;
   mutable ExternCContextDecl *ExternCContext;
   mutable BuiltinTemplateDecl *MakeIntegerSeqDecl;
+  mutable BuiltinTemplateDecl *TypePackElementDecl;
 
   /// \brief The associated SourceManager object.a
   SourceManager &SourceMgr;
@@ -880,6 +884,7 @@ public:
 
   ExternCContextDecl *getExternCContextDecl() const;
   BuiltinTemplateDecl *getMakeIntegerSeqDecl() const;
+  BuiltinTemplateDecl *getTypePackElementDecl() const;
 
   // Builtin Types.
   CanQualType VoidTy;
@@ -1473,6 +1478,12 @@ public:
     return MakeIntegerSeqName;
   }
 
+  IdentifierInfo *getTypePackElementName() const {
+    if (!TypePackElementName)
+      TypePackElementName = &Idents.get("__type_pack_element");
+    return TypePackElementName;
+  }
+
   /// \brief Retrieve the Objective-C "instancetype" type, if already known;
   /// otherwise, returns a NULL type;
   QualType getObjCInstanceType() {
index 8dcdeb6f84bb3f2d1301803913c65d0fadcf7e72..94bae7ba69a8192334416515653dbd214760eacf 100644 (file)
@@ -1490,8 +1490,8 @@ public:
 };
 
 /// \brief Represents the builtin template declaration which is used to
-/// implement __make_integer_seq.  It serves no real purpose beyond existing as
-/// a place to hold template parameters.
+/// implement __make_integer_seq and other builtin templates.  It serves
+/// no real purpose beyond existing as a place to hold template parameters.
 class BuiltinTemplateDecl : public TemplateDecl {
   void anchor() override;
 
index 8938e191bb4aa57e2ad5bafc7fa5b44c790e0e44..0ef35b6fbab4f286664b2483df34493d62b3cd17 100644 (file)
@@ -219,7 +219,10 @@ private:
 /// \brief Kinds of BuiltinTemplateDecl.
 enum BuiltinTemplateKind : int {
   /// \brief This names the __make_integer_seq BuiltinTemplateDecl.
-  BTK__make_integer_seq
+  BTK__make_integer_seq,
+
+  /// \brief This names the __type_pack_element BuiltinTemplateDecl.
+  BTK__type_pack_element
 };
 
 } // end namespace clang
index 5af8a291e3490426cfdea31ec80a149f68161d9e..f26bf47a5bae2089d1d0c8ed5d91a06785423778 100644 (file)
@@ -2128,6 +2128,10 @@ def err_integer_sequence_negative_length : Error<
 def err_integer_sequence_integral_element_type : Error<
   "integer sequences must have integral element type">;
 
+// __type_pack_element
+def err_type_pack_element_out_of_bounds : Error<
+  "a parameter pack may not be accessed at an out of bounds index">;
+
 // Objective-C++
 def err_objc_decls_may_only_appear_in_global_scope : Error<
   "Objective-C declarations may only appear in global scope">;
index 47137215349ffd0c19ac5ce50abcca960f6d06a4..51e4628094ad2875dbbc57bd56d12692204bc8fa 100644 (file)
@@ -983,13 +983,16 @@ namespace clang {
 
       /// \brief The internal '__NSConstantString' tag type.
       PREDEF_DECL_CF_CONSTANT_STRING_TAG_ID = 15,
+
+      /// \brief The internal '__type_pack_element' template.
+      PREDEF_DECL_TYPE_PACK_ELEMENT_ID = 16,
     };
 
     /// \brief The number of declaration IDs that are predefined.
     ///
     /// For more information about predefined declarations, see the
     /// \c PredefinedDeclIDs type and the PREDEF_DECL_*_ID constants.
-    const unsigned int NUM_PREDEF_DECL_IDS = 16;
+    const unsigned int NUM_PREDEF_DECL_IDS = 17;
 
     /// \brief Record of updates for a declaration that was modified after
     /// being deserialized. This can occur within DECLTYPES_BLOCK_ID.
index 276e9873ea31064e3226a03df86e7cc744818e59..564b93453ef410e37b5ec9cc7378fa0e24610728 100644 (file)
@@ -742,8 +742,8 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM,
       sigjmp_bufDecl(nullptr), ucontext_tDecl(nullptr),
       BlockDescriptorType(nullptr), BlockDescriptorExtendedType(nullptr),
       cudaConfigureCallDecl(nullptr), FirstLocalImport(), LastLocalImport(),
-      ExternCContext(nullptr), MakeIntegerSeqDecl(nullptr), SourceMgr(SM),
-      LangOpts(LOpts),
+      ExternCContext(nullptr), MakeIntegerSeqDecl(nullptr),
+      TypePackElementDecl(nullptr), SourceMgr(SM), LangOpts(LOpts),
       SanitizerBL(new SanitizerBlacklist(LangOpts.SanitizerBlacklistFiles, SM)),
       AddrSpaceMap(nullptr), Target(nullptr), AuxTarget(nullptr),
       PrintingPolicy(LOpts), Idents(idents), Selectors(sels),
@@ -928,6 +928,14 @@ ASTContext::getMakeIntegerSeqDecl() const {
   return MakeIntegerSeqDecl;
 }
 
+BuiltinTemplateDecl *
+ASTContext::getTypePackElementDecl() const {
+  if (!TypePackElementDecl)
+    TypePackElementDecl = buildBuiltinTemplateDecl(BTK__type_pack_element,
+                                                   getTypePackElementName());
+  return TypePackElementDecl;
+}
+
 RecordDecl *ASTContext::buildImplicitRecord(StringRef Name,
                                             RecordDecl::TagKind TK) const {
   SourceLocation Loc;
index de3ebd23ef4f5d7e363b28ab66321b848c147b1e..65bb2692de03924a29ebc9df0a20fd0e8a0130f1 100644 (file)
@@ -1239,11 +1239,34 @@ createMakeIntegerSeqParameterList(const ASTContext &C, DeclContext *DC) {
                                        Params, SourceLocation());
 }
 
+static TemplateParameterList *
+createTypePackElementParameterList(const ASTContext &C, DeclContext *DC) {
+  // std::size_t Index
+  TypeSourceInfo *TInfo = C.getTrivialTypeSourceInfo(C.getSizeType());
+  auto *Index = NonTypeTemplateParmDecl::Create(
+      C, DC, SourceLocation(), SourceLocation(), /*Depth=*/0, /*Position=*/0,
+      /*Id=*/nullptr, TInfo->getType(), /*ParameterPack=*/false, TInfo);
+
+  // typename ...T
+  auto *Ts = TemplateTypeParmDecl::Create(
+      C, DC, SourceLocation(), SourceLocation(), /*Depth=*/0, /*Position=*/1,
+      /*Id=*/nullptr, /*Typename=*/true, /*ParameterPack=*/true);
+  Ts->setImplicit(true);
+
+  // template <std::size_t Index, typename ...T>
+  NamedDecl *Params[] = {Index, Ts};
+  return TemplateParameterList::Create(C, SourceLocation(), SourceLocation(),
+                                       llvm::makeArrayRef(Params),
+                                       SourceLocation());
+}
+
 static TemplateParameterList *createBuiltinTemplateParameterList(
     const ASTContext &C, DeclContext *DC, BuiltinTemplateKind BTK) {
   switch (BTK) {
   case BTK__make_integer_seq:
     return createMakeIntegerSeqParameterList(C, DC);
+  case BTK__type_pack_element:
+    return createTypePackElementParameterList(C, DC);
   }
 
   llvm_unreachable("unhandled BuiltinTemplateKind!");
index a4734a281248962d97a07885b8671203419bc70a..2ade6df9456a6467dc20154d2162c93f76d4cbf8 100644 (file)
@@ -1695,6 +1695,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
           const LangOptions &LangOpts = getLangOpts();
           return llvm::StringSwitch<bool>(II->getName())
                       .Case("__make_integer_seq", LangOpts.CPlusPlus)
+                      .Case("__type_pack_element", LangOpts.CPlusPlus)
                       .Default(false);
         }
       });
index f25d3161ae4fbefd9644440ce94bc7fbac105c64..9160a3f674136349a7796ab3946d731c3a4775ad 100644 (file)
@@ -680,10 +680,14 @@ static bool LookupBuiltin(Sema &S, LookupResult &R) {
       NameKind == Sema::LookupRedeclarationWithLinkage) {
     IdentifierInfo *II = R.getLookupName().getAsIdentifierInfo();
     if (II) {
-      if (S.getLangOpts().CPlusPlus && NameKind == Sema::LookupOrdinaryName &&
-          II == S.getASTContext().getMakeIntegerSeqName()) {
-        R.addDecl(S.getASTContext().getMakeIntegerSeqDecl());
-        return true;
+      if (S.getLangOpts().CPlusPlus && NameKind == Sema::LookupOrdinaryName) {
+        if (II == S.getASTContext().getMakeIntegerSeqName()) {
+          R.addDecl(S.getASTContext().getMakeIntegerSeqDecl());
+          return true;
+        } else if (II == S.getASTContext().getTypePackElementName()) {
+          R.addDecl(S.getASTContext().getTypePackElementDecl());
+          return true;
+        }
       }
 
       // If this is a builtin on this (or all) targets, create the decl.
index 58da453e2a8e58cc7a1d7ca098316ab376395cb1..3a6f5cacc1157959d9a6be79d1780c36d093dacf 100644 (file)
@@ -33,6 +33,7 @@
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringExtras.h"
 
+#include <iterator>
 using namespace clang;
 using namespace sema;
 
@@ -2042,7 +2043,7 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD,
                            TemplateArgumentListInfo &TemplateArgs) {
   ASTContext &Context = SemaRef.getASTContext();
   switch (BTD->getBuiltinTemplateKind()) {
-  case BTK__make_integer_seq:
+  case BTK__make_integer_seq: {
     // Specializations of __make_integer_seq<S, T, N> are treated like
     // S<T, 0, ..., N-1>.
 
@@ -2084,6 +2085,29 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD,
     return SemaRef.CheckTemplateIdType(Converted[0].getAsTemplate(),
                                        TemplateLoc, SyntheticTemplateArgs);
   }
+
+  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");
+
+    // If the Index is out of bounds, the program is ill-formed.
+    TemplateArgument IndexArg = Converted[0], Ts = Converted[1];
+    llvm::APSInt Index = IndexArg.getAsIntegral();
+    assert(Index >= 0 && "the index used with __type_pack_element should be of "
+                         "type std::size_t, and hence be non-negative");
+    if (Index >= Ts.pack_size()) {
+      SemaRef.Diag(TemplateArgs[0].getLocation(),
+                   diag::err_type_pack_element_out_of_bounds);
+      return QualType();
+    }
+
+    // We simply return the type at index `Index`.
+    auto Nth = std::next(Ts.pack_begin(), Index.getExtValue());
+    return Nth->getAsType();
+  }
   llvm_unreachable("unexpected BuiltinTemplateDecl!");
 }
 
index 8650ce757698210e4773589324fa60417704cd69..27ef02f5b06a1177af026e0513e8d7369fdc763c 100644 (file)
@@ -6428,6 +6428,9 @@ static Decl *getPredefinedDecl(ASTContext &Context, PredefinedDeclIDs ID) {
 
   case PREDEF_DECL_CF_CONSTANT_STRING_TAG_ID:
     return Context.getCFConstantStringTagDecl();
+
+  case PREDEF_DECL_TYPE_PACK_ELEMENT_ID:
+    return Context.getTypePackElementDecl();
   }
   llvm_unreachable("PredefinedDeclIDs unknown enum value");
 }
index 4fa016cc896378286a82e45f66f0b44e21a76646..ef51225b60cf1b3a6b6451b4363f3a87d5793e6c 100644 (file)
@@ -4191,6 +4191,8 @@ uint64_t ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot,
                      PREDEF_DECL_CF_CONSTANT_STRING_ID);
   RegisterPredefDecl(Context.CFConstantStringTagDecl,
                      PREDEF_DECL_CF_CONSTANT_STRING_TAG_ID);
+  RegisterPredefDecl(Context.TypePackElementDecl,
+                     PREDEF_DECL_TYPE_PACK_ELEMENT_ID);
 
   // Build a record containing all of the tentative definitions in this file, in
   // TentativeDefinitions order.  Generally, this record will be empty for
diff --git a/test/PCH/type_pack_element.cpp b/test/PCH/type_pack_element.cpp
new file mode 100644 (file)
index 0000000..c4ed6c8
--- /dev/null
@@ -0,0 +1,16 @@
+// 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
+
+template <int i>
+struct X { };
+
+using SizeT = decltype(sizeof(int));
+
+template <SizeT i, typename ...T>
+using TypePackElement = __type_pack_element<i, T...>;
+
+void fn1() {
+  X<0> x0 = TypePackElement<0, X<0>, X<1>, X<2>>{};
+  X<1> x1 = TypePackElement<1, X<0>, X<1>, X<2>>{};
+  X<2> x2 = TypePackElement<2, X<0>, X<1>, X<2>>{};
+}
diff --git a/test/SemaCXX/type_pack_element.cpp b/test/SemaCXX/type_pack_element.cpp
new file mode 100644 (file)
index 0000000..d22d5fa
--- /dev/null
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+
+static_assert(__has_builtin(__type_pack_element), "");
+
+using SizeT = decltype(sizeof(int));
+
+template <SizeT i, typename ...T>
+using TypePackElement = __type_pack_element<i, T...>;
+
+template <int i>
+struct X;
+
+static_assert(__is_same(TypePackElement<0, X<0>>, X<0>), "");
+
+static_assert(__is_same(TypePackElement<0, X<0>, X<1>>, X<0>), "");
+static_assert(__is_same(TypePackElement<1, X<0>, X<1>>, X<1>), "");
+
+static_assert(__is_same(TypePackElement<0, X<0>, X<1>, X<2>>, X<0>), "");
+static_assert(__is_same(TypePackElement<1, X<0>, X<1>, X<2>>, X<1>), "");
+static_assert(__is_same(TypePackElement<2, X<0>, X<1>, X<2>>, X<2>), "");
+
+static_assert(__is_same(TypePackElement<0, X<0>, X<1>, X<2>, X<3>>, X<0>), "");
+static_assert(__is_same(TypePackElement<1, X<0>, X<1>, X<2>, X<3>>, X<1>), "");
+static_assert(__is_same(TypePackElement<2, X<0>, X<1>, X<2>, X<3>>, X<2>), "");
+static_assert(__is_same(TypePackElement<3, X<0>, X<1>, X<2>, X<3>>, X<3>), "");
+
+static_assert(__is_same(TypePackElement<0, X<0>, X<1>, X<2>, X<3>, X<4>>, X<0>), "");
+static_assert(__is_same(TypePackElement<1, X<0>, X<1>, X<2>, X<3>, X<4>>, X<1>), "");
+static_assert(__is_same(TypePackElement<2, X<0>, X<1>, X<2>, X<3>, X<4>>, X<2>), "");
+static_assert(__is_same(TypePackElement<3, X<0>, X<1>, X<2>, X<3>, X<4>>, X<3>), "");
+static_assert(__is_same(TypePackElement<4, X<0>, X<1>, X<2>, X<3>, X<4>>, X<4>), "");
+
+static_assert(__is_same(TypePackElement<0, X<0>, X<1>, X<2>, X<3>, X<4>, X<5>>, X<0>), "");
+static_assert(__is_same(TypePackElement<1, X<0>, X<1>, X<2>, X<3>, X<4>, X<5>>, X<1>), "");
+static_assert(__is_same(TypePackElement<2, X<0>, X<1>, X<2>, X<3>, X<4>, X<5>>, X<2>), "");
+static_assert(__is_same(TypePackElement<3, X<0>, X<1>, X<2>, X<3>, X<4>, X<5>>, X<3>), "");
+static_assert(__is_same(TypePackElement<4, X<0>, X<1>, X<2>, X<3>, X<4>, X<5>>, X<4>), "");
+static_assert(__is_same(TypePackElement<5, X<0>, X<1>, X<2>, X<3>, X<4>, X<5>>, X<5>), "");
+
+// Test __type_pack_element with more than 2 top-level template arguments.
+static_assert(__is_same(__type_pack_element<5, X<0>, X<1>, X<2>, X<3>, X<4>, X<5>>, X<5>), "");
+
+template <SizeT Index, typename ...T>
+using ErrorTypePackElement1 = __type_pack_element<Index, T...>; // expected-error{{may not be accessed at an out of bounds index}}
+using illformed1 = ErrorTypePackElement1<3, X<0>, X<1>>;  // expected-note{{in instantiation}}