From 3fb9e4b89f72823f162096086f0f964e6dcf66d6 Mon Sep 17 00:00:00 2001 From: Douglas Gregor <dgregor@apple.com> Date: Tue, 4 Jan 2011 00:32:56 +0000 Subject: [PATCH] Implement pack expansion of base initializers, so that we can initialize those lovely mixins that come from pack expansions of base specifiers. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@122793 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclCXX.h | 27 ++++++-- include/clang/Basic/DiagnosticSemaKinds.td | 2 + include/clang/Sema/Sema.h | 6 +- lib/AST/DeclCXX.cpp | 11 ++-- lib/Parse/ParseDeclCXX.cpp | 11 +++- lib/Sema/SemaDeclCXX.cpp | 51 +++++++++++--- lib/Sema/SemaTemplateInstantiate.cpp | 2 +- lib/Sema/SemaTemplateInstantiateDecl.cpp | 66 ++++++++++++++++++- lib/Serialization/ASTReader.cpp | 14 ++-- test/CXX/temp/temp.decls/temp.variadic/p4.cpp | 32 ++++++++- 10 files changed, 189 insertions(+), 33 deletions(-) diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 0c3d27b86a..ff99a1cc27 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -1122,11 +1122,14 @@ public: /// @endcode class CXXBaseOrMemberInitializer { /// \brief Either the base class name (stored as a TypeSourceInfo*), an normal - /// field (FieldDecl) or an anonymous field (IndirectFieldDecl*) being initialized. - llvm::PointerUnion3<TypeSourceInfo *, FieldDecl *, IndirectFieldDecl *> BaseOrMember; + /// field (FieldDecl) or an anonymous field (IndirectFieldDecl*) being + /// initialized. + llvm::PointerUnion3<TypeSourceInfo *, FieldDecl *, IndirectFieldDecl *> + BaseOrMember; - /// \brief The source location for the field name. - SourceLocation MemberLocation; + /// \brief The source location for the field name or, for a base initializer + /// pack expansion, the location of the ellipsis. + SourceLocation MemberOrEllipsisLocation; /// \brief The argument used to initialize the base or member, which may /// end up constructing an object (when multiple arguments are involved). @@ -1168,7 +1171,8 @@ public: TypeSourceInfo *TInfo, bool IsVirtual, SourceLocation L, Expr *Init, - SourceLocation R); + SourceLocation R, + SourceLocation EllipsisLoc); /// CXXBaseOrMemberInitializer - Creates a new member initializer. explicit @@ -1213,6 +1217,17 @@ public: return BaseOrMember.is<IndirectFieldDecl*>(); } + /// \brief Determine whether this initializer is a pack expansion. + bool isPackExpansion() const { + return isBaseInitializer() && MemberOrEllipsisLocation.isValid(); + } + + // \brief For a pack expansion, returns the location of the ellipsis. + SourceLocation getEllipsisLoc() const { + assert(isPackExpansion() && "Initializer is not a pack expansion"); + return MemberOrEllipsisLocation; + } + /// If this is a base class initializer, returns the type of the /// base class with location information. Otherwise, returns an NULL /// type location. @@ -1261,7 +1276,7 @@ public: } SourceLocation getMemberLocation() const { - return MemberLocation; + return MemberOrEllipsisLocation; } /// \brief Determine the source location of the initializer. diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index c8d2678e88..ffef24269a 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1862,6 +1862,8 @@ def err_pack_expansion_without_parameter_packs : Error< def err_pack_expansion_length_conflict : Error< "pack expansion contains parameter packs %0 and %1 that have different " "lengths (%2 vs. %3)">; +def err_pack_expansion_member_init : Error< + "pack expansion for initialization of member %0">; def err_function_parameter_pack_without_parameter_packs : Error< "type %0 of function parameter pack does not contain any unexpanded " diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index fda0cf2c31..13b440c8ab 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2541,7 +2541,8 @@ public: SourceLocation IdLoc, SourceLocation LParenLoc, Expr **Args, unsigned NumArgs, - SourceLocation RParenLoc); + SourceLocation RParenLoc, + SourceLocation EllipsisLoc); MemInitResult BuildMemberInitializer(ValueDecl *Member, Expr **Args, unsigned NumArgs, SourceLocation IdLoc, @@ -2553,7 +2554,8 @@ public: Expr **Args, unsigned NumArgs, SourceLocation LParenLoc, SourceLocation RParenLoc, - CXXRecordDecl *ClassDecl); + CXXRecordDecl *ClassDecl, + SourceLocation EllipsisLoc); bool SetBaseOrMemberInitializers(CXXConstructorDecl *Constructor, CXXBaseOrMemberInitializer **Initializers, diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 3304ad9a29..13a25321d4 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -1002,8 +1002,9 @@ bool CXXMethodDecl::hasInlineBody() const { CXXBaseOrMemberInitializer:: CXXBaseOrMemberInitializer(ASTContext &Context, TypeSourceInfo *TInfo, bool IsVirtual, - SourceLocation L, Expr *Init, SourceLocation R) - : BaseOrMember(TInfo), Init(Init), + SourceLocation L, Expr *Init, SourceLocation R, + SourceLocation EllipsisLoc) + : BaseOrMember(TInfo), MemberOrEllipsisLocation(EllipsisLoc), Init(Init), LParenLoc(L), RParenLoc(R), IsVirtual(IsVirtual), IsWritten(false), SourceOrderOrNumArrayIndices(0) { @@ -1013,7 +1014,7 @@ CXXBaseOrMemberInitializer:: CXXBaseOrMemberInitializer(ASTContext &Context, FieldDecl *Member, SourceLocation MemberLoc, SourceLocation L, Expr *Init, SourceLocation R) - : BaseOrMember(Member), MemberLocation(MemberLoc), Init(Init), + : BaseOrMember(Member), MemberOrEllipsisLocation(MemberLoc), Init(Init), LParenLoc(L), RParenLoc(R), IsVirtual(false), IsWritten(false), SourceOrderOrNumArrayIndices(0) { @@ -1023,7 +1024,7 @@ CXXBaseOrMemberInitializer:: CXXBaseOrMemberInitializer(ASTContext &Context, IndirectFieldDecl *Member, SourceLocation MemberLoc, SourceLocation L, Expr *Init, SourceLocation R) - : BaseOrMember(Member), MemberLocation(MemberLoc), Init(Init), + : BaseOrMember(Member), MemberOrEllipsisLocation(MemberLoc), Init(Init), LParenLoc(L), RParenLoc(R), IsVirtual(false), IsWritten(false), SourceOrderOrNumArrayIndices(0) { @@ -1035,7 +1036,7 @@ CXXBaseOrMemberInitializer(ASTContext &Context, SourceLocation L, Expr *Init, SourceLocation R, VarDecl **Indices, unsigned NumIndices) - : BaseOrMember(Member), MemberLocation(MemberLoc), Init(Init), + : BaseOrMember(Member), MemberOrEllipsisLocation(MemberLoc), Init(Init), LParenLoc(L), RParenLoc(R), IsVirtual(false), IsWritten(false), SourceOrderOrNumArrayIndices(NumIndices) { diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index adbecdc4a9..bc3f977bfc 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -1739,8 +1739,8 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, /// ':' mem-initializer-list /// /// [C++] mem-initializer-list: -/// mem-initializer -/// mem-initializer , mem-initializer-list +/// mem-initializer ...[opt] +/// mem-initializer ...[opt] , mem-initializer-list void Parser::ParseConstructorInitializer(Decl *ConstructorDecl) { assert(Tok.is(tok::colon) && "Constructor initializer always starts with ':'"); @@ -1839,10 +1839,15 @@ Parser::MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) { SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc); + SourceLocation EllipsisLoc; + if (Tok.is(tok::ellipsis)) + EllipsisLoc = ConsumeToken(); + return Actions.ActOnMemInitializer(ConstructorDecl, getCurScope(), SS, II, TemplateTypeTy, IdLoc, LParenLoc, ArgExprs.take(), - ArgExprs.size(), RParenLoc); + ArgExprs.size(), RParenLoc, + EllipsisLoc); } /// ParseExceptionSpecification - Parse a C++ exception-specification diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 10d04fa9a3..e2067de964 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -1057,7 +1057,8 @@ Sema::ActOnMemInitializer(Decl *ConstructorD, SourceLocation IdLoc, SourceLocation LParenLoc, ExprTy **Args, unsigned NumArgs, - SourceLocation RParenLoc) { + SourceLocation RParenLoc, + SourceLocation EllipsisLoc) { if (!ConstructorD) return true; @@ -1093,15 +1094,26 @@ Sema::ActOnMemInitializer(Decl *ConstructorD, if (Result.first != Result.second) { Member = dyn_cast<FieldDecl>(*Result.first); - if (Member) + if (Member) { + if (EllipsisLoc.isValid()) + Diag(EllipsisLoc, diag::err_pack_expansion_member_init) + << MemberOrBase << SourceRange(IdLoc, RParenLoc); + return BuildMemberInitializer(Member, (Expr**)Args, NumArgs, IdLoc, LParenLoc, RParenLoc); + } + // Handle anonymous union case. if (IndirectFieldDecl* IndirectField - = dyn_cast<IndirectFieldDecl>(*Result.first)) + = dyn_cast<IndirectFieldDecl>(*Result.first)) { + if (EllipsisLoc.isValid()) + Diag(EllipsisLoc, diag::err_pack_expansion_member_init) + << MemberOrBase << SourceRange(IdLoc, RParenLoc); + return BuildMemberInitializer(IndirectField, (Expr**)Args, NumArgs, IdLoc, LParenLoc, RParenLoc); + } } } // It didn't name a member, so see if it names a class. @@ -1210,7 +1222,7 @@ Sema::ActOnMemInitializer(Decl *ConstructorD, TInfo = Context.getTrivialTypeSourceInfo(BaseType, IdLoc); return BuildBaseInitializer(BaseType, TInfo, (Expr **)Args, NumArgs, - LParenLoc, RParenLoc, ClassDecl); + LParenLoc, RParenLoc, ClassDecl, EllipsisLoc); } /// Checks an initializer expression for use of uninitialized fields, such as @@ -1383,7 +1395,8 @@ MemInitResult Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo, Expr **Args, unsigned NumArgs, SourceLocation LParenLoc, SourceLocation RParenLoc, - CXXRecordDecl *ClassDecl) { + CXXRecordDecl *ClassDecl, + SourceLocation EllipsisLoc) { bool HasDependentArg = false; for (unsigned i = 0; i < NumArgs; i++) HasDependentArg |= Args[i]->isTypeDependent(); @@ -1403,6 +1416,24 @@ Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo, // name that denotes that base class type. bool Dependent = BaseType->isDependentType() || HasDependentArg; + if (EllipsisLoc.isValid()) { + // This is a pack expansion. + if (!BaseType->containsUnexpandedParameterPack()) { + Diag(EllipsisLoc, diag::err_pack_expansion_without_parameter_packs) + << SourceRange(BaseLoc, RParenLoc); + + EllipsisLoc = SourceLocation(); + } + } else { + // Check for any unexpanded parameter packs. + if (DiagnoseUnexpandedParameterPack(BaseLoc, BaseTInfo, UPPC_Initializer)) + return true; + + for (unsigned I = 0; I != NumArgs; ++I) + if (DiagnoseUnexpandedParameterPack(Args[I])) + return true; + } + // Check for direct and virtual base classes. const CXXBaseSpecifier *DirectBaseSpec = 0; const CXXBaseSpecifier *VirtualBaseSpec = 0; @@ -1447,7 +1478,8 @@ Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo, /*IsVirtual=*/false, LParenLoc, BaseInit.takeAs<Expr>(), - RParenLoc); + RParenLoc, + EllipsisLoc); } // C++ [base.class.init]p2: @@ -1501,14 +1533,16 @@ Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo, BaseSpec->isVirtual(), LParenLoc, Init.takeAs<Expr>(), - RParenLoc); + RParenLoc, + EllipsisLoc); } return new (Context) CXXBaseOrMemberInitializer(Context, BaseTInfo, BaseSpec->isVirtual(), LParenLoc, BaseInit.takeAs<Expr>(), - RParenLoc); + RParenLoc, + EllipsisLoc); } /// ImplicitInitializerKind - How an implicit base or member initializer should @@ -1586,6 +1620,7 @@ BuildImplicitBaseInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, BaseSpec->isVirtual(), SourceLocation(), BaseInit.takeAs<Expr>(), + SourceLocation(), SourceLocation()); return false; diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 77d3e64a15..16500d4207 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -1169,8 +1169,8 @@ Sema::SubstBaseSpecifiers(CXXRecordDecl *Instantiation, Unexpanded.data(), Unexpanded.size(), TemplateArgs, ShouldExpand, NumExpansions)) { - continue; Invalid = true; + continue; } // If we should expand this pack expansion now, do so. diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 9db3a8cacf..60a942ac2f 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2362,6 +2362,69 @@ Sema::InstantiateMemInitializers(CXXConstructorDecl *New, SourceLocation LParenLoc, RParenLoc; ASTOwningVector<Expr*> NewArgs(*this); + SourceLocation EllipsisLoc; + + if (Init->isPackExpansion()) { + // This is a pack expansion. We should expand it now. + TypeLoc BaseTL = Init->getBaseClassInfo()->getTypeLoc(); + llvm::SmallVector<UnexpandedParameterPack, 2> Unexpanded; + collectUnexpandedParameterPacks(BaseTL, Unexpanded); + bool ShouldExpand = false; + unsigned NumExpansions = 0; + if (CheckParameterPacksForExpansion(Init->getEllipsisLoc(), + BaseTL.getSourceRange(), + Unexpanded.data(), + Unexpanded.size(), + TemplateArgs, ShouldExpand, + NumExpansions)) { + AnyErrors = true; + New->setInvalidDecl(); + continue; + } + assert(ShouldExpand && "Partial instantiation of base initializer?"); + + // Loop over all of the arguments in the argument pack(s), + for (unsigned I = 0; I != NumExpansions; ++I) { + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(*this, I); + + // Instantiate the initializer. + if (InstantiateInitializer(*this, Init->getInit(), TemplateArgs, + LParenLoc, NewArgs, RParenLoc)) { + AnyErrors = true; + break; + } + + // Instantiate the base type. + TypeSourceInfo *BaseTInfo = SubstType(Init->getBaseClassInfo(), + TemplateArgs, + Init->getSourceLocation(), + New->getDeclName()); + if (!BaseTInfo) { + AnyErrors = true; + break; + } + + // Build the initializer. + MemInitResult NewInit = BuildBaseInitializer(BaseTInfo->getType(), + BaseTInfo, + (Expr **)NewArgs.data(), + NewArgs.size(), + Init->getLParenLoc(), + Init->getRParenLoc(), + New->getParent(), + SourceLocation()); + if (NewInit.isInvalid()) { + AnyErrors = true; + break; + } + + NewInits.push_back(NewInit.get()); + NewArgs.clear(); + } + + continue; + } + // Instantiate the initializer. if (InstantiateInitializer(*this, Init->getInit(), TemplateArgs, LParenLoc, NewArgs, RParenLoc)) { @@ -2386,7 +2449,8 @@ Sema::InstantiateMemInitializers(CXXConstructorDecl *New, NewArgs.size(), Init->getLParenLoc(), Init->getRParenLoc(), - New->getParent()); + New->getParent(), + EllipsisLoc); } else if (Init->isMemberInitializer()) { FieldDecl *Member = cast<FieldDecl>(FindInstantiatedDecl( Init->getMemberLocation(), diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index dd8807fb00..f3320a0415 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -4325,7 +4325,7 @@ ASTReader::ReadCXXBaseOrMemberInitializers(PerFileData &F, else Member = cast<FieldDecl>(GetDecl(Record[Idx++])); } - SourceLocation MemberLoc = ReadSourceLocation(F, Record, Idx); + SourceLocation MemberOrEllipsisLoc = ReadSourceLocation(F, Record, Idx); Expr *Init = ReadExpr(F); SourceLocation LParenLoc = ReadSourceLocation(F, Record, Idx); SourceLocation RParenLoc = ReadSourceLocation(F, Record, Idx); @@ -4345,18 +4345,22 @@ ASTReader::ReadCXXBaseOrMemberInitializers(PerFileData &F, if (IsBaseInitializer) { BOMInit = new (C) CXXBaseOrMemberInitializer(C, BaseClassInfo, IsBaseVirtual, LParenLoc, - Init, RParenLoc); + Init, RParenLoc, + MemberOrEllipsisLoc); } else if (IsWritten) { if (Member) - BOMInit = new (C) CXXBaseOrMemberInitializer(C, Member, MemberLoc, + BOMInit = new (C) CXXBaseOrMemberInitializer(C, Member, + MemberOrEllipsisLoc, LParenLoc, Init, RParenLoc); else BOMInit = new (C) CXXBaseOrMemberInitializer(C, IndirectMember, - MemberLoc, LParenLoc, + MemberOrEllipsisLoc, + LParenLoc, Init, RParenLoc); } else { - BOMInit = CXXBaseOrMemberInitializer::Create(C, Member, MemberLoc, + BOMInit = CXXBaseOrMemberInitializer::Create(C, Member, + MemberOrEllipsisLoc, LParenLoc, Init, RParenLoc, Indices.data(), Indices.size()); diff --git a/test/CXX/temp/temp.decls/temp.variadic/p4.cpp b/test/CXX/temp/temp.decls/temp.variadic/p4.cpp index 49f6993b39..7e6a6b0f0d 100644 --- a/test/CXX/temp/temp.decls/temp.variadic/p4.cpp +++ b/test/CXX/temp/temp.decls/temp.variadic/p4.cpp @@ -36,9 +36,14 @@ template void initializer_list_expansion<1, 2, 3, 4, 5, 6>(); // expected-note{{ // In a base-specifier-list (Clause 10); the pattern is a base-specifier. template<typename ...Mixins> -struct HasMixins : public Mixins... { }; +struct HasMixins : public Mixins... { + HasMixins(); + HasMixins(const HasMixins&); + HasMixins(int i); +}; -struct A { }; +struct A { }; // expected-note{{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'int' to 'const A' for 1st argument}} \ +// expected-note{{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} struct B { }; struct C { }; struct D { }; @@ -52,6 +57,29 @@ HasMixins<> *checkNone = new HasMixins<>; template<typename Mixins> struct BrokenMixins : public Mixins... { }; // expected-error{{pack expansion does not contain any unexpanded parameter packs}} +// In a mem-initializer-list (12.6.2); the pattern is a mem-initializer. +template<typename ...Mixins> +HasMixins<Mixins...>::HasMixins(): Mixins()... { } + +template<typename ...Mixins> +HasMixins<Mixins...>::HasMixins(const HasMixins &other): Mixins(other)... { } + +template<typename ...Mixins> +HasMixins<Mixins...>::HasMixins(int i): Mixins(i)... { } // expected-error{{no matching constructor for initialization of 'A'}} + +void test_has_mixins() { + HasMixins<A, B> ab; + HasMixins<A, B> ab2 = ab; + HasMixins<A, B> ab3(17); // expected-note{{in instantiation of member function 'HasMixins<A, B>::HasMixins' requested here}} +} + +template<typename T> +struct X { + T member; + + X() : member()... { } // expected-error{{pack expansion for initialization of member 'member'}} +}; + // In a template-argument-list (14.3); the pattern is a template-argument. template<typename ...Types> struct tuple_of_refs { -- 2.40.0