PR12057: Allow variadic template pack expansions to cross lambda boundaries.

Rather than adding a ContainsUnexpandedParameterPack bit to essentially every
AST node, we tunnel the bit directly up to the surrounding lambda expression
when we reach a context where an unexpanded pack can not normally appear.
Thus any statement or declaration within a lambda can now potentially contain
an unexpanded parameter pack.

llvm-svn: 160705
This commit is contained in:
Richard Smith 2012-07-25 03:56:55 +00:00
parent c42d243ace
commit 2589b9808e
12 changed files with 216 additions and 62 deletions

View File

@ -1254,7 +1254,8 @@ private:
ArrayRef<Expr *> CaptureInits,
ArrayRef<VarDecl *> ArrayIndexVars,
ArrayRef<unsigned> ArrayIndexStarts,
SourceLocation ClosingBrace);
SourceLocation ClosingBrace,
bool ContainsUnexpandedParameterPack);
/// \brief Construct an empty lambda expression.
LambdaExpr(EmptyShell Empty, unsigned NumCaptures, bool HasArrayIndexVars)
@ -1292,7 +1293,8 @@ public:
ArrayRef<Expr *> CaptureInits,
ArrayRef<VarDecl *> ArrayIndexVars,
ArrayRef<unsigned> ArrayIndexStarts,
SourceLocation ClosingBrace);
SourceLocation ClosingBrace,
bool ContainsUnexpandedParameterPack);
/// \brief Construct a new lambda expression that will be deserialized from
/// an external source.

View File

@ -344,6 +344,9 @@ public:
/// \brief Whether any of the capture expressions requires cleanups.
bool ExprNeedsCleanups;
/// \brief Whether the lambda contains an unexpanded parameter pack.
bool ContainsUnexpandedParameterPack;
/// \brief Variables used to index into by-copy array captures.
llvm::SmallVector<VarDecl *, 4> ArrayIndexVars;
@ -355,7 +358,7 @@ public:
CXXMethodDecl *CallOperator)
: CapturingScopeInfo(Diag, ImpCap_None), Lambda(Lambda),
CallOperator(CallOperator), NumExplicitCaptures(0), Mutable(false),
ExprNeedsCleanups(false)
ExprNeedsCleanups(false), ContainsUnexpandedParameterPack(false)
{
Kind = SK_Lambda;
}

View File

@ -5003,7 +5003,9 @@ public:
/// parameter packs.
///
/// \param Unexpanded the set of unexpanded parameter packs.
void DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
///
/// \returns true if an error occurred, false otherwise.
bool DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
UnexpandedParameterPackContext UPPC,
ArrayRef<UnexpandedParameterPack> Unexpanded);

View File

