SlaterLatiao created this revision.
Herald added a project: All.
SlaterLatiao requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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,47 @@
 // 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 + ...);
+}
+
+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);
+  return 0;
+}
+
Index: clang/lib/Sema/TreeTransform.h
===================================================================
--- clang/lib/Sema/TreeTransform.h
+++ clang/lib/Sema/TreeTransform.h
@@ -4168,6 +4168,58 @@
     if (PackExpansionExpr *Expansion = dyn_cast<PackExpansionExpr>(Inputs[I])) {
       Expr *Pattern = Expansion->getPattern();
 
+      if (CXXDependentScopeMemberExpr *MemberExpr =
+              dyn_cast<CXXDependentScopeMemberExpr>(Pattern)) {
+        if (ArgChanged)
+          *ArgChanged = true;
+        assert(MemberExpr->isMemberPackAccess() &&
+               "trying to expand non-pack member access");
+        std::string UnExpanedNameStr =
+            MemberExpr->getMemberNameInfo().getName().getAsString();
+
+        ExprResult Base = getDerived().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) {
+          // Transform unexpanded field name and create a new member expression.
+          DeclarationName ExpandedName = &SemaRef.Context.Idents.get(
+              UnExpanedNameStr + "@" + std::to_string(Arg));
+          // 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(getSema(), Arg);
+          ExprResult Out = getDerived().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++;
+        }
+        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
@@ -3285,6 +3285,12 @@
               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 invisible to users so an
+          // `@` is added to the name.
+          std::string NewFieldName =
+              PackedField->getName().str() + "@" + std::to_string(Arg);
+          PackedField->setDeclName(&Context.Idents.get(NewFieldName));
           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,32 @@
                                          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 ElaboratedType *ET = dyn_cast<const ElaboratedType>(BaseType)) {
+    if (const TemplateSpecializationType *TST =
+            dyn_cast<TemplateSpecializationType>(ET->getNamedType())) {
+      auto *TD = TST->getTemplateName().getAsTemplateDecl();
+      assert(isa<ClassTemplateDecl>(TD) &&
+             "template decl in member access is not ClassTemplateDecl");
+      for (FieldDecl *Field :
+           cast<ClassTemplateDecl>(TD)->getTemplatedDecl()->fields()) {
+        if (Field->getDeclName() == NameInfo.getName()) {
+          if (const PackExpansionType *PET =
+                  dyn_cast<PackExpansionType>(Field->getType())) {
+            isMemberPack = true;
+          }
+          break;
+        }
+      }
+    }
+  }
+  // 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 +1015,17 @@
         << isa<CXXDestructorDecl>(FD);
 
   if (R.empty()) {
+    // The member is expaned 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