diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 47f89c636130..b4f232bbdc50 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -255,6 +255,9 @@ class ASTContext : public RefCountedBase { /// 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() { diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 8dcdeb6f84bb..94bae7ba69a8 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -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; diff --git a/clang/include/clang/Basic/Builtins.h b/clang/include/clang/Basic/Builtins.h index 8938e191bb4a..0ef35b6fbab4 100644 --- a/clang/include/clang/Basic/Builtins.h +++ b/clang/include/clang/Basic/Builtins.h @@ -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 diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 5af8a291e349..f26bf47a5bae 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -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">; diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 47137215349f..51e4628094ad 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -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. diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 276e9873ea31..564b93453ef4 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -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; diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index de3ebd23ef4f..65bb2692de03 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -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 + 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!"); diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index a4734a281248..2ade6df9456a 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -1695,6 +1695,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { const LangOptions &LangOpts = getLangOpts(); return llvm::StringSwitch(II->getName()) .Case("__make_integer_seq", LangOpts.CPlusPlus) + .Case("__type_pack_element", LangOpts.CPlusPlus) .Default(false); } }); diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index f25d3161ae4f..9160a3f67413 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -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. diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 58da453e2a8e..3a6f5cacc115 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -33,6 +33,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include 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 are treated like // S. @@ -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 + // 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!"); } diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 8650ce757698..27ef02f5b06a 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -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"); } diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 4fa016cc8963..ef51225b60cf 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -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/clang/test/PCH/type_pack_element.cpp b/clang/test/PCH/type_pack_element.cpp new file mode 100644 index 000000000000..c4ed6c81e2e9 --- /dev/null +++ b/clang/test/PCH/type_pack_element.cpp @@ -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 +struct X { }; + +using SizeT = decltype(sizeof(int)); + +template +using TypePackElement = __type_pack_element; + +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/clang/test/SemaCXX/type_pack_element.cpp b/clang/test/SemaCXX/type_pack_element.cpp new file mode 100644 index 000000000000..d22d5fa2ba67 --- /dev/null +++ b/clang/test/SemaCXX/type_pack_element.cpp @@ -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 +using TypePackElement = __type_pack_element; + +template +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 +using ErrorTypePackElement1 = __type_pack_element; // expected-error{{may not be accessed at an out of bounds index}} +using illformed1 = ErrorTypePackElement1<3, X<0>, X<1>>; // expected-note{{in instantiation}}