@ -372,8 +372,10 @@ namespace clang {
public:
TemplateDeclInstantiator(Sema &SemaRef, DeclContext *Owner,
const MultiLevelTemplateArgumentList &TemplateArgs)
: SemaRef(SemaRef), SubstIndex(SemaRef, -1), Owner(Owner),
TemplateArgs(TemplateArgs), LateAttrs(0), StartingScope(0) { }
: SemaRef(SemaRef),
SubstIndex(SemaRef, SemaRef.ArgumentPackSubstitutionIndex),
Owner(Owner), TemplateArgs(TemplateArgs), LateAttrs(0), StartingScope(0)
{ }
// FIXME: Once we get closer to completion, replace these manually-written
// declarations with automatically-generated ones from

View File

@ -790,10 +790,11 @@ LambdaExpr::LambdaExpr(QualType T,
ArrayRef<Expr *> CaptureInits,
ArrayRef<VarDecl *> ArrayIndexVars,
ArrayRef<unsigned> ArrayIndexStarts,
SourceLocation ClosingBrace)
SourceLocation ClosingBrace,
bool ContainsUnexpandedParameterPack)
: Expr(LambdaExprClass, T, VK_RValue, OK_Ordinary,
T->isDependentType(), T->isDependentType(), T->isDependentType(),
/*ContainsUnexpandedParameterPack=*/false),
ContainsUnexpandedParameterPack),
IntroducerRange(IntroducerRange),
NumCaptures(Captures.size()),
CaptureDefault(CaptureDefault),
@ -850,7 +851,8 @@ LambdaExpr *LambdaExpr::Create(ASTContext &Context,
ArrayRef<Expr *> CaptureInits,
ArrayRef<VarDecl *> ArrayIndexVars,
ArrayRef<unsigned> ArrayIndexStarts,
SourceLocation ClosingBrace) {
SourceLocation ClosingBrace,
bool ContainsUnexpandedParameterPack) {
// Determine the type of the expression (i.e., the type of the
// function object we're creating).
QualType T = Context.getTypeDeclType(Class);
@ -863,7 +865,7 @@ LambdaExpr *LambdaExpr::Create(ASTContext &Context,
return new (Mem) LambdaExpr(T, IntroducerRange, CaptureDefault,
Captures, ExplicitParams, ExplicitResultType,
CaptureInits, ArrayIndexVars, ArrayIndexStarts,
ClosingBrace);
ClosingBrace, ContainsUnexpandedParameterPack);
}
LambdaExpr *LambdaExpr::CreateDeserialized(ASTContext &C, unsigned NumCaptures,

View File

@ -375,6 +375,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
TypeSourceInfo *MethodTyInfo;
bool ExplicitParams = true;
bool ExplicitResultType = true;
bool ContainsUnexpandedParameterPack = false;
SourceLocation EndLoc;
llvm::ArrayRef<ParmVarDecl *> Params;
if (ParamInfo.getNumTypeObjects() == 0) {
@ -416,21 +417,8 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
Proto.getNumArgs());
// Check for unexpanded parameter packs in the method type.
// FIXME: We should allow unexpanded parameter packs here, but that would,
// in turn, make the lambda expression contain unexpanded parameter packs.
if (DiagnoseUnexpandedParameterPack(Intro.Range.getBegin(), MethodTyInfo,
UPPC_Lambda)) {
// Drop the parameters.
Params = llvm::ArrayRef<ParmVarDecl *>();
FunctionProtoType::ExtProtoInfo EPI;
EPI.HasTrailingReturn = false;
EPI.TypeQuals |= DeclSpec::TQ_const;
QualType MethodTy = Context.getFunctionType(Context.DependentTy,
/*Args=*/0, /*NumArgs=*/0, EPI);
MethodTyInfo = Context.getTrivialTypeSourceInfo(MethodTy);
ExplicitParams = false;
ExplicitResultType = false;
}
if (MethodTyInfo->getType()->containsUnexpandedParameterPack())
ContainsUnexpandedParameterPack = true;
}
CXXMethodDecl *Method = startLambdaDefinition(Class, Intro.Range,
@ -571,8 +559,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
// Just ignore the ellipsis.
}
} else if (Var->isParameterPack()) {
Diag(C->Loc, diag::err_lambda_unexpanded_pack);
continue;
ContainsUnexpandedParameterPack = true;
}
TryCaptureKind Kind = C->Kind == LCK_ByRef ? TryCapture_ExplicitByRef :
@ -581,6 +568,8 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
}
finishLambdaExplicitCaptures(LSI);
LSI->ContainsUnexpandedParameterPack = ContainsUnexpandedParameterPack;
// Add lambda parameters into scope.
addLambdaParameters(Method, CurScope);
@ -743,6 +732,7 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body,
bool ExplicitParams;
bool ExplicitResultType;
bool LambdaExprNeedsCleanups;
bool ContainsUnexpandedParameterPack;
llvm::SmallVector<VarDecl *, 4> ArrayIndexVars;
llvm::SmallVector<unsigned, 4> ArrayIndexStarts;
{
@ -753,6 +743,7 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body,
ExplicitParams = LSI->ExplicitParams;
ExplicitResultType = !LSI->HasImplicitReturnType;
LambdaExprNeedsCleanups = LSI->ExprNeedsCleanups;
ContainsUnexpandedParameterPack = LSI->ContainsUnexpandedParameterPack;
ArrayIndexVars.swap(LSI->ArrayIndexVars);
ArrayIndexStarts.swap(LSI->ArrayIndexStarts);
@ -867,7 +858,8 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body,
CaptureDefault, Captures,
ExplicitParams, ExplicitResultType,
CaptureInits, ArrayIndexVars,
ArrayIndexStarts, Body->getLocEnd());
ArrayIndexStarts, Body->getLocEnd(),
ContainsUnexpandedParameterPack);
// C++11 [expr.prim.lambda]p2:
// A lambda-expression shall not appear in an unevaluated operand

