PR13811: Add a FunctionParmPackExpr node to handle references to function

parameter packs where the reference is not being expanded but the pack has
been. Previously, Clang would segfault in such cases.

llvm-svn: 163672
This commit is contained in:
Richard Smith 2012-09-12 00:56:43 +00:00
parent b28179bb80
commit b15fe3a5e4
23 changed files with 296 additions and 8 deletions

View File

@ -3616,6 +3616,73 @@ public:
child_range children() { return child_range(); }
};
/// \brief Represents a reference to a function parameter pack that has been
/// substituted but not yet expanded.
///
/// When a pack expansion contains multiple parameter packs at different levels,
/// this node is used to represent a function parameter pack at an outer level
/// which we have already substituted to refer to expanded parameters, but where
/// the containing pack expansion cannot yet be expanded.
///
/// \code
/// template<typename...Ts> struct S {
/// template<typename...Us> auto f(Ts ...ts) -> decltype(g(Us(ts)...));
/// };
/// template struct S<int, int>;
/// \endcode
class FunctionParmPackExpr : public Expr {
/// \brief The function parameter pack which was referenced.
ParmVarDecl *ParamPack;
/// \brief The location of the function parameter pack reference.
SourceLocation NameLoc;
/// \brief The number of expansions of this pack.
unsigned NumParameters;
FunctionParmPackExpr(QualType T, ParmVarDecl *ParamPack,
SourceLocation NameLoc, unsigned NumParams,
Decl * const *Params);
friend class ASTReader;
friend class ASTStmtReader;
public:
static FunctionParmPackExpr *Create(ASTContext &Context, QualType T,
ParmVarDecl *ParamPack,
SourceLocation NameLoc,
llvm::ArrayRef<Decl*> Params);
static FunctionParmPackExpr *CreateEmpty(ASTContext &Context,
unsigned NumParams);
/// \brief Get the parameter pack which this expression refers to.
ParmVarDecl *getParameterPack() const { return ParamPack; }
/// \brief Get the location of the parameter pack.
SourceLocation getParameterPackLocation() const { return NameLoc; }
/// \brief Iterators over the parameters which the parameter pack expanded
/// into.
typedef ParmVarDecl * const *iterator;
iterator begin() const { return reinterpret_cast<iterator>(this+1); }
iterator end() const { return begin() + NumParameters; }
/// \brief Get the number of parameters in this parameter pack.
unsigned getNumExpansions() const { return NumParameters; }
/// \brief Get an expansion of the parameter pack by index.
ParmVarDecl *getExpansion(unsigned I) const { return begin()[I]; }
SourceRange getSourceRange() const LLVM_READONLY { return NameLoc; }
static bool classof(const Stmt *T) {
return T->getStmtClass() == FunctionParmPackExprClass;
}
static bool classof(const FunctionParmPackExpr *) { return true; }
child_range children() { return child_range(); }
};
/// \brief Represents a prvalue temporary that written into memory so that
/// a reference can bind to it.
///

View File

@ -2221,6 +2221,7 @@ DEF_TRAVERSE_STMT(PackExpansionExpr, { })
DEF_TRAVERSE_STMT(SizeOfPackExpr, { })
DEF_TRAVERSE_STMT(SubstNonTypeTemplateParmPackExpr, { })
DEF_TRAVERSE_STMT(SubstNonTypeTemplateParmExpr, { })
DEF_TRAVERSE_STMT(FunctionParmPackExpr, { })
DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, { })
DEF_TRAVERSE_STMT(AtomicExpr, { })

View File

@ -131,6 +131,7 @@ def PackExpansionExpr : DStmt<Expr>;
def SizeOfPackExpr : DStmt<Expr>;
def SubstNonTypeTemplateParmExpr : DStmt<Expr>;
def SubstNonTypeTemplateParmPackExpr : DStmt<Expr>;
def FunctionParmPackExpr : DStmt<Expr>;
def MaterializeTemporaryExpr : DStmt<Expr>;
def LambdaExpr : DStmt<Expr>;

View File

