SlaterLatiao updated this revision to Diff 551348.
SlaterLatiao marked an inline comment as done.
SlaterLatiao added a comment.

- Added test case for partial expansion.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D158006/new/

https://reviews.llvm.org/D158006

Files:
  clang/include/clang/AST/ExprCXX.h
  clang/lib/AST/ComputeDependence.cpp
  clang/lib/AST/ExprCXX.cpp
  clang/lib/Sema/SemaExprMember.cpp
  clang/lib/Sema/SemaTemplateInstantiate.cpp
  clang/lib/Sema/TreeTransform.h
  clang/test/CodeGenCXX/data_member_packs.cpp

Index: clang/test/CodeGenCXX/data_member_packs.cpp
===================================================================
--- clang/test/CodeGenCXX/data_member_packs.cpp
+++ clang/test/CodeGenCXX/data_member_packs.cpp
@@ -10,6 +10,11 @@
     Ts... ts;
 };
 
+template<typename T, typename... Ts> struct S3 {
+    T t;
+    Ts... ts;
+};
+
 // CHECK: %struct.S1 = type { i32 }
 S1<int> s1;
 // CHECK-NEXT: %struct.S1.0 = type { i32, float, double }
@@ -28,3 +33,58 @@
 // CHECK-NEXT: %struct.S1.4 = type { i32, i32 }
 S1<int, int> s6;
 
+S1<int, long, float, double> s7;
+
+template<typename... Ts> auto sum(Ts... ts) {
+  return (ts + ...);
+}
+
+template<typename T, typename... Ts> auto partial_sum(T t, Ts... ts) {
+  return sum(ts...);
+}
+
+template<typename... Ts> auto partial_sum(S1<Ts...> s) {
+  return partial_sum(s.ts...);
+}
+
+auto take_empty() {
+  return 0;
+}
+
+template<typename... Ts> auto sum_pack(S1<Ts...> s) {
+  return sum(s.ts...);
+}
+// Test template arg + expansion.
+template<typename T, typename... Ts> auto sum_pack2(S1<T, Ts...> s) {
+  return sum(s.ts...);
+}
+// Test empty expansion.
+template<typename... Ts> auto take_empty(S3<Ts...> s) {
+  return take_empty(s.ts...);
+}
+// Test nested template args and multiple expansions.
+template<typename... Ts> struct S4 {
+  template<typename... Ts2> auto sum_pack(S1<Ts..., Ts2...> s) {
+    return sum(s.ts...);
+  }
+};
+
+int main() {
+  // Check calling take_empty()
+  // CHECK: %call = call noundef i32 @_Z10take_emptyv()
+  take_empty(S3<int>{});
+  // Check instantiation of sum(int, float, double)
+  // CHECK: double @_Z3sumIJifdEEDaDpT_(i32 noundef %ts, float noundef %ts1, double noundef %ts3)
+  sum_pack(s2);
+  // Check instantiation of sum(int, int)
+  // CHECK: i32 @_Z3sumIJiiEEDaDpT_(i32 noundef %ts, i32 noundef %ts1)
+  sum_pack2<int, int>(s6);
+  // Check instantiation of sum(int, long, float, double)
+  // CHECK: double @_Z3sumIJilfdEEDaDpT_(i32 noundef %ts, i64 noundef %ts1, float noundef %ts3, double noundef %ts5)
+  S4<int, long>{}.sum_pack<float, double>(s7);
+  // Check instantiation of sum(long, float, double)
+  // CHECK: double @_Z3sumIJlfdEEDaDpT_(i64 noundef %ts, float noundef %ts1, double noundef %ts3)
+  partial_sum(s7);
+  return 0;
+}
+
Index: clang/lib/Sema/TreeTransform.h
===================================================================
--- clang/lib/Sema/TreeTransform.h
+++ clang/lib/Sema/TreeTransform.h
@@ -4150,11 +4150,67 @@
                                            Parens.getEnd());
 }
 
