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