@ -1193,6 +1193,7 @@ namespace clang {
EXPR_SIZEOF_PACK, // SizeOfPackExpr
EXPR_SUBST_NON_TYPE_TEMPLATE_PARM, // SubstNonTypeTemplateParmExpr
EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK,// SubstNonTypeTemplateParmPackExpr
EXPR_FUNCTION_PARM_PACK, // FunctionParmPackExpr
EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr
// CUDA

View File

@ -2646,6 +2646,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx) const {
case UnresolvedMemberExprClass:
case PackExpansionExprClass:
case SubstNonTypeTemplateParmPackExprClass:
case FunctionParmPackExprClass:
llvm_unreachable("shouldn't see dependent / unresolved nodes here");
case DeclRefExprClass:

View File

@ -1299,6 +1299,34 @@ TemplateArgument SubstNonTypeTemplateParmPackExpr::getArgumentPack() const {
return TemplateArgument(Arguments, NumArguments);
}
FunctionParmPackExpr::FunctionParmPackExpr(QualType T, ParmVarDecl *ParamPack,
SourceLocation NameLoc,
unsigned NumParams,
Decl * const *Params)
: Expr(FunctionParmPackExprClass, T, VK_LValue, OK_Ordinary,
true, true, true, true),
ParamPack(ParamPack), NameLoc(NameLoc), NumParameters(NumParams) {
if (Params)
std::uninitialized_copy(Params, Params + NumParams,
reinterpret_cast<Decl**>(this+1));
}
FunctionParmPackExpr *
FunctionParmPackExpr::Create(ASTContext &Context, QualType T,
ParmVarDecl *ParamPack, SourceLocation NameLoc,
llvm::ArrayRef<Decl*> Params) {
return new (Context.Allocate(sizeof(FunctionParmPackExpr) +
sizeof(ParmVarDecl*) * Params.size()))
FunctionParmPackExpr(T, ParamPack, NameLoc, Params.size(), Params.data());
}
FunctionParmPackExpr *
FunctionParmPackExpr::CreateEmpty(ASTContext &Context, unsigned NumParams) {
return new (Context.Allocate(sizeof(FunctionParmPackExpr) +
sizeof(ParmVarDecl*) * NumParams))
FunctionParmPackExpr(QualType(), 0, SourceLocation(), 0, 0);
}
TypeTraitExpr::TypeTraitExpr(QualType T, SourceLocation Loc, TypeTrait Kind,
ArrayRef<TypeSourceInfo *> Args,
SourceLocation RParenLoc,

View File

@ -134,6 +134,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
// ObjC instance variables are lvalues
// FIXME: ObjC++0x might have different rules
case Expr::ObjCIvarRefExprClass:
case Expr::FunctionParmPackExprClass:
return Cl::CL_LValue;
// C99 6.5.2.5p5 says that compound literals are lvalues.

View File

@ -6480,6 +6480,7 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) {
case Expr::OpaqueValueExprClass:
case Expr::PackExpansionExprClass:
case Expr::SubstNonTypeTemplateParmPackExprClass:
case Expr::FunctionParmPackExprClass:
case Expr::AsTypeExprClass:
case Expr::ObjCIndirectCopyRestoreExprClass:
case Expr::MaterializeTemporaryExprClass:

View File

@ -2809,7 +2809,15 @@ recurse:
// };
Out << "_SUBSTPACK_";
break;
case Expr::FunctionParmPackExprClass: {
// FIXME: not clear how to mangle this!
const FunctionParmPackExpr *FPPE = cast<FunctionParmPackExpr>(E);
Out << "v110_SUBSTPACK";
mangleFunctionParam(FPPE->getParameterPack());
break;
}
case Expr::DependentScopeDeclRefExprClass: {
const DependentScopeDeclRefExpr *DRE = cast<DependentScopeDeclRefExpr>(E);
mangleUnresolvedName(DRE->getQualifier(), 0, DRE->getDeclName(), Arity);

View File

@ -1647,6 +1647,10 @@ void StmtPrinter::VisitSubstNonTypeTemplateParmExpr(
Visit(Node->getReplacement());
}
void StmtPrinter::VisitFunctionParmPackExpr(FunctionParmPackExpr *E) {
OS << *E->getParameterPack();
}
void StmtPrinter::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *Node){
PrintExpr(Node->GetTemporaryExpr());
}

View File

@ -973,6 +973,14 @@ void StmtProfiler::VisitSubstNonTypeTemplateParmExpr(
Visit(E->getReplacement());
}
void StmtProfiler::VisitFunctionParmPackExpr(const FunctionParmPackExpr *S) {
VisitExpr(S);
VisitDecl(S->getParameterPack());
ID.AddInteger(S->getNumExpansions());
for (FunctionParmPackExpr::iterator I = S->begin(), E = S->end(); I != E; ++I)
VisitDecl(*I);
}
void StmtProfiler::VisitMaterializeTemporaryExpr(
const MaterializeTemporaryExpr *S) {
VisitExpr(S);

View File

@ -1029,6 +1029,7 @@ CanThrowResult Sema::canThrow(const Expr *E) {
case Expr::PseudoObjectExprClass:
case Expr::SubstNonTypeTemplateParmExprClass:
case Expr::SubstNonTypeTemplateParmPackExprClass:
case Expr::FunctionParmPackExprClass:
case Expr::UnaryExprOrTypeTraitExprClass:
case Expr::UnresolvedLookupExprClass:
case Expr::UnresolvedMemberExprClass:

View File

@ -802,11 +802,24 @@ namespace {
ExprResult TransformPredefinedExpr(PredefinedExpr *E);
ExprResult TransformDeclRefExpr(DeclRefExpr *E);
ExprResult TransformCXXDefaultArgExpr(CXXDefaultArgExpr *E);
ExprResult TransformTemplateParmRefExpr(DeclRefExpr *E,
NonTypeTemplateParmDecl *D);
ExprResult TransformSubstNonTypeTemplateParmPackExpr(
SubstNonTypeTemplateParmPackExpr *E);
/// \brief Rebuild a DeclRefExpr for a ParmVarDecl reference.
ExprResult RebuildParmVarDeclRefExpr(ParmVarDecl *PD, SourceLocation Loc);
/// \brief Transform a reference to a function parameter pack.
ExprResult TransformFunctionParmPackRefExpr(DeclRefExpr *E,
ParmVarDecl *PD);
/// \brief Transform a FunctionParmPackExpr which was built when we couldn't
/// expand a function parameter pack reference which refers to an expanded
/// pack.
ExprResult TransformFunctionParmPackExpr(FunctionParmPackExpr *E);
QualType TransformFunctionProtoType(TypeLocBuilder &TLB,
FunctionProtoTypeLoc TL);
QualType TransformFunctionProtoType(TypeLocBuilder &TLB,
@ -1229,9 +1242,82 @@ TemplateInstantiator::TransformSubstNonTypeTemplateParmPackExpr(
Arg);
}
ExprResult
TemplateInstantiator::RebuildParmVarDeclRefExpr(ParmVarDecl *PD,
SourceLocation Loc) {
DeclarationNameInfo NameInfo(PD->getDeclName(), Loc);
return getSema().BuildDeclarationNameExpr(CXXScopeSpec(), NameInfo, PD);
}
ExprResult
TemplateInstantiator::TransformFunctionParmPackExpr(FunctionParmPackExpr *E) {
if (getSema().ArgumentPackSubstitutionIndex != -1) {
// We can expand this parameter pack now.
ParmVarDecl *D = E->getExpansion(getSema().ArgumentPackSubstitutionIndex);
ValueDecl *VD = cast_or_null<ValueDecl>(TransformDecl(E->getExprLoc(), D));
if (!VD)
return ExprError();
return RebuildParmVarDeclRefExpr(cast<ParmVarDecl>(VD), E->getExprLoc());
}
QualType T = TransformType(E->getType());
if (T.isNull())
return ExprError();
// Transform each of the parameter expansions into the corresponding
// parameters in the instantiation of the function decl.
llvm::SmallVector<Decl*, 8> Parms;
Parms.reserve(E->getNumExpansions());
for (FunctionParmPackExpr::iterator I = E->begin(), End = E->end();
I != End; ++I) {
ParmVarDecl *D =
cast_or_null<ParmVarDecl>(TransformDecl(E->getExprLoc(), *I));
if (!D)
return ExprError();
Parms.push_back(D);
}
return FunctionParmPackExpr::Create(getSema().Context, T,
E->getParameterPack(),
E->getParameterPackLocation(), Parms);
}
ExprResult
TemplateInstantiator::TransformFunctionParmPackRefExpr(DeclRefExpr *E,
ParmVarDecl *PD) {
typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
llvm::PointerUnion<Decl *, DeclArgumentPack *> *Found
= getSema().CurrentInstantiationScope->findInstantiationOf(PD);
assert(Found && "no instantiation for parameter pack");
Decl *TransformedDecl;
if (DeclArgumentPack *Pack = Found->dyn_cast<DeclArgumentPack *>()) {
// If this is a reference to a function parameter pack which we can substitute
// but can't yet expand, build a FunctionParmPackExpr for it.
if (getSema().ArgumentPackSubstitutionIndex == -1) {
QualType T = TransformType(E->getType());
if (T.isNull())
return ExprError();
return FunctionParmPackExpr::Create(getSema().Context, T, PD,
E->getExprLoc(), *Pack);
}
TransformedDecl = (*Pack)[getSema().ArgumentPackSubstitutionIndex];
} else {
TransformedDecl = Found->get<Decl*>();
}
// We have either an unexpanded pack or a specific expansion.
return RebuildParmVarDeclRefExpr(cast<ParmVarDecl>(TransformedDecl),
E->getExprLoc());
}
ExprResult
TemplateInstantiator::TransformDeclRefExpr(DeclRefExpr *E) {
NamedDecl *D = E->getDecl();
// Handle references to non-type template parameters and non-type template
// parameter packs.
if (NonTypeTemplateParmDecl *NTTP = dyn_cast<NonTypeTemplateParmDecl>(D)) {
if (NTTP->getDepth() < TemplateArgs.getNumLevels())
return TransformTemplateParmRefExpr(E, NTTP);
@ -1240,6 +1326,11 @@ TemplateInstantiator::TransformDeclRefExpr(DeclRefExpr *E) {
// FindInstantiatedDecl will find it in the local instantiation scope.
}
// Handle references to function parameter packs.
if (ParmVarDecl *PD = dyn_cast<ParmVarDecl>(D))
if (PD->isParameterPack())
return TransformFunctionParmPackRefExpr(E, PD);
return TreeTransform<TemplateInstantiator>::TransformDeclRefExpr(E);
}

View File

@ -3397,7 +3397,8 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
if (Decl *FD = Found->dyn_cast<Decl *>())
return cast<NamedDecl>(FD);
unsigned PackIdx = ArgumentPackSubstitutionIndex;
int PackIdx = ArgumentPackSubstitutionIndex;
assert(PackIdx != -1 && "found declaration pack but not pack expanding");
return cast<NamedDecl>((*Found->get<DeclArgumentPack *>())[PackIdx]);
}

View File

@ -8319,6 +8319,13 @@ TreeTransform<Derived>::TransformSubstNonTypeTemplateParmExpr(
return SemaRef.Owned(E);
}
template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformFunctionParmPackExpr(FunctionParmPackExpr *E) {
// Default behavior is to do nothing with this transformation.
return SemaRef.Owned(E);
}
template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformMaterializeTemporaryExpr(

View File

@ -1468,6 +1468,16 @@ void ASTStmtReader::VisitSubstNonTypeTemplateParmPackExpr(
E->NameLoc = ReadSourceLocation(Record, Idx);
}
void ASTStmtReader::VisitFunctionParmPackExpr(FunctionParmPackExpr *E) {
VisitExpr(E);
E->NumParameters = Record[Idx++];
E->ParamPack = ReadDeclAs<ParmVarDecl>(Record, Idx);
E->NameLoc = ReadSourceLocation(Record, Idx);
ParmVarDecl **Parms = reinterpret_cast<ParmVarDecl**>(E+1);
for (unsigned i = 0, n = E->NumParameters; i != n; ++i)
Parms[i] = ReadDeclAs<ParmVarDecl>(Record, Idx);
}
void ASTStmtReader::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E) {
VisitExpr(E);
E->Temporary = Reader.ReadSubExpr();
@ -2183,6 +2193,11 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
case EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK:
S = new (Context) SubstNonTypeTemplateParmPackExpr(Empty);
break;
case EXPR_FUNCTION_PARM_PACK:
S = FunctionParmPackExpr::CreateEmpty(Context,
Record[ASTStmtReader::NumExprFields]);
break;
case EXPR_MATERIALIZE_TEMPORARY:
S = new (Context) MaterializeTemporaryExpr(Empty);

View File

@ -1481,6 +1481,17 @@ void ASTStmtWriter::VisitSubstNonTypeTemplateParmPackExpr(
Code = serialization::EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK;
}
void ASTStmtWriter::VisitFunctionParmPackExpr(FunctionParmPackExpr *E) {
VisitExpr(E);
Record.push_back(E->getNumExpansions());
Writer.AddDeclRef(E->getParameterPack(), Record);
Writer.AddSourceLocation(E->getParameterPackLocation(), Record);
for (FunctionParmPackExpr::iterator I = E->begin(), End = E->end();
I != End; ++I)
Writer.AddDeclRef(*I, Record);
Code = serialization::EXPR_FUNCTION_PARM_PACK;
}
void ASTStmtWriter::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E) {
VisitExpr(E);
Writer.AddStmt(E->Temporary);

View File

@ -526,6 +526,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::CXXNoexceptExprClass:
case Stmt::PackExpansionExprClass:
case Stmt::SubstNonTypeTemplateParmPackExprClass:
case Stmt::FunctionParmPackExprClass:
case Stmt::SEHTryStmtClass:
case Stmt::SEHExceptStmtClass:
case Stmt::LambdaExprClass:

View File

@ -187,6 +187,30 @@ namespace PacksAtDifferentLevels {
add_pointer<float>,
add_const<double>>>::value == 0? 1 : -1];
namespace PR13811 {
constexpr int g(int n, int m) { return n * 10 + m; }
template<typename...A>
struct X6 {
template<typename...B>
constexpr auto f1(A ...a) -> decltype(g(A(a + B())...)) { return g(A(a + B())...); }
template<typename...B>
constexpr auto f2(A ...a, B ...b) -> decltype(g((&a)[b] ...)) { return g((&a)[b] ...); } // expected-note {{past-the-end}}
template<typename...B> struct Inner {
template<typename...C>
constexpr auto f(A ...a, B ...b, C ...c) -> decltype(g(a+b+c...)) { return g(a+b+c...); }
};
};
struct A { constexpr operator int() { return 2; } };
struct B { constexpr operator int() { return 1; } };
static_assert(X6<unsigned char, int>().f1<A, B>(255, 1) == 12, "");
static_assert(X6<int, int>().f2(3, 4, 0, 0) == 34, "");
static_assert(X6<int, int>().f2(3, 4, 0, 1) == 34, ""); // expected-error {{constant expression}} expected-note {{in call}}
static_assert(X6<int, int>::Inner<int, int>().f(1, 2, 3, 4, 5, 6) == 102, "");
}
}
namespace ExpandingNonTypeTemplateParameters {

View File

@ -1,11 +1,11 @@
// Test this without pch.
// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -include %S/cxx-templates.h -verify %s -ast-dump -o -
// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -include %S/cxx-templates.h %s -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fexceptions -include %S/cxx-templates.h -verify %s -ast-dump -o -
// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fexceptions -include %S/cxx-templates.h %s -emit-llvm -o - | FileCheck %s
// Test with pch.
// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -x c++-header -emit-pch -o %t %S/cxx-templates.h
// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -include-pch %t -verify %s -ast-dump -o -
// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -include-pch %t %s -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fexceptions -x c++-header -emit-pch -o %t %S/cxx-templates.h
// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fexceptions -include-pch %t -verify %s -ast-dump -o -
// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fexceptions -include-pch %t %s -emit-llvm -o - | FileCheck %s
// CHECK: define weak_odr void @_ZN2S4IiE1mEv
// CHECK: define linkonce_odr void @_ZN2S3IiE1mEv
@ -68,3 +68,12 @@ Foo< D >& Foo< D >::operator=( const Foo& other )
{
return *this;
}
namespace TestNestedExpansion {
struct Int {
Int(int);
friend Int operator+(Int, Int);
};
Int &g(Int, int, double);
Int &test = NestedExpansion<char, char, char>().f(0, 1, 2, Int(3), 4, 5.0);
}

View File

@ -215,3 +215,8 @@ class Foo : protected T
public:
Foo& operator=( const Foo& other );
};
template<typename...A> struct NestedExpansion {
template<typename...B> auto f(A...a, B...b) -> decltype(g(a + b...));
};
template struct NestedExpansion<char, char, char>;

View File

@ -432,6 +432,7 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, CXTranslationUnit TU,
case Stmt::DependentScopeDeclRefExprClass:
case Stmt::SubstNonTypeTemplateParmExprClass:
case Stmt::SubstNonTypeTemplateParmPackExprClass:
case Stmt::FunctionParmPackExprClass:
case Stmt::UnresolvedLookupExprClass:
K = CXCursor_DeclRefExpr;
break;

View File

@ -2141,6 +2141,7 @@ DEF_TRAVERSE_STMT(PackExpansionExpr, { })
DEF_TRAVERSE_STMT(SizeOfPackExpr, { })
DEF_TRAVERSE_STMT(SubstNonTypeTemplateParmPackExpr, { })
DEF_TRAVERSE_STMT(SubstNonTypeTemplateParmExpr, { })
DEF_TRAVERSE_STMT(FunctionParmPackExpr, { })
DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, { })
DEF_TRAVERSE_STMT(AtomicExpr, { })