SlaterLatiao updated this revision to Diff 550927.
SlaterLatiao added a comment.
- Refactor instantiating members into 2 functions.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D156546/new/
https://reviews.llvm.org/D156546
Files:
clang/lib/Sema/SemaTemplateInstantiate.cpp
clang/lib/Sema/SemaType.cpp
clang/test/CodeGenCXX/data_member_packs.cpp
Index: clang/test/CodeGenCXX/data_member_packs.cpp
===================================================================
--- /dev/null
+++ 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<typename... Ts> struct S1 {
+ Ts... ts;
+};
+
+template<typename T, typename... Ts> struct S2 {
+ T t[2];
+ Ts... ts;
+};
+
+// CHECK: %struct.S1 = type { i32 }
+S1<int> s1;
+// CHECK-NEXT: %struct.S1.0 = type { i32, float, double }
+S1<int, float, double> s2;
+// Test template args as the last arg.
+// CHECK-NEXT: %struct.S2 = type { [2 x i32], float, double }
+S2<int, float, double> 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<int, float, S1<double, double>> 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<int, int> s6;
+
Index: clang/lib/Sema/SemaType.cpp
===================================================================
--- clang/lib/Sema/SemaType.cpp
+++ 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:
Index: clang/lib/Sema/SemaTemplateInstantiate.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ 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<Decl *, 4> &Fields,
+ TemplateDeclInstantiator &Instantiator) {
+ QualType PatternType =
+ Field->getType()->castAs<PackExpansionType>()->getPattern();
+ std::optional<unsigned> 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<FieldDecl>(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<Decl *, 4> &Fields,
+ TemplateDeclInstantiator &Instantiator,
+ bool &MightHaveConstexprVirtualFunctions) {
+ Decl *NewMember = Instantiator.Visit(Member);
+ if (NewMember) {
+ if (FieldDecl *Field = dyn_cast<FieldDecl>(NewMember)) {
+ Fields.push_back(Field);
+ } else if (EnumDecl *Enum = dyn_cast<EnumDecl>(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<StaticAssertDecl>(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<CXXMethodDecl>(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<FieldDecl>(NewMember)) {
- Fields.push_back(Field);
- } else if (EnumDecl *Enum = dyn_cast<EnumDecl>(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<StaticAssertDecl>(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<CXXMethodDecl>(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<FieldDecl>(Member);
+ Field && isa<PackExpansionType>(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;
}
}
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits