diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -3135,6 +3135,93 @@ } } +/// Instantiate the member pack of a class. +void InstantiateMemberPack(Sema &S, CXXRecordDecl *Instantiation, + const MultiLevelTemplateArgumentList &TemplateArgs, + FieldDecl *Field, SmallVector &Fields, + TemplateDeclInstantiator &Instantiator) { + QualType PatternType = + Field->getType()->castAs()->getPattern(); + std::optional NumArgumentsInExpansion = + S.getNumArgumentsInExpansion(Field->getType(), TemplateArgs); + assert(NumArgumentsInExpansion && + "should not see unknown template argument here"); + for (unsigned Arg = 0; Arg < *NumArgumentsInExpansion; ++Arg) { + // Generate a new field from PackExpansion field. + if (Decl *NewMember = Instantiator.Visit(Field)) { + FieldDecl *PackedField = cast(NewMember); + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(S, Arg); + QualType T = + S.SubstType(PatternType, TemplateArgs, PackedField->getLocation(), + PackedField->getDeclName()); + PackedField->setType(T); + Fields.push_back(PackedField); + if (NewMember->isInvalidDecl()) { + // When `NewMember` has type of `PackExpansionType`, it escapes + // validation checks in `Visit`. Handling of such cases + // will be implemented in a future commit. + // Currently this branch should never be reached. + assert(false && "not implemented"); + Instantiation->setInvalidDecl(); + } + } else { + // FIXME: This is the same situation of InstantiateMember, when handling + // non-pack members. + continue; + } + } +} + +/// Instantiate the non-pack members of a class. +/// +/// \returns true if need to bail out the member instantiation ,loop, false otherwise. +bool InstantiateMember(Sema &S, SourceLocation &PointOfInstantiation, + CXXRecordDecl *Instantiation, Decl *Member, + TemplateSpecializationKind TSK, + SmallVector &Fields, + TemplateDeclInstantiator &Instantiator, + bool &MightHaveConstexprVirtualFunctions) { + Decl *NewMember = Instantiator.Visit(Member); + if (NewMember) { + if (FieldDecl *Field = dyn_cast(NewMember)) { + Fields.push_back(Field); + } else if (EnumDecl *Enum = dyn_cast(NewMember)) { + // C++11 [temp.inst]p1: The implicit instantiation of a class template + // specialization causes the implicit instantiation of the definitions + // of unscoped member enumerations. + // Record a point of instantiation for this implicit instantiation. + if (TSK == TSK_ImplicitInstantiation && !Enum->isScoped() && + Enum->isCompleteDefinition()) { + MemberSpecializationInfo *MSInfo = Enum->getMemberSpecializationInfo(); + assert(MSInfo && "no spec info for member enum specialization"); + MSInfo->setTemplateSpecializationKind(TSK_ImplicitInstantiation); + MSInfo->setPointOfInstantiation(PointOfInstantiation); + } + } else if (StaticAssertDecl *SA = dyn_cast(NewMember)) { + if (SA->isFailed()) { + // A static_assert failed. Bail out; instantiating this + // class is probably not meaningful. + Instantiation->setInvalidDecl(); + return true; + } + } else if (CXXMethodDecl *MD = dyn_cast(NewMember)) { + if (MD->isConstexpr() && !MD->getFriendObjectKind() && + (MD->isVirtualAsWritten() || Instantiation->getNumBases())) + MightHaveConstexprVirtualFunctions = true; + } + + if (NewMember->isInvalidDecl()) + Instantiation->setInvalidDecl(); + } else { + // FIXME: Eventually, a NULL return will mean that one of the + // instantiations was a semantic disaster, and we'll want to mark the + // declaration invalid. + // For now, we expect to skip some members that we can't yet handle. + // Same situation occurs when handling member packs, in InstantiateMember. + } + return false; +} + /// Instantiate the definition of a class from a given pattern. /// /// \param PointOfInstantiation The point of instantiation within the @@ -3267,42 +3354,16 @@ continue; } - Decl *NewMember = Instantiator.Visit(Member); - if (NewMember) { - if (FieldDecl *Field = dyn_cast(NewMember)) { - Fields.push_back(Field); - } else if (EnumDecl *Enum = dyn_cast(NewMember)) { - // C++11 [temp.inst]p1: The implicit instantiation of a class template - // specialization causes the implicit instantiation of the definitions - // of unscoped member enumerations. - // Record a point of instantiation for this implicit instantiation. - if (TSK == TSK_ImplicitInstantiation && !Enum->isScoped() && - Enum->isCompleteDefinition()) { - MemberSpecializationInfo *MSInfo =Enum->getMemberSpecializationInfo(); - assert(MSInfo && "no spec info for member enum specialization"); - MSInfo->setTemplateSpecializationKind(TSK_ImplicitInstantiation); - MSInfo->setPointOfInstantiation(PointOfInstantiation); - } - } else if (StaticAssertDecl *SA = dyn_cast(NewMember)) { - if (SA->isFailed()) { - // A static_assert failed. Bail out; instantiating this - // class is probably not meaningful. - Instantiation->setInvalidDecl(); - break; - } - } else if (CXXMethodDecl *MD = dyn_cast(NewMember)) { - if (MD->isConstexpr() && !MD->getFriendObjectKind() && - (MD->isVirtualAsWritten() || Instantiation->getNumBases())) - MightHaveConstexprVirtualFunctions = true; - } - - if (NewMember->isInvalidDecl()) - Instantiation->setInvalidDecl(); + // Instantiate data member packs. + if (FieldDecl *Field = dyn_cast(Member); + Field && isa(Field->getType().getTypePtr())) { + InstantiateMemberPack(*this, Instantiation, TemplateArgs, Field, Fields, Instantiator); } else { - // FIXME: Eventually, a NULL return will mean that one of the - // instantiations was a semantic disaster, and we'll want to mark the - // declaration invalid. - // For now, we expect to skip some members that we can't yet handle. + // Instantiate normal members. + if (InstantiateMember(*this, PointOfInstantiation, Instantiation, Member, + TSK, Fields, Instantiator, + MightHaveConstexprVirtualFunctions)) + break; } } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -5927,6 +5927,9 @@ /*ExpectPackInType=*/false); } break; + case DeclaratorContext::Member: + // Expand for data member packs. + // https://discourse.llvm.org/t/adding-support-for-data-member-packs/71333 case DeclaratorContext::TemplateParam: // C++0x [temp.param]p15: // If a template-parameter is a [...] is a parameter-declaration that @@ -5954,7 +5957,6 @@ case DeclaratorContext::CXXNew: case DeclaratorContext::AliasDecl: case DeclaratorContext::AliasTemplate: - case DeclaratorContext::Member: case DeclaratorContext::Block: case DeclaratorContext::ForInit: case DeclaratorContext::SelectionInit: diff --git a/clang/test/CodeGenCXX/data_member_packs.cpp b/clang/test/CodeGenCXX/data_member_packs.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/data_member_packs.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 --std=c++20 %s -emit-llvm -o - -triple x86_64-linux | FileCheck %s --check-prefixes=CHECK + +// Tests declaration data member packs. +template struct S1 { + Ts... ts; +}; + +template struct S2 { + T t[2]; + Ts... ts; +}; + +// CHECK: %struct.S1 = type { i32 } +S1 s1; +// CHECK-NEXT: %struct.S1.0 = type { i32, float, double } +S1 s2; +// Test template args as the last arg. +// CHECK-NEXT: %struct.S2 = type { [2 x i32], float, double } +S2 s3; +// Test nested template args. +// CHECK-NEXT: %struct.S1.1 = type { i32, float, %struct.S1.2 } +// CHECK-NEXT: %struct.S1.2 = type { double, double } +S1> s4; +// Test empty template arg. +// CHECK-NEXT: %struct.S1.3 = type { i8 } +S1<> s5; +// Test duplicate types in template args. +// CHECK-NEXT: %struct.S1.4 = type { i32, i32 } +S1 s6; +