+template <typename Derived>
+bool TransformExprsOnMemberPack(Derived &DerivedRef, Sema &SemaRef,
+                               bool *ArgChanged,
+                               SmallVectorImpl<Expr *> &Outputs,
+                               const CXXDependentScopeMemberExpr *MemberExpr) {
+  if (ArgChanged)
+    *ArgChanged = true;
+  assert(MemberExpr->isMemberPackAccess() &&
+         "trying to expand non-pack member access");
+  std::string UnExpandedNameStr =
+      MemberExpr->getMemberNameInfo().getName().getAsString();
+
+  ExprResult Base = DerivedRef.TransformExpr(MemberExpr->getBase());
+  if (Base.isInvalid())
+    return true;
+  QualType BaseType = ((Expr *)Base.get())->getType();
+  if (MemberExpr->isArrow()) {
+    assert(BaseType->isPointerType());
+    BaseType = BaseType->castAs<PointerType>()->getPointeeType();
+  }
+
+  unsigned Arg = 0;
+  while (true) {
+    Twine ExpandedNameStr = Twine(UnExpandedNameStr) + "@" + Twine(Arg);
+    // Transform unexpanded field name and create a new member
+    // expression.
+    DeclarationName ExpandedName =
+        &SemaRef.Context.Idents.get(ExpandedNameStr.str());
+    // Construct name info with new name and keep other members the same.
+    DeclarationNameInfo ExpandedNameInfo = DeclarationNameInfo(
+        ExpandedName, MemberExpr->getMemberNameInfo().getLoc(),
+        MemberExpr->getMemberNameInfo().getInfo());
+    TemplateArgumentListInfo TemplateArgs = TemplateArgumentListInfo();
+    MemberExpr->copyTemplateArgumentsInto(TemplateArgs);
+    auto *ExpandedMemberExpr = CXXDependentScopeMemberExpr::Create(
+        SemaRef.Context, MemberExpr->getBase(), MemberExpr->getBaseType(),
+        MemberExpr->isArrow(), MemberExpr->getOperatorLoc(),
+        MemberExpr->getQualifierLoc(), MemberExpr->getTemplateKeywordLoc(),
+        MemberExpr->getFirstQualifierFoundInScope(), ExpandedNameInfo,
+        &TemplateArgs, MemberExpr->isMemberPackAccess());
+
+    Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, Arg);
+    ExprResult Out = DerivedRef.TransformExpr(ExpandedMemberExpr);
+    if (Out.isInvalid())
+      return true;
+    // An empty expression is returned when name lookup fails in accessing
+    // member packs. This means the last field in member pack has been
+    // processd and time to exit the loop.
+    if (Out.isUnset())
+      break;
+    Outputs.push_back(Out.get());
+    ++Arg;
+  }
+  return false;
+}
+
 template<typename Derived>
 bool TreeTransform<Derived>::TransformExprs(Expr *const *Inputs,
                                             unsigned NumInputs,
                                             bool IsCall,
-                                      SmallVectorImpl<Expr *> &Outputs,
+                                            SmallVectorImpl<Expr *> &Outputs,
                                             bool *ArgChanged) {
   for (unsigned I = 0; I != NumInputs; ++I) {
     // If requested, drop call arguments that need to be dropped.
@@ -4168,6 +4224,14 @@
     if (PackExpansionExpr *Expansion = dyn_cast<PackExpansionExpr>(Inputs[I])) {
       Expr *Pattern = Expansion->getPattern();
 
+      if (const auto *MemberExpr =
+              dyn_cast<CXXDependentScopeMemberExpr>(Pattern)) {
+        TransformExprsOnMemberPack(getDerived(), getSema(), ArgChanged, Outputs,
+                                   MemberExpr);
+        // Proceed to next pack expansion expression.
+        continue;
+      }
+
       SmallVector<UnexpandedParameterPack, 2> Unexpanded;
       getSema().collectUnexpandedParameterPacks(Pattern, Unexpanded);
       assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
Index: clang/lib/Sema/SemaTemplateInstantiate.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -3155,6 +3155,12 @@
           S.SubstType(PatternType, TemplateArgs, PackedField->getLocation(),
                       PackedField->getDeclName());
       PackedField->setType(T);
+      // Rename the expanded fields to avoid ambiguous names in
+      // name lookup. The renamed fields need to be inaccessible by users so an
+      // `@` is added to the name.
+      Twine NewFieldName =
+          Twine(PackedField->getName()) + "@" + Twine(Arg);
+      PackedField->setDeclName(&(S.Context).Idents.get(NewFieldName.str()));
       Fields.push_back(PackedField);
       if (NewMember->isInvalidDecl()) {
         // When `NewMember` has type of `PackExpansionType`, it escapes
Index: clang/lib/Sema/SemaExprMember.cpp
===================================================================
--- clang/lib/Sema/SemaExprMember.cpp
+++ clang/lib/Sema/SemaExprMember.cpp
@@ -514,12 +514,26 @@
                                          return Arg.getArgument().isDependent();
                                        })));
 
