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
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to