View File

@ -838,6 +838,19 @@ namespace {
return move(Result);
}
ExprResult TransformLambdaExpr(LambdaExpr *E) {
LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
return TreeTransform<TemplateInstantiator>::TransformLambdaExpr(E);
}
ExprResult TransformLambdaScope(LambdaExpr *E,
CXXMethodDecl *CallOperator) {
CallOperator->setInstantiationOfMemberFunction(E->getCallOperator(),
TSK_ImplicitInstantiation);
return TreeTransform<TemplateInstantiator>::
TransformLambdaScope(E, CallOperator);
}
private:
ExprResult transformNonTypeTemplateParmRef(NonTypeTemplateParmDecl *parm,
SourceLocation loc,

View File

@ -12,6 +12,7 @@
#include "clang/Sema/Sema.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/ParsedTemplate.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/Template.h"
#include "clang/AST/Expr.h"
@ -34,10 +35,12 @@ namespace {
SmallVectorImpl<UnexpandedParameterPack> &Unexpanded;
bool InLambda;
public:
explicit CollectUnexpandedParameterPacksVisitor(
SmallVectorImpl<UnexpandedParameterPack> &Unexpanded)
: Unexpanded(Unexpanded) { }
: Unexpanded(Unexpanded), InLambda(false) { }
bool shouldWalkTypesOfTypeLocs() const { return false; }
@ -107,17 +110,17 @@ namespace {
/// \brief Suppress traversal into statements and expressions that
/// do not contain unexpanded parameter packs.
bool TraverseStmt(Stmt *S) {
if (Expr *E = dyn_cast_or_null<Expr>(S))
if (E->containsUnexpandedParameterPack())
return inherited::TraverseStmt(E);
Expr *E = dyn_cast_or_null<Expr>(S);
if ((E && E->containsUnexpandedParameterPack()) || InLambda)
return inherited::TraverseStmt(S);
return true;
return true;
}
/// \brief Suppress traversal into types that do not contain
/// unexpanded parameter packs.
bool TraverseType(QualType T) {
if (!T.isNull() && T->containsUnexpandedParameterPack())
if ((!T.isNull() && T->containsUnexpandedParameterPack()) || InLambda)
return inherited::TraverseType(T);
return true;
@ -126,8 +129,9 @@ namespace {
/// \brief Suppress traversel into types with location information
/// that do not contain unexpanded parameter packs.
bool TraverseTypeLoc(TypeLoc TL) {
if (!TL.getType().isNull() &&
TL.getType()->containsUnexpandedParameterPack())
if ((!TL.getType().isNull() &&
TL.getType()->containsUnexpandedParameterPack()) ||
InLambda)
return inherited::TraverseTypeLoc(TL);
return true;
@ -136,10 +140,10 @@ namespace {
/// \brief Suppress traversal of non-parameter declarations, since
/// they cannot contain unexpanded parameter packs.
bool TraverseDecl(Decl *D) {
if (D && isa<ParmVarDecl>(D))
if ((D && isa<ParmVarDecl>(D)) || InLambda)
return inherited::TraverseDecl(D);
return true;
return true;
}
/// \brief Suppress traversal of template argument pack expansions.
@ -157,17 +161,57 @@ namespace {
return inherited::TraverseTemplateArgumentLoc(ArgLoc);
}
/// \brief Note whether we're traversing a lambda containing an unexpanded
/// parameter pack. In this case, the unexpanded pack can occur anywhere,
/// including all the places where we normally wouldn't look. Within a
/// lambda, we don't propagate the 'contains unexpanded parameter pack' bit
/// outside an expression.
bool TraverseLambdaExpr(LambdaExpr *Lambda) {
// The ContainsUnexpandedParameterPack bit on a lambda is always correct,
// even if it's contained within another lambda.
if (!Lambda->containsUnexpandedParameterPack())
return true;
bool WasInLambda = InLambda;
InLambda = true;
// If any capture names a function parameter pack, that pack is expanded
// when the lambda is expanded.
for (LambdaExpr::capture_iterator I = Lambda->capture_begin(),
E = Lambda->capture_end(); I != E; ++I)
if (VarDecl *VD = I->getCapturedVar())
if (VD->isParameterPack())
Unexpanded.push_back(std::make_pair(VD, I->getLocation()));
inherited::TraverseLambdaExpr(Lambda);
InLambda = WasInLambda;
return true;
}
};
}
/// \brief Diagnose all of the unexpanded parameter packs in the given
/// vector.
void
bool
Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
UnexpandedParameterPackContext UPPC,
ArrayRef<UnexpandedParameterPack> Unexpanded) {
if (Unexpanded.empty())
return;
return false;
// If we are within a lambda expression, that lambda contains an unexpanded
// parameter pack, and we are done.
// FIXME: Store 'Unexpanded' on the lambda so we don't need to recompute it
// later.
for (unsigned N = FunctionScopes.size(); N; --N) {
if (sema::LambdaScopeInfo *LSI =
dyn_cast<sema::LambdaScopeInfo>(FunctionScopes[N-1])) {
LSI->ContainsUnexpandedParameterPack = true;
return false;
}
}
SmallVector<SourceLocation, 4> Locations;
SmallVector<IdentifierInfo *, 4> Names;
@ -200,6 +244,7 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
for (unsigned I = 0, N = Locations.size(); I != N; ++I)
DB << SourceRange(Locations[I]);
return true;
}
bool Sema::DiagnoseUnexpandedParameterPack(SourceLocation Loc,
@ -215,8 +260,7 @@ bool Sema::DiagnoseUnexpandedParameterPack(SourceLocation Loc,
CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseTypeLoc(
T->getTypeLoc());
assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs");
DiagnoseUnexpandedParameterPacks(Loc, UPPC, Unexpanded);
return true;
return DiagnoseUnexpandedParameterPacks(Loc, UPPC, Unexpanded);
}
bool Sema::DiagnoseUnexpandedParameterPack(Expr *E,
@ -230,8 +274,7 @@ bool Sema::DiagnoseUnexpandedParameterPack(Expr *E,
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseStmt(E);
assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs");
DiagnoseUnexpandedParameterPacks(E->getLocStart(), UPPC, Unexpanded);
return true;
return DiagnoseUnexpandedParameterPacks(E->getLocStart(), UPPC, Unexpanded);
}
bool Sema::DiagnoseUnexpandedParameterPack(const CXXScopeSpec &SS,
@ -247,9 +290,8 @@ bool Sema::DiagnoseUnexpandedParameterPack(const CXXScopeSpec &SS,
CollectUnexpandedParameterPacksVisitor(Unexpanded)
.TraverseNestedNameSpecifier(SS.getScopeRep());
assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs");
DiagnoseUnexpandedParameterPacks(SS.getRange().getBegin(),
UPPC, Unexpanded);
return true;
return DiagnoseUnexpandedParameterPacks(SS.getRange().getBegin(),
UPPC, Unexpanded);
}
bool Sema::DiagnoseUnexpandedParameterPack(const DeclarationNameInfo &NameInfo,
@ -284,8 +326,7 @@ bool Sema::DiagnoseUnexpandedParameterPack(const DeclarationNameInfo &NameInfo,
CollectUnexpandedParameterPacksVisitor(Unexpanded)
.TraverseType(NameInfo.getName().getCXXNameType());
assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs");
DiagnoseUnexpandedParameterPacks(NameInfo.getLoc(), UPPC, Unexpanded);
return true;
return DiagnoseUnexpandedParameterPacks(NameInfo.getLoc(), UPPC, Unexpanded);
}
bool Sema::DiagnoseUnexpandedParameterPack(SourceLocation Loc,
@ -299,8 +340,7 @@ bool Sema::DiagnoseUnexpandedParameterPack(SourceLocation Loc,
CollectUnexpandedParameterPacksVisitor(Unexpanded)
.TraverseTemplateName(Template);
assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs");
DiagnoseUnexpandedParameterPacks(Loc, UPPC, Unexpanded);
return true;
return DiagnoseUnexpandedParameterPacks(Loc, UPPC, Unexpanded);
}
bool Sema::DiagnoseUnexpandedParameterPack(TemplateArgumentLoc Arg,
@ -313,8 +353,7 @@ bool Sema::DiagnoseUnexpandedParameterPack(TemplateArgumentLoc Arg,
CollectUnexpandedParameterPacksVisitor(Unexpanded)
.TraverseTemplateArgumentLoc(Arg);
assert(!Unexpanded.empty() && "Unable to find unexpanded parameter packs");
DiagnoseUnexpandedParameterPacks(Arg.getLocation(), UPPC, Unexpanded);
return true;
return DiagnoseUnexpandedParameterPacks(Arg.getLocation(), UPPC, Unexpanded);
}
void Sema::collectUnexpandedParameterPacks(TemplateArgument Arg,

View File

@ -571,6 +571,9 @@ public:
StmtResult TransformCompoundStmt(CompoundStmt *S, bool IsStmtExpr);
ExprResult TransformCXXNamedCastExpr(CXXNamedCastExpr *E);
/// \brief Transform the captures and body of a lambda expression.
ExprResult TransformLambdaScope(LambdaExpr *E, CXXMethodDecl *CallOperator);
#define STMT(Node, Parent) \
StmtResult Transform##Node(Node *S);
#define EXPR(Node, Parent) \
@ -7894,14 +7897,13 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
return ExprError();
// Transform lambda parameters.
bool Invalid = false;
llvm::SmallVector<QualType, 4> ParamTypes;
llvm::SmallVector<ParmVarDecl *, 4> Params;
if (getDerived().TransformFunctionTypeParams(E->getLocStart(),
E->getCallOperator()->param_begin(),
E->getCallOperator()->param_size(),
0, ParamTypes, &Params))
Invalid = true;
return ExprError();
// Build the call operator.
CXXMethodDecl *CallOperator
@ -7910,11 +7912,14 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
E->getCallOperator()->getLocEnd(),
Params);
getDerived().transformAttrs(E->getCallOperator(), CallOperator);
// FIXME: Instantiation-specific.
CallOperator->setInstantiationOfMemberFunction(E->getCallOperator(),
TSK_ImplicitInstantiation);
return getDerived().TransformLambdaScope(E, CallOperator);
}
template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformLambdaScope(LambdaExpr *E,
CXXMethodDecl *CallOperator) {
// Introduce the context of the call operator.
Sema::ContextRAII SavedContext(getSema(), CallOperator);
@ -7927,6 +7932,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
E->isMutable());
// Transform captures.
bool Invalid = false;
bool FinishedExplicitCaptures = false;
for (LambdaExpr::capture_iterator C = E->capture_begin(),
CEnd = E->capture_end();

View File

@ -89,6 +89,8 @@ namespace overloading {
namespace PR13117 {
struct A {
template<typename ... Args> static void f(Args...);
template<typename ... Args> static void f1()
{
(void)^(Args args) { // expected-error{{block contains unexpanded parameter pack 'Args'}}
@ -97,9 +99,24 @@ namespace PR13117 {
template<typename ... Args> static void f2()
{
(void)[](Args args) { // expected-error{{lambda contains unexpanded parameter pack 'Args'}}
// FIXME: Allow this.
f(
^(Args args) // expected-error{{block contains unexpanded parameter pack 'Args'}}
{ }
... // expected-error{{pack expansion does not contain any unexpanded parameter packs}}
);
}
template<typename ... Args> static void f3()
{
(void)[](Args args) { // expected-error{{expression contains unexpanded parameter pack 'Args'}}
};
}
template<typename ... Args> static void f4()
{
f([](Args args) { } ...);
}
};
void g() {

View File

@ -9,8 +9,8 @@ void print(T first, Ts... rest) {
}
template<typename... Ts>
void unsupported(Ts ...values) {
auto unsup = [values] {}; // expected-error{{unexpanded function parameter pack capture is unsupported}}
void unexpanded_capture(Ts ...values) {
auto unexp = [values] {}; // expected-error{{initializer contains unexpanded parameter pack 'values'}}
}
template<typename... Ts>

View File

@ -145,3 +145,79 @@ namespace ModifyingCapture {
};
}
}
namespace VariadicPackExpansion {
template<typename T, typename U> using Fst = T;
template<typename...Ts> bool g(Fst<bool, Ts> ...bools);
template<typename...Ts> bool f(Ts &&...ts) {
return g<Ts...>([&ts] {
if (!ts)
return false;
--ts;
return true;
} () ...);
}
void h() {
int a = 5, b = 2, c = 3;
while (f(a, b, c)) {
}
}
struct sink {
template<typename...Ts> sink(Ts &&...) {}
};
template<typename...Ts> void local_class() {
sink {
[] (Ts t) {
struct S : Ts {
void f(Ts t) {
Ts &that = *this;
that = t;
}
Ts g() { return *this; };
};
S s;
s.f(t);
return s;
} (Ts()).g() ...
};
};
struct X {}; struct Y {};
template void local_class<X, Y>();
template<typename...Ts> void nested(Ts ...ts) {
f(
// Each expansion of this lambda implicitly captures all of 'ts', because
// the inner lambda also expands 'ts'.
[&] {
return ts + [&] { return f(ts...); } ();
} () ...
);
}
template void nested(int, int, int);
template<typename...Ts> void nested2(Ts ...ts) { // expected-note 2{{here}}
// Capture all 'ts', use only one.
f([&ts...] { return ts; } ()...);
// Capture each 'ts', use it.
f([&ts] { return ts; } ()...);
// Capture all 'ts', use all of them.
f([&ts...] { return (int)f(ts...); } ());
// Capture each 'ts', use all of them. Ill-formed. In more detail:
//
// We instantiate two lambdas here; the first captures ts$0, the second
// captures ts$1. Both of them reference both ts parameters, so both are
// ill-formed because ts can't be implicitly captured.
//
// FIXME: This diagnostic does not explain what's happening. We should
// specify which 'ts' we're referring to in its diagnostic name. We should
// also say which slice of the pack expansion is being performed in the
// instantiation backtrace.
f([&ts] { return (int)f(ts...); } ()...); // \
// expected-error 2{{'ts' cannot be implicitly captured}} \
// expected-note 2{{lambda expression begins here}}
}
template void nested2(int); // ok
template void nested2(int, int); // expected-note {{in instantiation of}}
}