-  // Get the type being accessed in BaseType.  If this is an arrow, the BaseExpr
+  bool isMemberPack = false;
+  // Check for member packs by looking up base's template decl.
+  if (const auto *ET = dyn_cast<ElaboratedType>(BaseType)) {
+    if (const auto *TST =
+            dyn_cast<TemplateSpecializationType>(ET->getNamedType())) {
+      const auto *TD = TST->getTemplateName().getAsTemplateDecl();
+      const auto Fields = cast<ClassTemplateDecl>(TD)->getTemplatedDecl()->fields();
+      auto It = llvm::find_if(Fields, [NameInfo](const FieldDecl *Field) {
+        return Field->getDeclName() == NameInfo.getName();
+      });
+      if (It != Fields.end() && isa<PackExpansionType>(*It->getType())) 
+        isMemberPack = true;
+    }
+  }
+  // Get the type being accessed in BaseType. If this is an arrow, the BaseExpr
   // must have pointer type, and the accessed type is the pointee.
   return CXXDependentScopeMemberExpr::Create(
       Context, BaseExpr, BaseType, IsArrow, OpLoc,
       SS.getWithLocInContext(Context), TemplateKWLoc, FirstQualifierInScope,
-      NameInfo, TemplateArgs);
+      NameInfo, TemplateArgs, isMemberPack);
 }
 
 /// We know that the given qualified member reference points only to
@@ -995,10 +1009,16 @@
         << isa<CXXDestructorDecl>(FD);
 
   if (R.empty()) {
+    // The member is expanded from member pack if its name has `@` in it. In this
+    // case a failed name lookup is not an error, but represents it's at the end
+    // of the expanded members. Return an empty expression to inform its
+    // caller.
+    auto MemberNameStr = MemberNameInfo.getName().getAsString();
+    if (MemberNameStr.find('@') != std::string::npos)
+      return ExprEmpty();
     // Rederive where we looked up.
-    DeclContext *DC = (SS.isSet()
-                       ? computeDeclContext(SS, false)
-                       : BaseType->castAs<RecordType>()->getDecl());
+    DeclContext *DC = (SS.isSet() ? computeDeclContext(SS, false)
+                                  : BaseType->castAs<RecordType>()->getDecl());
 
     if (ExtraArgs) {
       ExprResult RetryExpr;
Index: clang/lib/AST/ExprCXX.cpp
===================================================================
--- clang/lib/AST/ExprCXX.cpp
+++ clang/lib/AST/ExprCXX.cpp
@@ -1443,11 +1443,11 @@
     SourceLocation OperatorLoc, NestedNameSpecifierLoc QualifierLoc,
     SourceLocation TemplateKWLoc, NamedDecl *FirstQualifierFoundInScope,
     DeclarationNameInfo MemberNameInfo,
-    const TemplateArgumentListInfo *TemplateArgs)
+    const TemplateArgumentListInfo *TemplateArgs, bool isMemberPack)
     : Expr(CXXDependentScopeMemberExprClass, Ctx.DependentTy, VK_LValue,
            OK_Ordinary),
       Base(Base), BaseType(BaseType), QualifierLoc(QualifierLoc),
-      MemberNameInfo(MemberNameInfo) {
+      MemberNameInfo(MemberNameInfo), isMemberPack(isMemberPack) {
   CXXDependentScopeMemberExprBits.IsArrow = IsArrow;
   CXXDependentScopeMemberExprBits.HasTemplateKWAndArgsInfo =
       (TemplateArgs != nullptr) || TemplateKWLoc.isValid();
@@ -1478,6 +1478,7 @@
       HasTemplateKWAndArgsInfo;
   CXXDependentScopeMemberExprBits.HasFirstQualifierFoundInScope =
       HasFirstQualifierFoundInScope;
+  isMemberPack = false;
 }
 
 CXXDependentScopeMemberExpr *CXXDependentScopeMemberExpr::Create(
@@ -1485,7 +1486,8 @@
     SourceLocation OperatorLoc, NestedNameSpecifierLoc QualifierLoc,
     SourceLocation TemplateKWLoc, NamedDecl *FirstQualifierFoundInScope,
     DeclarationNameInfo MemberNameInfo,
-    const TemplateArgumentListInfo *TemplateArgs) {
+    const TemplateArgumentListInfo *TemplateArgs,
+    bool isMemberPack) {
   bool HasTemplateKWAndArgsInfo =
       (TemplateArgs != nullptr) || TemplateKWLoc.isValid();
   unsigned NumTemplateArgs = TemplateArgs ? TemplateArgs->size() : 0;
@@ -1498,7 +1500,7 @@
   void *Mem = Ctx.Allocate(Size, alignof(CXXDependentScopeMemberExpr));
   return new (Mem) CXXDependentScopeMemberExpr(
       Ctx, Base, BaseType, IsArrow, OperatorLoc, QualifierLoc, TemplateKWLoc,
-      FirstQualifierFoundInScope, MemberNameInfo, TemplateArgs);
+      FirstQualifierFoundInScope, MemberNameInfo, TemplateArgs, isMemberPack);
 }
 
 CXXDependentScopeMemberExpr *CXXDependentScopeMemberExpr::CreateEmpty(
Index: clang/lib/AST/ComputeDependence.cpp
===================================================================
--- clang/lib/AST/ComputeDependence.cpp
+++ clang/lib/AST/ComputeDependence.cpp
@@ -826,6 +826,8 @@
   D |= getDependenceInExpr(E->getMemberNameInfo());
   for (const auto &A : E->template_arguments())
     D |= toExprDependence(A.getArgument().getDependence());
+  if (E->isMemberPackAccess())
+    D |= ExprDependence::UnexpandedPack;
   return D;
 }
 
Index: clang/include/clang/AST/ExprCXX.h
===================================================================
--- clang/include/clang/AST/ExprCXX.h
+++ clang/include/clang/AST/ExprCXX.h
@@ -3658,6 +3658,9 @@
   /// FIXME: could also be a template-id
   DeclarationNameInfo MemberNameInfo;
 
+  /// Whether the member is a member pack.
+  bool isMemberPack;
+
   // CXXDependentScopeMemberExpr is followed by several trailing objects,
   // some of which optional. They are in order:
   //
@@ -3693,14 +3696,12 @@
     return hasFirstQualifierFoundInScope();
   }
 
-  CXXDependentScopeMemberExpr(const ASTContext &Ctx, Expr *Base,
-                              QualType BaseType, bool IsArrow,
-                              SourceLocation OperatorLoc,
-                              NestedNameSpecifierLoc QualifierLoc,
-                              SourceLocation TemplateKWLoc,
-                              NamedDecl *FirstQualifierFoundInScope,
-                              DeclarationNameInfo MemberNameInfo,
-                              const TemplateArgumentListInfo *TemplateArgs);
+  CXXDependentScopeMemberExpr(
+      const ASTContext &Ctx, Expr *Base, QualType BaseType, bool IsArrow,
+      SourceLocation OperatorLoc, NestedNameSpecifierLoc QualifierLoc,
+      SourceLocation TemplateKWLoc, NamedDecl *FirstQualifierFoundInScope,
+      DeclarationNameInfo MemberNameInfo,
+      const TemplateArgumentListInfo *TemplateArgs, bool isMemberPack);
 
   CXXDependentScopeMemberExpr(EmptyShell Empty, bool HasTemplateKWAndArgsInfo,
                               bool HasFirstQualifierFoundInScope);
@@ -3711,7 +3712,8 @@
          SourceLocation OperatorLoc, NestedNameSpecifierLoc QualifierLoc,
          SourceLocation TemplateKWLoc, NamedDecl *FirstQualifierFoundInScope,
          DeclarationNameInfo MemberNameInfo,
-         const TemplateArgumentListInfo *TemplateArgs);
+         const TemplateArgumentListInfo *TemplateArgs,
+         bool isMemberPack = false);
 
   static CXXDependentScopeMemberExpr *
   CreateEmpty(const ASTContext &Ctx, bool HasTemplateKWAndArgsInfo,
@@ -3735,6 +3737,8 @@
 
   QualType getBaseType() const { return BaseType; }
 
+  bool isMemberPackAccess() const { return isMemberPack; }
+
   /// Determine whether this member expression used the '->'
   /// operator; otherwise, it used the '.' operator.
   bool isArrow() const { return CXXDependentScopeMemberExprBits.IsArrow; }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to